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