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 {
139 if (message.getPresence() != null) {
140 if (message.getPresence().isBareJid()) {
141 info = message.getPresence().toString();
142 } else {
143 info = message.getPresence().getResourcepart();
144 }
145 } else {
146 info = message.getCounterpart().toString();
147 }
148 }
149 }
150 break;
151 }
152 if (error) {
153 viewHolder.time.setTextColor(activity.getWarningTextColor());
154 } else {
155 viewHolder.time.setTextColor(activity.getSecondaryTextColor());
156 }
157 if (message.getEncryption() == Message.ENCRYPTION_NONE) {
158 viewHolder.indicator.setVisibility(View.GONE);
159 } else {
160 viewHolder.indicator.setVisibility(View.VISIBLE);
161 }
162
163 String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(),
164 message.getMergedTimeSent());
165 if (message.getStatus() <= Message.STATUS_RECEIVED) {
166 if ((filesize != null) && (info != null)) {
167 viewHolder.time.setText(filesize + " \u00B7 " + info);
168 } else if ((filesize == null) && (info != null)) {
169 viewHolder.time.setText(formatedTime + " \u00B7 " + info);
170 } else if ((filesize != null) && (info == null)) {
171 viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
172 } else {
173 viewHolder.time.setText(formatedTime);
174 }
175 } else {
176 if ((filesize != null) && (info != null)) {
177 viewHolder.time.setText(filesize + " \u00B7 " + info);
178 } else if ((filesize == null) && (info != null)) {
179 if (error) {
180 viewHolder.time.setText(info + " \u00B7 " + formatedTime);
181 } else {
182 viewHolder.time.setText(info);
183 }
184 } else if ((filesize != null) && (info == null)) {
185 viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
186 } else {
187 viewHolder.time.setText(formatedTime);
188 }
189 }
190 }
191
192 private void displayInfoMessage(ViewHolder viewHolder, int r) {
193 if (viewHolder.download_button != null) {
194 viewHolder.download_button.setVisibility(View.GONE);
195 }
196 viewHolder.image.setVisibility(View.GONE);
197 viewHolder.messageBody.setVisibility(View.VISIBLE);
198 viewHolder.messageBody.setText(getContext().getString(r));
199 viewHolder.messageBody.setTextColor(activity.getSecondaryTextColor());
200 viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
201 viewHolder.messageBody.setTextIsSelectable(false);
202 }
203
204 private void displayDecryptionFailed(ViewHolder viewHolder) {
205 if (viewHolder.download_button != null) {
206 viewHolder.download_button.setVisibility(View.GONE);
207 }
208 viewHolder.image.setVisibility(View.GONE);
209 viewHolder.messageBody.setVisibility(View.VISIBLE);
210 viewHolder.messageBody.setText(getContext().getString(
211 R.string.decryption_failed));
212 viewHolder.messageBody.setTextColor(activity.getWarningTextColor());
213 viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
214 viewHolder.messageBody.setTextIsSelectable(false);
215 }
216
217 private void displayTextMessage(ViewHolder viewHolder, Message message) {
218 if (viewHolder.download_button != null) {
219 viewHolder.download_button.setVisibility(View.GONE);
220 }
221 viewHolder.image.setVisibility(View.GONE);
222 viewHolder.messageBody.setVisibility(View.VISIBLE);
223 if (message.getBody() != null) {
224 if (message.getType() != Message.TYPE_PRIVATE) {
225 String body = Config.PARSE_EMOTICONS ? UIHelper
226 .transformAsciiEmoticons(message.getMergedBody())
227 : message.getMergedBody();
228 viewHolder.messageBody.setText(body);
229 } else {
230 String privateMarker;
231 if (message.getStatus() <= Message.STATUS_RECEIVED) {
232 privateMarker = activity
233 .getString(R.string.private_message);
234 } else {
235 final Jid to;
236 if (message.getPresence() != null) {
237 to = message.getPresence();
238 } else {
239 to = message.getCounterpart();
240 }
241 privateMarker = activity.getString(R.string.private_message_to, to);
242 }
243 SpannableString span = new SpannableString(privateMarker + " "
244 + message.getBody());
245 span.setSpan(
246 new ForegroundColorSpan(activity
247 .getSecondaryTextColor()), 0, privateMarker
248 .length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
249 span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0,
250 privateMarker.length(),
251 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
252 viewHolder.messageBody.setText(span);
253 }
254 } else {
255 viewHolder.messageBody.setText("");
256 }
257 viewHolder.messageBody.setTextColor(activity.getPrimaryTextColor());
258 viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
259 viewHolder.messageBody.setTextIsSelectable(true);
260 }
261
262 private void displayDownloadableMessage(ViewHolder viewHolder,
263 final Message message, int resid) {
264 viewHolder.image.setVisibility(View.GONE);
265 viewHolder.messageBody.setVisibility(View.GONE);
266 viewHolder.download_button.setVisibility(View.VISIBLE);
267 viewHolder.download_button.setText(resid);
268 viewHolder.download_button.setOnClickListener(new OnClickListener() {
269
270 @Override
271 public void onClick(View v) {
272 startDonwloadable(message);
273 }
274 });
275 viewHolder.download_button.setOnLongClickListener(openContextMenu);
276 }
277
278 private void displayImageMessage(ViewHolder viewHolder,
279 final Message message) {
280 if (viewHolder.download_button != null) {
281 viewHolder.download_button.setVisibility(View.GONE);
282 }
283 viewHolder.messageBody.setVisibility(View.GONE);
284 viewHolder.image.setVisibility(View.VISIBLE);
285 ImageParams params = message.getImageParams();
286 double target = metrics.density * 288;
287 int scalledW;
288 int scalledH;
289 if (params.width <= params.height) {
290 scalledW = (int) (params.width / ((double) params.height / target));
291 scalledH = (int) target;
292 } else {
293 scalledW = (int) target;
294 scalledH = (int) (params.height / ((double) params.width / target));
295 }
296 viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
297 scalledW, scalledH));
298 activity.loadBitmap(message, viewHolder.image);
299 viewHolder.image.setOnClickListener(new OnClickListener() {
300
301 @Override
302 public void onClick(View v) {
303 Intent intent = new Intent(Intent.ACTION_VIEW);
304 intent.setDataAndType(activity.xmppConnectionService
305 .getFileBackend().getJingleFileUri(message), "image/*");
306 getContext().startActivity(intent);
307 }
308 });
309 viewHolder.image.setOnLongClickListener(openContextMenu);
310 }
311
312 @Override
313 public View getView(int position, View view, ViewGroup parent) {
314 final Message item = getItem(position);
315 int type = getItemViewType(position);
316 ViewHolder viewHolder;
317 if (view == null) {
318 viewHolder = new ViewHolder();
319 switch (type) {
320 case NULL:
321 view = activity.getLayoutInflater().inflate(
322 R.layout.message_null, parent, false);
323 break;
324 case SENT:
325 view = activity.getLayoutInflater().inflate(
326 R.layout.message_sent, parent, false);
327 viewHolder.message_box = (LinearLayout) view
328 .findViewById(R.id.message_box);
329 viewHolder.contact_picture = (ImageView) view
330 .findViewById(R.id.message_photo);
331 viewHolder.download_button = (Button) view
332 .findViewById(R.id.download_button);
333 viewHolder.indicator = (ImageView) view
334 .findViewById(R.id.security_indicator);
335 viewHolder.image = (ImageView) view
336 .findViewById(R.id.message_image);
337 viewHolder.messageBody = (TextView) view
338 .findViewById(R.id.message_body);
339 viewHolder.time = (TextView) view
340 .findViewById(R.id.message_time);
341 viewHolder.indicatorReceived = (ImageView) view
342 .findViewById(R.id.indicator_received);
343 view.setTag(viewHolder);
344 break;
345 case RECEIVED:
346 view = activity.getLayoutInflater().inflate(
347 R.layout.message_received, parent, false);
348 viewHolder.message_box = (LinearLayout) view
349 .findViewById(R.id.message_box);
350 viewHolder.contact_picture = (ImageView) view
351 .findViewById(R.id.message_photo);
352 viewHolder.download_button = (Button) view
353 .findViewById(R.id.download_button);
354 viewHolder.indicator = (ImageView) view
355 .findViewById(R.id.security_indicator);
356 viewHolder.image = (ImageView) view
357 .findViewById(R.id.message_image);
358 viewHolder.messageBody = (TextView) view
359 .findViewById(R.id.message_body);
360 viewHolder.time = (TextView) view
361 .findViewById(R.id.message_time);
362 viewHolder.indicatorReceived = (ImageView) view
363 .findViewById(R.id.indicator_received);
364 view.setTag(viewHolder);
365 break;
366 case STATUS:
367 view = activity.getLayoutInflater().inflate(
368 R.layout.message_status, parent, false);
369 viewHolder.contact_picture = (ImageView) view
370 .findViewById(R.id.message_photo);
371 view.setTag(viewHolder);
372 break;
373 default:
374 viewHolder = null;
375 break;
376 }
377 } else {
378 viewHolder = (ViewHolder) view.getTag();
379 }
380
381 if (type == STATUS) {
382 if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
383 viewHolder.contact_picture.setImageBitmap(activity
384 .avatarService().get(
385 item.getConversation().getContact(),
386 activity.getPixel(32)));
387 viewHolder.contact_picture.setAlpha(0.5f);
388 viewHolder.contact_picture
389 .setOnClickListener(new OnClickListener() {
390
391 @Override
392 public void onClick(View v) {
393 String name = item.getConversation()
394 .getName();
395 String read = getContext()
396 .getString(
397 R.string.contact_has_read_up_to_this_point,
398 name);
399 Toast.makeText(getContext(), read,
400 Toast.LENGTH_SHORT).show();
401 }
402 });
403
404 }
405 return view;
406 } else if (type == NULL) {
407 if (position == getCount() - 1) {
408 view.getLayoutParams().height = 1;
409 } else {
410 view.getLayoutParams().height = 0;
411
412 }
413 view.setLayoutParams(view.getLayoutParams());
414 return view;
415 } else if (type == RECEIVED) {
416 Contact contact = item.getContact();
417 if (contact != null) {
418 viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(contact, activity.getPixel(48)));
419 } else if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
420 final Jid name = item.getPresence() != null ? item.getPresence() : item.getCounterpart();
421 viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(
422 name.isBareJid() ? name.toString() : name.getResourcepart(),
423 activity.getPixel(48)));
424 }
425 } else if (type == SENT) {
426 viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(item.getConversation().getAccount(), activity.getPixel(48)));
427 }
428
429 if (viewHolder != null && viewHolder.contact_picture != null) {
430 viewHolder.contact_picture
431 .setOnClickListener(new OnClickListener() {
432
433 @Override
434 public void onClick(View v) {
435 if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
436 MessageAdapter.this.mOnContactPictureClickedListener
437 .onContactPictureClicked(item);
438 }
439
440 }
441 });
442 viewHolder.contact_picture
443 .setOnLongClickListener(new OnLongClickListener() {
444
445 @Override
446 public boolean onLongClick(View v) {
447 if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
448 MessageAdapter.this.mOnContactPictureLongClickedListener
449 .onContactPictureLongClicked(item);
450 return true;
451 } else {
452 return false;
453 }
454 }
455 });
456 }
457
458 if (item.getType() == Message.TYPE_IMAGE
459 || item.getDownloadable() != null) {
460 Downloadable d = item.getDownloadable();
461 if (d != null && d.getStatus() == Downloadable.STATUS_DOWNLOADING) {
462 displayInfoMessage(viewHolder, R.string.receiving_image);
463 } else if (d != null
464 && d.getStatus() == Downloadable.STATUS_CHECKING) {
465 displayInfoMessage(viewHolder, R.string.checking_image);
466 } else if (d != null
467 && d.getStatus() == Downloadable.STATUS_DELETED) {
468 displayInfoMessage(viewHolder, R.string.image_file_deleted);
469 } else if (d != null && d.getStatus() == Downloadable.STATUS_OFFER) {
470 displayDownloadableMessage(viewHolder, item,
471 R.string.download_image);
472 } else if (d != null
473 && d.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) {
474 displayDownloadableMessage(viewHolder, item,
475 R.string.check_image_filesize);
476 } else if (d != null && d.getStatus() == Downloadable.STATUS_FAILED) {
477 displayInfoMessage(viewHolder, R.string.image_transmission_failed);
478 } else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
479 || (item.getEncryption() == Message.ENCRYPTION_NONE)
480 || (item.getEncryption() == Message.ENCRYPTION_OTR)) {
481 displayImageMessage(viewHolder, item);
482 } else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
483 displayInfoMessage(viewHolder, R.string.encrypted_message);
484 } else {
485 displayDecryptionFailed(viewHolder);
486 }
487 } else {
488 if (item.getEncryption() == Message.ENCRYPTION_PGP) {
489 if (activity.hasPgp()) {
490 displayInfoMessage(viewHolder, R.string.encrypted_message);
491 } else {
492 displayInfoMessage(viewHolder,
493 R.string.install_openkeychain);
494 if (viewHolder != null) {
495 viewHolder.message_box
496 .setOnClickListener(new OnClickListener() {
497
498 @Override
499 public void onClick(View v) {
500 activity.showInstallPgpDialog();
501 }
502 });
503 }
504 }
505 } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
506 displayDecryptionFailed(viewHolder);
507 } else {
508 displayTextMessage(viewHolder, item);
509 }
510 }
511
512 displayStatus(viewHolder, item);
513
514 return view;
515 }
516
517 public void startDonwloadable(Message message) {
518 Downloadable downloadable = message.getDownloadable();
519 if (downloadable != null) {
520 if (!downloadable.start()) {
521 Toast.makeText(activity, R.string.not_connected_try_again,
522 Toast.LENGTH_SHORT).show();
523 }
524 }
525 }
526
527 public interface OnContactPictureClicked {
528 public void onContactPictureClicked(Message message);
529 }
530
531 public interface OnContactPictureLongClicked {
532 public void onContactPictureLongClicked(Message message);
533 }
534
535 private static class ViewHolder {
536
537 protected LinearLayout message_box;
538 protected Button download_button;
539 protected ImageView image;
540 protected ImageView indicator;
541 protected ImageView indicatorReceived;
542 protected TextView time;
543 protected TextView messageBody;
544 protected ImageView contact_picture;
545
546 }
547}