MessageAdapter.java

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