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