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 info = getDisplayedMucCounterpart(message.getCounterpart());
140 }
141 }
142 break;
143 }
144 if (error) {
145 viewHolder.time.setTextColor(activity.getWarningTextColor());
146 } else {
147 viewHolder.time.setTextColor(activity.getSecondaryTextColor());
148 }
149 if (message.getEncryption() == Message.ENCRYPTION_NONE) {
150 viewHolder.indicator.setVisibility(View.GONE);
151 } else {
152 viewHolder.indicator.setVisibility(View.VISIBLE);
153 }
154
155 String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(),
156 message.getMergedTimeSent());
157 if (message.getStatus() <= Message.STATUS_RECEIVED) {
158 if ((filesize != null) && (info != null)) {
159 viewHolder.time.setText(filesize + " \u00B7 " + info);
160 } else if ((filesize == null) && (info != null)) {
161 viewHolder.time.setText(formatedTime + " \u00B7 " + info);
162 } else if ((filesize != null) && (info == null)) {
163 viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
164 } else {
165 viewHolder.time.setText(formatedTime);
166 }
167 } else {
168 if ((filesize != null) && (info != null)) {
169 viewHolder.time.setText(filesize + " \u00B7 " + info);
170 } else if ((filesize == null) && (info != null)) {
171 if (error) {
172 viewHolder.time.setText(info + " \u00B7 " + formatedTime);
173 } else {
174 viewHolder.time.setText(info);
175 }
176 } else if ((filesize != null) && (info == null)) {
177 viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
178 } else {
179 viewHolder.time.setText(formatedTime);
180 }
181 }
182 }
183
184 private void displayInfoMessage(ViewHolder viewHolder, int r) {
185 if (viewHolder.download_button != null) {
186 viewHolder.download_button.setVisibility(View.GONE);
187 }
188 viewHolder.image.setVisibility(View.GONE);
189 viewHolder.messageBody.setVisibility(View.VISIBLE);
190 viewHolder.messageBody.setText(getContext().getString(r));
191 viewHolder.messageBody.setTextColor(activity.getSecondaryTextColor());
192 viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
193 viewHolder.messageBody.setTextIsSelectable(false);
194 }
195
196 private void displayDecryptionFailed(ViewHolder viewHolder) {
197 if (viewHolder.download_button != null) {
198 viewHolder.download_button.setVisibility(View.GONE);
199 }
200 viewHolder.image.setVisibility(View.GONE);
201 viewHolder.messageBody.setVisibility(View.VISIBLE);
202 viewHolder.messageBody.setText(getContext().getString(
203 R.string.decryption_failed));
204 viewHolder.messageBody.setTextColor(activity.getWarningTextColor());
205 viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
206 viewHolder.messageBody.setTextIsSelectable(false);
207 }
208
209 private void displayTextMessage(ViewHolder viewHolder, Message message) {
210 if (viewHolder.download_button != null) {
211 viewHolder.download_button.setVisibility(View.GONE);
212 }
213 viewHolder.image.setVisibility(View.GONE);
214 viewHolder.messageBody.setVisibility(View.VISIBLE);
215 if (message.getBody() != null) {
216 if (message.getType() != Message.TYPE_PRIVATE) {
217 String body = Config.PARSE_EMOTICONS ? UIHelper
218 .transformAsciiEmoticons(message.getMergedBody())
219 : message.getMergedBody();
220 viewHolder.messageBody.setText(body);
221 } else {
222 String privateMarker;
223 if (message.getStatus() <= Message.STATUS_RECEIVED) {
224 privateMarker = activity
225 .getString(R.string.private_message);
226 } else {
227 final String to;
228 if (message.getCounterpart() != null) {
229 to = message.getCounterpart().getResourcepart();
230 } else {
231 to = "";
232 }
233 privateMarker = activity.getString(R.string.private_message_to, to);
234 }
235 SpannableString span = new SpannableString(privateMarker + " "
236 + message.getBody());
237 span.setSpan(
238 new ForegroundColorSpan(activity
239 .getSecondaryTextColor()), 0, privateMarker
240 .length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
241 span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0,
242 privateMarker.length(),
243 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
244 viewHolder.messageBody.setText(span);
245 }
246 } else {
247 viewHolder.messageBody.setText("");
248 }
249 viewHolder.messageBody.setTextColor(activity.getPrimaryTextColor());
250 viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
251 viewHolder.messageBody.setTextIsSelectable(true);
252 }
253
254 private void displayDownloadableMessage(ViewHolder viewHolder,
255 final Message message, int resid) {
256 viewHolder.image.setVisibility(View.GONE);
257 viewHolder.messageBody.setVisibility(View.GONE);
258 viewHolder.download_button.setVisibility(View.VISIBLE);
259 viewHolder.download_button.setText(resid);
260 viewHolder.download_button.setOnClickListener(new OnClickListener() {
261
262 @Override
263 public void onClick(View v) {
264 startDonwloadable(message);
265 }
266 });
267 viewHolder.download_button.setOnLongClickListener(openContextMenu);
268 }
269
270 private void displayImageMessage(ViewHolder viewHolder,
271 final Message message) {
272 if (viewHolder.download_button != null) {
273 viewHolder.download_button.setVisibility(View.GONE);
274 }
275 viewHolder.messageBody.setVisibility(View.GONE);
276 viewHolder.image.setVisibility(View.VISIBLE);
277 ImageParams params = message.getImageParams();
278 double target = metrics.density * 288;
279 int scalledW;
280 int scalledH;
281 if (params.width <= params.height) {
282 scalledW = (int) (params.width / ((double) params.height / target));
283 scalledH = (int) target;
284 } else {
285 scalledW = (int) target;
286 scalledH = (int) (params.height / ((double) params.width / target));
287 }
288 viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
289 scalledW, scalledH));
290 activity.loadBitmap(message, viewHolder.image);
291 viewHolder.image.setOnClickListener(new OnClickListener() {
292
293 @Override
294 public void onClick(View v) {
295 Intent intent = new Intent(Intent.ACTION_VIEW);
296 intent.setDataAndType(activity.xmppConnectionService
297 .getFileBackend().getJingleFileUri(message), "image/*");
298 getContext().startActivity(intent);
299 }
300 });
301 viewHolder.image.setOnLongClickListener(openContextMenu);
302 }
303
304 private String getDisplayedMucCounterpart(final Jid counterpart) {
305 if (counterpart==null) {
306 return "";
307 } else if (!counterpart.isBareJid()) {
308 return counterpart.getResourcepart();
309 } else {
310 return counterpart.toString();
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 = activity.getLayoutInflater().inflate(
324 R.layout.message_null, parent, false);
325 break;
326 case SENT:
327 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.download_button = (Button) view
334 .findViewById(R.id.download_button);
335 viewHolder.indicator = (ImageView) view
336 .findViewById(R.id.security_indicator);
337 viewHolder.image = (ImageView) view
338 .findViewById(R.id.message_image);
339 viewHolder.messageBody = (TextView) view
340 .findViewById(R.id.message_body);
341 viewHolder.time = (TextView) view
342 .findViewById(R.id.message_time);
343 viewHolder.indicatorReceived = (ImageView) view
344 .findViewById(R.id.indicator_received);
345 view.setTag(viewHolder);
346 break;
347 case RECEIVED:
348 view = activity.getLayoutInflater().inflate(
349 R.layout.message_received, parent, false);
350 viewHolder.message_box = (LinearLayout) view
351 .findViewById(R.id.message_box);
352 viewHolder.contact_picture = (ImageView) view
353 .findViewById(R.id.message_photo);
354 viewHolder.download_button = (Button) view
355 .findViewById(R.id.download_button);
356 viewHolder.indicator = (ImageView) view
357 .findViewById(R.id.security_indicator);
358 viewHolder.image = (ImageView) view
359 .findViewById(R.id.message_image);
360 viewHolder.messageBody = (TextView) view
361 .findViewById(R.id.message_body);
362 viewHolder.time = (TextView) view
363 .findViewById(R.id.message_time);
364 viewHolder.indicatorReceived = (ImageView) view
365 .findViewById(R.id.indicator_received);
366 view.setTag(viewHolder);
367 break;
368 case STATUS:
369 view = activity.getLayoutInflater().inflate(
370 R.layout.message_status, parent, false);
371 viewHolder.contact_picture = (ImageView) view
372 .findViewById(R.id.message_photo);
373 view.setTag(viewHolder);
374 break;
375 default:
376 viewHolder = null;
377 break;
378 }
379 } else {
380 viewHolder = (ViewHolder) view.getTag();
381 }
382
383 if (type == STATUS) {
384 if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
385 viewHolder.contact_picture.setImageBitmap(activity
386 .avatarService().get(
387 item.getConversation().getContact(),
388 activity.getPixel(32)));
389 viewHolder.contact_picture.setAlpha(0.5f);
390 viewHolder.contact_picture
391 .setOnClickListener(new OnClickListener() {
392
393 @Override
394 public void onClick(View v) {
395 String name = item.getConversation()
396 .getName();
397 String read = getContext()
398 .getString(
399 R.string.contact_has_read_up_to_this_point,
400 name);
401 Toast.makeText(getContext(), read,
402 Toast.LENGTH_SHORT).show();
403 }
404 });
405
406 }
407 return view;
408 } else if (type == NULL) {
409 if (position == getCount() - 1) {
410 view.getLayoutParams().height = 1;
411 } else {
412 view.getLayoutParams().height = 0;
413
414 }
415 view.setLayoutParams(view.getLayoutParams());
416 return view;
417 } else if (type == RECEIVED) {
418 Contact contact = item.getContact();
419 if (contact != null) {
420 viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(contact, activity.getPixel(48)));
421 } else if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
422 viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(getDisplayedMucCounterpart(item.getCounterpart()),
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}