diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ae3b610029..12f1b2c122 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -455,6 +455,9 @@ + + { + handlePositiveAction(dialog); + }) + .setNegativeButton(android.R.string.cancel, ((dialog, which) -> { + dialog.dismiss(); + finish(); + })) + .show(); + } + + private void handlePositiveAction(@NonNull DialogInterface dialog) { + SimpleTask.run(getLifecycle(), () -> { + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(this); + + List marked = threadDatabase.setRead(getIntent().getLongExtra(EXTRA_THREAD_ID, -1), false); + MarkReadReceiver.process(this, marked); + + TextSecurePreferences.setNewContactsNotificationEnabled(this, false); + ApplicationDependencies.getMessageNotifier().updateNotification(this); + + return null; + }, unused -> { + dialog.dismiss(); + finish(); + }); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/DirectoryHelper.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/DirectoryHelper.java index 116127be86..910a36d304 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/DirectoryHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/DirectoryHelper.java @@ -37,9 +37,9 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; -import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.registration.RegistrationUtil; import org.thoughtcrime.securesms.storage.StorageSyncHelper; +import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.sms.IncomingJoinedMessage; import org.thoughtcrime.securesms.util.FeatureFlags; diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java index 5ae4afac32..7b06e1442f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java @@ -364,18 +364,25 @@ public class DefaultMessageNotifier implements MessageNotifier { long timestamp = notifications.get(0).getTimestamp(); if (timestamp != 0) builder.setWhen(timestamp); + boolean isSingleNotificationContactJoined = notifications.size() == 1 && notifications.get(0).isJoin(); + if (!KeyCachingService.isLocked(context) && RecipientUtil.isMessageRequestAccepted(context, recipient.resolve())) { ReplyMethod replyMethod = ReplyMethod.forRecipient(context, recipient); builder.addActions(notificationState.getMarkAsReadIntent(context, notificationId), notificationState.getQuickReplyIntent(context, notifications.get(0).getRecipient()), notificationState.getRemoteReplyIntent(context, notifications.get(0).getRecipient(), replyMethod), - replyMethod); + replyMethod, + !isSingleNotificationContactJoined); builder.addAndroidAutoAction(notificationState.getAndroidAutoReplyIntent(context, notifications.get(0).getRecipient()), notificationState.getAndroidAutoHeardIntent(context, notificationId), notifications.get(0).getTimestamp()); } + if (!KeyCachingService.isLocked(context) && isSingleNotificationContactJoined) { + builder.addTurnOffTheseNotificationsAction(notificationState.getTurnOffTheseNotificationsIntent(context)); + } + ListIterator iterator = notifications.listIterator(notifications.size()); while(iterator.hasPrevious()) { @@ -538,7 +545,7 @@ public class DefaultMessageNotifier implements MessageNotifier { } if (threadRecipients == null || includeMessage) { - notificationState.addNotification(new NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, receivedTimestamp, slideDeck, false)); + notificationState.addNotification(new NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, receivedTimestamp, slideDeck, false, record.isJoined())); } } @@ -572,7 +579,7 @@ public class DefaultMessageNotifier implements MessageNotifier { } if (threadRecipients == null || !threadRecipients.isMuted()) { - notificationState.addNotification(new NotificationItem(id, mms, reactionSender, conversationRecipient, threadRecipients, threadId, body, reaction.getDateReceived(), receivedTimestamp, null, true)); + notificationState.addNotification(new NotificationItem(id, mms, reactionSender, conversationRecipient, threadRecipients, threadId, body, reaction.getDateReceived(), receivedTimestamp, null, true, record.isJoined())); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java index 96d26067d4..e8e417c604 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java @@ -27,6 +27,7 @@ public class NotificationItem { private final long messageReceivedTimestamp; @Nullable private final SlideDeck slideDeck; private final boolean jumpToMessage; + private final boolean isJoin; public NotificationItem(long id, boolean mms, @@ -38,7 +39,8 @@ public class NotificationItem { long notificationTimestamp, long messageReceivedTimestamp, @Nullable SlideDeck slideDeck, - boolean jumpToMessage) + boolean jumpToMessage, + boolean isJoin) { this.id = id; this.mms = mms; @@ -51,6 +53,7 @@ public class NotificationItem { this.messageReceivedTimestamp = messageReceivedTimestamp; this.slideDeck = slideDeck; this.jumpToMessage = jumpToMessage; + this.isJoin = isJoin; } public @NonNull Recipient getRecipient() { @@ -98,6 +101,10 @@ public class NotificationItem { return mms; } + public boolean isJoin() { + return isJoin; + } + private static int getStartingPosition(@NonNull Context context, long threadId, long receivedTimestampMs) { return DatabaseFactory.getMmsSmsDatabase(context).getMessagePositionInConversation(threadId, receivedTimestampMs); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java index a060db9f9d..df20bfc631 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationState.java @@ -8,6 +8,7 @@ import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.thoughtcrime.securesms.contacts.TurnOffContactJoinedNotificationsActivity; import org.thoughtcrime.securesms.conversation.ConversationActivity; import org.thoughtcrime.securesms.conversation.ConversationPopupActivity; import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; @@ -101,6 +102,15 @@ public class NotificationState { return list; } + public PendingIntent getTurnOffTheseNotificationsIntent(Context context) { + long threadId = threads.iterator().next(); + + return PendingIntent.getActivity(context, + 0, + TurnOffContactJoinedNotificationsActivity.newIntent(context, threadId), + PendingIntent.FLAG_UPDATE_CURRENT); + } + public PendingIntent getMarkAsReadIntent(Context context, int notificationId) { long[] threadArray = new long[threads.size()]; int index = 0; diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index 35009941d2..765103ee56 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -7,6 +7,8 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; +import android.text.SpannableStringBuilder; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -17,8 +19,6 @@ import androidx.core.app.Person; import androidx.core.app.RemoteInput; import androidx.core.graphics.drawable.IconCompat; -import android.text.SpannableStringBuilder; - import com.annimon.stream.Stream; import com.bumptech.glide.load.engine.DiskCacheStrategy; @@ -156,43 +156,57 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil extend(new NotificationCompat.CarExtender().setUnreadConversation(unreadConversationBuilder.build())); } + public void addTurnOffTheseNotificationsAction(@NonNull PendingIntent turnOffTheseNotificationsIntent) { + Action turnOffTheseNotifications = new Action(R.drawable.check, + context.getString(R.string.MessageNotifier_turn_off_these_notifications), + turnOffTheseNotificationsIntent); + + addAction(turnOffTheseNotifications); + } + public void addActions(@NonNull PendingIntent markReadIntent, @NonNull PendingIntent quickReplyIntent, @NonNull PendingIntent wearableReplyIntent, - @NonNull ReplyMethod replyMethod) + @NonNull ReplyMethod replyMethod, + boolean replyEnabled) { - Action markAsReadAction = new Action(R.drawable.check, - context.getString(R.string.MessageNotifier_mark_read), - markReadIntent); - - String actionName = context.getString(R.string.MessageNotifier_reply); - String label = context.getString(replyMethodLongDescription(replyMethod)); - - Action replyAction = new Action(R.drawable.ic_reply_white_36dp, - actionName, - quickReplyIntent); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp, - actionName, - wearableReplyIntent) - .addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY) - .setLabel(label).build()) - .build(); - } - - Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply, - actionName, - wearableReplyIntent) - .addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY) - .setLabel(label).build()) - .build(); + NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender(); + Action markAsReadAction = new Action(R.drawable.check, + context.getString(R.string.MessageNotifier_mark_read), + markReadIntent); addAction(markAsReadAction); - addAction(replyAction); + extender.addAction(markAsReadAction); - extend(new NotificationCompat.WearableExtender().addAction(markAsReadAction) - .addAction(wearableReplyAction)); + if (replyEnabled) { + String actionName = context.getString(R.string.MessageNotifier_reply); + String label = context.getString(replyMethodLongDescription(replyMethod)); + + Action replyAction = new Action(R.drawable.ic_reply_white_36dp, + actionName, + quickReplyIntent); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp, + actionName, + wearableReplyIntent) + .addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY) + .setLabel(label) + .build()) + .build(); + } + + Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply, + actionName, + wearableReplyIntent) + .addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY) + .setLabel(label) + .build()) + .build(); + + addAction(replyAction); + extend(extender.addAction(wearableReplyAction)); + } } @StringRes diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 969f146b6a..73fd549b89 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -492,6 +492,10 @@ public class TextSecurePreferences { return new NotificationPrivacyPreference(getStringPreference(context, NOTIFICATION_PRIVACY_PREF, "all")); } + public static void setNewContactsNotificationEnabled(Context context, boolean isEnabled) { + setBooleanPreference(context, NEW_CONTACTS_NOTIFICATIONS, isEnabled); + } + public static boolean isNewContactsNotificationEnabled(Context context) { return getBooleanPreference(context, NEW_CONTACTS_NOTIFICATIONS, true); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7060828d3d..8f82851a63 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1340,6 +1340,7 @@ Error delivering message. Mark all as read Mark read + Turn off these notifications Media message Sticker View-once photo @@ -1361,6 +1362,8 @@ Reacted %1$s to your sticker. This message was deleted. + Turn off contact joined Signal notifications? You can enable them again in Signal > Settings > Notifications. + Default Calls