UIHelper.java

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