MessageAdapter.java

  1package eu.siacs.conversations.ui.adapter;
  2
  3import java.util.HashMap;
  4import java.util.List;
  5
  6import eu.siacs.conversations.R;
  7import eu.siacs.conversations.entities.Contact;
  8import eu.siacs.conversations.entities.Conversation;
  9import eu.siacs.conversations.entities.Message;
 10import eu.siacs.conversations.services.ImageProvider;
 11import eu.siacs.conversations.ui.ConversationActivity;
 12import eu.siacs.conversations.utils.UIHelper;
 13import eu.siacs.conversations.xmpp.jingle.JingleConnection;
 14import android.content.Context;
 15import android.content.Intent;
 16import android.graphics.Bitmap;
 17import android.graphics.Typeface;
 18import android.util.DisplayMetrics;
 19import android.view.View;
 20import android.view.ViewGroup;
 21import android.view.View.OnClickListener;
 22import android.view.View.OnLongClickListener;
 23import android.widget.ArrayAdapter;
 24import android.widget.Button;
 25import android.widget.ImageView;
 26import android.widget.LinearLayout;
 27import android.widget.TextView;
 28import android.widget.Toast;
 29
 30public class MessageAdapter extends ArrayAdapter<Message> {
 31
 32	private static final int SENT = 0;
 33	private static final int RECIEVED = 1;
 34	private static final int STATUS = 2;
 35
 36	private ConversationActivity activity;
 37
 38	private Bitmap accountBitmap;
 39
 40	private BitmapCache mBitmapCache = new BitmapCache();
 41	private DisplayMetrics metrics;
 42
 43	private OnContactPictureClicked mOnContactPictureClickedListener;
 44
 45	public MessageAdapter(ConversationActivity activity, List<Message> messages) {
 46		super(activity, 0, messages);
 47		this.activity = activity;
 48		metrics = getContext().getResources().getDisplayMetrics();
 49	}
 50
 51	private Bitmap getSelfBitmap() {
 52		if (this.accountBitmap == null) {
 53
 54			if (getCount() > 0) {
 55				this.accountBitmap = getItem(0)
 56						.getConversation().getAccount().getImage(getContext(), 48);
 57			}
 58		}
 59		return this.accountBitmap;
 60	}
 61
 62	public void setOnContactPictureClicked(OnContactPictureClicked listener) {
 63		this.mOnContactPictureClickedListener = listener;
 64	}
 65
 66	@Override
 67	public int getViewTypeCount() {
 68		return 3;
 69	}
 70
 71	@Override
 72	public int getItemViewType(int position) {
 73		if (getItem(position).getType() == Message.TYPE_STATUS) {
 74			return STATUS;
 75		} else if (getItem(position).getStatus() <= Message.STATUS_RECIEVED) {
 76			return RECIEVED;
 77		} else {
 78			return SENT;
 79		}
 80	}
 81
 82	private void displayStatus(ViewHolder viewHolder, Message message) {
 83		String filesize = null;
 84		String info = null;
 85		boolean error = false;
 86		boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
 87				&& message.getStatus() <= Message.STATUS_RECIEVED;
 88		if (message.getType() == Message.TYPE_IMAGE) {
 89			String[] fileParams = message.getBody().split(",");
 90			try {
 91				long size = Long.parseLong(fileParams[0]);
 92				filesize = size / 1024 + " KB";
 93			} catch (NumberFormatException e) {
 94				filesize = "0 KB";
 95			}
 96		}
 97		switch (message.getStatus()) {
 98		case Message.STATUS_WAITING:
 99			info = getContext().getString(R.string.waiting);
100			break;
101		case Message.STATUS_UNSEND:
102			info = getContext().getString(R.string.sending);
103			break;
104		case Message.STATUS_OFFERED:
105			info = getContext().getString(R.string.offering);
106			break;
107		case Message.STATUS_SEND_FAILED:
108			info = getContext().getString(R.string.send_failed);
109			error = true;
110			break;
111		case Message.STATUS_SEND_REJECTED:
112			info = getContext().getString(R.string.send_rejected);
113			error = true;
114			break;
115		case Message.STATUS_RECEPTION_FAILED:
116			info = getContext().getString(R.string.reception_failed);
117			error = true;
118		default:
119			if (multiReceived) {
120				Contact contact = message.getContact();
121				if (contact != null) {
122					info = contact.getDisplayName();
123				} else {
124					info = message.getCounterpart();
125				}
126			}
127			break;
128		}
129		if (error) {
130			viewHolder.time.setTextColor(0xFFe92727);
131		} else {
132			viewHolder.time.setTextColor(activity.getSecondaryTextColor());
133		}
134		if (message.getEncryption() == Message.ENCRYPTION_NONE) {
135			viewHolder.indicator.setVisibility(View.GONE);
136		} else {
137			viewHolder.indicator.setVisibility(View.VISIBLE);
138		}
139
140		String formatedTime = UIHelper.readableTimeDifference(getContext(),
141				message.getTimeSent());
142		if (message.getStatus() <= Message.STATUS_RECIEVED) {
143			if ((filesize != null) && (info != null)) {
144				viewHolder.time.setText(filesize + " \u00B7 " + info);
145			} else if ((filesize == null) && (info != null)) {
146				viewHolder.time.setText(formatedTime + " \u00B7 " + info);
147			} else if ((filesize != null) && (info == null)) {
148				viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
149			} else {
150				viewHolder.time.setText(formatedTime);
151			}
152		} else {
153			if ((filesize != null) && (info != null)) {
154				viewHolder.time.setText(filesize + " \u00B7 " + info);
155			} else if ((filesize == null) && (info != null)) {
156				if (error) {
157					viewHolder.time.setText(info + " \u00B7 " + formatedTime);
158				} else {
159					viewHolder.time.setText(info);
160				}
161			} else if ((filesize != null) && (info == null)) {
162				viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
163			} else {
164				viewHolder.time.setText(formatedTime);
165			}
166		}
167	}
168
169	private void displayInfoMessage(ViewHolder viewHolder, int r) {
170		if (viewHolder.download_button != null) {
171			viewHolder.download_button.setVisibility(View.GONE);
172		}
173		viewHolder.image.setVisibility(View.GONE);
174		viewHolder.messageBody.setVisibility(View.VISIBLE);
175		viewHolder.messageBody.setText(getContext().getString(r));
176		viewHolder.messageBody.setTextColor(0xff33B5E5);
177		viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
178		viewHolder.messageBody.setTextIsSelectable(false);
179	}
180
181	private void displayDecryptionFailed(ViewHolder viewHolder) {
182		if (viewHolder.download_button != null) {
183			viewHolder.download_button.setVisibility(View.GONE);
184		}
185		viewHolder.image.setVisibility(View.GONE);
186		viewHolder.messageBody.setVisibility(View.VISIBLE);
187		viewHolder.messageBody.setText(getContext().getString(
188				R.string.decryption_failed));
189		viewHolder.messageBody.setTextColor(0xFFe92727);
190		viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
191		viewHolder.messageBody.setTextIsSelectable(false);
192	}
193
194	private void displayTextMessage(ViewHolder viewHolder, String text) {
195		if (viewHolder.download_button != null) {
196			viewHolder.download_button.setVisibility(View.GONE);
197		}
198		viewHolder.image.setVisibility(View.GONE);
199		viewHolder.messageBody.setVisibility(View.VISIBLE);
200		if (text != null) {
201			viewHolder.messageBody.setText(text.trim());
202		} else {
203			viewHolder.messageBody.setText("");
204		}
205		viewHolder.messageBody.setTextColor(activity.getPrimaryTextColor());
206		viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
207		viewHolder.messageBody.setTextIsSelectable(true);
208	}
209
210	private void displayImageMessage(ViewHolder viewHolder,
211			final Message message) {
212		if (viewHolder.download_button != null) {
213			viewHolder.download_button.setVisibility(View.GONE);
214		}
215		viewHolder.messageBody.setVisibility(View.GONE);
216		viewHolder.image.setVisibility(View.VISIBLE);
217		String[] fileParams = message.getBody().split(",");
218		if (fileParams.length == 3) {
219			double target = metrics.density * 288;
220			int w = Integer.parseInt(fileParams[1]);
221			int h = Integer.parseInt(fileParams[2]);
222			int scalledW;
223			int scalledH;
224			if (w <= h) {
225				scalledW = (int) (w / ((double) h / target));
226				scalledH = (int) target;
227			} else {
228				scalledW = (int) target;
229				scalledH = (int) (h / ((double) w / target));
230			}
231			viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
232					scalledW, scalledH));
233		}
234		activity.loadBitmap(message, viewHolder.image);
235		viewHolder.image.setOnClickListener(new OnClickListener() {
236
237			@Override
238			public void onClick(View v) {
239				Intent intent = new Intent(Intent.ACTION_VIEW);
240				intent.setDataAndType(ImageProvider.getContentUri(message),
241						"image/*");
242				getContext().startActivity(intent);
243			}
244		});
245		viewHolder.image.setOnLongClickListener(new OnLongClickListener() {
246
247			@Override
248			public boolean onLongClick(View v) {
249				Intent shareIntent = new Intent();
250				shareIntent.setAction(Intent.ACTION_SEND);
251				shareIntent.putExtra(Intent.EXTRA_STREAM,
252						ImageProvider.getContentUri(message));
253				shareIntent.setType("image/webp");
254				getContext().startActivity(
255						Intent.createChooser(shareIntent,
256								getContext().getText(R.string.share_with)));
257				return true;
258			}
259		});
260	}
261
262	@Override
263	public View getView(int position, View view, ViewGroup parent) {
264		final Message item = getItem(position);
265		int type = getItemViewType(position);
266		ViewHolder viewHolder;
267		if (view == null) {
268			viewHolder = new ViewHolder();
269			switch (type) {
270			case SENT:
271				view = (View) activity.getLayoutInflater().inflate(
272						R.layout.message_sent, parent,false);
273				viewHolder.message_box = (LinearLayout) view
274						.findViewById(R.id.message_box);
275				viewHolder.contact_picture = (ImageView) view
276						.findViewById(R.id.message_photo);
277				viewHolder.contact_picture.setImageBitmap(getSelfBitmap());
278				viewHolder.indicator = (ImageView) view
279						.findViewById(R.id.security_indicator);
280				viewHolder.image = (ImageView) view
281						.findViewById(R.id.message_image);
282				viewHolder.messageBody = (TextView) view
283						.findViewById(R.id.message_body);
284				viewHolder.time = (TextView) view
285						.findViewById(R.id.message_time);
286				view.setTag(viewHolder);
287				break;
288			case RECIEVED:
289				view = (View) activity.getLayoutInflater().inflate(
290						R.layout.message_recieved, parent,false);
291				viewHolder.message_box = (LinearLayout) view
292						.findViewById(R.id.message_box);
293				viewHolder.contact_picture = (ImageView) view
294						.findViewById(R.id.message_photo);
295
296				viewHolder.download_button = (Button) view
297						.findViewById(R.id.download_button);
298
299				if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
300
301					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
302							item.getConversation().getContact(), getContext()));
303
304				}
305				viewHolder.indicator = (ImageView) view
306						.findViewById(R.id.security_indicator);
307				viewHolder.image = (ImageView) view
308						.findViewById(R.id.message_image);
309				viewHolder.messageBody = (TextView) view
310						.findViewById(R.id.message_body);
311				viewHolder.time = (TextView) view
312						.findViewById(R.id.message_time);
313				view.setTag(viewHolder);
314				break;
315			case STATUS:
316				view = (View) activity.getLayoutInflater().inflate(
317						R.layout.message_status, parent,false);
318				viewHolder.contact_picture = (ImageView) view
319						.findViewById(R.id.message_photo);
320				if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
321
322					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
323							item.getConversation().getContact(), getContext()));
324					viewHolder.contact_picture.setAlpha(128);
325					viewHolder.contact_picture
326							.setOnClickListener(new OnClickListener() {
327
328								@Override
329								public void onClick(View v) {
330									String name = item.getConversation()
331											.getName(true);
332									String read = getContext()
333											.getString(
334													R.string.contact_has_read_up_to_this_point,
335													name);
336									Toast.makeText(getContext(), read,
337											Toast.LENGTH_SHORT).show();
338								}
339							});
340
341				}
342				break;
343			default:
344				viewHolder = null;
345				break;
346			}
347		} else {
348			viewHolder = (ViewHolder) view.getTag();
349		}
350
351		if (type == STATUS) {
352			return view;
353		}
354
355		if (type == RECIEVED) {
356			if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
357				Contact contact = item.getContact();
358				if (contact != null) {
359					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
360							contact, getContext()));
361				} else {
362					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
363							item.getCounterpart(), getContext()));
364				}
365				viewHolder.contact_picture
366						.setOnClickListener(new OnClickListener() {
367
368							@Override
369							public void onClick(View v) {
370								if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
371									MessageAdapter.this.mOnContactPictureClickedListener
372											.onContactPictureClicked(item);
373									;
374								}
375
376							}
377						});
378			}
379		}
380
381		if (item.getType() == Message.TYPE_IMAGE) {
382			if (item.getStatus() == Message.STATUS_RECIEVING) {
383				displayInfoMessage(viewHolder, R.string.receiving_image);
384			} else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
385				viewHolder.image.setVisibility(View.GONE);
386				viewHolder.messageBody.setVisibility(View.GONE);
387				viewHolder.download_button.setVisibility(View.VISIBLE);
388				viewHolder.download_button
389						.setOnClickListener(new OnClickListener() {
390
391							@Override
392							public void onClick(View v) {
393								JingleConnection connection = item
394										.getJingleConnection();
395								if (connection != null) {
396									connection.accept();
397								}
398							}
399						});
400			} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
401					|| (item.getEncryption() == Message.ENCRYPTION_NONE)
402					|| (item.getEncryption() == Message.ENCRYPTION_OTR)) {
403				displayImageMessage(viewHolder, item);
404			} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
405				displayInfoMessage(viewHolder, R.string.encrypted_message);
406			} else {
407				displayDecryptionFailed(viewHolder);
408			}
409		} else {
410			if (item.getEncryption() == Message.ENCRYPTION_PGP) {
411				if (activity.hasPgp()) {
412					displayInfoMessage(viewHolder, R.string.encrypted_message);
413				} else {
414					displayInfoMessage(viewHolder,
415							R.string.install_openkeychain);
416					viewHolder.message_box
417							.setOnClickListener(new OnClickListener() {
418
419								@Override
420								public void onClick(View v) {
421									activity.showInstallPgpDialog();
422								}
423							});
424				}
425			} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
426				displayDecryptionFailed(viewHolder);
427			} else {
428				displayTextMessage(viewHolder, item.getBody());
429			}
430		}
431
432		displayStatus(viewHolder, item);
433
434		return view;
435	}
436
437	private static class ViewHolder {
438
439		protected LinearLayout message_box;
440		protected Button download_button;
441		protected ImageView image;
442		protected ImageView indicator;
443		protected TextView time;
444		protected TextView messageBody;
445		protected ImageView contact_picture;
446
447	}
448
449	private class BitmapCache {
450		private HashMap<String, Bitmap> contactBitmaps = new HashMap<String, Bitmap>();
451		private HashMap<String, Bitmap> unknownBitmaps = new HashMap<String, Bitmap>();
452
453		public Bitmap get(Contact contact, Context context) {
454			if (contactBitmaps.containsKey(contact.getJid())) {
455				return contactBitmaps.get(contact.getJid());
456			} else {
457				Bitmap bm = UIHelper.getContactPicture(contact, 48, context,
458						false);
459				contactBitmaps.put(contact.getJid(), bm);
460				return bm;
461			}
462		}
463
464		public Bitmap get(String name, Context context) {
465			if (unknownBitmaps.containsKey(name)) {
466				return unknownBitmaps.get(name);
467			} else {
468				Bitmap bm = UIHelper
469						.getContactPicture(name, 48, context, false);
470				unknownBitmaps.put(name, bm);
471				return bm;
472			}
473		}
474	}
475
476	public interface OnContactPictureClicked {
477		public void onContactPictureClicked(Message message);
478	}
479}