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