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.SQLiteAxolotlStore;
 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				SQLiteAxolotlStore.Trust trust = message.getConversation()
173						.getAccount().getAxolotlService().getFingerprintTrust(
174								message.getAxolotlFingerprint());
175
176				if(trust == null || trust != SQLiteAxolotlStore.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(activity
307							.getSecondaryTextColor()), 0, privateMarker
308						.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
309				span.setSpan(new StyleSpan(Typeface.BOLD), 0,
310						privateMarker.length(),
311						Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
312				if (message.hasMeCommand()) {
313					span.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarker.length() + 1,
314							privateMarker.length() + 1 + nick.length(),
315							Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
316				}
317				viewHolder.messageBody.setText(span);
318			}
319		} else {
320			viewHolder.messageBody.setText("");
321		}
322		viewHolder.messageBody.setTextColor(this.getMessageTextColor(type,true));
323		viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
324		viewHolder.messageBody.setTextIsSelectable(true);
325	}
326
327	private void displayDownloadableMessage(ViewHolder viewHolder,
328			final Message message, String text) {
329		viewHolder.image.setVisibility(View.GONE);
330		viewHolder.messageBody.setVisibility(View.GONE);
331		viewHolder.download_button.setVisibility(View.VISIBLE);
332		viewHolder.download_button.setText(text);
333		viewHolder.download_button.setOnClickListener(new OnClickListener() {
334
335			@Override
336			public void onClick(View v) {
337				startDownloadable(message);
338			}
339		});
340		viewHolder.download_button.setOnLongClickListener(openContextMenu);
341	}
342
343	private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
344		viewHolder.image.setVisibility(View.GONE);
345		viewHolder.messageBody.setVisibility(View.GONE);
346		viewHolder.download_button.setVisibility(View.VISIBLE);
347		viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
348		viewHolder.download_button.setOnClickListener(new OnClickListener() {
349
350			@Override
351			public void onClick(View v) {
352				openDownloadable(message);
353			}
354		});
355		viewHolder.download_button.setOnLongClickListener(openContextMenu);
356	}
357
358	private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
359		viewHolder.image.setVisibility(View.GONE);
360		viewHolder.messageBody.setVisibility(View.GONE);
361		viewHolder.download_button.setVisibility(View.VISIBLE);
362		viewHolder.download_button.setText(R.string.show_location);
363		viewHolder.download_button.setOnClickListener(new OnClickListener() {
364
365			@Override
366			public void onClick(View v) {
367				showLocation(message);
368			}
369		});
370		viewHolder.download_button.setOnLongClickListener(openContextMenu);
371	}
372
373	private void displayImageMessage(ViewHolder viewHolder,
374			final Message message) {
375		if (viewHolder.download_button != null) {
376			viewHolder.download_button.setVisibility(View.GONE);
377		}
378		viewHolder.messageBody.setVisibility(View.GONE);
379		viewHolder.image.setVisibility(View.VISIBLE);
380		FileParams params = message.getFileParams();
381		double target = metrics.density * 288;
382		int scalledW;
383		int scalledH;
384		if (params.width <= params.height) {
385			scalledW = (int) (params.width / ((double) params.height / target));
386			scalledH = (int) target;
387		} else {
388			scalledW = (int) target;
389			scalledH = (int) (params.height / ((double) params.width / target));
390		}
391		LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scalledW, scalledH);
392		layoutParams.setMargins(0, (int)(metrics.density * 4), 0, (int)(metrics.density * 4));
393		viewHolder.image.setLayoutParams(layoutParams);
394		activity.loadBitmap(message, viewHolder.image);
395		viewHolder.image.setOnClickListener(new OnClickListener() {
396
397			@Override
398			public void onClick(View v) {
399				Intent intent = new Intent(Intent.ACTION_VIEW);
400				intent.setDataAndType(activity.xmppConnectionService
401						.getFileBackend().getJingleFileUri(message), "image/*");
402				getContext().startActivity(intent);
403			}
404		});
405		viewHolder.image.setOnLongClickListener(openContextMenu);
406	}
407
408	@Override
409	public View getView(int position, View view, ViewGroup parent) {
410		final Message message = getItem(position);
411		final Conversation conversation = message.getConversation();
412		final Account account = conversation.getAccount();
413		final int type = getItemViewType(position);
414		ViewHolder viewHolder;
415		if (view == null) {
416			viewHolder = new ViewHolder();
417			switch (type) {
418				case SENT:
419					view = activity.getLayoutInflater().inflate(
420							R.layout.message_sent, parent, false);
421					viewHolder.message_box = (LinearLayout) view
422						.findViewById(R.id.message_box);
423					viewHolder.contact_picture = (ImageView) view
424						.findViewById(R.id.message_photo);
425					viewHolder.download_button = (Button) view
426						.findViewById(R.id.download_button);
427					viewHolder.indicator = (ImageView) view
428						.findViewById(R.id.security_indicator);
429					viewHolder.image = (ImageView) view
430						.findViewById(R.id.message_image);
431					viewHolder.messageBody = (TextView) view
432						.findViewById(R.id.message_body);
433					viewHolder.time = (TextView) view
434						.findViewById(R.id.message_time);
435					viewHolder.indicatorReceived = (ImageView) view
436						.findViewById(R.id.indicator_received);
437					break;
438				case RECEIVED:
439					view = activity.getLayoutInflater().inflate(
440							R.layout.message_received, parent, false);
441					viewHolder.message_box = (LinearLayout) view
442						.findViewById(R.id.message_box);
443					viewHolder.contact_picture = (ImageView) view
444						.findViewById(R.id.message_photo);
445					viewHolder.download_button = (Button) view
446						.findViewById(R.id.download_button);
447					viewHolder.indicator = (ImageView) view
448						.findViewById(R.id.security_indicator);
449					viewHolder.image = (ImageView) view
450						.findViewById(R.id.message_image);
451					viewHolder.messageBody = (TextView) view
452						.findViewById(R.id.message_body);
453					viewHolder.time = (TextView) view
454						.findViewById(R.id.message_time);
455					viewHolder.indicatorReceived = (ImageView) view
456						.findViewById(R.id.indicator_received);
457					break;
458				case STATUS:
459					view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
460					viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
461					viewHolder.status_message = (TextView) view.findViewById(R.id.status_message);
462					break;
463				default:
464					viewHolder = null;
465					break;
466			}
467			view.setTag(viewHolder);
468		} else {
469			viewHolder = (ViewHolder) view.getTag();
470			if (viewHolder == null) {
471				return view;
472			}
473		}
474
475		if (type == STATUS) {
476			if (conversation.getMode() == Conversation.MODE_SINGLE) {
477				viewHolder.contact_picture.setImageBitmap(activity
478						.avatarService().get(conversation.getContact(),
479							activity.getPixel(32)));
480				viewHolder.contact_picture.setAlpha(0.5f);
481				viewHolder.status_message.setText(message.getBody());
482			}
483			return view;
484		} else if (type == RECEIVED) {
485			Contact contact = message.getContact();
486			if (contact != null) {
487				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(contact, activity.getPixel(48)));
488			} else if (conversation.getMode() == Conversation.MODE_MULTI) {
489				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(
490						UIHelper.getMessageDisplayName(message),
491						activity.getPixel(48)));
492			}
493		} else if (type == SENT) {
494			viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48)));
495		}
496
497		viewHolder.contact_picture
498			.setOnClickListener(new OnClickListener() {
499
500				@Override
501				public void onClick(View v) {
502					if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
503						MessageAdapter.this.mOnContactPictureClickedListener
504							.onContactPictureClicked(message);
505					}
506
507				}
508			});
509		viewHolder.contact_picture
510			.setOnLongClickListener(new OnLongClickListener() {
511
512				@Override
513				public boolean onLongClick(View v) {
514					if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
515						MessageAdapter.this.mOnContactPictureLongClickedListener
516							.onContactPictureLongClicked(message);
517						return true;
518					} else {
519						return false;
520					}
521				}
522			});
523
524		final Transferable transferable = message.getTransferable();
525		if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) {
526			if (transferable.getStatus() == Transferable.STATUS_OFFER) {
527				displayDownloadableMessage(viewHolder,message,activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
528			} else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
529				displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
530			} else {
531				displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first,type);
532			}
533		} else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
534			displayImageMessage(viewHolder, message);
535		} else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
536			if (message.getFileParams().width > 0) {
537				displayImageMessage(viewHolder,message);
538			} else {
539				displayOpenableMessage(viewHolder, message);
540			}
541		} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
542			if (activity.hasPgp()) {
543				displayInfoMessage(viewHolder,activity.getString(R.string.encrypted_message),type);
544			} else {
545				displayInfoMessage(viewHolder,activity.getString(R.string.install_openkeychain),type);
546				if (viewHolder != null) {
547					viewHolder.message_box
548						.setOnClickListener(new OnClickListener() {
549
550							@Override
551							public void onClick(View v) {
552								activity.showInstallPgpDialog();
553							}
554						});
555				}
556			}
557		} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
558			displayDecryptionFailed(viewHolder,type);
559		} else {
560			if (GeoHelper.isGeoUri(message.getBody())) {
561				displayLocationMessage(viewHolder,message);
562			} else if (message.bodyIsHeart()) {
563				displayHeartMessage(viewHolder, message.getBody().trim());
564			} else if (message.treatAsDownloadable() == Message.Decision.MUST) {
565				displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
566			} else {
567				displayTextMessage(viewHolder, message, type);
568			}
569		}
570
571		if (type == RECEIVED) {
572			if(message.isValidInSession()) {
573				viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received);
574			} else {
575				viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning);
576			}
577		}
578
579		displayStatus(viewHolder, message, type);
580
581		return view;
582	}
583
584	public void startDownloadable(Message message) {
585		Transferable transferable = message.getTransferable();
586		if (transferable != null) {
587			if (!transferable.start()) {
588				Toast.makeText(activity, R.string.not_connected_try_again,
589						Toast.LENGTH_SHORT).show();
590			}
591		} else if (message.treatAsDownloadable() != Message.Decision.NEVER) {
592			activity.xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message,true);
593		}
594	}
595
596	public void openDownloadable(Message message) {
597		DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
598		if (!file.exists()) {
599			Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
600			return;
601		}
602		Intent openIntent = new Intent(Intent.ACTION_VIEW);
603		openIntent.setDataAndType(Uri.fromFile(file), file.getMimeType());
604		PackageManager manager = activity.getPackageManager();
605		List<ResolveInfo> infos = manager.queryIntentActivities(openIntent, 0);
606		if (infos.size() > 0) {
607			getContext().startActivity(openIntent);
608		} else {
609			Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show();
610		}
611	}
612
613	public void showLocation(Message message) {
614		for(Intent intent : GeoHelper.createGeoIntentsFromMessage(message)) {
615			if (intent.resolveActivity(getContext().getPackageManager()) != null) {
616				getContext().startActivity(intent);
617				return;
618			}
619		}
620		Toast.makeText(activity,R.string.no_application_found_to_display_location,Toast.LENGTH_SHORT).show();
621	}
622
623	public interface OnContactPictureClicked {
624		public void onContactPictureClicked(Message message);
625	}
626
627	public interface OnContactPictureLongClicked {
628		public void onContactPictureLongClicked(Message message);
629	}
630
631	private static class ViewHolder {
632
633		protected LinearLayout message_box;
634		protected Button download_button;
635		protected ImageView image;
636		protected ImageView indicator;
637		protected ImageView indicatorReceived;
638		protected TextView time;
639		protected TextView messageBody;
640		protected ImageView contact_picture;
641		protected TextView status_message;
642	}
643}