1package eu.siacs.conversations.ui.adapter;
2
3import java.util.HashMap;
4import java.util.List;
5
6import eu.siacs.conversations.R;
7import eu.siacs.conversations.entities.Contact;
8import eu.siacs.conversations.entities.Conversation;
9import eu.siacs.conversations.entities.Message;
10import eu.siacs.conversations.ui.ConversationActivity;
11import eu.siacs.conversations.utils.UIHelper;
12import eu.siacs.conversations.xmpp.jingle.JingleConnection;
13import android.content.Context;
14import android.content.Intent;
15import android.graphics.Bitmap;
16import android.graphics.Typeface;
17import android.text.Html;
18import android.util.DisplayMetrics;
19import android.view.View;
20import android.view.ViewGroup;
21import android.view.View.OnClickListener;
22import android.view.View.OnLongClickListener;
23import android.widget.ArrayAdapter;
24import android.widget.Button;
25import android.widget.ImageView;
26import android.widget.LinearLayout;
27import android.widget.TextView;
28import android.widget.Toast;
29
30public class MessageAdapter extends ArrayAdapter<Message> {
31
32 private static final int SENT = 0;
33 private static final int RECIEVED = 1;
34 private static final int STATUS = 2;
35
36 private ConversationActivity activity;
37
38 private Bitmap accountBitmap;
39
40 private BitmapCache mBitmapCache = new BitmapCache();
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 private Bitmap getSelfBitmap() {
53 if (this.accountBitmap == null) {
54
55 if (getCount() > 0) {
56 this.accountBitmap = getItem(0).getConversation().getAccount()
57 .getImage(getContext(), 48);
58 }
59 }
60 return this.accountBitmap;
61 }
62
63 public void setOnContactPictureClicked(OnContactPictureClicked listener) {
64 this.mOnContactPictureClickedListener = listener;
65 }
66
67 public void setOnContactPictureLongClicked(OnContactPictureLongClicked listener) {
68 this.mOnContactPictureLongClickedListener = listener;
69 }
70
71 @Override
72 public int getViewTypeCount() {
73 return 3;
74 }
75
76 @Override
77 public int getItemViewType(int position) {
78 if (getItem(position).getType() == Message.TYPE_STATUS) {
79 return STATUS;
80 } else if (getItem(position).getStatus() <= Message.STATUS_RECIEVED) {
81 return RECIEVED;
82 } else {
83 return SENT;
84 }
85 }
86
87 private void displayStatus(ViewHolder viewHolder, Message message) {
88 String filesize = null;
89 String info = null;
90 boolean error = false;
91 boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
92 && message.getStatus() <= Message.STATUS_RECIEVED;
93 if (message.getType() == Message.TYPE_IMAGE) {
94 String[] fileParams = message.getBody().split(",");
95 try {
96 long size = Long.parseLong(fileParams[0]);
97 filesize = size / 1024 + " KB";
98 } catch (NumberFormatException e) {
99 filesize = "0 KB";
100 }
101 }
102 switch (message.getStatus()) {
103 case Message.STATUS_WAITING:
104 info = getContext().getString(R.string.waiting);
105 break;
106 case Message.STATUS_UNSEND:
107 info = getContext().getString(R.string.sending);
108 break;
109 case Message.STATUS_OFFERED:
110 info = getContext().getString(R.string.offering);
111 break;
112 case Message.STATUS_SEND_FAILED:
113 info = getContext().getString(R.string.send_failed);
114 error = true;
115 break;
116 case Message.STATUS_SEND_REJECTED:
117 info = getContext().getString(R.string.send_rejected);
118 error = true;
119 break;
120 case Message.STATUS_RECEPTION_FAILED:
121 info = getContext().getString(R.string.reception_failed);
122 error = true;
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(0xFFe92727);
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.readableTimeDifference(getContext(),
150 message.getTimeSent());
151 if (message.getStatus() <= Message.STATUS_RECIEVED) {
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(0xff33B5E5);
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(0xFFe92727);
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 viewHolder.messageBody.setText(message.getBody().trim());
212 } else {
213 StringBuilder builder = new StringBuilder();
214 builder.append(message.getBody().trim());
215 builder.append(" <b><i>(");
216 if (message.getStatus() <= Message.STATUS_RECIEVED) {
217 builder.append(activity.getString(R.string.private_message));
218 } else {
219 String to;
220 if (message.getPresence() != null) {
221 to = message.getPresence();
222 } else {
223 to = message.getCounterpart();
224 }
225 builder.append(activity.getString(R.string.private_message_to, to));
226 }
227 builder.append(")</i></b>");
228 viewHolder.messageBody.setText(Html.fromHtml(builder.toString()));
229 }
230 } else {
231 viewHolder.messageBody.setText("");
232 }
233 viewHolder.messageBody.setTextColor(activity.getPrimaryTextColor());
234 viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
235 viewHolder.messageBody.setTextIsSelectable(true);
236 }
237
238 private void displayImageMessage(ViewHolder viewHolder,
239 final Message message) {
240 if (viewHolder.download_button != null) {
241 viewHolder.download_button.setVisibility(View.GONE);
242 }
243 viewHolder.messageBody.setVisibility(View.GONE);
244 viewHolder.image.setVisibility(View.VISIBLE);
245 String[] fileParams = message.getBody().split(",");
246 if (fileParams.length == 3) {
247 double target = metrics.density * 288;
248 int w = Integer.parseInt(fileParams[1]);
249 int h = Integer.parseInt(fileParams[2]);
250 int scalledW;
251 int scalledH;
252 if (w <= h) {
253 scalledW = (int) (w / ((double) h / target));
254 scalledH = (int) target;
255 } else {
256 scalledW = (int) target;
257 scalledH = (int) (h / ((double) w / target));
258 }
259 viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
260 scalledW, scalledH));
261 }
262 activity.loadBitmap(message, viewHolder.image);
263 viewHolder.image.setOnClickListener(new OnClickListener() {
264
265 @Override
266 public void onClick(View v) {
267 Intent intent = new Intent(Intent.ACTION_VIEW);
268 intent.setDataAndType(activity.xmppConnectionService
269 .getFileBackend().getJingleFileUri(message), "image/*");
270 getContext().startActivity(intent);
271 }
272 });
273 viewHolder.image.setOnLongClickListener(new OnLongClickListener() {
274
275 @Override
276 public boolean onLongClick(View v) {
277 Intent shareIntent = new Intent();
278 shareIntent.setAction(Intent.ACTION_SEND);
279 shareIntent.putExtra(Intent.EXTRA_STREAM,
280 activity.xmppConnectionService.getFileBackend()
281 .getJingleFileUri(message));
282 shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
283 shareIntent.setType("image/webp");
284 getContext().startActivity(
285 Intent.createChooser(shareIntent,
286 getContext().getText(R.string.share_with)));
287 return true;
288 }
289 });
290 }
291
292 @Override
293 public View getView(int position, View view, ViewGroup parent) {
294 final Message item = getItem(position);
295 int type = getItemViewType(position);
296 ViewHolder viewHolder;
297 if (view == null) {
298 viewHolder = new ViewHolder();
299 switch (type) {
300 case SENT:
301 view = (View) activity.getLayoutInflater().inflate(
302 R.layout.message_sent, parent, false);
303 viewHolder.message_box = (LinearLayout) view
304 .findViewById(R.id.message_box);
305 viewHolder.contact_picture = (ImageView) view
306 .findViewById(R.id.message_photo);
307 viewHolder.contact_picture.setImageBitmap(getSelfBitmap());
308 viewHolder.indicator = (ImageView) view
309 .findViewById(R.id.security_indicator);
310 viewHolder.image = (ImageView) view
311 .findViewById(R.id.message_image);
312 viewHolder.messageBody = (TextView) view
313 .findViewById(R.id.message_body);
314 viewHolder.time = (TextView) view
315 .findViewById(R.id.message_time);
316 view.setTag(viewHolder);
317 break;
318 case RECIEVED:
319 view = (View) activity.getLayoutInflater().inflate(
320 R.layout.message_recieved, parent, false);
321 viewHolder.message_box = (LinearLayout) view
322 .findViewById(R.id.message_box);
323 viewHolder.contact_picture = (ImageView) view
324 .findViewById(R.id.message_photo);
325
326 viewHolder.download_button = (Button) view
327 .findViewById(R.id.download_button);
328
329 if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
330
331 viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
332 item.getConversation().getContact(), getContext()));
333
334 }
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 view.setTag(viewHolder);
344 break;
345 case STATUS:
346 view = (View) activity.getLayoutInflater().inflate(
347 R.layout.message_status, parent, false);
348 viewHolder.contact_picture = (ImageView) view
349 .findViewById(R.id.message_photo);
350 if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
351
352 viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
353 item.getConversation().getContact(), getContext()));
354 viewHolder.contact_picture.setAlpha(128);
355 viewHolder.contact_picture
356 .setOnClickListener(new OnClickListener() {
357
358 @Override
359 public void onClick(View v) {
360 String name = item.getConversation()
361 .getName(true);
362 String read = getContext()
363 .getString(
364 R.string.contact_has_read_up_to_this_point,
365 name);
366 Toast.makeText(getContext(), read,
367 Toast.LENGTH_SHORT).show();
368 }
369 });
370
371 }
372 break;
373 default:
374 viewHolder = null;
375 break;
376 }
377 } else {
378 viewHolder = (ViewHolder) view.getTag();
379 }
380
381 if (type == STATUS) {
382 return view;
383 }
384
385 if (type == RECIEVED) {
386 if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
387 Contact contact = item.getContact();
388 if (contact != null) {
389 viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
390 contact, getContext()));
391 } else {
392 String name = item.getPresence();
393 if (name==null) {
394 name = item.getCounterpart();
395 }
396 viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(name, getContext()));
397 }
398 viewHolder.contact_picture
399 .setOnClickListener(new OnClickListener() {
400
401 @Override
402 public void onClick(View v) {
403 if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
404 MessageAdapter.this.mOnContactPictureClickedListener
405 .onContactPictureClicked(item);
406 ;
407 }
408
409 }
410 });
411 viewHolder.contact_picture.setOnLongClickListener(new OnLongClickListener() {
412
413 @Override
414 public boolean onLongClick(View v) {
415 if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
416 MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(item);
417 return true;
418 } else {
419 return false;
420 }
421 }
422 });
423 }
424 }
425
426 if (item.getType() == Message.TYPE_IMAGE) {
427 if (item.getStatus() == Message.STATUS_RECIEVING) {
428 displayInfoMessage(viewHolder, R.string.receiving_image);
429 } else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
430 viewHolder.image.setVisibility(View.GONE);
431 viewHolder.messageBody.setVisibility(View.GONE);
432 viewHolder.download_button.setVisibility(View.VISIBLE);
433 viewHolder.download_button
434 .setOnClickListener(new OnClickListener() {
435
436 @Override
437 public void onClick(View v) {
438 JingleConnection connection = item
439 .getJingleConnection();
440 if (connection != null) {
441 connection.accept();
442 }
443 }
444 });
445 } else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
446 || (item.getEncryption() == Message.ENCRYPTION_NONE)
447 || (item.getEncryption() == Message.ENCRYPTION_OTR)) {
448 displayImageMessage(viewHolder, item);
449 } else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
450 displayInfoMessage(viewHolder, R.string.encrypted_message);
451 } else {
452 displayDecryptionFailed(viewHolder);
453 }
454 } else {
455 if (item.getEncryption() == Message.ENCRYPTION_PGP) {
456 if (activity.hasPgp()) {
457 displayInfoMessage(viewHolder, R.string.encrypted_message);
458 } else {
459 displayInfoMessage(viewHolder,
460 R.string.install_openkeychain);
461 viewHolder.message_box
462 .setOnClickListener(new OnClickListener() {
463
464 @Override
465 public void onClick(View v) {
466 activity.showInstallPgpDialog();
467 }
468 });
469 }
470 } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
471 displayDecryptionFailed(viewHolder);
472 } else {
473 displayTextMessage(viewHolder, item);
474 }
475 }
476
477 displayStatus(viewHolder, item);
478
479 return view;
480 }
481
482 private static class ViewHolder {
483
484 protected LinearLayout message_box;
485 protected Button download_button;
486 protected ImageView image;
487 protected ImageView indicator;
488 protected TextView time;
489 protected TextView messageBody;
490 protected ImageView contact_picture;
491
492 }
493
494 private class BitmapCache {
495 private HashMap<String, Bitmap> contactBitmaps = new HashMap<String, Bitmap>();
496 private HashMap<String, Bitmap> unknownBitmaps = new HashMap<String, Bitmap>();
497
498 public Bitmap get(Contact contact, Context context) {
499 if (!contactBitmaps.containsKey(contact.getJid())) {
500 contactBitmaps.put(contact.getJid(),
501 contact.getImage(48, context));
502 }
503 return contactBitmaps.get(contact.getJid());
504 }
505
506 public Bitmap get(String name, Context context) {
507 if (unknownBitmaps.containsKey(name)) {
508 return unknownBitmaps.get(name);
509 } else {
510 Bitmap bm = UIHelper
511 .getContactPicture(name, 48, context, false);
512 unknownBitmaps.put(name, bm);
513 return bm;
514 }
515 }
516 }
517
518 public interface OnContactPictureClicked {
519 public void onContactPictureClicked(Message message);
520 }
521
522 public interface OnContactPictureLongClicked {
523 public void onContactPictureLongClicked(Message message);
524 }
525}