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