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				Intent intent = new Intent(Intent.ACTION_VIEW);
399				intent.setDataAndType(activity.xmppConnectionService
400						.getFileBackend().getJingleFileUri(message), "image/*");
401				getContext().startActivity(intent);
402			}
403		});
404		viewHolder.image.setOnLongClickListener(openContextMenu);
405	}
406
407	@Override
408	public View getView(int position, View view, ViewGroup parent) {
409		final Message message = getItem(position);
410		final Conversation conversation = message.getConversation();
411		final Account account = conversation.getAccount();
412		final int type = getItemViewType(position);
413		ViewHolder viewHolder;
414		if (view == null) {
415			viewHolder = new ViewHolder();
416			switch (type) {
417				case SENT:
418					view = activity.getLayoutInflater().inflate(
419							R.layout.message_sent, parent, false);
420					viewHolder.message_box = (LinearLayout) view
421						.findViewById(R.id.message_box);
422					viewHolder.contact_picture = (ImageView) view
423						.findViewById(R.id.message_photo);
424					viewHolder.download_button = (Button) view
425						.findViewById(R.id.download_button);
426					viewHolder.indicator = (ImageView) view
427						.findViewById(R.id.security_indicator);
428					viewHolder.image = (ImageView) view
429						.findViewById(R.id.message_image);
430					viewHolder.messageBody = (TextView) view
431						.findViewById(R.id.message_body);
432					viewHolder.time = (TextView) view
433						.findViewById(R.id.message_time);
434					viewHolder.indicatorReceived = (ImageView) view
435						.findViewById(R.id.indicator_received);
436					break;
437				case RECEIVED:
438					view = activity.getLayoutInflater().inflate(
439							R.layout.message_received, parent, false);
440					viewHolder.message_box = (LinearLayout) view
441						.findViewById(R.id.message_box);
442					viewHolder.contact_picture = (ImageView) view
443						.findViewById(R.id.message_photo);
444					viewHolder.download_button = (Button) view
445						.findViewById(R.id.download_button);
446					viewHolder.indicator = (ImageView) view
447						.findViewById(R.id.security_indicator);
448					viewHolder.image = (ImageView) view
449						.findViewById(R.id.message_image);
450					viewHolder.messageBody = (TextView) view
451						.findViewById(R.id.message_body);
452					viewHolder.time = (TextView) view
453						.findViewById(R.id.message_time);
454					viewHolder.indicatorReceived = (ImageView) view
455						.findViewById(R.id.indicator_received);
456					break;
457				case STATUS:
458					view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
459					viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
460					viewHolder.status_message = (TextView) view.findViewById(R.id.status_message);
461					break;
462				default:
463					viewHolder = null;
464					break;
465			}
466			view.setTag(viewHolder);
467		} else {
468			viewHolder = (ViewHolder) view.getTag();
469			if (viewHolder == null) {
470				return view;
471			}
472		}
473
474		if (type == STATUS) {
475			if (conversation.getMode() == Conversation.MODE_SINGLE) {
476				viewHolder.contact_picture.setImageBitmap(activity
477						.avatarService().get(conversation.getContact(),
478							activity.getPixel(32)));
479				viewHolder.contact_picture.setAlpha(0.5f);
480				viewHolder.status_message.setText(message.getBody());
481			}
482			return view;
483		} else if (type == RECEIVED) {
484			Contact contact = message.getContact();
485			if (contact != null) {
486				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(contact, activity.getPixel(48)));
487			} else if (conversation.getMode() == Conversation.MODE_MULTI) {
488				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(
489						UIHelper.getMessageDisplayName(message),
490						activity.getPixel(48)));
491			}
492		} else if (type == SENT) {
493			viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48)));
494		}
495
496		viewHolder.contact_picture
497			.setOnClickListener(new OnClickListener() {
498
499				@Override
500				public void onClick(View v) {
501					if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
502						MessageAdapter.this.mOnContactPictureClickedListener
503							.onContactPictureClicked(message);
504					}
505
506				}
507			});
508		viewHolder.contact_picture
509			.setOnLongClickListener(new OnLongClickListener() {
510
511				@Override
512				public boolean onLongClick(View v) {
513					if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
514						MessageAdapter.this.mOnContactPictureLongClickedListener
515							.onContactPictureLongClicked(message);
516						return true;
517					} else {
518						return false;
519					}
520				}
521			});
522
523		final Transferable transferable = message.getTransferable();
524		if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) {
525			if (transferable.getStatus() == Transferable.STATUS_OFFER) {
526				displayDownloadableMessage(viewHolder,message,activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
527			} else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
528				displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
529			} else {
530				displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first,type);
531			}
532		} else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
533			displayImageMessage(viewHolder, message);
534		} else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
535			if (message.getFileParams().width > 0) {
536				displayImageMessage(viewHolder,message);
537			} else {
538				displayOpenableMessage(viewHolder, message);
539			}
540		} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
541			if (activity.hasPgp()) {
542				displayInfoMessage(viewHolder,activity.getString(R.string.encrypted_message),type);
543			} else {
544				displayInfoMessage(viewHolder,activity.getString(R.string.install_openkeychain),type);
545				if (viewHolder != null) {
546					viewHolder.message_box
547						.setOnClickListener(new OnClickListener() {
548
549							@Override
550							public void onClick(View v) {
551								activity.showInstallPgpDialog();
552							}
553						});
554				}
555			}
556		} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
557			displayDecryptionFailed(viewHolder,type);
558		} else {
559			if (GeoHelper.isGeoUri(message.getBody())) {
560				displayLocationMessage(viewHolder,message);
561			} else if (message.bodyIsHeart()) {
562				displayHeartMessage(viewHolder, message.getBody().trim());
563			} else if (message.treatAsDownloadable() == Message.Decision.MUST) {
564				displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
565			} else {
566				displayTextMessage(viewHolder, message, type);
567			}
568		}
569
570		if (type == RECEIVED) {
571			if(message.isValidInSession()) {
572				viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received);
573			} else {
574				viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning);
575			}
576		}
577
578		displayStatus(viewHolder, message, type);
579
580		return view;
581	}
582
583	public void startDownloadable(Message message) {
584		Transferable transferable = message.getTransferable();
585		if (transferable != null) {
586			if (!transferable.start()) {
587				Toast.makeText(activity, R.string.not_connected_try_again,
588						Toast.LENGTH_SHORT).show();
589			}
590		} else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
591			activity.xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message,true);
592		}
593	}
594
595	public void openDownloadable(Message message) {
596		DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
597		if (!file.exists()) {
598			Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
599			return;
600		}
601		Intent openIntent = new Intent(Intent.ACTION_VIEW);
602		openIntent.setDataAndType(Uri.fromFile(file), file.getMimeType());
603		PackageManager manager = activity.getPackageManager();
604		List<ResolveInfo> infos = manager.queryIntentActivities(openIntent, 0);
605		if (infos.size() > 0) {
606			getContext().startActivity(openIntent);
607		} else {
608			Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show();
609		}
610	}
611
612	public void showLocation(Message message) {
613		for(Intent intent : GeoHelper.createGeoIntentsFromMessage(message)) {
614			if (intent.resolveActivity(getContext().getPackageManager()) != null) {
615				getContext().startActivity(intent);
616				return;
617			}
618		}
619		Toast.makeText(activity,R.string.no_application_found_to_display_location,Toast.LENGTH_SHORT).show();
620	}
621
622	public interface OnContactPictureClicked {
623		public void onContactPictureClicked(Message message);
624	}
625
626	public interface OnContactPictureLongClicked {
627		public void onContactPictureLongClicked(Message message);
628	}
629
630	private static class ViewHolder {
631
632		protected LinearLayout message_box;
633		protected Button download_button;
634		protected ImageView image;
635		protected ImageView indicator;
636		protected ImageView indicatorReceived;
637		protected TextView time;
638		protected TextView messageBody;
639		protected ImageView contact_picture;
640		protected TextView status_message;
641	}
642}