MessageAdapter.java

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