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}