NotificationService.java

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