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