NotificationService.java

  1package eu.siacs.conversations.services;
  2
  3import android.app.Notification;
  4import android.app.PendingIntent;
  5import android.content.Context;
  6import android.content.Intent;
  7import android.content.SharedPreferences;
  8import android.graphics.Bitmap;
  9import android.net.Uri;
 10import android.os.Build;
 11import android.os.SystemClock;
 12import android.support.v4.app.NotificationCompat;
 13import android.support.v4.app.NotificationCompat.BigPictureStyle;
 14import android.support.v4.app.NotificationCompat.Builder;
 15import android.support.v4.app.NotificationManagerCompat;
 16import android.support.v4.app.RemoteInput;
 17import android.support.v4.app.TaskStackBuilder;
 18import android.text.Html;
 19import android.util.DisplayMetrics;
 20import android.util.Log;
 21
 22import org.json.JSONArray;
 23import org.json.JSONObject;
 24
 25import java.io.FileNotFoundException;
 26import java.util.ArrayList;
 27import java.util.Calendar;
 28import java.util.HashMap;
 29import java.util.LinkedHashMap;
 30import java.util.List;
 31import java.util.Map;
 32import java.util.regex.Matcher;
 33import java.util.regex.Pattern;
 34
 35import eu.siacs.conversations.Config;
 36import eu.siacs.conversations.R;
 37import eu.siacs.conversations.entities.Account;
 38import eu.siacs.conversations.entities.Contact;
 39import eu.siacs.conversations.entities.Conversation;
 40import eu.siacs.conversations.entities.Message;
 41import eu.siacs.conversations.ui.ConversationActivity;
 42import eu.siacs.conversations.ui.ManageAccountActivity;
 43import eu.siacs.conversations.ui.TimePreference;
 44import eu.siacs.conversations.utils.GeoHelper;
 45import eu.siacs.conversations.utils.UIHelper;
 46
 47public class NotificationService {
 48
 49	private static final String CONVERSATIONS_GROUP = "eu.siacs.conversations";
 50	private final XmppConnectionService mXmppConnectionService;
 51
 52	private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>();
 53
 54	public static final int NOTIFICATION_ID = 0x2342;
 55	public static final int FOREGROUND_NOTIFICATION_ID = 0x8899;
 56	public static final int ERROR_NOTIFICATION_ID = 0x5678;
 57
 58	private Conversation mOpenConversation;
 59	private boolean mIsInForeground;
 60	private long mLastNotification;
 61
 62	public NotificationService(final XmppConnectionService service) {
 63		this.mXmppConnectionService = service;
 64	}
 65
 66	public boolean notify(final Message message) {
 67		return (message.getStatus() == Message.STATUS_RECEIVED)
 68				&& notificationsEnabled()
 69				&& !message.getConversation().isMuted()
 70				&& (message.getConversation().alwaysNotify() || wasHighlightedOrPrivate(message)
 71		);
 72	}
 73
 74	public void notifyPebble(final Message message) {
 75		final Intent i = new Intent("com.getpebble.action.SEND_NOTIFICATION");
 76
 77		final Conversation conversation = message.getConversation();
 78		final JSONObject jsonData = new JSONObject(new HashMap<String, String>(2) {{
 79			put("title", conversation.getName());
 80			put("body", message.getBody());
 81		}});
 82		final String notificationData = new JSONArray().put(jsonData).toString();
 83
 84		i.putExtra("messageType", "PEBBLE_ALERT");
 85		i.putExtra("sender", "Conversations"); /* XXX: Shouldn't be hardcoded, e.g., AbstractGenerator.APP_NAME); */
 86		i.putExtra("notificationData", notificationData);
 87		// notify Pebble App
 88		i.setPackage("com.getpebble.android");
 89		mXmppConnectionService.sendBroadcast(i);
 90		// notify Gadgetbridge
 91		i.setPackage("nodomain.freeyourgadget.gadgetbridge");
 92		mXmppConnectionService.sendBroadcast(i);
 93	}
 94
 95
 96	public boolean notificationsEnabled() {
 97		return mXmppConnectionService.getPreferences().getBoolean("show_notification", true);
 98	}
 99
100	public boolean isQuietHours() {
101		if (!mXmppConnectionService.getPreferences().getBoolean("enable_quiet_hours", false)) {
102			return false;
103		}
104		final long startTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_start", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY;
105		final long endTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_end", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY;
106		final long nowTime = Calendar.getInstance().getTimeInMillis() % Config.MILLISECONDS_IN_DAY;
107
108		if (endTime < startTime) {
109			return nowTime > startTime || nowTime < endTime;
110		} else {
111			return nowTime > startTime && nowTime < endTime;
112		}
113	}
114
115	public void pushFromBacklog(final Message message) {
116		if (notify(message)) {
117			synchronized (notifications) {
118				pushToStack(message);
119			}
120		}
121	}
122
123	public void pushFromDirectReply(final Message message) {
124		synchronized (notifications) {
125			pushToStack(message);
126			updateNotification(false);
127		}
128	}
129
130	public void finishBacklog(boolean notify) {
131		synchronized (notifications) {
132			mXmppConnectionService.updateUnreadCountBadge();
133			updateNotification(notify);
134		}
135	}
136
137	private void pushToStack(final Message message) {
138		final String conversationUuid = message.getConversationUuid();
139		if (notifications.containsKey(conversationUuid)) {
140			notifications.get(conversationUuid).add(message);
141		} else {
142			final ArrayList<Message> mList = new ArrayList<>();
143			mList.add(message);
144			notifications.put(conversationUuid, mList);
145		}
146	}
147
148	public void push(final Message message) {
149		mXmppConnectionService.updateUnreadCountBadge();
150		if (!notify(message)) {
151			Log.d(Config.LOGTAG,message.getConversation().getAccount().getJid().toBareJid()+": suppressing notification because turned off");
152			return;
153		}
154		final boolean isScreenOn = mXmppConnectionService.isInteractive();
155		if (this.mIsInForeground && isScreenOn && this.mOpenConversation == message.getConversation()) {
156			Log.d(Config.LOGTAG,message.getConversation().getAccount().getJid().toBareJid()+": suppressing notification because conversation is open");
157			return;
158		}
159		synchronized (notifications) {
160			pushToStack(message);
161			final Account account = message.getConversation().getAccount();
162			final boolean doNotify = (!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn)
163					&& !account.inGracePeriod()
164					&& !this.inMiniGracePeriod(account);
165			updateNotification(doNotify);
166			if (doNotify) {
167				notifyPebble(message);
168			}
169		}
170	}
171
172	public void clear() {
173		synchronized (notifications) {
174			for(ArrayList<Message> messages : notifications.values()) {
175				markAsReadIfHasDirectReply(messages);
176			}
177			notifications.clear();
178			updateNotification(false);
179		}
180	}
181
182	public void clear(final Conversation conversation) {
183		synchronized (notifications) {
184			markAsReadIfHasDirectReply(conversation);
185			notifications.remove(conversation.getUuid());
186			final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService);
187			notificationManager.cancel(conversation.getUuid(), NOTIFICATION_ID);
188			updateNotification(false);
189		}
190	}
191
192	private void markAsReadIfHasDirectReply(final Conversation conversation) {
193		markAsReadIfHasDirectReply(notifications.get(conversation.getUuid()));
194	}
195
196	private void markAsReadIfHasDirectReply(final ArrayList<Message> messages) {
197		if (messages != null && messages.size() > 0) {
198			Message last = messages.get(messages.size() - 1);
199			if (last.getStatus() != Message.STATUS_RECEIVED) {
200				mXmppConnectionService.markRead(last.getConversation(), false);
201			}
202		}
203	}
204
205	private void setNotificationColor(final Builder mBuilder) {
206		mBuilder.setColor(mXmppConnectionService.getResources().getColor(R.color.primary500));
207	}
208
209	public void updateNotification(final boolean notify) {
210		final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService);
211		final SharedPreferences preferences = mXmppConnectionService.getPreferences();
212
213		if (notifications.size() == 0) {
214			notificationManager.cancel(NOTIFICATION_ID);
215		} else {
216			if (notify) {
217				this.markLastNotification();
218			}
219			final Builder mBuilder;
220			if (notifications.size() == 1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
221				mBuilder = buildSingleConversations(notifications.values().iterator().next());
222				modifyForSoundVibrationAndLight(mBuilder, notify, preferences);
223				notificationManager.notify(NOTIFICATION_ID, mBuilder.build());
224			} else {
225				mBuilder = buildMultipleConversation();
226				modifyForSoundVibrationAndLight(mBuilder, notify, preferences);
227				notificationManager.notify(NOTIFICATION_ID, mBuilder.build());
228				for(Map.Entry<String,ArrayList<Message>> entry : notifications.entrySet()) {
229					Builder singleBuilder = buildSingleConversations(entry.getValue());
230					singleBuilder.setGroup(CONVERSATIONS_GROUP);
231					modifyForSoundVibrationAndLight(singleBuilder,notify,preferences);
232					notificationManager.notify(entry.getKey(), NOTIFICATION_ID ,singleBuilder.build());
233				}
234			}
235		}
236	}
237
238
239	private void modifyForSoundVibrationAndLight(Builder mBuilder, boolean notify, SharedPreferences preferences) {
240		final String ringtone = preferences.getString("notification_ringtone", null);
241		final boolean vibrate = preferences.getBoolean("vibrate_on_notification", true);
242		final boolean led = preferences.getBoolean("led", true);
243		if (notify && !isQuietHours()) {
244			if (vibrate) {
245				final int dat = 70;
246				final long[] pattern = {0, 3 * dat, dat, dat};
247				mBuilder.setVibrate(pattern);
248			}
249			if (ringtone != null) {
250				mBuilder.setSound(Uri.parse(ringtone));
251			}
252		}
253		if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
254			mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
255		}
256		setNotificationColor(mBuilder);
257		mBuilder.setDefaults(0);
258		if (led) {
259			mBuilder.setLights(0xff00FF00, 2000, 3000);
260		}
261	}
262
263	private Builder buildMultipleConversation() {
264		final Builder mBuilder = new NotificationCompat.Builder(
265				mXmppConnectionService);
266		final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
267		style.setBigContentTitle(notifications.size()
268				+ " "
269				+ mXmppConnectionService
270				.getString(R.string.unread_conversations));
271		final StringBuilder names = new StringBuilder();
272		Conversation conversation = null;
273		for (final ArrayList<Message> messages : notifications.values()) {
274			if (messages.size() > 0) {
275				conversation = messages.get(0).getConversation();
276				final String name = conversation.getName();
277				if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) {
278					int count = messages.size();
279					style.addLine(Html.fromHtml("<b>"+name+"</b>: "+mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages,count,count)));
280				} else {
281					style.addLine(Html.fromHtml("<b>" + name + "</b>: "
282							+ UIHelper.getMessagePreview(mXmppConnectionService, messages.get(0)).first));
283				}
284				names.append(name);
285				names.append(", ");
286			}
287		}
288		if (names.length() >= 2) {
289			names.delete(names.length() - 2, names.length());
290		}
291		mBuilder.setContentTitle(notifications.size()
292				+ " "
293				+ mXmppConnectionService
294				.getString(R.string.unread_conversations));
295		mBuilder.setContentText(names.toString());
296		mBuilder.setStyle(style);
297		if (conversation != null) {
298			mBuilder.setContentIntent(createContentIntent(conversation));
299		}
300		mBuilder.setGroupSummary(true);
301		mBuilder.setGroup(CONVERSATIONS_GROUP);
302		mBuilder.setDeleteIntent(createDeleteIntent(null));
303		mBuilder.setSmallIcon(R.drawable.ic_notification);
304		return mBuilder;
305	}
306
307	private Builder buildSingleConversations(final ArrayList<Message> messages) {
308		final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
309		if (messages.size() >= 1) {
310			final Conversation conversation = messages.get(0).getConversation();
311			mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService()
312					.get(conversation, getPixel(64)));
313			mBuilder.setContentTitle(conversation.getName());
314			if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) {
315				int count = messages.size();
316				mBuilder.setContentText(mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages,count,count));
317			} else {
318				Message message;
319				if ((message = getImage(messages)) != null) {
320					modifyForImage(mBuilder, message, messages);
321				} else {
322					modifyForTextOnly(mBuilder, messages);
323				}
324				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
325					RemoteInput remoteInput = new RemoteInput.Builder("text_reply").setLabel(UIHelper.getMessageHint(mXmppConnectionService, conversation)).build();
326					NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_send_text_offline, "Reply", createReplyIntent(conversation)).addRemoteInput(remoteInput).build();
327					mBuilder.addAction(action);
328				}
329				if ((message = getFirstDownloadableMessage(messages)) != null) {
330					mBuilder.addAction(
331							Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
332									R.drawable.ic_file_download_white_24dp : R.drawable.ic_action_download,
333							mXmppConnectionService.getResources().getString(R.string.download_x_file,
334									UIHelper.getFileDescriptionString(mXmppConnectionService, message)),
335							createDownloadIntent(message)
336					);
337				}
338				if ((message = getFirstLocationMessage(messages)) != null) {
339					mBuilder.addAction(R.drawable.ic_room_white_24dp,
340							mXmppConnectionService.getString(R.string.show_location),
341							createShowLocationIntent(message));
342				}
343			}
344			if (conversation.getMode() == Conversation.MODE_SINGLE) {
345				Contact contact = conversation.getContact();
346				Uri systemAccount = contact.getSystemAccount();
347				if (systemAccount != null) {
348					mBuilder.addPerson(systemAccount.toString());
349				}
350			}
351			mBuilder.setWhen(conversation.getLatestMessage().getTimeSent());
352			mBuilder.setSmallIcon(R.drawable.ic_notification);
353			mBuilder.setDeleteIntent(createDeleteIntent(conversation));
354			mBuilder.setContentIntent(createContentIntent(conversation));
355		}
356		return mBuilder;
357	}
358
359	private void modifyForImage(final Builder builder, final Message message,
360								final ArrayList<Message> messages) {
361		try {
362			final Bitmap bitmap = mXmppConnectionService.getFileBackend()
363					.getThumbnail(message, getPixel(288), false);
364			final ArrayList<Message> tmp = new ArrayList<>();
365			for (final Message msg : messages) {
366				if (msg.getType() == Message.TYPE_TEXT
367						&& msg.getTransferable() == null) {
368					tmp.add(msg);
369				}
370			}
371			final BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
372			bigPictureStyle.bigPicture(bitmap);
373			if (tmp.size() > 0) {
374				CharSequence text = getMergedBodies(tmp);
375				bigPictureStyle.setSummaryText(text);
376				builder.setContentText(text);
377			} else {
378				builder.setContentText(mXmppConnectionService.getString(
379						R.string.received_x_file,
380						UIHelper.getFileDescriptionString(mXmppConnectionService, message)));
381			}
382			builder.setStyle(bigPictureStyle);
383		} catch (final FileNotFoundException e) {
384			modifyForTextOnly(builder, messages);
385		}
386	}
387
388	private void modifyForTextOnly(final Builder builder, final ArrayList<Message> messages) {
389		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
390			NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(mXmppConnectionService.getString(R.string.me));
391			Conversation conversation = messages.get(0).getConversation();
392			if (conversation.getMode() == Conversation.MODE_MULTI) {
393				messagingStyle.setConversationTitle(conversation.getName());
394			}
395			for (Message message : messages) {
396				String sender = message.getStatus() == Message.STATUS_RECEIVED ? UIHelper.getMessageDisplayName(message) : null;
397				messagingStyle.addMessage(UIHelper.getMessagePreview(mXmppConnectionService,message).first, message.getTimeSent(), sender);
398			}
399			builder.setStyle(messagingStyle);
400		} else {
401			builder.setStyle(new NotificationCompat.BigTextStyle().bigText(getMergedBodies(messages)));
402			builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, messages.get(0)).first);
403		}
404	}
405
406	private Message getImage(final Iterable<Message> messages) {
407		Message image = null;
408		for (final Message message : messages) {
409			if (message.getStatus() != Message.STATUS_RECEIVED) {
410				return null;
411			}
412			if (message.getType() != Message.TYPE_TEXT
413					&& message.getTransferable() == null
414					&& message.getEncryption() != Message.ENCRYPTION_PGP
415					&& message.getFileParams().height > 0) {
416				image = message;
417			}
418		}
419		return image;
420	}
421
422	private Message getFirstDownloadableMessage(final Iterable<Message> messages) {
423		for (final Message message : messages) {
424			if (message.getTransferable() != null
425					&& (message.getType() == Message.TYPE_FILE
426							|| message.getType() == Message.TYPE_IMAGE
427							|| message.treatAsDownloadable() != Message.Decision.NEVER)) {
428				return message;
429			}
430		}
431		return null;
432	}
433
434	private Message getFirstLocationMessage(final Iterable<Message> messages) {
435		for (final Message message : messages) {
436			if (GeoHelper.isGeoUri(message.getBody())) {
437				return message;
438			}
439		}
440		return null;
441	}
442
443	private CharSequence getMergedBodies(final ArrayList<Message> messages) {
444		final StringBuilder text = new StringBuilder();
445		for (int i = 0; i < messages.size(); ++i) {
446			text.append(UIHelper.getMessagePreview(mXmppConnectionService, messages.get(i)).first);
447			if (i != messages.size() - 1) {
448				text.append("\n");
449			}
450		}
451		return text.toString();
452	}
453
454	private PendingIntent createShowLocationIntent(final Message message) {
455		Iterable<Intent> intents = GeoHelper.createGeoIntentsFromMessage(message);
456		for (Intent intent : intents) {
457			if (intent.resolveActivity(mXmppConnectionService.getPackageManager()) != null) {
458				return PendingIntent.getActivity(mXmppConnectionService, 18, intent, PendingIntent.FLAG_UPDATE_CURRENT);
459			}
460		}
461		return createOpenConversationsIntent();
462	}
463
464	private PendingIntent createContentIntent(final String conversationUuid, final String downloadMessageUuid) {
465		final Intent viewConversationIntent = new Intent(mXmppConnectionService,ConversationActivity.class);
466		viewConversationIntent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION);
467		viewConversationIntent.putExtra(ConversationActivity.CONVERSATION, conversationUuid);
468		if (downloadMessageUuid != null) {
469			viewConversationIntent.putExtra(ConversationActivity.EXTRA_DOWNLOAD_UUID, downloadMessageUuid);
470			return PendingIntent.getActivity(mXmppConnectionService,
471					conversationUuid.hashCode() % 389782,
472					viewConversationIntent,
473					PendingIntent.FLAG_UPDATE_CURRENT);
474		} else {
475			return PendingIntent.getActivity(mXmppConnectionService,
476					conversationUuid.hashCode() % 936236,
477					viewConversationIntent,
478					PendingIntent.FLAG_UPDATE_CURRENT);
479		}
480	}
481
482	private PendingIntent createDownloadIntent(final Message message) {
483		return createContentIntent(message.getConversationUuid(), message.getUuid());
484	}
485
486	private PendingIntent createContentIntent(final Conversation conversation) {
487		return createContentIntent(conversation.getUuid(), null);
488	}
489
490	private PendingIntent createDeleteIntent(Conversation conversation) {
491		final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
492		intent.setAction(XmppConnectionService.ACTION_CLEAR_NOTIFICATION);
493		if (conversation != null) {
494			intent.putExtra("uuid", conversation.getUuid());
495			return PendingIntent.getService(mXmppConnectionService, conversation.getUuid().hashCode() % 247527, intent, 0);
496		}
497		return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
498	}
499
500	private PendingIntent createReplyIntent(Conversation conversation) {
501		final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
502		intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION);
503		intent.putExtra("uuid",conversation.getUuid());
504		return PendingIntent.getService(mXmppConnectionService, conversation.getUuid().hashCode() % 402361, intent, 0);
505	}
506
507	private PendingIntent createDisableForeground() {
508		final Intent intent = new Intent(mXmppConnectionService,
509				XmppConnectionService.class);
510		intent.setAction(XmppConnectionService.ACTION_DISABLE_FOREGROUND);
511		return PendingIntent.getService(mXmppConnectionService, 34, intent, 0);
512	}
513
514	private PendingIntent createTryAgainIntent() {
515		final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
516		intent.setAction(XmppConnectionService.ACTION_TRY_AGAIN);
517		return PendingIntent.getService(mXmppConnectionService, 45, intent, 0);
518	}
519
520	private PendingIntent createDisableAccountIntent(final Account account) {
521		final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
522		intent.setAction(XmppConnectionService.ACTION_DISABLE_ACCOUNT);
523		intent.putExtra("account", account.getJid().toBareJid().toString());
524		return PendingIntent.getService(mXmppConnectionService, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
525	}
526
527	private boolean wasHighlightedOrPrivate(final Message message) {
528		final String nick = message.getConversation().getMucOptions().getActualNick();
529		final Pattern highlight = generateNickHighlightPattern(nick);
530		if (message.getBody() == null || nick == null) {
531			return false;
532		}
533		final Matcher m = highlight.matcher(message.getBody());
534		return (m.find() || message.getType() == Message.TYPE_PRIVATE);
535	}
536
537	private static Pattern generateNickHighlightPattern(final String nick) {
538		// We expect a word boundary, i.e. space or start of string, followed by
539		// the
540		// nick (matched in case-insensitive manner), followed by optional
541		// punctuation (for example "bob: i disagree" or "how are you alice?"),
542		// followed by another word boundary.
543		return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b",
544				Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
545	}
546
547	public void setOpenConversation(final Conversation conversation) {
548		this.mOpenConversation = conversation;
549	}
550
551	public void setIsInForeground(final boolean foreground) {
552		this.mIsInForeground = foreground;
553	}
554
555	private int getPixel(final int dp) {
556		final DisplayMetrics metrics = mXmppConnectionService.getResources()
557				.getDisplayMetrics();
558		return ((int) (dp * metrics.density));
559	}
560
561	private void markLastNotification() {
562		this.mLastNotification = SystemClock.elapsedRealtime();
563	}
564
565	private boolean inMiniGracePeriod(final Account account) {
566		final int miniGrace = account.getStatus() == Account.State.ONLINE ? Config.MINI_GRACE_PERIOD
567				: Config.MINI_GRACE_PERIOD * 2;
568		return SystemClock.elapsedRealtime() < (this.mLastNotification + miniGrace);
569	}
570
571	public Notification createForegroundNotification() {
572		final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
573
574		mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service));
575		if (Config.SHOW_CONNECTED_ACCOUNTS) {
576			List<Account> accounts = mXmppConnectionService.getAccounts();
577			int enabled = 0;
578			int connected = 0;
579			for (Account account : accounts) {
580				if (account.isOnlineAndConnected()) {
581					connected++;
582					enabled++;
583				} else if (!account.isOptionSet(Account.OPTION_DISABLED)) {
584					enabled++;
585				}
586			}
587			mBuilder.setContentText(mXmppConnectionService.getString(R.string.connected_accounts, connected, enabled));
588		} else {
589			mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_open_conversations));
590		}
591		mBuilder.setContentIntent(createOpenConversationsIntent());
592		mBuilder.setWhen(0);
593		mBuilder.setPriority(Config.SHOW_CONNECTED_ACCOUNTS ? NotificationCompat.PRIORITY_DEFAULT : NotificationCompat.PRIORITY_MIN);
594		mBuilder.setSmallIcon(R.drawable.ic_link_white_24dp);
595		if (Config.SHOW_DISABLE_FOREGROUND) {
596			final int cancelIcon;
597			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
598				mBuilder.setCategory(Notification.CATEGORY_SERVICE);
599				cancelIcon = R.drawable.ic_cancel_white_24dp;
600			} else {
601				cancelIcon = R.drawable.ic_action_cancel;
602			}
603			mBuilder.addAction(cancelIcon,
604					mXmppConnectionService.getString(R.string.disable_foreground_service),
605					createDisableForeground());
606		}
607		return mBuilder.build();
608	}
609
610	private PendingIntent createOpenConversationsIntent() {
611		return PendingIntent.getActivity(mXmppConnectionService, 0, new Intent(mXmppConnectionService, ConversationActivity.class), 0);
612	}
613
614	public void updateErrorNotification() {
615		final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService);
616		final List<Account> errors = new ArrayList<>();
617		for (final Account account : mXmppConnectionService.getAccounts()) {
618			if (account.hasErrorStatus()) {
619				errors.add(account);
620			}
621		}
622		if (mXmppConnectionService.getPreferences().getBoolean("keep_foreground_service", false)) {
623			notificationManager.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
624		}
625		final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
626		if (errors.size() == 0) {
627			notificationManager.cancel(ERROR_NOTIFICATION_ID);
628			return;
629		} else if (errors.size() == 1) {
630			mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.problem_connecting_to_account));
631			mBuilder.setContentText(errors.get(0).getJid().toBareJid().toString());
632		} else {
633			mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.problem_connecting_to_accounts));
634			mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_fix));
635		}
636		mBuilder.addAction(R.drawable.ic_autorenew_white_24dp,
637				mXmppConnectionService.getString(R.string.try_again),
638				createTryAgainIntent());
639		if (errors.size() == 1) {
640			mBuilder.addAction(R.drawable.ic_block_white_24dp,
641					mXmppConnectionService.getString(R.string.disable_account),
642					createDisableAccountIntent(errors.get(0)));
643		}
644		mBuilder.setOngoing(true);
645		//mBuilder.setLights(0xffffffff, 2000, 4000);
646		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
647			mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp);
648		} else {
649			mBuilder.setSmallIcon(R.drawable.ic_stat_alert_warning);
650		}
651		final TaskStackBuilder stackBuilder = TaskStackBuilder.create(mXmppConnectionService);
652		stackBuilder.addParentStack(ConversationActivity.class);
653
654		final Intent manageAccountsIntent = new Intent(mXmppConnectionService, ManageAccountActivity.class);
655		stackBuilder.addNextIntent(manageAccountsIntent);
656
657		final PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
658
659		mBuilder.setContentIntent(resultPendingIntent);
660		notificationManager.notify(ERROR_NOTIFICATION_ID, mBuilder.build());
661	}
662}