MessageAdapter.java

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