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).getConversation().getAccount()
56 .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(activity.xmppConnectionService
241 .getFileBackend().getJingleFileUri(message), "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 activity.xmppConnectionService.getFileBackend()
253 .getJingleFileUri(message));
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}