MessageAdapter.java

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