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