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.util.DisplayMetrics;
18import android.view.View;
19import android.view.ViewGroup;
20import android.view.View.OnClickListener;
21import android.view.View.OnLongClickListener;
22import android.widget.ArrayAdapter;
23import android.widget.Button;
24import android.widget.ImageView;
25import android.widget.LinearLayout;
26import android.widget.TextView;
27import android.widget.Toast;
28
29public class MessageAdapter extends ArrayAdapter<Message> {
30
31 private static final int SENT = 0;
32 private static final int RECIEVED = 1;
33 private static final int STATUS = 2;
34
35 private ConversationActivity activity;
36
37 private Bitmap accountBitmap;
38
39 private BitmapCache mBitmapCache = new BitmapCache();
40 private DisplayMetrics metrics;
41
42 private OnContactPictureClicked mOnContactPictureClickedListener;
43
44 public MessageAdapter(ConversationActivity activity, List<Message> messages) {
45 super(activity, 0, messages);
46 this.activity = activity;
47 metrics = getContext().getResources().getDisplayMetrics();
48 }
49
50 private Bitmap getSelfBitmap() {
51 if (this.accountBitmap == null) {
52
53 if (getCount() > 0) {
54 this.accountBitmap = getItem(0).getConversation().getAccount()
55 .getImage(getContext(), 48);
56 }
57 }
58 return this.accountBitmap;
59 }
60
61 public void setOnContactPictureClicked(OnContactPictureClicked listener) {
62 this.mOnContactPictureClickedListener = listener;
63 }
64
65 @Override
66 public int getViewTypeCount() {
67 return 3;
68 }
69
70 @Override
71 public int getItemViewType(int position) {
72 if (getItem(position).getType() == Message.TYPE_STATUS) {
73 return STATUS;
74 } else if (getItem(position).getStatus() <= Message.STATUS_RECIEVED) {
75 return RECIEVED;
76 } else {
77 return SENT;
78 }
79 }
80
81 private void displayStatus(ViewHolder viewHolder, Message message) {
82 String filesize = null;
83 String info = null;
84 boolean error = false;
85 boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
86 && message.getStatus() <= Message.STATUS_RECIEVED;
87 if (message.getType() == Message.TYPE_IMAGE) {
88 String[] fileParams = message.getBody().split(",");
89 try {
90 long size = Long.parseLong(fileParams[0]);
91 filesize = size / 1024 + " KB";
92 } catch (NumberFormatException e) {
93 filesize = "0 KB";
94 }
95 }
96 switch (message.getStatus()) {
97 case Message.STATUS_WAITING:
98 info = getContext().getString(R.string.waiting);
99 break;
100 case Message.STATUS_UNSEND:
101 info = getContext().getString(R.string.sending);
102 break;
103 case Message.STATUS_OFFERED:
104 info = getContext().getString(R.string.offering);
105 break;
106 case Message.STATUS_SEND_FAILED:
107 info = getContext().getString(R.string.send_failed);
108 error = true;
109 break;
110 case Message.STATUS_SEND_REJECTED:
111 info = getContext().getString(R.string.send_rejected);
112 error = true;
113 break;
114 case Message.STATUS_RECEPTION_FAILED:
115 info = getContext().getString(R.string.reception_failed);
116 error = true;
117 default:
118 if (multiReceived) {
119 Contact contact = message.getContact();
120 if (contact != null) {
121 info = contact.getDisplayName();
122 } else {
123 info = message.getCounterpart();
124 }
125 }
126 break;
127 }
128 if (error) {
129 viewHolder.time.setTextColor(0xFFe92727);
130 } else {
131 viewHolder.time.setTextColor(activity.getSecondaryTextColor());
132 }
133 if (message.getEncryption() == Message.ENCRYPTION_NONE) {
134 viewHolder.indicator.setVisibility(View.GONE);
135 } else {
136 viewHolder.indicator.setVisibility(View.VISIBLE);
137 }
138
139 String formatedTime = UIHelper.readableTimeDifference(getContext(),
140 message.getTimeSent());
141 if (message.getStatus() <= Message.STATUS_RECIEVED) {
142 if ((filesize != null) && (info != null)) {
143 viewHolder.time.setText(filesize + " \u00B7 " + info);
144 } else if ((filesize == null) && (info != null)) {
145 viewHolder.time.setText(formatedTime + " \u00B7 " + info);
146 } else if ((filesize != null) && (info == null)) {
147 viewHolder.time.setText(formatedTime + " \u00B7 " + filesize);
148 } else {
149 viewHolder.time.setText(formatedTime);
150 }
151 } else {
152 if ((filesize != null) && (info != null)) {
153 viewHolder.time.setText(filesize + " \u00B7 " + info);
154 } else if ((filesize == null) && (info != null)) {
155 if (error) {
156 viewHolder.time.setText(info + " \u00B7 " + formatedTime);
157 } else {
158 viewHolder.time.setText(info);
159 }
160 } else if ((filesize != null) && (info == null)) {
161 viewHolder.time.setText(filesize + " \u00B7 " + formatedTime);
162 } else {
163 viewHolder.time.setText(formatedTime);
164 }
165 }
166 }
167
168 private void displayInfoMessage(ViewHolder viewHolder, int r) {
169 if (viewHolder.download_button != null) {
170 viewHolder.download_button.setVisibility(View.GONE);
171 }
172 viewHolder.image.setVisibility(View.GONE);
173 viewHolder.messageBody.setVisibility(View.VISIBLE);
174 viewHolder.messageBody.setText(getContext().getString(r));
175 viewHolder.messageBody.setTextColor(0xff33B5E5);
176 viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
177 viewHolder.messageBody.setTextIsSelectable(false);
178 }
179
180 private void displayDecryptionFailed(ViewHolder viewHolder) {
181 if (viewHolder.download_button != null) {
182 viewHolder.download_button.setVisibility(View.GONE);
183 }
184 viewHolder.image.setVisibility(View.GONE);
185 viewHolder.messageBody.setVisibility(View.VISIBLE);
186 viewHolder.messageBody.setText(getContext().getString(
187 R.string.decryption_failed));
188 viewHolder.messageBody.setTextColor(0xFFe92727);
189 viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
190 viewHolder.messageBody.setTextIsSelectable(false);
191 }
192
193 private void displayTextMessage(ViewHolder viewHolder, String text) {
194 if (viewHolder.download_button != null) {
195 viewHolder.download_button.setVisibility(View.GONE);
196 }
197 viewHolder.image.setVisibility(View.GONE);
198 viewHolder.messageBody.setVisibility(View.VISIBLE);
199 if (text != null) {
200 viewHolder.messageBody.setText(text.trim());
201 } else {
202 viewHolder.messageBody.setText("");
203 }
204 viewHolder.messageBody.setTextColor(activity.getPrimaryTextColor());
205 viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
206 viewHolder.messageBody.setTextIsSelectable(true);
207 }
208
209 private void displayImageMessage(ViewHolder viewHolder,
210 final Message message) {
211 if (viewHolder.download_button != null) {
212 viewHolder.download_button.setVisibility(View.GONE);
213 }
214 viewHolder.messageBody.setVisibility(View.GONE);
215 viewHolder.image.setVisibility(View.VISIBLE);
216 String[] fileParams = message.getBody().split(",");
217 if (fileParams.length == 3) {
218 double target = metrics.density * 288;
219 int w = Integer.parseInt(fileParams[1]);
220 int h = Integer.parseInt(fileParams[2]);
221 int scalledW;
222 int scalledH;
223 if (w <= h) {
224 scalledW = (int) (w / ((double) h / target));
225 scalledH = (int) target;
226 } else {
227 scalledW = (int) target;
228 scalledH = (int) (h / ((double) w / target));
229 }
230 viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
231 scalledW, scalledH));
232 }
233 activity.loadBitmap(message, viewHolder.image);
234 viewHolder.image.setOnClickListener(new OnClickListener() {
235
236 @Override
237 public void onClick(View v) {
238 Intent intent = new Intent(Intent.ACTION_VIEW);
239 intent.setDataAndType(activity.xmppConnectionService
240 .getFileBackend().getJingleFileUri(message), "image/*");
241 getContext().startActivity(intent);
242 }
243 });
244 viewHolder.image.setOnLongClickListener(new OnLongClickListener() {
245
246 @Override
247 public boolean onLongClick(View v) {
248 Intent shareIntent = new Intent();
249 shareIntent.setAction(Intent.ACTION_SEND);
250 shareIntent.putExtra(Intent.EXTRA_STREAM,
251 activity.xmppConnectionService.getFileBackend()
252 .getJingleFileUri(message));
253 shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
254 shareIntent.setType("image/webp");
255 getContext().startActivity(
256 Intent.createChooser(shareIntent,
257 getContext().getText(R.string.share_with)));
258 return true;
259 }
260 });
261 }
262
263 @Override
264 public View getView(int position, View view, ViewGroup parent) {
265 final Message item = getItem(position);
266 int type = getItemViewType(position);
267 ViewHolder viewHolder;
268 if (view == null) {
269 viewHolder = new ViewHolder();
270 switch (type) {
271 case SENT:
272 view = (View) activity.getLayoutInflater().inflate(
273 R.layout.message_sent, parent, false);
274 viewHolder.message_box = (LinearLayout) view
275 .findViewById(R.id.message_box);
276 viewHolder.contact_picture = (ImageView) view
277 .findViewById(R.id.message_photo);
278 viewHolder.contact_picture.setImageBitmap(getSelfBitmap());
279 viewHolder.indicator = (ImageView) view
280 .findViewById(R.id.security_indicator);
281 viewHolder.image = (ImageView) view
282 .findViewById(R.id.message_image);
283 viewHolder.messageBody = (TextView) view
284 .findViewById(R.id.message_body);
285 viewHolder.time = (TextView) view
286 .findViewById(R.id.message_time);
287 view.setTag(viewHolder);
288 break;
289 case RECIEVED:
290 view = (View) activity.getLayoutInflater().inflate(
291 R.layout.message_recieved, parent, false);
292 viewHolder.message_box = (LinearLayout) view
293 .findViewById(R.id.message_box);
294 viewHolder.contact_picture = (ImageView) view
295 .findViewById(R.id.message_photo);
296
297 viewHolder.download_button = (Button) view
298 .findViewById(R.id.download_button);
299
300 if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
301
302 viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
303 item.getConversation().getContact(), getContext()));
304
305 }
306 viewHolder.indicator = (ImageView) view
307 .findViewById(R.id.security_indicator);
308 viewHolder.image = (ImageView) view
309 .findViewById(R.id.message_image);
310 viewHolder.messageBody = (TextView) view
311 .findViewById(R.id.message_body);
312 viewHolder.time = (TextView) view
313 .findViewById(R.id.message_time);
314 view.setTag(viewHolder);
315 break;
316 case STATUS:
317 view = (View) activity.getLayoutInflater().inflate(
318 R.layout.message_status, parent, false);
319 viewHolder.contact_picture = (ImageView) view
320 .findViewById(R.id.message_photo);
321 if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
322
323 viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
324 item.getConversation().getContact(), getContext()));
325 viewHolder.contact_picture.setAlpha(128);
326 viewHolder.contact_picture
327 .setOnClickListener(new OnClickListener() {
328
329 @Override
330 public void onClick(View v) {
331 String name = item.getConversation()
332 .getName(true);
333 String read = getContext()
334 .getString(
335 R.string.contact_has_read_up_to_this_point,
336 name);
337 Toast.makeText(getContext(), read,
338 Toast.LENGTH_SHORT).show();
339 }
340 });
341
342 }
343 break;
344 default:
345 viewHolder = null;
346 break;
347 }
348 } else {
349 viewHolder = (ViewHolder) view.getTag();
350 }
351
352 if (type == STATUS) {
353 return view;
354 }
355
356 if (type == RECIEVED) {
357 if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
358 Contact contact = item.getContact();
359 if (contact != null) {
360 viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
361 contact, getContext()));
362 } else {
363 viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(
364 item.getCounterpart(), getContext()));
365 }
366 viewHolder.contact_picture
367 .setOnClickListener(new OnClickListener() {
368
369 @Override
370 public void onClick(View v) {
371 if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
372 MessageAdapter.this.mOnContactPictureClickedListener
373 .onContactPictureClicked(item);
374 ;
375 }
376
377 }
378 });
379 }
380 }
381
382 if (item.getType() == Message.TYPE_IMAGE) {
383 if (item.getStatus() == Message.STATUS_RECIEVING) {
384 displayInfoMessage(viewHolder, R.string.receiving_image);
385 } else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
386 viewHolder.image.setVisibility(View.GONE);
387 viewHolder.messageBody.setVisibility(View.GONE);
388 viewHolder.download_button.setVisibility(View.VISIBLE);
389 viewHolder.download_button
390 .setOnClickListener(new OnClickListener() {
391
392 @Override
393 public void onClick(View v) {
394 JingleConnection connection = item
395 .getJingleConnection();
396 if (connection != null) {
397 connection.accept();
398 }
399 }
400 });
401 } else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
402 || (item.getEncryption() == Message.ENCRYPTION_NONE)
403 || (item.getEncryption() == Message.ENCRYPTION_OTR)) {
404 displayImageMessage(viewHolder, item);
405 } else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
406 displayInfoMessage(viewHolder, R.string.encrypted_message);
407 } else {
408 displayDecryptionFailed(viewHolder);
409 }
410 } else {
411 if (item.getEncryption() == Message.ENCRYPTION_PGP) {
412 if (activity.hasPgp()) {
413 displayInfoMessage(viewHolder, R.string.encrypted_message);
414 } else {
415 displayInfoMessage(viewHolder,
416 R.string.install_openkeychain);
417 viewHolder.message_box
418 .setOnClickListener(new OnClickListener() {
419
420 @Override
421 public void onClick(View v) {
422 activity.showInstallPgpDialog();
423 }
424 });
425 }
426 } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
427 displayDecryptionFailed(viewHolder);
428 } else {
429 displayTextMessage(viewHolder, item.getBody());
430 }
431 }
432
433 displayStatus(viewHolder, item);
434
435 return view;
436 }
437
438 private static class ViewHolder {
439
440 protected LinearLayout message_box;
441 protected Button download_button;
442 protected ImageView image;
443 protected ImageView indicator;
444 protected TextView time;
445 protected TextView messageBody;
446 protected ImageView contact_picture;
447
448 }
449
450 private class BitmapCache {
451 private HashMap<String, Bitmap> contactBitmaps = new HashMap<String, Bitmap>();
452 private HashMap<String, Bitmap> unknownBitmaps = new HashMap<String, Bitmap>();
453
454 public Bitmap get(Contact contact, Context context) {
455 if (!contactBitmaps.containsKey(contact.getJid())) {
456 contactBitmaps.put(contact.getJid(),
457 contact.getImage(48, context));
458 }
459 return contactBitmaps.get(contact.getJid());
460 }
461
462 public Bitmap get(String name, Context context) {
463 if (unknownBitmaps.containsKey(name)) {
464 return unknownBitmaps.get(name);
465 } else {
466 Bitmap bm = UIHelper
467 .getContactPicture(name, 48, context, false);
468 unknownBitmaps.put(name, bm);
469 return bm;
470 }
471 }
472 }
473
474 public interface OnContactPictureClicked {
475 public void onContactPictureClicked(Message message);
476 }
477}