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