1package eu.siacs.conversations.utils;
2
3import java.io.FileNotFoundException;
4import java.text.SimpleDateFormat;
5import java.util.ArrayList;
6import java.util.Date;
7import java.util.List;
8import java.util.Locale;
9
10import eu.siacs.conversations.R;
11import eu.siacs.conversations.entities.Account;
12import eu.siacs.conversations.entities.Contact;
13import eu.siacs.conversations.entities.Conversation;
14import eu.siacs.conversations.entities.Message;
15import eu.siacs.conversations.entities.MucOptions;
16import eu.siacs.conversations.entities.MucOptions.User;
17import eu.siacs.conversations.ui.ConversationActivity;
18import eu.siacs.conversations.ui.ManageAccountActivity;
19
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.app.Notification;
23import android.app.NotificationManager;
24import android.app.PendingIntent;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.DialogInterface.OnClickListener;
28import android.content.Intent;
29import android.content.SharedPreferences;
30import android.content.res.Resources;
31import android.graphics.Bitmap;
32import android.graphics.BitmapFactory;
33import android.graphics.Canvas;
34import android.graphics.Paint;
35import android.graphics.Rect;
36import android.net.Uri;
37import android.preference.PreferenceManager;
38import android.provider.ContactsContract.Contacts;
39import android.support.v4.app.NotificationCompat;
40import android.support.v4.app.NotificationCompat.InboxStyle;
41import android.support.v4.app.TaskStackBuilder;
42import android.text.Html;
43import android.util.DisplayMetrics;
44import android.util.Log;
45import android.view.LayoutInflater;
46import android.view.View;
47import android.widget.LinearLayout;
48import android.widget.QuickContactBadge;
49import android.widget.TextView;
50
51public class UIHelper {
52 private static final int BG_COLOR = 0xFF181818;
53 private static final int FG_COLOR = 0xFFE5E5E5;
54 private static final int TRANSPARENT = 0x00000000;
55
56 public static String readableTimeDifference(long time) {
57 if (time == 0) {
58 return "just now";
59 }
60 Date date = new Date(time);
61 long difference = (System.currentTimeMillis() - time) / 1000;
62 if (difference < 60) {
63 return "just now";
64 } else if (difference < 60 * 10) {
65 return difference / 60 + " min ago";
66 } else if (difference < 60 * 60 * 24) {
67 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm",Locale.US);
68 return sdf.format(date);
69 } else {
70 SimpleDateFormat sdf = new SimpleDateFormat("MM/dd",Locale.US);
71 return sdf.format(date);
72 }
73 }
74
75 public static int getRealPx(int dp, Context context) {
76 final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
77 return ((int) (dp * metrics.density));
78 }
79
80 private static int getNameColor(String name) {
81 int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713,
82 0xFFe92727 };
83 int color = holoColors[Math.abs(name.toLowerCase(Locale.getDefault()).hashCode()) % holoColors.length];
84 return color;
85 }
86
87 private static Bitmap getUnknownContactPicture(String[] names, int size, int bgColor, int fgColor) {
88 int tiles = (names.length > 4)? 4 :
89 (names.length < 1)? 1 :
90 names.length;
91 Bitmap bitmap = Bitmap
92 .createBitmap(size, size, Bitmap.Config.ARGB_8888);
93 Canvas canvas = new Canvas(bitmap);
94
95 String[] letters = new String[tiles];
96 int[] colors = new int[tiles];
97 if (names.length < 1) {
98 letters[0] = "?";
99 colors[0] = 0xFFe92727;
100 } else {
101 for(int i = 0; i < tiles; ++i) {
102 letters[i] = (names[i].length() > 0) ?
103 names[i].substring(0, 1).toUpperCase(Locale.US) : " ";
104 colors[i] = getNameColor(names[i]);
105 }
106
107 if (names.length > 4) {
108 letters[3] = "\u2026"; // Unicode ellipsis
109 colors[3] = 0xFF444444;
110 }
111 }
112 Paint textPaint = new Paint(), tilePaint = new Paint();
113 textPaint.setColor(fgColor);
114 Rect rect, left, right, topLeft, bottomLeft, topRight, bottomRight;
115 float width;
116
117 switch(tiles) {
118 case 1:
119 bitmap.eraseColor(colors[0]);
120
121 textPaint.setTextSize((float) (size * 0.9));
122 textPaint.setAntiAlias(true);
123 rect = new Rect();
124 textPaint.getTextBounds(letters[0], 0, 1, rect);
125 width = textPaint.measureText(letters[0]);
126 canvas.drawText(letters[0], (size / 2) - (width / 2), (size / 2)
127 + (rect.height() / 2), textPaint);
128 break;
129
130 case 2:
131 bitmap.eraseColor(bgColor);
132
133 tilePaint.setColor(colors[0]);
134 left = new Rect(0, 0, (size/2)-2, size);
135 canvas.drawRect(left, tilePaint);
136
137 tilePaint.setColor(colors[1]);
138 right = new Rect((size/2)+2, 0, size, size);
139 canvas.drawRect(right, tilePaint);
140
141 textPaint.setTextSize((float) (size * 0.9*0.5));
142 textPaint.setAntiAlias(true);
143 rect = new Rect();
144 textPaint.getTextBounds(letters[0], 0, 1, rect);
145 width = textPaint.measureText(letters[0]);
146 canvas.drawText(letters[0], (size / 4) - (width / 2), (size / 2)
147 + (rect.height() / 2), textPaint);
148 textPaint.getTextBounds(letters[1], 0, 1, rect);
149 width = textPaint.measureText(letters[1]);
150 canvas.drawText(letters[1], (3 * size / 4) - (width / 2), (size / 2)
151 + (rect.height() / 2), textPaint);
152 break;
153
154 case 3:
155 bitmap.eraseColor(bgColor);
156
157 tilePaint.setColor(colors[0]);
158 left = new Rect(0, 0, (size/2)-2, size);
159 canvas.drawRect(left, tilePaint);
160
161 tilePaint.setColor(colors[1]);
162 topRight = new Rect((size/2)+2, 0, size, (size/2 - 2));
163 canvas.drawRect(topRight, tilePaint);
164
165 tilePaint.setColor(colors[2]);
166 bottomRight = new Rect((size/2)+2, (size/2 + 2), size, size);
167 canvas.drawRect(bottomRight, tilePaint);
168
169 textPaint.setTextSize((float) (size * 0.9*0.5));
170 textPaint.setAntiAlias(true);
171 rect = new Rect();
172
173 textPaint.getTextBounds(letters[0], 0, 1, rect);
174 width = textPaint.measureText(letters[0]);
175 canvas.drawText(letters[0], (size / 4) - (width / 2), (size / 2)
176 + (rect.height() / 2), textPaint);
177
178 textPaint.getTextBounds(letters[1], 0, 1, rect);
179 width = textPaint.measureText(letters[1]);
180 canvas.drawText(letters[1], (3 * size / 4) - (width / 2), (size / 4)
181 + (rect.height() / 2), textPaint);
182
183 textPaint.getTextBounds(letters[2], 0, 1, rect);
184 width = textPaint.measureText(letters[2]);
185 canvas.drawText(letters[2], (3 * size / 4) - (width / 2), (3* size / 4)
186 + (rect.height() / 2), textPaint);
187 break;
188
189 case 4:
190 bitmap.eraseColor(bgColor);
191
192 tilePaint.setColor(colors[0]);
193 topLeft = new Rect(0, 0, (size/2)-2, (size/2)-2);
194 canvas.drawRect(topLeft, tilePaint);
195
196 tilePaint.setColor(colors[1]);
197 bottomLeft = new Rect(0, (size/2)+2, (size/2)-2, size);
198 canvas.drawRect(bottomLeft, tilePaint);
199
200 tilePaint.setColor(colors[2]);
201 topRight = new Rect((size/2)+2, 0, size, (size/2 - 2));
202 canvas.drawRect(topRight, tilePaint);
203
204 tilePaint.setColor(colors[3]);
205 bottomRight = new Rect((size/2)+2, (size/2 + 2), size, size);
206 canvas.drawRect(bottomRight, tilePaint);
207
208 textPaint.setTextSize((float) (size * 0.9*0.5));
209 textPaint.setAntiAlias(true);
210 rect = new Rect();
211
212 textPaint.getTextBounds(letters[0], 0, 1, rect);
213 width = textPaint.measureText(letters[0]);
214 canvas.drawText(letters[0], (size / 4) - (width / 2), (size / 4)
215 + (rect.height() / 2), textPaint);
216
217 textPaint.getTextBounds(letters[1], 0, 1, rect);
218 width = textPaint.measureText(letters[1]);
219 canvas.drawText(letters[1], (size / 4) - (width / 2), (3* size / 4)
220 + (rect.height() / 2), textPaint);
221
222 textPaint.getTextBounds(letters[2], 0, 1, rect);
223 width = textPaint.measureText(letters[2]);
224 canvas.drawText(letters[2], (3 * size / 4) - (width / 2), (size / 4)
225 + (rect.height() / 2), textPaint);
226
227 textPaint.getTextBounds(letters[3], 0, 1, rect);
228 width = textPaint.measureText(letters[3]);
229 canvas.drawText(letters[3], (3 * size / 4) - (width / 2), (3* size / 4)
230 + (rect.height() / 2), textPaint);
231 break;
232 }
233 return bitmap;
234 }
235
236 private static Bitmap getUnknownContactPicture(String[] names, int size) {
237 return getUnknownContactPicture(names, size, UIHelper.BG_COLOR, UIHelper.FG_COLOR);
238 }
239
240 private static Bitmap getMucContactPicture(Conversation conversation, int size, int bgColor, int fgColor) {
241 List<User> members = conversation.getMucOptions().getUsers();
242 if (members.size() == 0) {
243 return getUnknownContactPicture(new String[]{conversation.getName(false)}, size, bgColor, fgColor);
244 }
245 String[] names = new String[members.size()+1];
246 names[0] = conversation.getMucOptions().getNick();
247 for(int i = 0; i < members.size(); ++i) {
248 names[i+1] = members.get(i).getName();
249 }
250
251 return getUnknownContactPicture(names, size, bgColor, fgColor);
252 }
253
254 public static Bitmap getContactPicture(Conversation conversation, int dpSize, Context context, boolean notification) {
255 if(conversation.getMode() == Conversation.MODE_SINGLE) {
256 if (conversation.getContact() != null){
257 return getContactPicture(conversation.getContact(), dpSize,
258 context, notification);
259 } else {
260 return getContactPicture(conversation.getName(false), dpSize,
261 context, notification);
262 }
263 } else{
264 int fgColor = UIHelper.FG_COLOR,
265 bgColor = (notification) ?
266 UIHelper.BG_COLOR : UIHelper.TRANSPARENT;
267
268 return getMucContactPicture(conversation, getRealPx(dpSize, context),
269 bgColor, fgColor);
270 }
271 }
272
273 public static Bitmap getContactPicture(Contact contact, int dpSize, Context context, boolean notification) {
274 int fgColor = UIHelper.FG_COLOR,
275 bgColor = (notification) ?
276 UIHelper.BG_COLOR : UIHelper.TRANSPARENT;
277
278 String uri = contact.getProfilePhoto();
279 if (uri==null) {
280 return getContactPicture(contact.getDisplayName(), dpSize,
281 context, notification);
282 }
283 try {
284 Bitmap bm = BitmapFactory.decodeStream(context.getContentResolver()
285 .openInputStream(Uri.parse(uri)));
286 return Bitmap.createScaledBitmap(bm, getRealPx(dpSize, context),
287 getRealPx(dpSize, context), false);
288 } catch (FileNotFoundException e) {
289 return getContactPicture(contact.getDisplayName(), dpSize,
290 context, notification);
291 }
292 }
293
294 public static Bitmap getContactPicture(String name, int dpSize, Context context, boolean notification) {
295 int fgColor = UIHelper.FG_COLOR,
296 bgColor = (notification) ?
297 UIHelper.BG_COLOR : UIHelper.TRANSPARENT;
298
299 return getUnknownContactPicture(new String[]{name}, getRealPx(dpSize, context),
300 bgColor, fgColor);
301 }
302
303 public static Bitmap getErrorPicture(int size) {
304 Bitmap bitmap = Bitmap
305 .createBitmap(size, size, Bitmap.Config.ARGB_8888);
306 Canvas canvas = new Canvas(bitmap);
307
308 bitmap.eraseColor(0xFFe92727);
309
310 Paint paint = new Paint();
311 paint.setColor(0xffe5e5e5);
312 paint.setTextSize((float) (size * 0.9));
313 paint.setAntiAlias(true);
314 Rect rect = new Rect();
315 paint.getTextBounds("!", 0, 1, rect);
316 float width = paint.measureText("!");
317 canvas.drawText("!", (size / 2) - (width / 2),
318 (size / 2) + (rect.height() / 2), paint);
319
320 return bitmap;
321 }
322
323 public static void showErrorNotification(Context context, List<Account> accounts) {
324 NotificationManager mNotificationManager = (NotificationManager) context
325 .getSystemService(Context.NOTIFICATION_SERVICE);
326 List<Account> accountsWproblems = new ArrayList<Account>();
327 for(Account account : accounts) {
328 if (account.hasErrorStatus()) {
329 accountsWproblems.add(account);
330 }
331 }
332 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context);
333 if (accountsWproblems.size() == 0) {
334 mNotificationManager.cancel(1111);
335 return;
336 } else if (accountsWproblems.size() == 1) {
337 mBuilder.setContentTitle(context.getString(R.string.problem_connecting_to_account));
338 mBuilder.setContentText(accountsWproblems.get(0).getJid());
339 } else {
340 mBuilder.setContentTitle(context.getString(R.string.problem_connecting_to_accounts));
341 mBuilder.setContentText(context.getString(R.string.touch_to_fix));
342 }
343 mBuilder.setOngoing(true);
344 mBuilder.setLights(0xffffffff, 2000, 4000);
345 mBuilder.setSmallIcon(R.drawable.notification);
346 TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
347 stackBuilder.addParentStack(ConversationActivity.class);
348
349 Intent manageAccountsIntent = new Intent(context,
350 ManageAccountActivity.class);
351 stackBuilder.addNextIntent(manageAccountsIntent);
352
353 PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
354 0, PendingIntent.FLAG_UPDATE_CURRENT);
355
356 mBuilder.setContentIntent(resultPendingIntent);
357 Notification notification = mBuilder.build();
358 mNotificationManager.notify(1111, notification);
359 }
360
361 public static void updateNotification(Context context,
362 List<Conversation> conversations, Conversation currentCon, boolean notify) {
363 NotificationManager mNotificationManager = (NotificationManager) context
364 .getSystemService(Context.NOTIFICATION_SERVICE);
365
366 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
367 boolean useSubject = preferences.getBoolean("use_subject_in_muc", true);
368 boolean showNofifications = preferences.getBoolean("show_notification",true);
369 boolean vibrate = preferences.getBoolean("vibrate_on_notification", true);
370 boolean alwaysNotify = preferences.getBoolean("notify_in_conversation_when_highlighted", false);
371
372 if (!showNofifications) {
373 mNotificationManager.cancel(2342);
374 return;
375 }
376
377 String targetUuid = "";
378
379 if ((currentCon != null) &&(currentCon.getMode() == Conversation.MODE_MULTI)&&(!alwaysNotify)) {
380 String nick = currentCon.getMucOptions().getNick();
381 notify = currentCon.getLatestMessage().getBody().contains(nick);
382 }
383
384 List<Conversation> unread = new ArrayList<Conversation>();
385 for (Conversation conversation : conversations) {
386 if (conversation.getMode() == Conversation.MODE_MULTI) {
387 if ((!conversation.isRead())&&((wasHighlighted(conversation)||(alwaysNotify)))) {
388 unread.add(conversation);
389 }
390 } else {
391 if (!conversation.isRead()) {
392 unread.add(conversation);
393 }
394 }
395 }
396 String ringtone = preferences.getString("notification_ringtone", null);
397
398 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
399 context);
400 if (unread.size() == 0) {
401 mNotificationManager.cancel(2342);
402 return;
403 } else if (unread.size() == 1) {
404 Conversation conversation = unread.get(0);
405 targetUuid = conversation.getUuid();
406 mBuilder.setLargeIcon(UIHelper.getContactPicture(conversation, 64,
407 context, true));
408 mBuilder.setContentTitle(conversation.getName(useSubject));
409 if (notify) {
410 mBuilder.setTicker(conversation.getLatestMessage().getBody().trim());
411 }
412 StringBuilder bigText = new StringBuilder();
413 List<Message> messages = conversation.getMessages();
414 String firstLine = "";
415 for (int i = messages.size() - 1; i >= 0; --i) {
416 if (!messages.get(i).isRead()) {
417 if (i == messages.size() - 1) {
418 firstLine = messages.get(i).getBody().trim();
419 bigText.append(firstLine);
420 } else {
421 firstLine = messages.get(i).getBody().trim();
422 bigText.insert(0, firstLine + "\n");
423 }
424 } else {
425 break;
426 }
427 }
428 mBuilder.setContentText(firstLine);
429 mBuilder.setStyle(new NotificationCompat.BigTextStyle()
430 .bigText(bigText.toString()));
431 } else {
432 NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
433 style.setBigContentTitle(unread.size() + " unread Conversations");
434 StringBuilder names = new StringBuilder();
435 for (int i = 0; i < unread.size(); ++i) {
436 targetUuid = unread.get(i).getUuid();
437 if (i < unread.size() - 1) {
438 names.append(unread.get(i).getName(useSubject) + ", ");
439 } else {
440 names.append(unread.get(i).getName(useSubject));
441 }
442 style.addLine(Html.fromHtml("<b>" + unread.get(i).getName(useSubject)
443 + "</b> " + unread.get(i).getLatestMessage().getBody().trim()));
444 }
445 mBuilder.setContentTitle(unread.size() + " unread Conversations");
446 mBuilder.setContentText(names.toString());
447 mBuilder.setStyle(style);
448 }
449 if ((currentCon!=null)&&(notify)) {
450 targetUuid=currentCon.getUuid();
451 }
452 if (unread.size() != 0) {
453 mBuilder.setSmallIcon(R.drawable.notification);
454 if (notify) {
455 if (vibrate) {
456 int dat = 70;
457 long[] pattern = {0,3*dat,dat,dat};
458 mBuilder.setVibrate(pattern);
459 }
460 mBuilder.setLights(0xffffffff, 2000, 4000);
461 if (ringtone != null) {
462 mBuilder.setSound(Uri.parse(ringtone));
463 }
464 }
465
466 TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
467 stackBuilder.addParentStack(ConversationActivity.class);
468
469 Intent viewConversationIntent = new Intent(context,
470 ConversationActivity.class);
471 viewConversationIntent.setAction(Intent.ACTION_VIEW);
472 viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
473 targetUuid);
474 viewConversationIntent
475 .setType(ConversationActivity.VIEW_CONVERSATION);
476
477 stackBuilder.addNextIntent(viewConversationIntent);
478
479 PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
480 0, PendingIntent.FLAG_UPDATE_CURRENT);
481
482 mBuilder.setContentIntent(resultPendingIntent);
483 Notification notification = mBuilder.build();
484 mNotificationManager.notify(2342, notification);
485 }
486 }
487
488 private static boolean wasHighlighted(Conversation conversation) {
489 List<Message> messages = conversation.getMessages();
490 String nick = conversation.getMucOptions().getNick();
491 for(int i = messages.size() - 1; i >= 0; --i) {
492 if (messages.get(i).isRead()) {
493 break;
494 } else {
495 if (messages.get(i).getBody().contains(nick)) {
496 return true;
497 }
498 }
499 }
500 return false;
501 }
502
503 public static void prepareContactBadge(final Activity activity,
504 QuickContactBadge badge, final Contact contact, Context context) {
505 if (contact.getSystemAccount() != null) {
506 String[] systemAccount = contact.getSystemAccount().split("#");
507 long id = Long.parseLong(systemAccount[0]);
508 badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
509 }
510 badge.setImageBitmap(UIHelper.getContactPicture(contact, 72, context, false));
511 }
512
513 public static AlertDialog getVerifyFingerprintDialog(
514 final ConversationActivity activity,
515 final Conversation conversation, final LinearLayout msg) {
516 final Contact contact = conversation.getContact();
517 final Account account = conversation.getAccount();
518
519 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
520 builder.setTitle("Verify fingerprint");
521 LayoutInflater inflater = activity.getLayoutInflater();
522 View view = inflater.inflate(R.layout.dialog_verify_otr, null);
523 TextView jid = (TextView) view.findViewById(R.id.verify_otr_jid);
524 TextView fingerprint = (TextView) view
525 .findViewById(R.id.verify_otr_fingerprint);
526 TextView yourprint = (TextView) view
527 .findViewById(R.id.verify_otr_yourprint);
528
529 jid.setText(contact.getJid());
530 fingerprint.setText(conversation.getOtrFingerprint());
531 yourprint.setText(account.getOtrFingerprint());
532 builder.setNegativeButton("Cancel", null);
533 builder.setPositiveButton("Verify", new OnClickListener() {
534
535 @Override
536 public void onClick(DialogInterface dialog, int which) {
537 contact.addOtrFingerprint(conversation.getOtrFingerprint());
538 msg.setVisibility(View.GONE);
539 activity.xmppConnectionService.updateContact(contact);
540 }
541 });
542 builder.setView(view);
543 return builder.create();
544 }
545
546 public static Bitmap getSelfContactPicture(Account account, int size, boolean showPhoneSelfContactPicture, Context context) {
547 if (showPhoneSelfContactPicture) {
548 Uri selfiUri = PhoneHelper.getSefliUri(context);
549 if (selfiUri != null) {
550 try {
551 return BitmapFactory.decodeStream(context
552 .getContentResolver().openInputStream(selfiUri));
553 } catch (FileNotFoundException e) {
554 return getContactPicture(account.getJid(), size, context, false);
555 }
556 }
557 return getContactPicture(account.getJid(), size, context, false);
558 } else {
559 return getContactPicture(account.getJid(), size, context, false);
560 }
561 }
562}