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 void showErrorNotification(Context context, List<Account> accounts) {
306		NotificationManager mNotificationManager = (NotificationManager) context
307				.getSystemService(Context.NOTIFICATION_SERVICE);
308		List<Account> accountsWproblems = new ArrayList<Account>();
309		for(Account account : accounts) {
310			if (account.hasErrorStatus()) {
311				accountsWproblems.add(account);
312			}
313		}
314		NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context);
315		if (accountsWproblems.size() == 0) {
316			mNotificationManager.cancel(1111);
317			return;
318		} else if (accountsWproblems.size() == 1) {
319			mBuilder.setContentTitle(context.getString(R.string.problem_connecting_to_account));
320			mBuilder.setContentText(accountsWproblems.get(0).getJid());
321		} else {
322			mBuilder.setContentTitle(context.getString(R.string.problem_connecting_to_accounts));
323			mBuilder.setContentText(context.getString(R.string.touch_to_fix));
324		}
325		mBuilder.setOngoing(true);
326		mBuilder.setLights(0xffffffff, 2000, 4000);
327		mBuilder.setSmallIcon(R.drawable.notification);
328		TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
329		stackBuilder.addParentStack(ConversationActivity.class);
330
331		Intent manageAccountsIntent = new Intent(context,
332				ManageAccountActivity.class);
333		stackBuilder.addNextIntent(manageAccountsIntent);
334
335		PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
336				0, PendingIntent.FLAG_UPDATE_CURRENT);
337		
338		mBuilder.setContentIntent(resultPendingIntent);
339		Notification notification = mBuilder.build();
340		mNotificationManager.notify(1111, notification);
341	}
342
343	public static void updateNotification(Context context,
344			List<Conversation> conversations, Conversation currentCon, boolean notify) {
345		NotificationManager mNotificationManager = (NotificationManager) context
346				.getSystemService(Context.NOTIFICATION_SERVICE);
347		
348		SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
349		boolean useSubject = preferences.getBoolean("use_subject_in_muc", true);
350		boolean showNofifications = preferences.getBoolean("show_notification",true);
351		boolean vibrate = preferences.getBoolean("vibrate_on_notification", true);
352		boolean alwaysNotify = preferences.getBoolean("notify_in_conversation_when_highlighted", false);
353
354		if (!showNofifications) {
355			mNotificationManager.cancel(2342);
356			return;
357		}
358		
359		String targetUuid = "";
360		
361		if ((currentCon != null) &&(currentCon.getMode() == Conversation.MODE_MULTI)&&(!alwaysNotify)) {
362			String nick = currentCon.getMucOptions().getNick();
363			notify = currentCon.getLatestMessage().getBody().contains(nick);
364		}
365		
366		List<Conversation> unread = new ArrayList<Conversation>();
367		for (Conversation conversation : conversations) {
368			if (conversation.getMode() == Conversation.MODE_MULTI) {
369				if ((!conversation.isRead())&&((wasHighlighted(conversation)||(alwaysNotify)))) {
370					unread.add(conversation);
371				}
372			} else {
373				if (!conversation.isRead()) {
374					unread.add(conversation);
375				}
376			}
377		}
378		String ringtone = preferences.getString("notification_ringtone", null);
379
380		NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
381				context);
382		if (unread.size() == 0) {
383			mNotificationManager.cancel(2342);
384			return;
385		} else if (unread.size() == 1) {
386			Conversation conversation = unread.get(0);
387			targetUuid = conversation.getUuid();
388			mBuilder.setLargeIcon(UIHelper.getContactPicture(conversation, 64,
389							context, true));
390			mBuilder.setContentTitle(conversation.getName(useSubject));
391			if (notify) {
392				mBuilder.setTicker(conversation.getLatestMessage().getReadableBody(context));
393			}
394			StringBuilder bigText = new StringBuilder();
395			List<Message> messages = conversation.getMessages();
396			String firstLine = "";
397			for (int i = messages.size() - 1; i >= 0; --i) {
398				if (!messages.get(i).isRead()) {
399					if (i == messages.size() - 1) {
400						firstLine = messages.get(i).getReadableBody(context);
401						bigText.append(firstLine);
402					} else {
403						firstLine = messages.get(i).getReadableBody(context);
404						bigText.insert(0, firstLine + "\n");
405					}
406				} else {
407					break;
408				}
409			}
410			mBuilder.setContentText(firstLine);
411			mBuilder.setStyle(new NotificationCompat.BigTextStyle()
412					.bigText(bigText.toString()));
413		} else {
414			NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
415			style.setBigContentTitle(unread.size() + " unread Conversations");
416			StringBuilder names = new StringBuilder();
417			for (int i = 0; i < unread.size(); ++i) {
418				targetUuid = unread.get(i).getUuid();
419				if (i < unread.size() - 1) {
420					names.append(unread.get(i).getName(useSubject) + ", ");
421				} else {
422					names.append(unread.get(i).getName(useSubject));
423				}
424				style.addLine(Html.fromHtml("<b>" + unread.get(i).getName(useSubject)
425						+ "</b> " + unread.get(i).getLatestMessage().getReadableBody(context)));
426			}
427			mBuilder.setContentTitle(unread.size() + " unread Conversations");
428			mBuilder.setContentText(names.toString());
429			mBuilder.setStyle(style);
430		}
431		if ((currentCon!=null)&&(notify)) {
432			targetUuid=currentCon.getUuid();
433		}
434		if (unread.size() != 0) {
435			mBuilder.setSmallIcon(R.drawable.notification);
436			if (notify) {
437				if (vibrate) {
438					int dat = 70;
439					long[] pattern = {0,3*dat,dat,dat};
440					mBuilder.setVibrate(pattern);
441				}
442				mBuilder.setLights(0xffffffff, 2000, 4000);
443				if (ringtone != null) {
444					mBuilder.setSound(Uri.parse(ringtone));
445				}
446			}
447
448			TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
449			stackBuilder.addParentStack(ConversationActivity.class);
450
451			Intent viewConversationIntent = new Intent(context,
452					ConversationActivity.class);
453			viewConversationIntent.setAction(Intent.ACTION_VIEW);
454			viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
455					targetUuid);
456			viewConversationIntent
457					.setType(ConversationActivity.VIEW_CONVERSATION);
458			
459			stackBuilder.addNextIntent(viewConversationIntent);
460
461			PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
462					0, PendingIntent.FLAG_UPDATE_CURRENT);
463			
464			mBuilder.setContentIntent(resultPendingIntent);
465			Notification notification = mBuilder.build();
466			mNotificationManager.notify(2342, notification);
467		}
468	}
469	
470	private static boolean wasHighlighted(Conversation conversation) {
471		List<Message> messages = conversation.getMessages();
472		String nick = conversation.getMucOptions().getNick();
473		for(int i = messages.size() - 1; i >= 0; --i) {
474			if (messages.get(i).isRead()) {
475				break;
476			} else {
477				if (messages.get(i).getBody().contains(nick)) {
478					return true;
479				}
480			}
481		}
482		return false;
483	}
484
485	public static void prepareContactBadge(final Activity activity,
486			QuickContactBadge badge, final Contact contact, Context context) {
487		if (contact.getSystemAccount() != null) {
488			String[] systemAccount = contact.getSystemAccount().split("#");
489			long id = Long.parseLong(systemAccount[0]);
490			badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
491		}
492		badge.setImageBitmap(UIHelper.getContactPicture(contact, 72, context, false));
493	}
494
495	public static AlertDialog getVerifyFingerprintDialog(
496			final ConversationActivity activity,
497			final Conversation conversation, final LinearLayout msg) {
498		final Contact contact = conversation.getContact();
499		final Account account = conversation.getAccount();
500
501		AlertDialog.Builder builder = new AlertDialog.Builder(activity);
502		builder.setTitle("Verify fingerprint");
503		LayoutInflater inflater = activity.getLayoutInflater();
504		View view = inflater.inflate(R.layout.dialog_verify_otr, null);
505		TextView jid = (TextView) view.findViewById(R.id.verify_otr_jid);
506		TextView fingerprint = (TextView) view
507				.findViewById(R.id.verify_otr_fingerprint);
508		TextView yourprint = (TextView) view
509				.findViewById(R.id.verify_otr_yourprint);
510
511		jid.setText(contact.getJid());
512		fingerprint.setText(conversation.getOtrFingerprint());
513		yourprint.setText(account.getOtrFingerprint());
514		builder.setNegativeButton("Cancel", null);
515		builder.setPositiveButton("Verify", new OnClickListener() {
516
517			@Override
518			public void onClick(DialogInterface dialog, int which) {
519				contact.addOtrFingerprint(conversation.getOtrFingerprint());
520				msg.setVisibility(View.GONE);
521				activity.xmppConnectionService.updateContact(contact);
522			}
523		});
524		builder.setView(view);
525		return builder.create();
526	}
527
528	public static Bitmap getSelfContactPicture(Account account, int size, boolean showPhoneSelfContactPicture, Context context) {
529		if (showPhoneSelfContactPicture) {
530			Uri selfiUri = PhoneHelper.getSefliUri(context);
531			if (selfiUri != null) {
532				try {
533					return BitmapFactory.decodeStream(context
534							.getContentResolver().openInputStream(selfiUri));
535				} catch (FileNotFoundException e) {
536					return getContactPicture(account.getJid(), size, context, false);
537				}
538			}
539			return getContactPicture(account.getJid(), size, context, false);
540		} else {
541			return getContactPicture(account.getJid(), size, context, false);
542		}
543	}
544}