package eu.siacs.conversations.services; import android.app.Notification; import android.app.PendingIntent; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Typeface; import android.net.Uri; import android.os.Build; import android.os.SystemClock; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.BigPictureStyle; import android.support.v4.app.NotificationCompat.Builder; import android.support.v4.app.NotificationManagerCompat; import android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation; import android.support.v4.app.RemoteInput; import android.support.v4.content.ContextCompat; import android.text.SpannableString; import android.text.style.StyleSpan; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.ui.ManageAccountActivity; import eu.siacs.conversations.ui.SettingsActivity; import eu.siacs.conversations.ui.TimePreference; import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.XmppConnection; public class NotificationService { private static final String CONVERSATIONS_GROUP = "eu.siacs.conversations"; private final XmppConnectionService mXmppConnectionService; private final LinkedHashMap> notifications = new LinkedHashMap<>(); private static final int NOTIFICATION_ID_MULTIPLIER = 1024 * 1024; public static final int NOTIFICATION_ID = 2 * NOTIFICATION_ID_MULTIPLIER; public static final int FOREGROUND_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 4; public static final int ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6; private Conversation mOpenConversation; private boolean mIsInForeground; private long mLastNotification; private final HashMap mBacklogMessageCounter = new HashMap<>(); public NotificationService(final XmppConnectionService service) { this.mXmppConnectionService = service; } public boolean notify(final Message message) { return message.getStatus() == Message.STATUS_RECEIVED && notificationsEnabled() && !message.getConversation().isMuted() && (message.getConversation().alwaysNotify() || wasHighlightedOrPrivate(message)) && (!message.getConversation().isWithStranger() || notificationsFromStrangers()) ; } public boolean notificationsEnabled() { return mXmppConnectionService.getPreferences().getBoolean("show_notification", true); } private boolean notificationsFromStrangers() { return mXmppConnectionService.getPreferences().getBoolean("notifications_from_strangers", mXmppConnectionService.getResources().getBoolean(R.bool.notifications_from_strangers)); } public boolean isQuietHours() { if (!mXmppConnectionService.getPreferences().getBoolean("enable_quiet_hours", false)) { return false; } final long startTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_start", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY; final long endTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_end", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY; final long nowTime = Calendar.getInstance().getTimeInMillis() % Config.MILLISECONDS_IN_DAY; if (endTime < startTime) { return nowTime > startTime || nowTime < endTime; } else { return nowTime > startTime && nowTime < endTime; } } public void pushFromBacklog(final Message message) { if (notify(message)) { synchronized (notifications) { getBacklogMessageCounter(message.getConversation()).incrementAndGet(); pushToStack(message); } } } private AtomicInteger getBacklogMessageCounter(Conversation conversation) { synchronized (mBacklogMessageCounter) { if (!mBacklogMessageCounter.containsKey(conversation)) { mBacklogMessageCounter.put(conversation,new AtomicInteger(0)); } return mBacklogMessageCounter.get(conversation); } } public void pushFromDirectReply(final Message message) { synchronized (notifications) { pushToStack(message); updateNotification(false); } } public void finishBacklog(boolean notify, Account account) { synchronized (notifications) { mXmppConnectionService.updateUnreadCountBadge(); if (account == null || !notify) { updateNotification(notify); } else { updateNotification(getBacklogMessageCount(account) > 0); } } } private int getBacklogMessageCount(Account account) { int count = 0; synchronized (this.mBacklogMessageCounter) { for(Iterator> it = mBacklogMessageCounter.entrySet().iterator(); it.hasNext(); ) { Map.Entry entry = it.next(); if (entry.getKey().getAccount() == account) { count += entry.getValue().get(); it.remove(); } } } Log.d(Config.LOGTAG,account.getJid().toBareJid()+": backlog message count="+count); return count; } public void finishBacklog(boolean notify) { finishBacklog(notify,null); } private void pushToStack(final Message message) { final String conversationUuid = message.getConversationUuid(); if (notifications.containsKey(conversationUuid)) { notifications.get(conversationUuid).add(message); } else { final ArrayList mList = new ArrayList<>(); mList.add(message); notifications.put(conversationUuid, mList); } } public void push(final Message message) { synchronized (message.getConversation().getAccount()) { final XmppConnection connection = message.getConversation().getAccount().getXmppConnection(); if (connection.isWaitingForSmCatchup()) { connection.incrementSmCatchupMessageCounter(); pushFromBacklog(message); } else { pushNow(message); } } } private void pushNow(final Message message) { mXmppConnectionService.updateUnreadCountBadge(); if (!notify(message)) { Log.d(Config.LOGTAG,message.getConversation().getAccount().getJid().toBareJid()+": suppressing notification because turned off"); return; } final boolean isScreenOn = mXmppConnectionService.isInteractive(); if (this.mIsInForeground && isScreenOn && this.mOpenConversation == message.getConversation()) { Log.d(Config.LOGTAG,message.getConversation().getAccount().getJid().toBareJid()+": suppressing notification because conversation is open"); return; } synchronized (notifications) { pushToStack(message); final Account account = message.getConversation().getAccount(); final boolean doNotify = (!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn) && !account.inGracePeriod() && !this.inMiniGracePeriod(account); updateNotification(doNotify); } } public void clear() { synchronized (notifications) { for(ArrayList messages : notifications.values()) { markAsReadIfHasDirectReply(messages); } notifications.clear(); updateNotification(false); } } public void clear(final Conversation conversation) { synchronized (this.mBacklogMessageCounter) { this.mBacklogMessageCounter.remove(conversation); } synchronized (notifications) { markAsReadIfHasDirectReply(conversation); notifications.remove(conversation.getUuid()); final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService); notificationManager.cancel(conversation.getUuid(), NOTIFICATION_ID); updateNotification(false); } } private void markAsReadIfHasDirectReply(final Conversation conversation) { markAsReadIfHasDirectReply(notifications.get(conversation.getUuid())); } private void markAsReadIfHasDirectReply(final ArrayList messages) { if (messages != null && messages.size() > 0) { Message last = messages.get(messages.size() - 1); if (last.getStatus() != Message.STATUS_RECEIVED) { if (mXmppConnectionService.markRead(last.getConversation(), false)) { mXmppConnectionService.updateConversationUi(); } } } } private void setNotificationColor(final Builder mBuilder) { mBuilder.setColor(ContextCompat.getColor(mXmppConnectionService, R.color.primary500)); } public void updateNotification(final boolean notify) { Log.d(Config.LOGTAG,"updateNotification("+Boolean.toString(notify)+")"); final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService); final SharedPreferences preferences = mXmppConnectionService.getPreferences(); if (notifications.size() == 0) { notificationManager.cancel(NOTIFICATION_ID); } else { if (notify) { this.markLastNotification(); } final Builder mBuilder; if (notifications.size() == 1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { mBuilder = buildSingleConversations(notifications.values().iterator().next()); modifyForSoundVibrationAndLight(mBuilder, notify, preferences); notificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } else { mBuilder = buildMultipleConversation(); modifyForSoundVibrationAndLight(mBuilder, notify, preferences); for(Map.Entry> entry : notifications.entrySet()) { Builder singleBuilder = buildSingleConversations(entry.getValue()); singleBuilder.setGroup(CONVERSATIONS_GROUP); modifyForSoundVibrationAndLight(singleBuilder,notify,preferences); notificationManager.notify(entry.getKey(), NOTIFICATION_ID ,singleBuilder.build()); } notificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } } } private void modifyForSoundVibrationAndLight(Builder mBuilder, boolean notify, SharedPreferences preferences) { final String ringtone = preferences.getString("notification_ringtone", null); final boolean vibrate = preferences.getBoolean("vibrate_on_notification", true); final boolean led = preferences.getBoolean("led", true); if (notify && !isQuietHours()) { if (vibrate) { final int dat = 70; final long[] pattern = {0, 3 * dat, dat, dat}; mBuilder.setVibrate(pattern); } else { mBuilder.setVibrate(new long[]{0}); } if (ringtone != null) { mBuilder.setSound(Uri.parse(ringtone)); } } if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mBuilder.setCategory(Notification.CATEGORY_MESSAGE); } mBuilder.setPriority(notify ? NotificationCompat.PRIORITY_DEFAULT : NotificationCompat.PRIORITY_LOW); setNotificationColor(mBuilder); mBuilder.setDefaults(0); if (led) { mBuilder.setLights(0xff00FF00, 2000, 3000); } } private Builder buildMultipleConversation() { final Builder mBuilder = new NotificationCompat.Builder( mXmppConnectionService); final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); style.setBigContentTitle(notifications.size() + " " + mXmppConnectionService .getString(R.string.unread_conversations)); final StringBuilder names = new StringBuilder(); Conversation conversation = null; for (final ArrayList messages : notifications.values()) { if (messages.size() > 0) { conversation = messages.get(0).getConversation(); final String name = conversation.getName(); SpannableString styledString; if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) { int count = messages.size(); styledString = new SpannableString(name + ": " + mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages,count,count)); styledString.setSpan(new StyleSpan(Typeface.BOLD), 0, name.length(), 0); style.addLine(styledString); } else { styledString = new SpannableString(name + ": " + UIHelper.getMessagePreview(mXmppConnectionService, messages.get(0)).first); styledString.setSpan(new StyleSpan(Typeface.BOLD), 0, name.length(), 0); style.addLine(styledString); } names.append(name); names.append(", "); } } if (names.length() >= 2) { names.delete(names.length() - 2, names.length()); } mBuilder.setContentTitle(notifications.size() + " " + mXmppConnectionService .getString(R.string.unread_conversations)); mBuilder.setContentText(names.toString()); mBuilder.setStyle(style); if (conversation != null) { mBuilder.setContentIntent(createContentIntent(conversation)); } mBuilder.setGroupSummary(true); mBuilder.setGroup(CONVERSATIONS_GROUP); mBuilder.setDeleteIntent(createDeleteIntent(null)); mBuilder.setSmallIcon(R.drawable.ic_notification); return mBuilder; } private Builder buildSingleConversations(final ArrayList messages) { final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); if (messages.size() >= 1) { final Conversation conversation = messages.get(0).getConversation(); final UnreadConversation.Builder mUnreadBuilder = new UnreadConversation.Builder(conversation.getName()); mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService() .get(conversation, getPixel(64))); mBuilder.setContentTitle(conversation.getName()); if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) { int count = messages.size(); mBuilder.setContentText(mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages,count,count)); } else { Message message; if ((message = getImage(messages)) != null) { modifyForImage(mBuilder, mUnreadBuilder, message, messages); } else { modifyForTextOnly(mBuilder, mUnreadBuilder, messages); } RemoteInput remoteInput = new RemoteInput.Builder("text_reply").setLabel(UIHelper.getMessageHint(mXmppConnectionService, conversation)).build(); NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(R.drawable.ic_send_text_offline, "Reply", createReplyIntent(conversation, false)).addRemoteInput(remoteInput).build(); NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_wear_reply, "Reply", createReplyIntent(conversation, true)).addRemoteInput(remoteInput).build(); mBuilder.extend(new NotificationCompat.WearableExtender().addAction(wearReplyAction)); mUnreadBuilder.setReplyAction(createReplyIntent(conversation, true), remoteInput); mUnreadBuilder.setReadPendingIntent(createReadPendingIntent(conversation)); mBuilder.extend(new NotificationCompat.CarExtender().setUnreadConversation(mUnreadBuilder.build())); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { mBuilder.addAction(replyAction); } if ((message = getFirstDownloadableMessage(messages)) != null) { mBuilder.addAction( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.ic_file_download_white_24dp : R.drawable.ic_action_download, mXmppConnectionService.getResources().getString(R.string.download_x_file, UIHelper.getFileDescriptionString(mXmppConnectionService, message)), createDownloadIntent(message) ); } if ((message = getFirstLocationMessage(messages)) != null) { mBuilder.addAction(R.drawable.ic_room_white_24dp, mXmppConnectionService.getString(R.string.show_location), createShowLocationIntent(message)); } } if (conversation.getMode() == Conversation.MODE_SINGLE) { Contact contact = conversation.getContact(); Uri systemAccount = contact.getSystemAccount(); if (systemAccount != null) { mBuilder.addPerson(systemAccount.toString()); } } mBuilder.setWhen(conversation.getLatestMessage().getTimeSent()); mBuilder.setSmallIcon(R.drawable.ic_notification); mBuilder.setDeleteIntent(createDeleteIntent(conversation)); mBuilder.setContentIntent(createContentIntent(conversation)); } return mBuilder; } private void modifyForImage(final Builder builder, final UnreadConversation.Builder uBuilder, final Message message, final ArrayList messages) { try { final Bitmap bitmap = mXmppConnectionService.getFileBackend() .getThumbnail(message, getPixel(288), false); final ArrayList tmp = new ArrayList<>(); for (final Message msg : messages) { if (msg.getType() == Message.TYPE_TEXT && msg.getTransferable() == null) { tmp.add(msg); } } final BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle(); bigPictureStyle.bigPicture(bitmap); if (tmp.size() > 0) { CharSequence text = getMergedBodies(tmp); bigPictureStyle.setSummaryText(text); builder.setContentText(text); } else { builder.setContentText(mXmppConnectionService.getString( R.string.received_x_file, UIHelper.getFileDescriptionString(mXmppConnectionService, message))); } builder.setStyle(bigPictureStyle); } catch (final FileNotFoundException e) { modifyForTextOnly(builder, uBuilder, messages); } } private void modifyForTextOnly(final Builder builder, final UnreadConversation.Builder uBuilder, final ArrayList messages) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(mXmppConnectionService.getString(R.string.me)); Conversation conversation = messages.get(0).getConversation(); if (conversation.getMode() == Conversation.MODE_MULTI) { messagingStyle.setConversationTitle(conversation.getName()); } for (Message message : messages) { String sender = message.getStatus() == Message.STATUS_RECEIVED ? UIHelper.getMessageDisplayName(message) : null; messagingStyle.addMessage(UIHelper.getMessagePreview(mXmppConnectionService,message).first, message.getTimeSent(), sender); } builder.setStyle(messagingStyle); } else { if(messages.get(0).getConversation().getMode() == Conversation.MODE_SINGLE) { builder.setStyle(new NotificationCompat.BigTextStyle().bigText(getMergedBodies(messages))); builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, messages.get(0)).first); } else { final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); SpannableString styledString; for (Message message : messages) { final String name = UIHelper.getMessageDisplayName(message); styledString = new SpannableString(name + ": " + message.getBody()); styledString.setSpan(new StyleSpan(Typeface.BOLD), 0, name.length(), 0); style.addLine(styledString); } builder.setStyle(style); int count = messages.size(); if(count == 1) { final String name = UIHelper.getMessageDisplayName(messages.get(0)); styledString = new SpannableString(name + ": " + messages.get(0).getBody()); styledString.setSpan(new StyleSpan(Typeface.BOLD), 0, name.length(), 0); builder.setContentText(styledString); } else { builder.setContentText(mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages,count,count)); } } } /** message preview for Android Auto **/ for (Message message : messages) { Pair preview = UIHelper.getMessagePreview(mXmppConnectionService, message); // only show user written text if (preview.second == false) { uBuilder.addMessage(preview.first); uBuilder.setLatestTimestamp(message.getTimeSent()); } } } private Message getImage(final Iterable messages) { Message image = null; for (final Message message : messages) { if (message.getStatus() != Message.STATUS_RECEIVED) { return null; } if (message.getType() != Message.TYPE_TEXT && message.getTransferable() == null && message.getEncryption() != Message.ENCRYPTION_PGP && message.getFileParams().height > 0) { image = message; } } return image; } private Message getFirstDownloadableMessage(final Iterable messages) { for (final Message message : messages) { if (message.getTransferable() != null || (message.getType() == Message.TYPE_TEXT && message.treatAsDownloadable())) { return message; } } return null; } private Message getFirstLocationMessage(final Iterable messages) { for (final Message message : messages) { if (GeoHelper.isGeoUri(message.getBody())) { return message; } } return null; } private CharSequence getMergedBodies(final ArrayList messages) { final StringBuilder text = new StringBuilder(); for(Message message : messages) { if (text.length() != 0) { text.append("\n"); } text.append(UIHelper.getMessagePreview(mXmppConnectionService, message).first); } return text.toString(); } private PendingIntent createShowLocationIntent(final Message message) { Iterable intents = GeoHelper.createGeoIntentsFromMessage(message); for (Intent intent : intents) { if (intent.resolveActivity(mXmppConnectionService.getPackageManager()) != null) { return PendingIntent.getActivity(mXmppConnectionService, 18, intent, PendingIntent.FLAG_UPDATE_CURRENT); } } return createOpenConversationsIntent(); } private PendingIntent createContentIntent(final String conversationUuid, final String downloadMessageUuid) { final Intent viewConversationIntent = new Intent(mXmppConnectionService,ConversationActivity.class); viewConversationIntent.setAction(ConversationActivity.ACTION_VIEW_CONVERSATION); viewConversationIntent.putExtra(ConversationActivity.CONVERSATION, conversationUuid); if (downloadMessageUuid != null) { viewConversationIntent.putExtra(ConversationActivity.EXTRA_DOWNLOAD_UUID, downloadMessageUuid); return PendingIntent.getActivity(mXmppConnectionService, (conversationUuid.hashCode() % NOTIFICATION_ID_MULTIPLIER) + 8 * NOTIFICATION_ID_MULTIPLIER, viewConversationIntent, PendingIntent.FLAG_UPDATE_CURRENT); } else { return PendingIntent.getActivity(mXmppConnectionService, (conversationUuid.hashCode() % NOTIFICATION_ID_MULTIPLIER) + 10 * NOTIFICATION_ID_MULTIPLIER, viewConversationIntent, PendingIntent.FLAG_UPDATE_CURRENT); } } private PendingIntent createDownloadIntent(final Message message) { return createContentIntent(message.getConversationUuid(), message.getUuid()); } private PendingIntent createContentIntent(final Conversation conversation) { return createContentIntent(conversation.getUuid(), null); } private PendingIntent createDeleteIntent(Conversation conversation) { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_CLEAR_NOTIFICATION); if (conversation != null) { intent.putExtra("uuid", conversation.getUuid()); return PendingIntent.getService(mXmppConnectionService, (conversation.getUuid().hashCode() % NOTIFICATION_ID_MULTIPLIER) + 12 * NOTIFICATION_ID_MULTIPLIER, intent, 0); } return PendingIntent.getService(mXmppConnectionService, 0, intent, 0); } private PendingIntent createReplyIntent(Conversation conversation, boolean dismissAfterReply) { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION); intent.putExtra("uuid",conversation.getUuid()); intent.putExtra("dismiss_notification",dismissAfterReply); int id = (conversation.getUuid().hashCode() % NOTIFICATION_ID_MULTIPLIER) + (dismissAfterReply ? 12 : 14) * NOTIFICATION_ID_MULTIPLIER; return PendingIntent.getService(mXmppConnectionService, id, intent, 0); } private PendingIntent createReadPendingIntent(Conversation conversation) { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_MARK_AS_READ); intent.putExtra("uuid", conversation.getUuid()); intent.setPackage(mXmppConnectionService.getPackageName()); return PendingIntent.getService(mXmppConnectionService, (conversation.getUuid().hashCode() % NOTIFICATION_ID_MULTIPLIER) + 16 * NOTIFICATION_ID_MULTIPLIER, intent, PendingIntent.FLAG_UPDATE_CURRENT); } private PendingIntent createDisableForeground() { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_DISABLE_FOREGROUND); return PendingIntent.getService(mXmppConnectionService, 34, intent, 0); } private PendingIntent createTryAgainIntent() { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_TRY_AGAIN); return PendingIntent.getService(mXmppConnectionService, 45, intent, 0); } private PendingIntent createDismissErrorIntent() { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_DISMISS_ERROR_NOTIFICATIONS); return PendingIntent.getService(mXmppConnectionService, 69, intent, 0); } private boolean wasHighlightedOrPrivate(final Message message) { final String nick = message.getConversation().getMucOptions().getActualNick(); final Pattern highlight = generateNickHighlightPattern(nick); if (message.getBody() == null || nick == null) { return false; } final Matcher m = highlight.matcher(message.getBody()); return (m.find() || message.getType() == Message.TYPE_PRIVATE); } public static Pattern generateNickHighlightPattern(final String nick) { // We expect a word boundary, i.e. space or start of string, followed by // the // nick (matched in case-insensitive manner), followed by optional // punctuation (for example "bob: i disagree" or "how are you alice?"), // followed by another word boundary. return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); } public void setOpenConversation(final Conversation conversation) { this.mOpenConversation = conversation; } public void setIsInForeground(final boolean foreground) { this.mIsInForeground = foreground; } private int getPixel(final int dp) { final DisplayMetrics metrics = mXmppConnectionService.getResources() .getDisplayMetrics(); return ((int) (dp * metrics.density)); } private void markLastNotification() { this.mLastNotification = SystemClock.elapsedRealtime(); } private boolean inMiniGracePeriod(final Account account) { final int miniGrace = account.getStatus() == Account.State.ONLINE ? Config.MINI_GRACE_PERIOD : Config.MINI_GRACE_PERIOD * 2; return SystemClock.elapsedRealtime() < (this.mLastNotification + miniGrace); } public Notification createForegroundNotification() { final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service)); if (Config.SHOW_CONNECTED_ACCOUNTS) { List accounts = mXmppConnectionService.getAccounts(); int enabled = 0; int connected = 0; for (Account account : accounts) { if (account.isOnlineAndConnected()) { connected++; enabled++; } else if (!account.isOptionSet(Account.OPTION_DISABLED)) { enabled++; } } mBuilder.setContentText(mXmppConnectionService.getString(R.string.connected_accounts, connected, enabled)); } else { mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_open_conversations)); } mBuilder.setContentIntent(createOpenConversationsIntent()); mBuilder.setWhen(0); mBuilder.setPriority(Config.SHOW_CONNECTED_ACCOUNTS ? NotificationCompat.PRIORITY_DEFAULT : NotificationCompat.PRIORITY_MIN); mBuilder.setSmallIcon(R.drawable.ic_link_white_24dp); if (Config.SHOW_DISABLE_FOREGROUND) { final int cancelIcon; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mBuilder.setCategory(Notification.CATEGORY_SERVICE); cancelIcon = R.drawable.ic_cancel_white_24dp; } else { cancelIcon = R.drawable.ic_action_cancel; } mBuilder.addAction(cancelIcon, mXmppConnectionService.getString(R.string.disable_foreground_service), createDisableForeground()); } return mBuilder.build(); } private PendingIntent createOpenConversationsIntent() { return PendingIntent.getActivity(mXmppConnectionService, 0, new Intent(mXmppConnectionService, ConversationActivity.class), 0); } public void updateErrorNotification() { final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService); final List errors = new ArrayList<>(); for (final Account account : mXmppConnectionService.getAccounts()) { if (account.hasErrorStatus() && account.showErrorNotification()) { errors.add(account); } } if (mXmppConnectionService.getPreferences().getBoolean(SettingsActivity.KEEP_FOREGROUND_SERVICE, false)) { notificationManager.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification()); } final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); if (errors.size() == 0) { notificationManager.cancel(ERROR_NOTIFICATION_ID); return; } else if (errors.size() == 1) { mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.problem_connecting_to_account)); mBuilder.setContentText(errors.get(0).getJid().toBareJid().toString()); } else { mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.problem_connecting_to_accounts)); mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_fix)); } mBuilder.addAction(R.drawable.ic_autorenew_white_24dp, mXmppConnectionService.getString(R.string.try_again), createTryAgainIntent()); mBuilder.setDeleteIntent(createDismissErrorIntent()); mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp); } else { mBuilder.setSmallIcon(R.drawable.ic_stat_alert_warning); } mBuilder.setContentIntent(PendingIntent.getActivity(mXmppConnectionService, 145, new Intent(mXmppConnectionService,ManageAccountActivity.class), PendingIntent.FLAG_UPDATE_CURRENT)); notificationManager.notify(ERROR_NOTIFICATION_ID, mBuilder.build()); } }