diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index 73a4a6340..688dd633d 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -85,6 +85,7 @@ import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer.Attach import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer.DrawerState; import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider; import org.thoughtcrime.securesms.components.emoji.MediaKeyboard; +import org.thoughtcrime.securesms.connect.AccountManager; import org.thoughtcrime.securesms.connect.DcEventCenter; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.connect.DirectShareUtil; @@ -157,10 +158,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity { private static final String TAG = ConversationActivity.class.getSimpleName(); - public static final String CHAT_ID_EXTRA = "chat_id"; - public static final String FROM_ARCHIVED_CHATS_EXTRA = "from_archived"; - public static final String TEXT_EXTRA = "draft_text"; - public static final String STARTING_POSITION_EXTRA = "starting_position"; + public static final String ACCOUNT_ID_EXTRA = TAG + ".account_id"; + public static final String CHAT_ID_EXTRA = TAG + ".chat_id"; + public static final String FROM_ARCHIVED_CHATS_EXTRA = TAG + ".from_archived"; + public static final String TEXT_EXTRA = TAG + ".draft_text"; + public static final String STARTING_POSITION_EXTRA = TAG + ".starting_position"; private static final int PICK_GALLERY = 1; private static final int PICK_DOCUMENT = 2; @@ -203,6 +205,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private QuickAttachmentDrawer quickAttachmentDrawer; private InputPanel inputPanel; + private ApplicationContext context; private Recipient recipient; private DcContext dcContext; private DcChat dcChat = new DcChat(0); @@ -226,7 +229,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected void onCreate(Bundle state, boolean ready) { - final Context context = getApplicationContext(); + this.context = ApplicationContext.getInstance(getApplicationContext()); this.dcContext = DcHelper.getContext(context); supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY); @@ -327,7 +330,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity titleView.setTitle(glideRequests, dcChat); - DcHelper.getNotificationCenter(this).updateVisibleChat(chatId); + DcHelper.getNotificationCenter(this).updateVisibleChat(dcContext.getAccountId(), chatId); if (doReinitializeDraft) { initializeDraft(); @@ -347,7 +350,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity doReinitializeDraft = true; } - DcHelper.getNotificationCenter(this).updateVisibleChat(0); + DcHelper.getNotificationCenter(this).clearVisibleChat(); if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right); quickAttachmentDrawer.onPause(); inputPanel.onPause(); @@ -962,6 +965,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void initializeResources() { + int accountId = getIntent().getIntExtra(ACCOUNT_ID_EXTRA, dcContext.getAccountId()); + if (accountId != dcContext.getAccountId()) { + AccountManager.getInstance().switchAccount(context, accountId); + dcContext = context.dcContext; + fragment.dcContext = context.dcContext; + } chatId = getIntent().getIntExtra(CHAT_ID_EXTRA, -1); if(chatId == DcChat.DC_CHAT_NO_CHAT) throw new IllegalStateException("can't display a conversation for no chat."); @@ -1211,7 +1220,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (refreshFragment) { fragment.reload(recipient, chatId); - DcHelper.getNotificationCenter(this).updateVisibleChat(chatId); + DcHelper.getNotificationCenter(this).updateVisibleChat(dcContext.getAccountId(), chatId); } fragment.scrollToBottom(); diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index 630422b87..0751effb6 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -76,6 +76,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit private static final String OPENPGP4FPR = "openpgp4fpr"; private static final String NDK_ARCH_WARNED = "ndk_arch_warned"; public static final String CLEAR_NOTIFICATIONS = "clear_notifications"; + public static final String ACCOUNT_ID_EXTRA = TAG + ".account_id"; private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); @@ -201,6 +202,15 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit } private void refresh() { + DcContext dcContext = DcHelper.getContext(this); + int accountId = getIntent().getIntExtra(ACCOUNT_ID_EXTRA, dcContext.getAccountId()); + if (getIntent().getBooleanExtra(CLEAR_NOTIFICATIONS, false)) { + DcHelper.getNotificationCenter(this).removeAllNotifiations(accountId); + } + if (accountId != dcContext.getAccountId()) { + AccountManager.getInstance().switchAccountAndStartActivity(this, accountId, null); + } + refreshAvatar(); refreshTitle(); handleOpenpgp4fpr(); @@ -211,10 +221,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit if (isDirectSharing(this)) { openConversation(getDirectSharingChatId(this), -1); } - - if (getIntent().getBooleanExtra(CLEAR_NOTIFICATIONS, false)) { - DcHelper.getNotificationCenter(this).removeAllNotifiations(); - } } public void refreshTitle() { diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java index 46a217543..8148e85fa 100644 --- a/src/org/thoughtcrime/securesms/ConversationListFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java @@ -350,8 +350,9 @@ public class ConversationListFragment extends Fragment @Override protected Void doInBackground(Void... params) { + int accountId = dcContext.getAccountId(); for (long chatId : selectedConversations) { - DcHelper.getNotificationCenter(getContext()).removeNotifications((int) chatId); + DcHelper.getNotificationCenter(getContext()).removeNotifications(accountId, (int) chatId); dcContext.deleteChat((int) chatId); DirectShareUtil.clearShortcut(getContext(), (int) chatId); } diff --git a/src/org/thoughtcrime/securesms/ProfileActivity.java b/src/org/thoughtcrime/securesms/ProfileActivity.java index d4af63976..21cc7f4ae 100644 --- a/src/org/thoughtcrime/securesms/ProfileActivity.java +++ b/src/org/thoughtcrime/securesms/ProfileActivity.java @@ -455,7 +455,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity } private void onSoundSettings() { - Uri current = Prefs.getChatRingtone(this, chatId); + Uri current = Prefs.getChatRingtone(this, dcContext.getAccountId(), chatId); Uri defaultUri = Prefs.getNotificationRingtone(this); if (current == null) current = Settings.System.DEFAULT_NOTIFICATION_URI; @@ -472,14 +472,14 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity } private void onVibrateSettings() { - int checkedItem = Prefs.getChatVibrate(this, chatId).getId(); + int checkedItem = Prefs.getChatVibrate(this, dcContext.getAccountId(), chatId).getId(); int[] selectedChoice = new int[]{checkedItem}; new AlertDialog.Builder(this) .setTitle(R.string.pref_vibrate) .setSingleChoiceItems(R.array.recipient_vibrate_entries, checkedItem, (dialog, which) -> selectedChoice[0] = which) .setPositiveButton(R.string.ok, - (dialog, which) -> Prefs.setChatVibrate(this, chatId, Prefs.VibrateState.fromId(selectedChoice[0]))) + (dialog, which) -> Prefs.setChatVibrate(this, dcContext.getAccountId(), chatId, Prefs.VibrateState.fromId(selectedChoice[0]))) .setNegativeButton(R.string.cancel, null) .show(); } @@ -588,7 +588,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity if (defaultValue.equals(value)) value = null; else if (value == null) value = Uri.EMPTY; - Prefs.setChatRingtone(this, chatId, value); + Prefs.setChatRingtone(this, dcContext.getAccountId(), chatId, value); } } diff --git a/src/org/thoughtcrime/securesms/connect/AccountManager.java b/src/org/thoughtcrime/securesms/connect/AccountManager.java index e175a4f42..8371268c3 100644 --- a/src/org/thoughtcrime/securesms/connect/AccountManager.java +++ b/src/org/thoughtcrime/securesms/connect/AccountManager.java @@ -30,10 +30,7 @@ public class AccountManager { private void resetDcContext(Context context) { ApplicationContext appContext = (ApplicationContext)context.getApplicationContext(); - DcHelper.getNotificationCenter(context).removeAllNotifiations(); appContext.dcContext = appContext.dcAccounts.getSelectedAccount(); - appContext.notificationCenter = new NotificationCenter(context); - appContext.eventCenter = new DcEventCenter(context); DcHelper.setStockTranslations(context); DirectShareUtil.resetAllShortcuts(appContext); } diff --git a/src/org/thoughtcrime/securesms/connect/DcEventCenter.java b/src/org/thoughtcrime/securesms/connect/DcEventCenter.java index 957ec600a..1c0fecd9c 100644 --- a/src/org/thoughtcrime/securesms/connect/DcEventCenter.java +++ b/src/org/thoughtcrime/securesms/connect/DcEventCenter.java @@ -137,11 +137,22 @@ public class DcEventCenter { public long handleEvent(@NonNull DcEvent event) { int accountId = event.getAccountId(); + int id = event.getId(); + + switch (id) { + case DcContext.DC_EVENT_INCOMING_MSG: + DcHelper.getNotificationCenter(context).addNotification(accountId, event.getData1Int(), event.getData2Int()); + break; + + case DcContext.DC_EVENT_MSGS_NOTICED: + DcHelper.getNotificationCenter(context).removeNotifications(accountId, event.getData1Int()); + break; + } + if (accountId != context.dcContext.getAccountId()) { return 0; } - int id = event.getId(); switch (id) { case DcContext.DC_EVENT_INFO: Log.i("DeltaChat", event.getData2Str()); @@ -160,12 +171,10 @@ public class DcEventCenter { break; case DcContext.DC_EVENT_INCOMING_MSG: - DcHelper.getNotificationCenter(context).addNotification(event.getData1Int(), event.getData2Int()); sendToObservers(event); break; case DcContext.DC_EVENT_MSGS_NOTICED: - DcHelper.getNotificationCenter(context).removeNotifications(event.getData1Int()); sendToObservers(event); break; diff --git a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java index 3c0df2378..bc4e0a975 100644 --- a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java @@ -10,9 +10,11 @@ import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.util.Util; public class MarkReadReceiver extends BroadcastReceiver { + private static final String TAG = MarkReadReceiver.class.getSimpleName(); 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"; + public static final String ACCOUNT_ID_EXTRA = TAG + ".account_id"; + public static final String CHAT_ID_EXTRA = TAG + ".chat_id"; @Override public void onReceive(final Context context, Intent intent) { @@ -21,15 +23,16 @@ public class MarkReadReceiver extends BroadcastReceiver { return; } + final int accountId = intent.getIntExtra(ACCOUNT_ID_EXTRA, 0); final int chatId = intent.getIntExtra(CHAT_ID_EXTRA, 0); - if (chatId==0) { + if (accountId == 0 || chatId == 0) { return; } Util.runOnAnyBackgroundThread(() -> { - DcHelper.getNotificationCenter(context).removeNotifications(chatId); + DcHelper.getNotificationCenter(context).removeNotifications(accountId, chatId); if (markNoticed) { - DcHelper.getContext(context).marknoticedChat(chatId); + DcHelper.getAccounts(context).getAccount(accountId).marknoticedChat(chatId); } }); } diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java b/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java index 198b7799d..28192047d 100644 --- a/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java +++ b/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java @@ -28,10 +28,10 @@ import com.b44t.messenger.DcContext; import com.b44t.messenger.DcMsg; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ConversationActivity; import org.thoughtcrime.securesms.ConversationListActivity; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference; @@ -50,20 +50,23 @@ import java.util.concurrent.TimeUnit; public class NotificationCenter { private static final String TAG = NotificationCenter.class.getSimpleName(); - @NonNull private DcContext dcContext; - @NonNull private Context context; - private volatile int visibleChatId = 0; + @NonNull private final ApplicationContext context; + private volatile ChatData visibleChat = null; private volatile long lastAudibleNotification = 0; private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2); - private final HashMap> inboxes = new HashMap<>(); // contains the last lines of each chat + + // Map, contains the last lines of each chat for each account + private final HashMap>> inboxes = new HashMap<>(); public NotificationCenter(Context context) { - this.context = context.getApplicationContext(); - this.dcContext = DcHelper.getContext(context); + this.context = ApplicationContext.getInstance(context); } - private @Nullable Uri effectiveSound(int chatId) { // chatId=0: return app-global setting - @Nullable Uri chatRingtone = Prefs.getChatRingtone(context, chatId); + private @Nullable Uri effectiveSound(ChatData chatData) { // chatData=null: return app-global setting + if (chatData == null) { + chatData = new ChatData(0, 0); + } + @Nullable Uri chatRingtone = Prefs.getChatRingtone(context, chatData.accountId, chatData.chatId); if (chatRingtone!=null) { return chatRingtone; } else { @@ -75,8 +78,11 @@ public class NotificationCenter { return null; } - private boolean effectiveVibrate(int chatId) { // chatId=0: return app-global setting - Prefs.VibrateState vibrate = Prefs.getChatVibrate(context, chatId); + private boolean effectiveVibrate(ChatData chatData) { // chatData=null: return app-global setting + if (chatData == null) { + chatData = new ChatData(0, 0); + } + Prefs.VibrateState vibrate = Prefs.getChatVibrate(context, chatData.accountId, chatData.chatId); if (vibrate == Prefs.VibrateState.ENABLED) { return true; } else if (vibrate == Prefs.VibrateState.DISABLED) { @@ -85,9 +91,12 @@ public class NotificationCenter { return Prefs.isNotificationVibrateEnabled(context); } - private boolean requiresIndependentChannel(int chatId) { - return Prefs.getChatRingtone(context, chatId) != null - || Prefs.getChatVibrate(context, chatId) != Prefs.VibrateState.DEFAULT; + private boolean requiresIndependentChannel(ChatData chatData) { + if (chatData == null) { + chatData = new ChatData(0, 0); + } + return Prefs.getChatRingtone(context, chatData.accountId, chatData.chatId) != null + || Prefs.getChatVibrate(context, chatData.accountId, chatData.chatId) != Prefs.VibrateState.DEFAULT; } private int getLedArgb(String ledColor) { @@ -101,35 +110,40 @@ public class NotificationCenter { return argb; } - private PendingIntent getOpenChatlistIntent() { + private PendingIntent getOpenChatlistIntent(int accountId) { Intent intent = new Intent(context, ConversationListActivity.class); + intent.putExtra(ConversationListActivity.ACCOUNT_ID_EXTRA, accountId); intent.putExtra(ConversationListActivity.CLEAR_NOTIFICATIONS, true); + intent.setData(Uri.parse("custom://"+accountId)); return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | IntentUtils.FLAG_MUTABLE()); } - private PendingIntent getOpenChatIntent(int chatId) { + private PendingIntent getOpenChatIntent(ChatData chatData) { Intent intent = new Intent(context, ConversationActivity.class); - intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId); - intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); + intent.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, chatData.accountId); + intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatData.chatId); + intent.setData(Uri.parse("custom://"+chatData.accountId+"."+chatData.chatId)); return TaskStackBuilder.create(context) .addNextIntentWithParentStack(intent) .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT | IntentUtils.FLAG_MUTABLE()); } - private PendingIntent getRemoteReplyIntent(int chatId) { + private PendingIntent getRemoteReplyIntent(ChatData chatData) { Intent intent = new Intent(RemoteReplyReceiver.REPLY_ACTION); intent.setClass(context, RemoteReplyReceiver.class); - intent.setData((Uri.parse("custom://"+System.currentTimeMillis()))); - intent.putExtra(RemoteReplyReceiver.CHAT_ID_EXTRA, chatId); + intent.setData(Uri.parse("custom://"+chatData.accountId+"."+chatData.chatId)); + intent.putExtra(RemoteReplyReceiver.ACCOUNT_ID_EXTRA, chatData.accountId); + intent.putExtra(RemoteReplyReceiver.CHAT_ID_EXTRA, chatData.chatId); intent.setPackage(context.getPackageName()); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | IntentUtils.FLAG_MUTABLE()); } - private PendingIntent getMarkAsReadIntent(int chatId, boolean markNoticed) { + private PendingIntent getMarkAsReadIntent(ChatData chatData, 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); + intent.setData(Uri.parse("custom://"+chatData.accountId+"."+chatData.chatId)); + intent.putExtra(MarkReadReceiver.ACCOUNT_ID_EXTRA, chatData.accountId); + intent.putExtra(MarkReadReceiver.CHAT_ID_EXTRA, chatData.chatId); intent.setPackage(context.getPackageName()); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | IntentUtils.FLAG_MUTABLE()); } @@ -182,23 +196,19 @@ public class NotificationCenter { return Build.VERSION.SDK_INT >= 26; } - // full name is "ch_msgV_HASH" or "ch_msgV_HASH.CHATID" - private String computeChannelId(String ledColor, boolean vibrate, @Nullable Uri ringtone, int chatId) { + // full name is "ch_msgV_HASH" or "ch_msgV_HASH.ACCOUNTID.CHATID" + private String computeChannelId(String ledColor, boolean vibrate, @Nullable Uri ringtone, ChatData chatData) { String channelId = CH_MSG_PREFIX; try { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(ledColor.getBytes()); md.update(vibrate ? (byte) 1 : (byte) 0); md.update((ringtone != null ? ringtone.toString() : "").getBytes()); - if (chatId!=0) { - // for multi-account, force different channelIds for maybe the same chatIds in multiple accounts - md.update(dcContext.getConfig("addr").getBytes()); - } String hash = String.format("%X", new BigInteger(1, md.digest())).substring(0, 16); channelId = CH_MSG_PREFIX + CH_MSG_VERSION + "_" + hash; - if (chatId!=0) { - channelId += String.format(".%d", chatId); + if (chatData != null) { + channelId += String.format(".%d.%d", chatData.accountId, chatData.chatId); } } catch(Exception e) { @@ -207,15 +217,21 @@ public class NotificationCenter { return channelId; } - // return chatId from "ch_msgV_HASH.CHATID" or 0 - private int parseNotificationChannelChatId(String channelId) { + // return ChatData(ACCOUNTID, CHATID) from "ch_msgV_HASH.ACCOUNTID.CHATID" or null + private ChatData parseNotificationChannelChat(String channelId) { try { int point = channelId.lastIndexOf("."); if (point>0) { - return Integer.parseInt(channelId.substring(point + 1)); + int chatId = Integer.parseInt(channelId.substring(point + 1)); + channelId = channelId.substring(0, point); + point = channelId.lastIndexOf("."); + if (point>0) { + int accountId = Integer.parseInt(channelId.substring(point + 1)); + return new ChatData(accountId, chatId); + } } } catch(Exception e) { } - return 0; + return null; } private String getNotificationChannelGroup(NotificationManagerCompat notificationManager) { @@ -226,20 +242,19 @@ public class NotificationCenter { return CH_GRP_MSG; } - private String getNotificationChannel(NotificationManagerCompat notificationManager, DcChat dcChat) { - int chatId = dcChat.getId(); + private String getNotificationChannel(NotificationManagerCompat notificationManager, ChatData chatData, DcChat dcChat) { String channelId = CH_MSG_PREFIX; if (notificationChannelsSupported()) { try { // get all values we'll use as settings for the NotificationChannel String ledColor = Prefs.getNotificationLedColor(context); - boolean defaultVibrate = effectiveVibrate(chatId); - @Nullable Uri ringtone = effectiveSound(chatId); - boolean isIndependent = requiresIndependentChannel(chatId); + boolean defaultVibrate = effectiveVibrate(chatData); + @Nullable Uri ringtone = effectiveSound(chatData); + boolean isIndependent = requiresIndependentChannel(chatData); // get channel id from these settings - channelId = computeChannelId(ledColor, defaultVibrate, ringtone, isIndependent? chatId : 0); + channelId = computeChannelId(ledColor, defaultVibrate, ringtone, isIndependent? chatData : null); // user-visible name of the channel - // we just use the name of the chat or "Default" @@ -263,8 +278,8 @@ public class NotificationCenter { channels.get(i).setName(name); } else { // ... another message channel, delete if it is not in use. - int currChatId = parseNotificationChannelChatId(currChannelId); - if (!currChannelId.equals(computeChannelId(ledColor, effectiveVibrate(currChatId), effectiveSound(currChatId), currChatId))) { + ChatData currChat = parseNotificationChannelChat(currChannelId); + if (!currChannelId.equals(computeChannelId(ledColor, effectiveVibrate(currChat), effectiveSound(currChat), currChat))) { notificationManager.deleteNotificationChannel(currChannelId); } } @@ -311,16 +326,18 @@ public class NotificationCenter { // add notifications & co. // -------------------------------------------------------------------------------------------- - public void addNotification(int chatId, int msgId) { + public void addNotification(int accountId, int chatId, int msgId) { Util.runOnAnyBackgroundThread(() -> { + DcContext dcContext = context.dcAccounts.getAccount(accountId); DcChat dcChat = dcContext.getChat(chatId); + ChatData chatData = new ChatData(accountId, chatId); if (!Prefs.isNotificationsEnabled(context) || dcChat.isMuted()) { return; } - if (chatId == visibleChatId) { + if (Util.equals(visibleChat, chatData)) { if (Prefs.isInChatNotifications(context)) { InChatSounds.getInstance(context).playIncomingSound(); } @@ -349,7 +366,7 @@ 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 - String notificationChannel = getNotificationChannel(notificationManager, dcChat); + String notificationChannel = getNotificationChannel(notificationManager, chatData, dcChat); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, notificationChannel) .setSmallIcon(R.drawable.icon_notification) .setColor(context.getResources().getColor(R.color.delta_primary)) @@ -357,15 +374,17 @@ public class NotificationCenter { .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setOnlyAlertOnce(!signal) .setContentText(line) - .setDeleteIntent(getMarkAsReadIntent(chatId, false)) - .setContentIntent(getOpenChatIntent(chatId)); + .setDeleteIntent(getMarkAsReadIntent(chatData, false)) + .setContentIntent(getOpenChatIntent(chatData)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - builder.setGroup(GRP_MSG); + builder.setGroup(GRP_MSG + "." + accountId); } + String accountAddr = dcContext.getConfig("addr"); if (privacy.isDisplayContact()) { builder.setContentTitle(dcChat.getName()); + builder.setSubText(accountAddr); } // if privacy allows, for better accessibility, @@ -379,11 +398,11 @@ public class NotificationCenter { // set sound, vibrate, led for systems that do not have notification channels if (!notificationChannelsSupported()) { if (signal) { - Uri sound = effectiveSound(chatId); + Uri sound = effectiveSound(chatData); if (sound != null && !TextUtils.isEmpty(sound.toString())) { builder.setSound(sound); } - boolean vibrate = effectiveVibrate(chatId); + boolean vibrate = effectiveVibrate(chatData); if (vibrate) { builder.setDefaults(Notification.DEFAULT_VIBRATE); } @@ -427,8 +446,8 @@ public class NotificationCenter { if (privacy.isDisplayContact() && privacy.isDisplayMessage() && !Prefs.isScreenLockEnabled(context)) { try { - PendingIntent inNotificationReplyIntent = getRemoteReplyIntent(chatId); - PendingIntent markReadIntent = getMarkAsReadIntent(chatId, true); + PendingIntent inNotificationReplyIntent = getRemoteReplyIntent(chatData); + PendingIntent markReadIntent = getMarkAsReadIntent(chatData, true); NotificationCompat.Action markAsReadAction = new NotificationCompat.Action(R.drawable.check, context.getString(R.string.notify_dismiss), @@ -460,10 +479,15 @@ public class NotificationCenter { try { NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); synchronized (inboxes) { - ArrayList lines = inboxes.get(chatId); + HashMap> accountInbox = inboxes.get(accountId); + if (accountInbox == null) { + accountInbox = new HashMap<>(); + inboxes.put(accountId, accountInbox); + } + ArrayList lines = accountInbox.get(chatId); if (lines == null) { lines = new ArrayList<>(); - inboxes.put(chatId, lines); + accountInbox.put(chatId, lines); } lines.add(line); @@ -486,7 +510,7 @@ public class NotificationCenter { // add notification, we use one notification per chat, // esp. older android are not that great at grouping try { - notificationManager.notify(ID_MSG_OFFSET + chatId, builder.build()); + notificationManager.notify(String.valueOf(accountId), ID_MSG_OFFSET + chatId, builder.build()); } catch (Exception e) { Log.e(TAG, "cannot add notification", e); } @@ -497,15 +521,18 @@ public class NotificationCenter { if (Build.VERSION.SDK_INT >= 24) { try { NotificationCompat.Builder summary = new NotificationCompat.Builder(context, notificationChannel) - .setGroup(GRP_MSG) + .setGroup(GRP_MSG + "." + accountId) .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()); + .setContentIntent(getOpenChatlistIntent(accountId)); + if (privacy.isDisplayContact()) { + summary.setSubText(accountAddr); + } + notificationManager.notify(String.valueOf(accountId), ID_MSG_SUMMARY, summary.build()); } catch (Exception e) { Log.e(TAG, "cannot add notification summary", e); } @@ -513,46 +540,83 @@ public class NotificationCenter { }); } - public void removeNotifications(int chatId) { + public void removeNotifications(int accountId, int chatId) { boolean removeSummary; synchronized (inboxes) { - inboxes.remove(chatId); - removeSummary = inboxes.isEmpty(); + HashMap> accountInbox = inboxes.get(accountId); + if (accountInbox == null) { + accountInbox = new HashMap<>(); + } + accountInbox.remove(chatId); + removeSummary = accountInbox.isEmpty(); } // cancel notification independently of inboxes array, // due to restarts, the app may have notification even when inboxes is empty. try { NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - notificationManager.cancel(ID_MSG_OFFSET + chatId); + String tag = String.valueOf(accountId); + notificationManager.cancel(tag, ID_MSG_OFFSET + chatId); if (removeSummary) { - notificationManager.cancel(ID_MSG_SUMMARY); + notificationManager.cancel(tag, ID_MSG_SUMMARY); } } catch (Exception e) { Log.w(TAG, e); } } - public void removeAllNotifiations() { - synchronized (inboxes) { - inboxes.clear(); - } + public void removeAllNotifiations(int accountId) { NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - notificationManager.cancelAll(); + String tag = String.valueOf(accountId); + synchronized (inboxes) { + HashMap> accountInbox = inboxes.get(accountId); + notificationManager.cancel(tag, ID_MSG_SUMMARY); + if (accountInbox != null) { + for (Integer chatId : accountInbox.keySet()) { + notificationManager.cancel(tag, chatId); + } + accountInbox.clear(); + } + } } - public void updateVisibleChat(int chatId) { + public void updateVisibleChat(int accountId, int chatId) { Util.runOnAnyBackgroundThread(() -> { - visibleChatId = chatId; - if (chatId!=0) { - removeNotifications(chatId); + if (accountId != 0 && chatId != 0) { + visibleChat = new ChatData(accountId, chatId); + removeNotifications(accountId, chatId); + } else { + visibleChat = null; } }); } + public void clearVisibleChat() { + visibleChat = null; + } + public void maybePlaySendSound(DcChat dcChat) { if (Prefs.isInChatNotifications(context) && Prefs.isNotificationsEnabled(context) && !dcChat.isMuted()) { InChatSounds.getInstance(context).playSendSound(); } } + + private class ChatData { + public int accountId; + public int chatId; + + public ChatData(int accountId, int chatId) { + this.accountId = accountId; + this.chatId = chatId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ChatData chatData = (ChatData) o; + return accountId == chatData.accountId && chatId == chatData.chatId; + } + } } diff --git a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java index b60456558..6a034190b 100644 --- a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java +++ b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java @@ -21,13 +21,13 @@ import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.AsyncTask; import android.os.Bundle; import androidx.core.app.RemoteInput; import com.b44t.messenger.DcContext; import org.thoughtcrime.securesms.connect.DcHelper; +import org.thoughtcrime.securesms.util.Util; /** @@ -37,7 +37,8 @@ public class RemoteReplyReceiver extends BroadcastReceiver { public static final String TAG = RemoteReplyReceiver.class.getSimpleName(); public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.WEAR_REPLY"; - public static final String CHAT_ID_EXTRA = "chat_id"; + public static final String ACCOUNT_ID_EXTRA = TAG + ".account_id"; + public static final String CHAT_ID_EXTRA = TAG + ".chat_id"; public static final String EXTRA_REMOTE_REPLY = "extra_remote_reply"; @SuppressLint("StaticFieldLeak") @@ -45,26 +46,22 @@ public class RemoteReplyReceiver extends BroadcastReceiver { public void onReceive(final Context context, Intent intent) { if (!REPLY_ACTION.equals(intent.getAction())) return; Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + final int accountId = intent.getIntExtra(ACCOUNT_ID_EXTRA, 0); final int chatId = intent.getIntExtra(CHAT_ID_EXTRA, 0); - if (remoteInput == null || chatId == 0) return; + if (remoteInput == null || chatId == 0 || accountId == 0) return; final CharSequence responseText = remoteInput.getCharSequence(EXTRA_REMOTE_REPLY); if (responseText != null) { - new AsyncTask() { - @Override - protected Void doInBackground(Void... params) { - DcContext dcContext = DcHelper.getContext(context); - if (dcContext.getChat(chatId).isContactRequest()) { - dcContext.acceptChat(chatId); - } - dcContext.sendTextMsg(chatId, responseText.toString()); - DcHelper.getNotificationCenter(context).removeNotifications(chatId); - return null; + Util.runOnAnyBackgroundThread(() -> { + DcContext dcContext = DcHelper.getAccounts(context).getAccount(accountId); + if (dcContext.getChat(chatId).isContactRequest()) { + dcContext.acceptChat(chatId); } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + dcContext.sendTextMsg(chatId, responseText.toString()); + DcHelper.getNotificationCenter(context).removeNotifications(accountId, chatId); + }); } - } } diff --git a/src/org/thoughtcrime/securesms/util/Prefs.java b/src/org/thoughtcrime/securesms/util/Prefs.java index 25ed02b7d..c9b0015ce 100644 --- a/src/org/thoughtcrime/securesms/util/Prefs.java +++ b/src/org/thoughtcrime/securesms/util/Prefs.java @@ -225,17 +225,19 @@ public class Prefs { setStringPreference(context, RINGTONE_PREF, ringtone.toString()); } - public static void setChatRingtone(Context context, int chatId, Uri ringtone) { + public static void setChatRingtone(Context context, int accountId, int chatId, Uri ringtone) { + final String KEY = (accountId != 0 && chatId != 0)? CHAT_RINGTONE+accountId+"."+chatId : CHAT_RINGTONE; if(ringtone!=null) { - setStringPreference(context, CHAT_RINGTONE+chatId, ringtone.toString()); + setStringPreference(context, KEY, ringtone.toString()); } else { - removePreference(context, CHAT_RINGTONE+chatId); + removePreference(context, KEY); } } - public static @Nullable Uri getChatRingtone(Context context, int chatId) { - String result = getStringPreference(context, CHAT_RINGTONE+chatId, null); + public static @Nullable Uri getChatRingtone(Context context, int accountId, int chatId) { + final String KEY = (accountId != 0 && chatId != 0)? CHAT_RINGTONE+accountId+"."+chatId : CHAT_RINGTONE; + String result = getStringPreference(context, KEY, null); return result==null? null : Uri.parse(result); } @@ -254,17 +256,19 @@ public class Prefs { return getBooleanPreference(context, VIBRATE_PREF, true); } - public static void setChatVibrate(Context context, int chatId, VibrateState vibrateState) { + public static void setChatVibrate(Context context, int accountId, int chatId, VibrateState vibrateState) { + final String KEY = (accountId != 0 && chatId != 0)? CHAT_VIBRATE+accountId+"."+chatId : CHAT_VIBRATE; if(vibrateState!=VibrateState.DEFAULT) { - setIntegerPrefrence(context, CHAT_VIBRATE+chatId, vibrateState.getId()); + setIntegerPrefrence(context, KEY, vibrateState.getId()); } else { - removePreference(context, CHAT_VIBRATE+chatId); + removePreference(context, KEY); } } - public static VibrateState getChatVibrate(Context context, int chatId) { - return VibrateState.fromId(getIntegerPreference(context, CHAT_VIBRATE+chatId, VibrateState.DEFAULT.getId())); + public static VibrateState getChatVibrate(Context context, int accountId, int chatId) { + final String KEY = (accountId != 0 && chatId != 0)? CHAT_VIBRATE+accountId+"."+chatId : CHAT_VIBRATE; + return VibrateState.fromId(getIntegerPreference(context, KEY, VibrateState.DEFAULT.getId())); } // led