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