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.Html;
 18import android.text.Spannable;
 19import android.text.SpannableString;
 20import android.text.style.ForegroundColorSpan;
 21import android.text.style.StyleSpan;
 22import android.util.DisplayMetrics;
 23import android.view.View;
 24import android.view.ViewGroup;
 25import android.view.View.OnClickListener;
 26import android.view.View.OnLongClickListener;
 27import android.widget.ArrayAdapter;
 28import android.widget.Button;
 29import android.widget.ImageView;
 30import android.widget.LinearLayout;
 31import android.widget.TextView;
 32import android.widget.Toast;
 33
 34public class MessageAdapter extends ArrayAdapter<Message> {
 35
 36	private static final int SENT = 0;
 37	private static final int RECEIVED = 1;
 38	private static final int STATUS = 2;
 39	private static final int NULL = 3;
 40
 41	private ConversationActivity activity;
 42
 43	private Bitmap accountBitmap;
 44
 45	private BitmapCache mBitmapCache = new BitmapCache();
 46	private DisplayMetrics metrics;
 47
 48	private OnContactPictureClicked mOnContactPictureClickedListener;
 49	private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
 50
 51	public MessageAdapter(ConversationActivity activity, List<Message> messages) {
 52		super(activity, 0, messages);
 53		this.activity = activity;
 54		metrics = getContext().getResources().getDisplayMetrics();
 55	}
 56
 57	private Bitmap getSelfBitmap() {
 58		if (this.accountBitmap == null) {
 59
 60			if (getCount() > 0) {
 61				this.accountBitmap = getItem(0).getConversation().getAccount()
 62						.getImage(getContext(), 48);
 63			}
 64		}
 65		return this.accountBitmap;
 66	}
 67
 68	public void setOnContactPictureClicked(OnContactPictureClicked listener) {
 69		this.mOnContactPictureClickedListener = listener;
 70	}
 71	
 72	public void setOnContactPictureLongClicked(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.getStatus() <= 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.getStatus()) {
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.getTimeSent());
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.getString(R.string.private_message);
223				} else {
224					String to;
225					if (message.getPresence() != null) {
226						to = message.getPresence();
227					} else {
228						to = message.getCounterpart();
229					}
230					privateMarker = activity.getString(R.string.private_message_to, to);
231				}
232				SpannableString span = new SpannableString(privateMarker+" "+message.getBody());
233				span.setSpan(new ForegroundColorSpan(activity.getSecondaryTextColor()), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
234				span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
235				viewHolder.messageBody.setText(span);
236			}
237		} else {
238			viewHolder.messageBody.setText("");
239		}
240		viewHolder.messageBody.setTextColor(activity.getPrimaryTextColor());
241		viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
242		viewHolder.messageBody.setTextIsSelectable(true);
243	}
244
245	private void displayImageMessage(ViewHolder viewHolder,
246			final Message message) {
247		if (viewHolder.download_button != null) {
248			viewHolder.download_button.setVisibility(View.GONE);
249		}
250		viewHolder.messageBody.setVisibility(View.GONE);
251		viewHolder.image.setVisibility(View.VISIBLE);
252		String[] fileParams = message.getBody().split(",");
253		if (fileParams.length == 3) {
254			double target = metrics.density * 288;
255			int w = Integer.parseInt(fileParams[1]);
256			int h = Integer.parseInt(fileParams[2]);
257			int scalledW;
258			int scalledH;
259			if (w <= h) {
260				scalledW = (int) (w / ((double) h / target));
261				scalledH = (int) target;
262			} else {
263				scalledW = (int) target;
264				scalledH = (int) (h / ((double) w / target));
265			}
266			viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
267					scalledW, scalledH));
268		}
269		activity.loadBitmap(message, viewHolder.image);
270		viewHolder.image.setOnClickListener(new OnClickListener() {
271
272			@Override
273			public void onClick(View v) {
274				Intent intent = new Intent(Intent.ACTION_VIEW);
275				intent.setDataAndType(activity.xmppConnectionService
276						.getFileBackend().getJingleFileUri(message), "image/*");
277				getContext().startActivity(intent);
278			}
279		});
280		viewHolder.image.setOnLongClickListener(new OnLongClickListener() {
281
282			@Override
283			public boolean onLongClick(View v) {
284				Intent shareIntent = new Intent();
285				shareIntent.setAction(Intent.ACTION_SEND);
286				shareIntent.putExtra(Intent.EXTRA_STREAM,
287						activity.xmppConnectionService.getFileBackend()
288								.getJingleFileUri(message));
289				shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
290				shareIntent.setType("image/webp");
291				getContext().startActivity(
292						Intent.createChooser(shareIntent,
293								getContext().getText(R.string.share_with)));
294				return true;
295			}
296		});
297	}
298
299	@Override
300	public View getView(int position, View view, ViewGroup parent) {
301		final Message item = getItem(position);
302		int type = getItemViewType(position);
303		ViewHolder viewHolder;
304		if (view == null) {
305			viewHolder = new ViewHolder();
306			switch (type) {
307			case NULL:
308				view = (View) activity.getLayoutInflater().inflate(
309						R.layout.message_null, parent, false);
310				break;
311			case SENT:
312				view = (View) activity.getLayoutInflater().inflate(
313						R.layout.message_sent, parent, false);
314				viewHolder.message_box = (LinearLayout) view
315						.findViewById(R.id.message_box);
316				viewHolder.contact_picture = (ImageView) view
317						.findViewById(R.id.message_photo);
318				viewHolder.contact_picture.setImageBitmap(getSelfBitmap());
319				viewHolder.indicator = (ImageView) view
320						.findViewById(R.id.security_indicator);
321				viewHolder.image = (ImageView) view
322						.findViewById(R.id.message_image);
323				viewHolder.messageBody = (TextView) view
324						.findViewById(R.id.message_body);
325				viewHolder.time = (TextView) view
326						.findViewById(R.id.message_time);
327				view.setTag(viewHolder);
328				break;
329			case RECEIVED:
330				view = (View) activity.getLayoutInflater().inflate(
331						R.layout.message_received, parent, false);
332				viewHolder.message_box = (LinearLayout) view
333						.findViewById(R.id.message_box);
334				viewHolder.contact_picture = (ImageView) view
335						.findViewById(R.id.message_photo);
336
337				viewHolder.download_button = (Button) view
338						.findViewById(R.id.download_button);
339
340				if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
341
342					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
343							item.getConversation().getContact(), getContext()));
344
345				}
346				viewHolder.indicator = (ImageView) view
347						.findViewById(R.id.security_indicator);
348				viewHolder.image = (ImageView) view
349						.findViewById(R.id.message_image);
350				viewHolder.messageBody = (TextView) view
351						.findViewById(R.id.message_body);
352				viewHolder.time = (TextView) view
353						.findViewById(R.id.message_time);
354				view.setTag(viewHolder);
355				break;
356			case STATUS:
357				view = (View) activity.getLayoutInflater().inflate(
358						R.layout.message_status, parent, false);
359				viewHolder.contact_picture = (ImageView) view
360						.findViewById(R.id.message_photo);
361				if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
362
363					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
364							item.getConversation().getContact(), getContext()));
365					viewHolder.contact_picture.setAlpha(128);
366					viewHolder.contact_picture
367							.setOnClickListener(new OnClickListener() {
368
369								@Override
370								public void onClick(View v) {
371									String name = item.getConversation()
372											.getName(true);
373									String read = getContext()
374											.getString(
375													R.string.contact_has_read_up_to_this_point,
376													name);
377									Toast.makeText(getContext(), read,
378											Toast.LENGTH_SHORT).show();
379								}
380							});
381
382				}
383				break;
384			default:
385				viewHolder = null;
386				break;
387			}
388		} else {
389			viewHolder = (ViewHolder) view.getTag();
390		}
391
392		if (type == STATUS || type == NULL) {
393			return view;
394		}
395
396		if (type == RECEIVED) {
397			if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
398				Contact contact = item.getContact();
399				if (contact != null) {
400					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
401							contact, getContext()));
402				} else {
403					String name = item.getPresence();
404					if (name==null) {
405						name = item.getCounterpart();
406					}
407					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(name, getContext()));
408				}
409				viewHolder.contact_picture
410						.setOnClickListener(new OnClickListener() {
411
412							@Override
413							public void onClick(View v) {
414								if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
415									MessageAdapter.this.mOnContactPictureClickedListener
416											.onContactPictureClicked(item);
417									;
418								}
419
420							}
421						});
422				viewHolder.contact_picture.setOnLongClickListener(new OnLongClickListener() {
423					
424					@Override
425					public boolean onLongClick(View v) {
426						if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
427							MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(item);
428							return true;
429						} else {
430							return false;
431						}
432					}
433				});
434			}
435		}
436
437		if (item.getType() == Message.TYPE_IMAGE) {
438			if (item.getStatus() == Message.STATUS_RECEIVING) {
439				displayInfoMessage(viewHolder, R.string.receiving_image);
440			} else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
441				viewHolder.image.setVisibility(View.GONE);
442				viewHolder.messageBody.setVisibility(View.GONE);
443				viewHolder.download_button.setVisibility(View.VISIBLE);
444				viewHolder.download_button
445						.setOnClickListener(new OnClickListener() {
446
447							@Override
448							public void onClick(View v) {
449								JingleConnection connection = item
450										.getJingleConnection();
451								if (connection != null) {
452									connection.accept();
453								}
454							}
455						});
456			} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
457					|| (item.getEncryption() == Message.ENCRYPTION_NONE)
458					|| (item.getEncryption() == Message.ENCRYPTION_OTR)) {
459				displayImageMessage(viewHolder, item);
460			} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
461				displayInfoMessage(viewHolder, R.string.encrypted_message);
462			} else {
463				displayDecryptionFailed(viewHolder);
464			}
465		} else {
466			if (item.getEncryption() == Message.ENCRYPTION_PGP) {
467				if (activity.hasPgp()) {
468					displayInfoMessage(viewHolder, R.string.encrypted_message);
469				} else {
470					displayInfoMessage(viewHolder,
471							R.string.install_openkeychain);
472					viewHolder.message_box
473							.setOnClickListener(new OnClickListener() {
474
475								@Override
476								public void onClick(View v) {
477									activity.showInstallPgpDialog();
478								}
479							});
480				}
481			} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
482				displayDecryptionFailed(viewHolder);
483			} else {
484				displayTextMessage(viewHolder, item);
485			}
486		}
487
488		displayStatus(viewHolder, item);
489
490		return view;
491	}
492
493	private static class ViewHolder {
494
495		protected LinearLayout message_box;
496		protected Button download_button;
497		protected ImageView image;
498		protected ImageView indicator;
499		protected TextView time;
500		protected TextView messageBody;
501		protected ImageView contact_picture;
502
503	}
504
505	private class BitmapCache {
506		private HashMap<String, Bitmap> contactBitmaps = new HashMap<String, Bitmap>();
507		private HashMap<String, Bitmap> unknownBitmaps = new HashMap<String, Bitmap>();
508
509		public Bitmap get(Contact contact, Context context) {
510			if (!contactBitmaps.containsKey(contact.getJid())) {
511				contactBitmaps.put(contact.getJid(),
512						contact.getImage(48, context));
513			}
514			return contactBitmaps.get(contact.getJid());
515		}
516
517		public Bitmap get(String name, Context context) {
518			if (unknownBitmaps.containsKey(name)) {
519				return unknownBitmaps.get(name);
520			} else {
521				Bitmap bm = UIHelper
522						.getContactPicture(name, 48, context, false);
523				unknownBitmaps.put(name, bm);
524				return bm;
525			}
526		}
527	}
528
529	public interface OnContactPictureClicked {
530		public void onContactPictureClicked(Message message);
531	}
532	
533	public interface OnContactPictureLongClicked {
534		public void onContactPictureLongClicked(Message message);
535	}
536}