NotificationService.java

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