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().getResourcepart();
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(R.string.private_message_to, to);
250				}
251				SpannableString span = new SpannableString(privateMarker + " "
252						+ message.getBody());
253				span.setSpan(
254						new ForegroundColorSpan(activity
255								.getSecondaryTextColor()), 0, privateMarker
256								.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
257				span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0,
258						privateMarker.length(),
259						Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
260				viewHolder.messageBody.setText(span);
261			}
262		} else {
263			viewHolder.messageBody.setText("");
264		}
265		viewHolder.messageBody.setTextColor(activity.getPrimaryTextColor());
266		viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
267		viewHolder.messageBody.setTextIsSelectable(true);
268	}
269
270	private void displayDownloadableMessage(ViewHolder viewHolder,
271			final Message message, int resid) {
272		viewHolder.image.setVisibility(View.GONE);
273		viewHolder.messageBody.setVisibility(View.GONE);
274		viewHolder.download_button.setVisibility(View.VISIBLE);
275		viewHolder.download_button.setText(resid);
276		viewHolder.download_button.setOnClickListener(new OnClickListener() {
277
278			@Override
279			public void onClick(View v) {
280				startDonwloadable(message);
281			}
282		});
283		viewHolder.download_button.setOnLongClickListener(openContextMenu);
284	}
285
286	private void displayImageMessage(ViewHolder viewHolder,
287			final Message message) {
288		if (viewHolder.download_button != null) {
289			viewHolder.download_button.setVisibility(View.GONE);
290		}
291		viewHolder.messageBody.setVisibility(View.GONE);
292		viewHolder.image.setVisibility(View.VISIBLE);
293		ImageParams params = message.getImageParams();
294		double target = metrics.density * 288;
295		int scalledW;
296		int scalledH;
297		if (params.width <= params.height) {
298			scalledW = (int) (params.width / ((double) params.height / target));
299			scalledH = (int) target;
300		} else {
301			scalledW = (int) target;
302			scalledH = (int) (params.height / ((double) params.width / target));
303		}
304		viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
305				scalledW, scalledH));
306		activity.loadBitmap(message, viewHolder.image);
307		viewHolder.image.setOnClickListener(new OnClickListener() {
308
309			@Override
310			public void onClick(View v) {
311				Intent intent = new Intent(Intent.ACTION_VIEW);
312				intent.setDataAndType(activity.xmppConnectionService
313						.getFileBackend().getJingleFileUri(message), "image/*");
314				getContext().startActivity(intent);
315			}
316		});
317		viewHolder.image.setOnLongClickListener(openContextMenu);
318	}
319
320	@Override
321	public View getView(int position, View view, ViewGroup parent) {
322		final Message item = getItem(position);
323		int type = getItemViewType(position);
324		ViewHolder viewHolder;
325		if (view == null) {
326			viewHolder = new ViewHolder();
327			switch (type) {
328			case NULL:
329				view = activity.getLayoutInflater().inflate(
330						R.layout.message_null, parent, false);
331				break;
332			case SENT:
333				view = activity.getLayoutInflater().inflate(
334						R.layout.message_sent, parent, false);
335				viewHolder.message_box = (LinearLayout) view
336						.findViewById(R.id.message_box);
337				viewHolder.contact_picture = (ImageView) view
338						.findViewById(R.id.message_photo);
339				viewHolder.download_button = (Button) view
340						.findViewById(R.id.download_button);
341				viewHolder.indicator = (ImageView) view
342						.findViewById(R.id.security_indicator);
343				viewHolder.image = (ImageView) view
344						.findViewById(R.id.message_image);
345				viewHolder.messageBody = (TextView) view
346						.findViewById(R.id.message_body);
347				viewHolder.time = (TextView) view
348						.findViewById(R.id.message_time);
349				viewHolder.indicatorReceived = (ImageView) view
350						.findViewById(R.id.indicator_received);
351				view.setTag(viewHolder);
352				break;
353			case RECEIVED:
354				view = activity.getLayoutInflater().inflate(
355						R.layout.message_received, parent, false);
356				viewHolder.message_box = (LinearLayout) view
357						.findViewById(R.id.message_box);
358				viewHolder.contact_picture = (ImageView) view
359						.findViewById(R.id.message_photo);
360				viewHolder.download_button = (Button) view
361						.findViewById(R.id.download_button);
362				viewHolder.indicator = (ImageView) view
363						.findViewById(R.id.security_indicator);
364				viewHolder.image = (ImageView) view
365						.findViewById(R.id.message_image);
366				viewHolder.messageBody = (TextView) view
367						.findViewById(R.id.message_body);
368				viewHolder.time = (TextView) view
369						.findViewById(R.id.message_time);
370				viewHolder.indicatorReceived = (ImageView) view
371						.findViewById(R.id.indicator_received);
372				view.setTag(viewHolder);
373				break;
374			case STATUS:
375				view = activity.getLayoutInflater().inflate(
376						R.layout.message_status, parent, false);
377				viewHolder.contact_picture = (ImageView) view
378						.findViewById(R.id.message_photo);
379				view.setTag(viewHolder);
380				break;
381			default:
382				viewHolder = null;
383				break;
384			}
385		} else {
386			viewHolder = (ViewHolder) view.getTag();
387		}
388
389		if (type == STATUS) {
390			if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
391				viewHolder.contact_picture.setImageBitmap(activity
392						.avatarService().get(
393								item.getConversation().getContact(),
394								activity.getPixel(32)));
395				viewHolder.contact_picture.setAlpha(0.5f);
396				viewHolder.contact_picture
397						.setOnClickListener(new OnClickListener() {
398
399							@Override
400							public void onClick(View v) {
401								String name = item.getConversation()
402										.getName();
403								String read = getContext()
404										.getString(
405												R.string.contact_has_read_up_to_this_point,
406												name);
407								Toast.makeText(getContext(), read,
408										Toast.LENGTH_SHORT).show();
409							}
410						});
411
412			}
413			return view;
414		} else if (type == NULL) {
415			if (position == getCount() - 1) {
416				view.getLayoutParams().height = 1;
417			} else {
418				view.getLayoutParams().height = 0;
419
420			}
421			view.setLayoutParams(view.getLayoutParams());
422			return view;
423		} else if (type == RECEIVED) {
424			Contact contact = item.getContact();
425			if (contact != null) {
426				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(contact, activity.getPixel(48)));
427			} else if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
428				String name = item.getPresence();
429				if (name == null) {
430					name = item.getCounterpart();
431				}
432				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(name, activity.getPixel(48)));
433			}
434		} else if (type == SENT) {
435			viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(item.getConversation().getAccount(), activity.getPixel(48)));
436		}
437
438		if (viewHolder != null && viewHolder.contact_picture != null) {
439			viewHolder.contact_picture
440					.setOnClickListener(new OnClickListener() {
441
442						@Override
443						public void onClick(View v) {
444							if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
445								MessageAdapter.this.mOnContactPictureClickedListener
446										.onContactPictureClicked(item);
447							}
448
449						}
450					});
451			viewHolder.contact_picture
452					.setOnLongClickListener(new OnLongClickListener() {
453
454						@Override
455						public boolean onLongClick(View v) {
456							if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
457								MessageAdapter.this.mOnContactPictureLongClickedListener
458										.onContactPictureLongClicked(item);
459								return true;
460							} else {
461								return false;
462							}
463						}
464					});
465		}
466
467		if (item.getType() == Message.TYPE_IMAGE
468				|| item.getDownloadable() != null) {
469			Downloadable d = item.getDownloadable();
470			if (d != null && d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
471				displayInfoMessage(viewHolder, R.string.receiving_image);
472			} else if (d != null
473					&& d.getStatus() == Downloadable.STATUS_CHECKING) {
474				displayInfoMessage(viewHolder, R.string.checking_image);
475			} else if (d != null
476					&& d.getStatus() == Downloadable.STATUS_DELETED) {
477				displayInfoMessage(viewHolder, R.string.image_file_deleted);
478			} else if (d != null && d.getStatus() == Downloadable.STATUS_OFFER) {
479				displayDownloadableMessage(viewHolder, item,
480						R.string.download_image);
481			} else if (d != null
482					&& d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
483				displayDownloadableMessage(viewHolder, item,
484						R.string.check_image_filesize);
485			} else if (d != null && d.getStatus() == Downloadable.STATUS_FAILED) {
486				displayInfoMessage(viewHolder, R.string.image_transmission_failed);
487			} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
488					|| (item.getEncryption() == Message.ENCRYPTION_NONE)
489					|| (item.getEncryption() == Message.ENCRYPTION_OTR)) {
490				displayImageMessage(viewHolder, item);
491			} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
492				displayInfoMessage(viewHolder, R.string.encrypted_message);
493			} else {
494				displayDecryptionFailed(viewHolder);
495			}
496		} else {
497			if (item.getEncryption() == Message.ENCRYPTION_PGP) {
498				if (activity.hasPgp()) {
499					displayInfoMessage(viewHolder, R.string.encrypted_message);
500				} else {
501					displayInfoMessage(viewHolder,
502							R.string.install_openkeychain);
503                    if (viewHolder != null) {
504                        viewHolder.message_box
505                                .setOnClickListener(new OnClickListener() {
506
507                                    @Override
508                                    public void onClick(View v) {
509                                        activity.showInstallPgpDialog();
510                                    }
511                                });
512                    }
513				}
514			} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
515				displayDecryptionFailed(viewHolder);
516			} else {
517				displayTextMessage(viewHolder, item);
518			}
519		}
520
521		displayStatus(viewHolder, item);
522
523		return view;
524	}
525
526	public void startDonwloadable(Message message) {
527		Downloadable downloadable = message.getDownloadable();
528		if (downloadable != null) {
529			if (!downloadable.start()) {
530				Toast.makeText(activity, R.string.not_connected_try_again,
531						Toast.LENGTH_SHORT).show();
532			}
533		}
534	}
535
536	public interface OnContactPictureClicked {
537		public void onContactPictureClicked(Message message);
538	}
539
540	public interface OnContactPictureLongClicked {
541		public void onContactPictureLongClicked(Message message);
542	}
543
544	private static class ViewHolder {
545
546		protected LinearLayout message_box;
547		protected Button download_button;
548		protected ImageView image;
549		protected ImageView indicator;
550		protected ImageView indicatorReceived;
551		protected TextView time;
552		protected TextView messageBody;
553		protected ImageView contact_picture;
554
555	}
556}