MessageAdapter.java

  1package eu.siacs.conversations.ui.adapter;
  2
  3import android.content.ActivityNotFoundException;
  4import android.content.Intent;
  5import android.content.pm.PackageManager;
  6import android.content.pm.ResolveInfo;
  7import android.graphics.Typeface;
  8import android.net.Uri;
  9import android.text.Spannable;
 10import android.text.SpannableString;
 11import android.text.Spanned;
 12import android.text.style.ForegroundColorSpan;
 13import android.text.style.RelativeSizeSpan;
 14import android.text.style.StyleSpan;
 15import android.util.DisplayMetrics;
 16import android.view.View;
 17import android.view.View.OnClickListener;
 18import android.view.View.OnLongClickListener;
 19import android.view.ViewGroup;
 20import android.widget.ArrayAdapter;
 21import android.widget.Button;
 22import android.widget.ImageView;
 23import android.widget.LinearLayout;
 24import android.widget.TextView;
 25import android.widget.Toast;
 26
 27import java.util.List;
 28
 29import eu.siacs.conversations.R;
 30import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
 31import eu.siacs.conversations.entities.Account;
 32import eu.siacs.conversations.entities.Contact;
 33import eu.siacs.conversations.entities.Conversation;
 34import eu.siacs.conversations.entities.DownloadableFile;
 35import eu.siacs.conversations.entities.Message;
 36import eu.siacs.conversations.entities.Message.FileParams;
 37import eu.siacs.conversations.entities.Transferable;
 38import eu.siacs.conversations.ui.ConversationActivity;
 39import eu.siacs.conversations.utils.CryptoHelper;
 40import eu.siacs.conversations.utils.GeoHelper;
 41import eu.siacs.conversations.utils.UIHelper;
 42
 43public class MessageAdapter extends ArrayAdapter<Message> {
 44
 45	private static final int SENT = 0;
 46	private static final int RECEIVED = 1;
 47	private static final int STATUS = 2;
 48
 49	private ConversationActivity activity;
 50
 51	private DisplayMetrics metrics;
 52
 53	private OnContactPictureClicked mOnContactPictureClickedListener;
 54	private OnContactPictureLongClicked mOnContactPictureLongClickedListener;
 55
 56	private OnLongClickListener openContextMenu = new OnLongClickListener() {
 57
 58		@Override
 59		public boolean onLongClick(View v) {
 60			v.showContextMenu();
 61			return true;
 62		}
 63	};
 64	private boolean mIndicateReceived = false;
 65	private boolean mUseWhiteBackground = false;
 66
 67	public MessageAdapter(ConversationActivity activity, List<Message> messages) {
 68		super(activity, 0, messages);
 69		this.activity = activity;
 70		metrics = getContext().getResources().getDisplayMetrics();
 71		updatePreferences();
 72	}
 73
 74	public void setOnContactPictureClicked(OnContactPictureClicked listener) {
 75		this.mOnContactPictureClickedListener = listener;
 76	}
 77
 78	public void setOnContactPictureLongClicked(
 79			OnContactPictureLongClicked listener) {
 80		this.mOnContactPictureLongClickedListener = listener;
 81			}
 82
 83	@Override
 84	public int getViewTypeCount() {
 85		return 3;
 86	}
 87
 88	public int getItemViewType(Message message) {
 89		if (message.getType() == Message.TYPE_STATUS) {
 90			return STATUS;
 91		} else if (message.getStatus() <= Message.STATUS_RECEIVED) {
 92			return RECEIVED;
 93		}
 94
 95		return SENT;
 96	}
 97
 98	@Override
 99	public int getItemViewType(int position) {
100		return this.getItemViewType(getItem(position));
101	}
102
103	private int getMessageTextColor(boolean onDark, boolean primary) {
104		if (onDark) {
105			return activity.getResources().getColor(primary ? R.color.white : R.color.white70);
106		} else {
107			return activity.getResources().getColor(primary ? R.color.black87 : R.color.black54);
108		}
109	}
110
111	private void displayStatus(ViewHolder viewHolder, Message message, int type, boolean darkBackground) {
112		String filesize = null;
113		String info = null;
114		boolean error = false;
115		if (viewHolder.indicatorReceived != null) {
116			viewHolder.indicatorReceived.setVisibility(View.GONE);
117		}
118		boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
119			&& message.getMergedStatus() <= Message.STATUS_RECEIVED;
120		if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
121			FileParams params = message.getFileParams();
122			if (params.size > (1.5 * 1024 * 1024)) {
123				filesize = params.size / (1024 * 1024)+ " MiB";
124			} else if (params.size > 0) {
125				filesize = params.size / 1024 + " KiB";
126			}
127			if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) {
128				error = true;
129			}
130		}
131		switch (message.getMergedStatus()) {
132			case Message.STATUS_WAITING:
133				info = getContext().getString(R.string.waiting);
134				break;
135			case Message.STATUS_UNSEND:
136				Transferable d = message.getTransferable();
137				if (d!=null) {
138					info = getContext().getString(R.string.sending_file,d.getProgress());
139				} else {
140					info = getContext().getString(R.string.sending);
141				}
142				break;
143			case Message.STATUS_OFFERED:
144				info = getContext().getString(R.string.offering);
145				break;
146			case Message.STATUS_SEND_RECEIVED:
147				if (mIndicateReceived) {
148					viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
149				}
150				break;
151			case Message.STATUS_SEND_DISPLAYED:
152				if (mIndicateReceived) {
153					viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
154				}
155				break;
156			case Message.STATUS_SEND_FAILED:
157				info = getContext().getString(R.string.send_failed);
158				error = true;
159				break;
160			default:
161				if (multiReceived) {
162					info = UIHelper.getMessageDisplayName(message);
163				}
164				break;
165		}
166		if (error && type == SENT) {
167			viewHolder.time.setTextColor(activity.getWarningTextColor());
168		} else {
169			viewHolder.time.setTextColor(this.getMessageTextColor(darkBackground,false));
170		}
171		if (message.getEncryption() == Message.ENCRYPTION_NONE) {
172			viewHolder.indicator.setVisibility(View.GONE);
173		} else {
174			viewHolder.indicator.setImageResource(darkBackground ? R.drawable.ic_secure_indicator_white : R.drawable.ic_secure_indicator);
175			viewHolder.indicator.setVisibility(View.VISIBLE);
176			if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
177				XmppAxolotlSession.Trust trust = message.getConversation()
178						.getAccount().getAxolotlService().getFingerprintTrust(
179								message.getAxolotlFingerprint());
180
181				if(trust == null || trust != XmppAxolotlSession.Trust.TRUSTED) {
182					viewHolder.indicator.setColorFilter(activity.getWarningTextColor());
183					viewHolder.indicator.setAlpha(1.0f);
184				} else {
185					viewHolder.indicator.clearColorFilter();
186					if (darkBackground) {
187						viewHolder.indicator.setAlpha(0.7f);
188					} else {
189						viewHolder.indicator.setAlpha(0.57f);
190					}
191				}
192			} else {
193				viewHolder.indicator.clearColorFilter();
194				if (darkBackground) {
195					viewHolder.indicator.setAlpha(0.7f);
196				} else {
197					viewHolder.indicator.setAlpha(0.57f);
198				}
199			}
200		}
201
202		String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(),
203				message.getMergedTimeSent());
204		if (message.getStatus() <= Message.STATUS_RECEIVED) {
205			if ((filesize != null) && (info != null)) {
206				viewHolder.time.setText(formatedTime + " \u00B7 " + filesize +" \u00B7 " + info);
207			} else if ((filesize == null) && (info != null)) {
208				viewHolder.time.setText(formatedTime + " \u00B7 " + info);
209			} else if ((filesize != null) && (info == null)) {
210				viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
211			} else {
212				viewHolder.time.setText(formatedTime);
213			}
214		} else {
215			if ((filesize != null) && (info != null)) {
216				viewHolder.time.setText(filesize + " \u00B7 " + info);
217			} else if ((filesize == null) && (info != null)) {
218				if (error) {
219					viewHolder.time.setText(info + " \u00B7 " + formatedTime);
220				} else {
221					viewHolder.time.setText(info);
222				}
223			} else if ((filesize != null) && (info == null)) {
224				viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
225			} else {
226				viewHolder.time.setText(formatedTime);
227			}
228		}
229	}
230
231	private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
232		if (viewHolder.download_button != null) {
233			viewHolder.download_button.setVisibility(View.GONE);
234		}
235		viewHolder.image.setVisibility(View.GONE);
236		viewHolder.messageBody.setVisibility(View.VISIBLE);
237		viewHolder.messageBody.setText(text);
238		viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
239		viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
240		viewHolder.messageBody.setTextIsSelectable(false);
241	}
242
243	private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) {
244		if (viewHolder.download_button != null) {
245			viewHolder.download_button.setVisibility(View.GONE);
246		}
247		viewHolder.image.setVisibility(View.GONE);
248		viewHolder.messageBody.setVisibility(View.VISIBLE);
249		viewHolder.messageBody.setText(getContext().getString(
250				R.string.decryption_failed));
251		viewHolder.messageBody.setTextColor(getMessageTextColor(darkBackground, false));
252		viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
253		viewHolder.messageBody.setTextIsSelectable(false);
254	}
255
256	private void displayHeartMessage(final ViewHolder viewHolder, final String body) {
257		if (viewHolder.download_button != null) {
258			viewHolder.download_button.setVisibility(View.GONE);
259		}
260		viewHolder.image.setVisibility(View.GONE);
261		viewHolder.messageBody.setVisibility(View.VISIBLE);
262		viewHolder.messageBody.setIncludeFontPadding(false);
263		Spannable span = new SpannableString(body);
264		span.setSpan(new RelativeSizeSpan(4.0f), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
265		span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
266		viewHolder.messageBody.setText(span);
267	}
268
269	private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground) {
270		if (viewHolder.download_button != null) {
271			viewHolder.download_button.setVisibility(View.GONE);
272		}
273		viewHolder.image.setVisibility(View.GONE);
274		viewHolder.messageBody.setVisibility(View.VISIBLE);
275		viewHolder.messageBody.setIncludeFontPadding(true);
276		if (message.getBody() != null) {
277			final String nick = UIHelper.getMessageDisplayName(message);
278			String body;
279			try {
280				body = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND, nick + " ");
281			} catch (ArrayIndexOutOfBoundsException e) {
282				body = message.getMergedBody();
283			}
284			final SpannableString formattedBody = new SpannableString(body);
285			int i = body.indexOf(Message.MERGE_SEPARATOR);
286			while(i >= 0) {
287				final int end = i + Message.MERGE_SEPARATOR.length();
288				formattedBody.setSpan(new RelativeSizeSpan(0.3f),i,end,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
289				i = body.indexOf(Message.MERGE_SEPARATOR,end);
290			}
291			if (message.getType() != Message.TYPE_PRIVATE) {
292				if (message.hasMeCommand()) {
293					final Spannable span = new SpannableString(formattedBody);
294					span.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(),
295							Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
296					viewHolder.messageBody.setText(span);
297				} else {
298					viewHolder.messageBody.setText(formattedBody);
299				}
300			} else {
301				String privateMarker;
302				if (message.getStatus() <= Message.STATUS_RECEIVED) {
303					privateMarker = activity
304						.getString(R.string.private_message);
305				} else {
306					final String to;
307					if (message.getCounterpart() != null) {
308						to = message.getCounterpart().getResourcepart();
309					} else {
310						to = "";
311					}
312					privateMarker = activity.getString(R.string.private_message_to, to);
313				}
314				final Spannable span = new SpannableString(privateMarker + " "
315						+ formattedBody);
316				span.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground,false)), 0, privateMarker
317						.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
318				span.setSpan(new StyleSpan(Typeface.BOLD), 0,
319						privateMarker.length(),
320						Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
321				if (message.hasMeCommand()) {
322					span.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarker.length() + 1,
323							privateMarker.length() + 1 + nick.length(),
324							Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
325				}
326				viewHolder.messageBody.setText(span);
327			}
328		} else {
329			viewHolder.messageBody.setText("");
330		}
331		viewHolder.messageBody.setTextColor(this.getMessageTextColor(darkBackground, true));
332		viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground, true));
333		viewHolder.messageBody.setHighlightColor(activity.getResources().getColor(darkBackground ? R.color.grey800 : R.color.grey500));
334		viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
335		viewHolder.messageBody.setTextIsSelectable(true);
336	}
337
338	private void displayDownloadableMessage(ViewHolder viewHolder,
339			final Message message, String text) {
340		viewHolder.image.setVisibility(View.GONE);
341		viewHolder.messageBody.setVisibility(View.GONE);
342		viewHolder.download_button.setVisibility(View.VISIBLE);
343		viewHolder.download_button.setText(text);
344		viewHolder.download_button.setOnClickListener(new OnClickListener() {
345
346			@Override
347			public void onClick(View v) {
348				startDownloadable(message);
349			}
350		});
351		viewHolder.download_button.setOnLongClickListener(openContextMenu);
352	}
353
354	private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
355		viewHolder.image.setVisibility(View.GONE);
356		viewHolder.messageBody.setVisibility(View.GONE);
357		viewHolder.download_button.setVisibility(View.VISIBLE);
358		viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
359		viewHolder.download_button.setOnClickListener(new OnClickListener() {
360
361			@Override
362			public void onClick(View v) {
363				openDownloadable(message);
364			}
365		});
366		viewHolder.download_button.setOnLongClickListener(openContextMenu);
367	}
368
369	private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
370		viewHolder.image.setVisibility(View.GONE);
371		viewHolder.messageBody.setVisibility(View.GONE);
372		viewHolder.download_button.setVisibility(View.VISIBLE);
373		viewHolder.download_button.setText(R.string.show_location);
374		viewHolder.download_button.setOnClickListener(new OnClickListener() {
375
376			@Override
377			public void onClick(View v) {
378				showLocation(message);
379			}
380		});
381		viewHolder.download_button.setOnLongClickListener(openContextMenu);
382	}
383
384	private void displayImageMessage(ViewHolder viewHolder,
385			final Message message) {
386		if (viewHolder.download_button != null) {
387			viewHolder.download_button.setVisibility(View.GONE);
388		}
389		viewHolder.messageBody.setVisibility(View.GONE);
390		viewHolder.image.setVisibility(View.VISIBLE);
391		FileParams params = message.getFileParams();
392		double target = metrics.density * 288;
393		int scalledW;
394		int scalledH;
395		if (params.width <= params.height) {
396			scalledW = (int) (params.width / ((double) params.height / target));
397			scalledH = (int) target;
398		} else {
399			scalledW = (int) target;
400			scalledH = (int) (params.height / ((double) params.width / target));
401		}
402		LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scalledW, scalledH);
403		layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4));
404		viewHolder.image.setLayoutParams(layoutParams);
405		activity.loadBitmap(message, viewHolder.image);
406		viewHolder.image.setOnClickListener(new OnClickListener() {
407
408			@Override
409			public void onClick(View v) {
410				openDownloadable(message);
411			}
412		});
413		viewHolder.image.setOnLongClickListener(openContextMenu);
414	}
415
416	@Override
417	public View getView(int position, View view, ViewGroup parent) {
418		final Message message = getItem(position);
419		final boolean isInValidSession = message.isValidInSession();
420		final Conversation conversation = message.getConversation();
421		final Account account = conversation.getAccount();
422		final int type = getItemViewType(position);
423		ViewHolder viewHolder;
424		if (view == null) {
425			viewHolder = new ViewHolder();
426			switch (type) {
427				case SENT:
428					view = activity.getLayoutInflater().inflate(
429							R.layout.message_sent, parent, false);
430					viewHolder.message_box = (LinearLayout) view
431						.findViewById(R.id.message_box);
432					viewHolder.contact_picture = (ImageView) view
433						.findViewById(R.id.message_photo);
434					viewHolder.download_button = (Button) view
435						.findViewById(R.id.download_button);
436					viewHolder.indicator = (ImageView) view
437						.findViewById(R.id.security_indicator);
438					viewHolder.image = (ImageView) view
439						.findViewById(R.id.message_image);
440					viewHolder.messageBody = (TextView) view
441						.findViewById(R.id.message_body);
442					viewHolder.time = (TextView) view
443						.findViewById(R.id.message_time);
444					viewHolder.indicatorReceived = (ImageView) view
445						.findViewById(R.id.indicator_received);
446					break;
447				case RECEIVED:
448					view = activity.getLayoutInflater().inflate(
449							R.layout.message_received, parent, false);
450					viewHolder.message_box = (LinearLayout) view
451						.findViewById(R.id.message_box);
452					viewHolder.contact_picture = (ImageView) view
453						.findViewById(R.id.message_photo);
454					viewHolder.download_button = (Button) view
455						.findViewById(R.id.download_button);
456					viewHolder.indicator = (ImageView) view
457						.findViewById(R.id.security_indicator);
458					viewHolder.image = (ImageView) view
459						.findViewById(R.id.message_image);
460					viewHolder.messageBody = (TextView) view
461						.findViewById(R.id.message_body);
462					viewHolder.time = (TextView) view
463						.findViewById(R.id.message_time);
464					viewHolder.indicatorReceived = (ImageView) view
465						.findViewById(R.id.indicator_received);
466					viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
467					break;
468				case STATUS:
469					view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
470					viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
471					viewHolder.status_message = (TextView) view.findViewById(R.id.status_message);
472					break;
473				default:
474					viewHolder = null;
475					break;
476			}
477			view.setTag(viewHolder);
478		} else {
479			viewHolder = (ViewHolder) view.getTag();
480			if (viewHolder == null) {
481				return view;
482			}
483		}
484
485		boolean darkBackground = (type == RECEIVED && (!isInValidSession || !mUseWhiteBackground));
486
487		if (type == STATUS) {
488			if (conversation.getMode() == Conversation.MODE_SINGLE) {
489				viewHolder.contact_picture.setImageBitmap(activity
490						.avatarService().get(conversation.getContact(),
491							activity.getPixel(32)));
492				viewHolder.contact_picture.setAlpha(0.5f);
493				viewHolder.status_message.setText(message.getBody());
494			}
495			return view;
496		} else if (type == RECEIVED) {
497			Contact contact = message.getContact();
498			if (contact != null) {
499				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(contact, activity.getPixel(48)));
500			} else if (conversation.getMode() == Conversation.MODE_MULTI) {
501				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(
502						UIHelper.getMessageDisplayName(message),
503						activity.getPixel(48)));
504			}
505		} else if (type == SENT) {
506			viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48)));
507		}
508
509		viewHolder.contact_picture
510			.setOnClickListener(new OnClickListener() {
511
512				@Override
513				public void onClick(View v) {
514					if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
515						MessageAdapter.this.mOnContactPictureClickedListener
516								.onContactPictureClicked(message);
517					}
518
519				}
520			});
521		viewHolder.contact_picture
522			.setOnLongClickListener(new OnLongClickListener() {
523
524				@Override
525				public boolean onLongClick(View v) {
526					if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
527						MessageAdapter.this.mOnContactPictureLongClickedListener
528								.onContactPictureLongClicked(message);
529						return true;
530					} else {
531						return false;
532					}
533				}
534			});
535
536		final Transferable transferable = message.getTransferable();
537		if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) {
538			if (transferable.getStatus() == Transferable.STATUS_OFFER) {
539				displayDownloadableMessage(viewHolder,message,activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
540			} else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
541				displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
542			} else {
543				displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first,darkBackground);
544			}
545		} else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
546			displayImageMessage(viewHolder, message);
547		} else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
548			if (message.getFileParams().width > 0) {
549				displayImageMessage(viewHolder,message);
550			} else {
551				displayOpenableMessage(viewHolder, message);
552			}
553		} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
554			if (activity.hasPgp()) {
555				displayInfoMessage(viewHolder,activity.getString(R.string.encrypted_message),darkBackground);
556			} else {
557				displayInfoMessage(viewHolder,activity.getString(R.string.install_openkeychain),darkBackground);
558				if (viewHolder != null) {
559					viewHolder.message_box
560						.setOnClickListener(new OnClickListener() {
561
562							@Override
563							public void onClick(View v) {
564								activity.showInstallPgpDialog();
565							}
566						});
567				}
568			}
569		} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
570			displayDecryptionFailed(viewHolder,darkBackground);
571		} else {
572			if (GeoHelper.isGeoUri(message.getBody())) {
573				displayLocationMessage(viewHolder,message);
574			} else if (message.bodyIsHeart()) {
575				displayHeartMessage(viewHolder, message.getBody().trim());
576			} else if (message.treatAsDownloadable() == Message.Decision.MUST) {
577				displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
578			} else {
579				displayTextMessage(viewHolder, message, darkBackground);
580			}
581		}
582
583		if (type == RECEIVED) {
584			if(isInValidSession) {
585				if (mUseWhiteBackground) {
586					viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_white);
587				} else {
588					viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received);
589				}
590				viewHolder.encryption.setVisibility(View.GONE);
591			} else {
592				viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning);
593				viewHolder.encryption.setVisibility(View.VISIBLE);
594				viewHolder.encryption.setText(CryptoHelper.encryptionTypeToText(message.getEncryption()));
595			}
596		}
597
598		displayStatus(viewHolder, message, type, darkBackground);
599
600		return view;
601	}
602
603	public void startDownloadable(Message message) {
604		Transferable transferable = message.getTransferable();
605		if (transferable != null) {
606			if (!transferable.start()) {
607				Toast.makeText(activity, R.string.not_connected_try_again,
608						Toast.LENGTH_SHORT).show();
609			}
610		} else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
611			activity.xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message, true);
612		}
613	}
614
615	public void openDownloadable(Message message) {
616		DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
617		if (!file.exists()) {
618			Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
619			return;
620		}
621		Intent openIntent = new Intent(Intent.ACTION_VIEW);
622		String mime = file.getMimeType();
623		if (mime == null) {
624			mime = "*/*";
625		}
626		openIntent.setDataAndType(Uri.fromFile(file), mime);
627		PackageManager manager = activity.getPackageManager();
628		List<ResolveInfo> infos = manager.queryIntentActivities(openIntent, 0);
629		if (infos.size() == 0) {
630			openIntent.setDataAndType(Uri.fromFile(file),"*/*");
631		}
632		try {
633			getContext().startActivity(openIntent);
634			return;
635		}  catch (ActivityNotFoundException e) {
636			//ignored
637		}
638		Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show();
639	}
640
641	public void showLocation(Message message) {
642		for(Intent intent : GeoHelper.createGeoIntentsFromMessage(message)) {
643			if (intent.resolveActivity(getContext().getPackageManager()) != null) {
644				getContext().startActivity(intent);
645				return;
646			}
647		}
648		Toast.makeText(activity,R.string.no_application_found_to_display_location,Toast.LENGTH_SHORT).show();
649	}
650
651	public void updatePreferences() {
652		this.mIndicateReceived = activity.indicateReceived();
653		this.mUseWhiteBackground = activity.useWhiteBackground();
654	}
655
656	public interface OnContactPictureClicked {
657		void onContactPictureClicked(Message message);
658	}
659
660	public interface OnContactPictureLongClicked {
661		void onContactPictureLongClicked(Message message);
662	}
663
664	private static class ViewHolder {
665
666		protected LinearLayout message_box;
667		protected Button download_button;
668		protected ImageView image;
669		protected ImageView indicator;
670		protected ImageView indicatorReceived;
671		protected TextView time;
672		protected TextView messageBody;
673		protected ImageView contact_picture;
674		protected TextView status_message;
675		protected TextView encryption;
676	}
677}