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					String name = item.getPresence();
393					if (name==null) {
394						name = item.getCounterpart();
395					}
396					viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(name, getContext()));
397				}
398				viewHolder.contact_picture
399						.setOnClickListener(new OnClickListener() {
400
401							@Override
402							public void onClick(View v) {
403								if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
404									MessageAdapter.this.mOnContactPictureClickedListener
405											.onContactPictureClicked(item);
406									;
407								}
408
409							}
410						});
411				viewHolder.contact_picture.setOnLongClickListener(new OnLongClickListener() {
412					
413					@Override
414					public boolean onLongClick(View v) {
415						if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
416							MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(item);
417							return true;
418						} else {
419							return false;
420						}
421					}
422				});
423			}
424		}
425
426		if (item.getType() == Message.TYPE_IMAGE) {
427			if (item.getStatus() == Message.STATUS_RECIEVING) {
428				displayInfoMessage(viewHolder, R.string.receiving_image);
429			} else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
430				viewHolder.image.setVisibility(View.GONE);
431				viewHolder.messageBody.setVisibility(View.GONE);
432				viewHolder.download_button.setVisibility(View.VISIBLE);
433				viewHolder.download_button
434						.setOnClickListener(new OnClickListener() {
435
436							@Override
437							public void onClick(View v) {
438								JingleConnection connection = item
439										.getJingleConnection();
440								if (connection != null) {
441									connection.accept();
442								}
443							}
444						});
445			} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
446					|| (item.getEncryption() == Message.ENCRYPTION_NONE)
447					|| (item.getEncryption() == Message.ENCRYPTION_OTR)) {
448				displayImageMessage(viewHolder, item);
449			} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
450				displayInfoMessage(viewHolder, R.string.encrypted_message);
451			} else {
452				displayDecryptionFailed(viewHolder);
453			}
454		} else {
455			if (item.getEncryption() == Message.ENCRYPTION_PGP) {
456				if (activity.hasPgp()) {
457					displayInfoMessage(viewHolder, R.string.encrypted_message);
458				} else {
459					displayInfoMessage(viewHolder,
460							R.string.install_openkeychain);
461					viewHolder.message_box
462							.setOnClickListener(new OnClickListener() {
463
464								@Override
465								public void onClick(View v) {
466									activity.showInstallPgpDialog();
467								}
468							});
469				}
470			} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
471				displayDecryptionFailed(viewHolder);
472			} else {
473				displayTextMessage(viewHolder, item);
474			}
475		}
476
477		displayStatus(viewHolder, item);
478
479		return view;
480	}
481
482	private static class ViewHolder {
483
484		protected LinearLayout message_box;
485		protected Button download_button;
486		protected ImageView image;
487		protected ImageView indicator;
488		protected TextView time;
489		protected TextView messageBody;
490		protected ImageView contact_picture;
491
492	}
493
494	private class BitmapCache {
495		private HashMap<String, Bitmap> contactBitmaps = new HashMap<String, Bitmap>();
496		private HashMap<String, Bitmap> unknownBitmaps = new HashMap<String, Bitmap>();
497
498		public Bitmap get(Contact contact, Context context) {
499			if (!contactBitmaps.containsKey(contact.getJid())) {
500				contactBitmaps.put(contact.getJid(),
501						contact.getImage(48, context));
502			}
503			return contactBitmaps.get(contact.getJid());
504		}
505
506		public Bitmap get(String name, Context context) {
507			if (unknownBitmaps.containsKey(name)) {
508				return unknownBitmaps.get(name);
509			} else {
510				Bitmap bm = UIHelper
511						.getContactPicture(name, 48, context, false);
512				unknownBitmaps.put(name, bm);
513				return bm;
514			}
515		}
516	}
517
518	public interface OnContactPictureClicked {
519		public void onContactPictureClicked(Message message);
520	}
521	
522	public interface OnContactPictureLongClicked {
523		public void onContactPictureLongClicked(Message message);
524	}
525}