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