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