diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ad500c2e8..89a1c6008 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -285,7 +285,8 @@ android:enabled="true" android:exported="false"> - + + diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index c45e24f35..9983def02 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -64,6 +64,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit @SuppressWarnings("unused") private static final String TAG = ConversationListActivity.class.getSimpleName(); private static final String OPENPGP4FPR = "openpgp4fpr"; + public static final String CLEAR_NOTIFICATIONS = "clear_notifications"; private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); @@ -128,6 +129,10 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit getSupportActionBar().setDisplayHomeAsUpEnabled(false); } handleOpenpgp4fpr(); + + if (getIntent().getBooleanExtra(CLEAR_NOTIFICATIONS, false)) { + DcHelper.getContext(this).notificationCenter.removeAllNotifiations(); + } } @Override diff --git a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java index add98df8b..71a199816 100644 --- a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java @@ -11,13 +11,14 @@ import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.util.Util; public class MarkReadReceiver extends BroadcastReceiver { - - public static final String CLEAR_ACTION = "org.thoughtcrime.securesms.notifications.CLEAR"; + public static final String MARK_NOTICED_ACTION = "org.thoughtcrime.securesms.notifications.MARK_NOTICED"; + public static final String CANCEL_ACTION = "org.thoughtcrime.securesms.notifications.CANCEL"; public static final String CHAT_ID_EXTRA = "chat_id"; @Override public void onReceive(final Context context, Intent intent) { - if (!CLEAR_ACTION.equals(intent.getAction())) { + boolean markNoticed = MARK_NOTICED_ACTION.equals(intent.getAction()); + if (!markNoticed && !CANCEL_ACTION.equals(intent.getAction())) { return; } @@ -30,7 +31,9 @@ public class MarkReadReceiver extends BroadcastReceiver { Util.runOnAnyBackgroundThread(() -> { dcContext.notificationCenter.removeNotifications(chatId); - dcContext.marknoticedChat(chatId); + if (markNoticed) { + dcContext.marknoticedChat(chatId); + } }); } } diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java b/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java index 4eaa2ba85..4b94e8be6 100644 --- a/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java +++ b/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java @@ -28,6 +28,7 @@ import com.b44t.messenger.DcMsg; import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.thoughtcrime.securesms.ConversationActivity; +import org.thoughtcrime.securesms.ConversationListActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.connect.ApplicationDcContext; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; @@ -97,7 +98,13 @@ public class NotificationCenter { } return argb; } - + + private PendingIntent getOpenChatlistIntent() { + Intent intent = new Intent(context, ConversationListActivity.class); + intent.putExtra(ConversationListActivity.CLEAR_NOTIFICATIONS, true); + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } + private PendingIntent getOpenChatIntent(int chatId) { Intent intent = new Intent(context, ConversationActivity.class); intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId); @@ -116,8 +123,8 @@ public class NotificationCenter { return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } - private PendingIntent getMarkAsReadIntent(int chatId) { - Intent intent = new Intent(MarkReadReceiver.CLEAR_ACTION); + private PendingIntent getMarkAsReadIntent(int chatId, boolean markNoticed) { + Intent intent = new Intent(markNoticed? MarkReadReceiver.MARK_NOTICED_ACTION : MarkReadReceiver.CANCEL_ACTION); intent.setClass(context, MarkReadReceiver.class); intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); intent.putExtra(MarkReadReceiver.CHAT_ID_EXTRA, chatId); @@ -129,14 +136,20 @@ public class NotificationCenter { // Groups and Notification channel groups // -------------------------------------------------------------------------------------------- - public static final String GRP_MSG = "chgrp_msg"; + // this is just to further organize the appearance of channels in the settings UI + private static final String CH_GRP_MSG = "chgrp_msg"; + + // this is to group together notifications as such, maybe including a summary, + // see https://developer.android.com/training/notify-user/group.html + private static final String GRP_MSG = "grp_msg"; // Notification IDs // -------------------------------------------------------------------------------------------- - public static final int ID_PERMANTENT = 1; - public static final int ID_MSG_OFFSET = 0; // msgId is added - as msgId start at 10, there are no conflicts with lower numbers + public static final int ID_PERMANTENT = 1; + public static final int ID_MSG_SUMMARY = 2; + public static final int ID_MSG_OFFSET = 0; // msgId is added - as msgId start at 10, there are no conflicts with lower numbers // Notification channels @@ -202,11 +215,11 @@ public class NotificationCenter { } private String getNotificationChannelGroup(NotificationManagerCompat notificationManager) { - if (notificationChannelsSupported() && notificationManager.getNotificationChannelGroup(GRP_MSG) == null) { - NotificationChannelGroup chGrp = new NotificationChannelGroup(GRP_MSG, context.getString(R.string.pref_chats)); + if (notificationChannelsSupported() && notificationManager.getNotificationChannelGroup(CH_GRP_MSG) == null) { + NotificationChannelGroup chGrp = new NotificationChannelGroup(CH_GRP_MSG, context.getString(R.string.pref_chats)); notificationManager.createNotificationChannelGroup(chGrp); } - return GRP_MSG; + return CH_GRP_MSG; } private String getNotificationChannel(NotificationManagerCompat notificationManager, DcChat dcChat) { @@ -320,7 +333,7 @@ public class NotificationCenter { NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context); DcMsg dcMsg = dcContext.getMsg(msgId); - String line = privacy.isDisplayMessage()? dcMsg.getSummarytext(100) : context.getString(R.string.notify_new_message); + String line = privacy.isDisplayMessage()? dcMsg.getSummarytext(2000) : context.getString(R.string.notify_new_message); if (dcChat.isGroup() && privacy.isDisplayContact()) { line = dcContext.getContact(dcMsg.getFromId()).getFirstName() + ": " + line; } @@ -336,7 +349,8 @@ public class NotificationCenter { // even without a name or message displayed, // it makes sense to use separate notification channels and to open the respective chat directly - // the user may eg. have chosen a different sound - NotificationCompat.Builder builder = new NotificationCompat.Builder(context, getNotificationChannel(notificationManager, dcChat)) + String notificationChannel = getNotificationChannel(notificationManager, dcChat); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, notificationChannel) .setSmallIcon(R.drawable.icon_notification) .setColor(context.getResources().getColor(R.color.delta_primary)) .setPriority(Prefs.getNotificationPriority(context)) @@ -344,6 +358,7 @@ public class NotificationCenter { .setGroup(GRP_MSG) .setOnlyAlertOnce(!signal) .setContentText(line) + .setDeleteIntent(getMarkAsReadIntent(chatId, false)) .setContentIntent(getOpenChatIntent(chatId)); if (privacy.isDisplayContact()) { builder.setContentTitle(dcChat.getName()); @@ -409,7 +424,7 @@ public class NotificationCenter { && !Prefs.isScreenLockEnabled(context)) { try { PendingIntent inNotificationReplyIntent = getRemoteReplyIntent(chatId); - PendingIntent markReadIntent = getMarkAsReadIntent(chatId); + PendingIntent markReadIntent = getMarkAsReadIntent(chatId, true); NotificationCompat.Action markAsReadAction = new NotificationCompat.Action(R.drawable.check, context.getString(R.string.notify_mark_read), @@ -467,15 +482,39 @@ public class NotificationCenter { // add notification, we use one notification per chat, // esp. older android are not that great at grouping notificationManager.notify(ID_MSG_OFFSET + chatId, builder.build()); + + // group notifications together in a summary, this is possible since SDK 24 (Android 7) + // https://developer.android.com/training/notify-user/group.html + // in theory, this won't be needed due to setGroup(), however, in practise, it is needed up to at least Android 10. + if (Build.VERSION.SDK_INT >= 24) { + NotificationCompat.Builder summary = new NotificationCompat.Builder(context, notificationChannel) + .setGroup(GRP_MSG) + .setGroupSummary(true) + .setSmallIcon(R.drawable.icon_notification) + .setColor(context.getResources().getColor(R.color.delta_primary)) + .setCategory(NotificationCompat.CATEGORY_MESSAGE) + .setContentTitle("Delta Chat") // content title would only be used on SDK <24 + .setContentText("New messages") // content text would only be used on SDK <24 + .setContentIntent(getOpenChatlistIntent()); + notificationManager.notify(ID_MSG_SUMMARY, summary.build()); + } }); } public void removeNotifications(int chatId) { + boolean removeSummary = false; synchronized (inboxes) { inboxes.remove(chatId); + removeSummary = inboxes.isEmpty(); } - NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - notificationManager.cancel(ID_MSG_OFFSET + chatId); + + try { + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); + notificationManager.cancel(ID_MSG_OFFSET + chatId); + if (removeSummary) { + notificationManager.cancel(ID_MSG_SUMMARY); + } + } catch (Exception e) { Log.w(TAG, e); } } public void removeAllNotifiations() {