From 696fffb60331bc9855b33e99592625f99e5c3554 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 11 Nov 2020 14:49:48 -0500 Subject: [PATCH] Improve mention notifications by only showing alerting notifications once. --- .../securesms/database/MessageDatabase.java | 15 ++++++ .../securesms/database/MmsDatabase.java | 11 ++-- .../securesms/database/MmsSmsColumns.java | 1 + .../securesms/database/MmsSmsDatabase.java | 16 ++++-- .../securesms/database/SmsDatabase.java | 13 +++-- .../database/helpers/SQLCipherOpenHelper.java | 9 +++- .../database/model/MediaMmsMessageRecord.java | 5 +- .../database/model/MessageRecord.java | 8 ++- .../database/model/MmsMessageRecord.java | 4 +- .../model/NotificationMmsMessageRecord.java | 4 +- .../database/model/SmsMessageRecord.java | 5 +- .../notifications/DefaultMessageNotifier.java | 52 ++++++++++++++----- .../notifications/NotificationItem.java | 17 ++++-- 13 files changed, 118 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java index 877c9671c..09fc6daf8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java @@ -36,6 +36,7 @@ import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo; import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.util.JsonUtils; +import org.thoughtcrime.securesms.util.SqlUtil; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; @@ -335,6 +336,20 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns return false; } + public void setNotifiedTimestamp(long timestamp, @NonNull List ids) { + if (ids.isEmpty()) { + return; + } + + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + SqlUtil.Query where = SqlUtil.buildCollectionQuery(ID, ids); + ContentValues values = new ContentValues(); + + values.put(NOTIFIED_TIMESTAMP, timestamp); + + db.update(getTableName(), values, where.getWhere(), where.getWhereArgs()); + } + public void addMismatchedIdentity(long messageId, @NonNull RecipientId recipientId, IdentityKey identityKey) { try { addToDocument(messageId, MISMATCHED_IDENTITIES, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index ff1f74677..2da95b396 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -183,7 +183,8 @@ public class MmsDatabase extends MessageDatabase { REACTIONS_UNREAD + " INTEGER DEFAULT 0, " + REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " + REMOTE_DELETED + " INTEGER DEFAULT 0, " + - MENTIONS_SELF + " INTEGER DEFAULT 0);"; + MENTIONS_SELF + " INTEGER DEFAULT 0, " + + NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0);"; public static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", @@ -208,7 +209,7 @@ public class MmsDatabase extends MessageDatabase { DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, QUOTE_MENTIONS, SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, VIEW_ONCE, REACTIONS, REACTIONS_UNREAD, REACTIONS_LAST_SEEN, - REMOTE_DELETED, MENTIONS_SELF, + REMOTE_DELETED, MENTIONS_SELF, NOTIFIED_TIMESTAMP, "json_group_array(json_object(" + "'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " + "'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " + @@ -1794,7 +1795,8 @@ public class MmsDatabase extends MessageDatabase { false, Collections.emptyList(), false, - false); + false, + 0); } } @@ -1892,6 +1894,7 @@ public class MmsDatabase extends MessageDatabase { boolean remoteDelete = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.REMOTE_DELETED)) == 1; List reactions = parseReactions(cursor); boolean mentionsSelf = CursorUtil.requireBoolean(cursor, MENTIONS_SELF); + long notifiedTimestamp = CursorUtil.requireLong(cursor, NOTIFIED_TIMESTAMP); if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { readReceiptCount = 0; @@ -1913,7 +1916,7 @@ public class MmsDatabase extends MessageDatabase { threadId, body, slideDeck, partCount, box, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, isViewOnce, readReceiptCount, quote, contacts, previews, unidentified, reactions, - remoteDelete, mentionsSelf); + remoteDelete, mentionsSelf, notifiedTimestamp); } private List getMismatchedIdentities(String document) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java index 36117dedb..ada28e68b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -20,6 +20,7 @@ public interface MmsSmsColumns { public static final String EXPIRES_IN = "expires_in"; public static final String EXPIRE_STARTED = "expire_started"; public static final String NOTIFIED = "notified"; + public static final String NOTIFIED_TIMESTAMP = "notified_timestamp"; public static final String UNIDENTIFIED = "unidentified"; public static final String REACTIONS = "reactions"; public static final String REACTIONS_UNREAD = "reactions_unread"; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index 4189cf470..638bb5364 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -100,7 +100,8 @@ public class MmsSmsDatabase extends Database { MmsSmsColumns.REACTIONS_UNREAD, MmsSmsColumns.REACTIONS_LAST_SEEN, MmsSmsColumns.REMOTE_DELETED, - MmsDatabase.MENTIONS_SELF}; + MmsDatabase.MENTIONS_SELF, + MmsSmsColumns.NOTIFIED_TIMESTAMP}; public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) { super(context, databaseHelper); @@ -466,6 +467,11 @@ public class MmsSmsDatabase extends Database { return 0; } + public void setNotifiedTimestamp(long timestamp, @NonNull List smsIds, @NonNull List mmsIds) { + DatabaseFactory.getSmsDatabase(context).setNotifiedTimestamp(timestamp, smsIds); + DatabaseFactory.getMmsDatabase(context).setNotifiedTimestamp(timestamp, mmsIds); + } + public void deleteMessagesInThreadBeforeDate(long threadId, long trimBeforeDate) { Log.d(TAG, "deleteMessagesInThreadBeforeData(" + threadId + ", " + trimBeforeDate + ")"); DatabaseFactory.getSmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, trimBeforeDate); @@ -539,7 +545,8 @@ public class MmsSmsDatabase extends Database { MmsSmsColumns.REACTIONS_LAST_SEEN, MmsSmsColumns.DATE_SERVER, MmsSmsColumns.REMOTE_DELETED, - MmsDatabase.MENTIONS_SELF }; + MmsDatabase.MENTIONS_SELF, + MmsSmsColumns.NOTIFIED_TIMESTAMP }; String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT, SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED, @@ -573,7 +580,8 @@ public class MmsSmsDatabase extends Database { MmsSmsColumns.REACTIONS_LAST_SEEN, MmsSmsColumns.DATE_SERVER, MmsSmsColumns.REMOTE_DELETED, - MmsDatabase.MENTIONS_SELF }; + MmsDatabase.MENTIONS_SELF, + MmsSmsColumns.NOTIFIED_TIMESTAMP }; SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder(); SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder(); @@ -628,6 +636,7 @@ public class MmsSmsDatabase extends Database { mmsColumnsPresent.add(MmsDatabase.REACTIONS_LAST_SEEN); mmsColumnsPresent.add(MmsDatabase.REMOTE_DELETED); mmsColumnsPresent.add(MmsDatabase.MENTIONS_SELF); + mmsColumnsPresent.add(MmsSmsColumns.NOTIFIED_TIMESTAMP); Set smsColumnsPresent = new HashSet<>(); smsColumnsPresent.add(MmsSmsColumns.ID); @@ -654,6 +663,7 @@ public class MmsSmsDatabase extends Database { smsColumnsPresent.add(SmsDatabase.REACTIONS_UNREAD); smsColumnsPresent.add(SmsDatabase.REACTIONS_LAST_SEEN); smsColumnsPresent.add(MmsDatabase.REMOTE_DELETED); + smsColumnsPresent.add(MmsSmsColumns.NOTIFIED_TIMESTAMP); @SuppressWarnings("deprecation") String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(TRANSPORT, mmsProjection, mmsColumnsPresent, 4, MMS_TRANSPORT, selection, null, MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID, null); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index d8a1c539e..5543f5b8d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -53,6 +53,7 @@ import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; import org.thoughtcrime.securesms.tracing.Trace; import org.thoughtcrime.securesms.util.Base64; +import org.thoughtcrime.securesms.util.CursorUtil; import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.SqlUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -118,7 +119,8 @@ public class SmsDatabase extends MessageDatabase { REACTIONS + " BLOB DEFAULT NULL, " + REACTIONS_UNREAD + " INTEGER DEFAULT 0, " + REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " + - REMOTE_DELETED + " INTEGER DEFAULT 0);"; + REMOTE_DELETED + " INTEGER DEFAULT 0, " + + NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0);"; public static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", @@ -140,7 +142,7 @@ public class SmsDatabase extends MessageDatabase { REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, DELIVERY_RECEIPT_COUNT, MISMATCHED_IDENTITIES, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, READ_RECEIPT_COUNT, UNIDENTIFIED, REACTIONS, REACTIONS_UNREAD, REACTIONS_LAST_SEEN, - REMOTE_DELETED + REMOTE_DELETED, NOTIFIED_TIMESTAMP }; private final String OUTGOING_INSECURE_MESSAGE_CLAUSE = "(" + TYPE + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_SENT_TYPE + " AND NOT (" + TYPE + " & " + Types.SECURE_MESSAGE_BIT + ")"; @@ -1271,7 +1273,8 @@ public class SmsDatabase extends MessageDatabase { 0, false, Collections.emptyList(), - false); + false, + 0); } } @@ -1317,6 +1320,7 @@ public class SmsDatabase extends MessageDatabase { boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.UNIDENTIFIED)) == 1; boolean remoteDelete = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.REMOTE_DELETED)) == 1; List reactions = parseReactions(cursor); + long notifiedTimestamp = CursorUtil.requireLong(cursor, NOTIFIED_TIMESTAMP); if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { readReceiptCount = 0; @@ -1331,7 +1335,8 @@ public class SmsDatabase extends MessageDatabase { dateSent, dateReceived, dateServer, deliveryReceiptCount, type, threadId, status, mismatches, subscriptionId, expiresIn, expireStarted, - readReceiptCount, unidentified, reactions, remoteDelete); + readReceiptCount, unidentified, reactions, remoteDelete, + notifiedTimestamp); } private List getMismatches(String document) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 9b5a95d34..7715131c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -64,7 +64,6 @@ import org.thoughtcrime.securesms.util.SqlUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Triple; import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import java.io.ByteArrayInputStream; import java.io.File; @@ -159,8 +158,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int REACTION_CLEANUP = 78; private static final int CAPABILITIES_REFACTOR = 79; private static final int GV1_MIGRATION = 80; + private static final int NOTIFIED_TIMESTAMP = 81; - private static final int DATABASE_VERSION = 80; + private static final int DATABASE_VERSION = 81; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -1160,6 +1160,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { Log.i(TAG, "Updated " + count + " GV1 groups with expected GV2 IDs."); } + if (oldVersion < NOTIFIED_TIMESTAMP) { + db.execSQL("ALTER TABLE sms ADD COLUMN notified_timestamp INTEGER DEFAULT 0"); + db.execSQL("ALTER TABLE mms ADD COLUMN notified_timestamp INTEGER DEFAULT 0"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java index 069f95f8d..e7e1bdd72 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java @@ -73,12 +73,13 @@ public class MediaMmsMessageRecord extends MmsMessageRecord { boolean unidentified, @NonNull List reactions, boolean remoteDelete, - boolean mentionsSelf) + boolean mentionsSelf, + long notifiedTimestamp) { super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, dateServer, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures, subscriptionId, expiresIn, expireStarted, viewOnce, slideDeck, - readReceiptCount, quote, contacts, linkPreviews, unidentified, reactions, remoteDelete); + readReceiptCount, quote, contacts, linkPreviews, unidentified, reactions, remoteDelete, notifiedTimestamp); this.partCount = partCount; this.mentionsSelf = mentionsSelf; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index 8e59ed60d..885e21ed5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -80,6 +80,7 @@ public abstract class MessageRecord extends DisplayRecord { private final List reactions; private final long serverTimestamp; private final boolean remoteDelete; + private final long notifiedTimestamp; MessageRecord(long id, String body, Recipient conversationRecipient, Recipient individualRecipient, int recipientDeviceId, @@ -89,7 +90,7 @@ public abstract class MessageRecord extends DisplayRecord { List networkFailures, int subscriptionId, long expiresIn, long expireStarted, int readReceiptCount, boolean unidentified, - @NonNull List reactions, boolean remoteDelete) + @NonNull List reactions, boolean remoteDelete, long notifiedTimestamp) { super(body, conversationRecipient, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, readReceiptCount); @@ -105,6 +106,7 @@ public abstract class MessageRecord extends DisplayRecord { this.reactions = reactions; this.serverTimestamp = dateServer; this.remoteDelete = remoteDelete; + this.notifiedTimestamp = notifiedTimestamp; } public abstract boolean isMms(); @@ -455,6 +457,10 @@ public abstract class MessageRecord extends DisplayRecord { return false; } + public long getNotifiedTimestamp() { + return notifiedTimestamp; + } + public static final class InviteAddState { private final boolean invited; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java index f2c37743e..c95e62053 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java @@ -33,9 +33,9 @@ public abstract class MmsMessageRecord extends MessageRecord { @NonNull SlideDeck slideDeck, int readReceiptCount, @Nullable Quote quote, @NonNull List contacts, @NonNull List linkPreviews, boolean unidentified, - @NonNull List reactions, boolean remoteDelete) + @NonNull List reactions, boolean remoteDelete, long notifiedTimestamp) { - super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, dateServer, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete); + super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, dateServer, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete, notifiedTimestamp); this.slideDeck = slideDeck; this.quote = quote; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java index 92ed85c16..3df9a9fd1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java @@ -23,8 +23,6 @@ import android.text.SpannableString; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase.Status; -import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; -import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.recipients.Recipient; @@ -58,7 +56,7 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord { dateSent, dateReceived, -1, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, new LinkedList<>(), new LinkedList<>(), subscriptionId, 0, 0, false, slideDeck, readReceiptCount, null, Collections.emptyList(), Collections.emptyList(), false, - Collections.emptyList(), false); + Collections.emptyList(), false, 0); this.contentLocation = contentLocation; this.messageSize = messageSize; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java index 673778fd2..f64466160 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java @@ -49,12 +49,13 @@ public class SmsMessageRecord extends MessageRecord { int status, List mismatches, int subscriptionId, long expiresIn, long expireStarted, int readReceiptCount, boolean unidentified, - @NonNull List reactions, boolean remoteDelete) + @NonNull List reactions, boolean remoteDelete, + long notifiedTimestamp) { super(id, body, recipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, dateServer, threadId, status, deliveryReceiptCount, type, mismatches, new LinkedList<>(), subscriptionId, - expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete); + expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete, notifiedTimestamp); } public long getType() { 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 425e4cb79..3c1a03a19 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java @@ -39,6 +39,8 @@ import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; +import com.annimon.stream.Stream; + import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.contactshare.Contact; import org.thoughtcrime.securesms.contactshare.ContactUtil; @@ -71,6 +73,7 @@ import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; import org.whispersystems.signalservice.internal.util.Util; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Set; @@ -281,8 +284,9 @@ public class DefaultMessageNotifier implements MessageNotifier { boolean signal, int reminderCount) { - Cursor telcoCursor = null; - Cursor pushCursor = null; + boolean isReminder = reminderCount > 0; + Cursor telcoCursor = null; + Cursor pushCursor = null; try { telcoCursor = DatabaseFactory.getMmsSmsDatabase(context).getUnread(); @@ -305,6 +309,8 @@ public class DefaultMessageNotifier implements MessageNotifier { lastAudibleNotification = System.currentTimeMillis(); } + boolean shouldScheduleReminder = signal; + if (notificationState.hasMultipleThreads()) { if (Build.VERSION.SDK_INT >= 23) { for (long threadId : notificationState.getThreads()) { @@ -312,14 +318,15 @@ public class DefaultMessageNotifier implements MessageNotifier { sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), signal && (threadId == targetThread), - true); + true, + isReminder); } } } sendMultipleThreadNotification(context, notificationState, signal && (Build.VERSION.SDK_INT < 23)); } else { - sendSingleThreadNotification(context, notificationState, signal, false); + shouldScheduleReminder = sendSingleThreadNotification(context, notificationState, signal, false, isReminder); if (isDisplayingSummaryNotification(context)) { sendMultipleThreadNotification(context, notificationState, false); @@ -329,7 +336,18 @@ public class DefaultMessageNotifier implements MessageNotifier { cancelOrphanedNotifications(context, notificationState); updateBadge(context, notificationState.getMessageCount()); - if (signal) { + List smsIds = new LinkedList<>(); + List mmsIds = new LinkedList<>(); + for (NotificationItem item : notificationState.getNotifications()) { + if (item.isMms()) { + mmsIds.add(item.getId()); + } else { + smsIds.add(item.getId()); + } + } + DatabaseFactory.getMmsSmsDatabase(context).setNotifiedTimestamp(System.currentTimeMillis(), smsIds, mmsIds); + + if (shouldScheduleReminder) { scheduleReminder(context, reminderCount); } } finally { @@ -338,23 +356,25 @@ public class DefaultMessageNotifier implements MessageNotifier { } } - private static void sendSingleThreadNotification(@NonNull Context context, - @NonNull NotificationState notificationState, - boolean signal, - boolean bundled) + private static boolean sendSingleThreadNotification(@NonNull Context context, + @NonNull NotificationState notificationState, + boolean signal, + boolean bundled, + boolean isReminder) { Log.i(TAG, "sendSingleThreadNotification() signal: " + signal + " bundled: " + bundled); if (notificationState.getNotifications().isEmpty()) { if (!bundled) cancelActiveNotifications(context); Log.i(TAG, "[sendSingleThreadNotification] Empty notification state. Skipping."); - return; + return false; } NotificationPrivacyPreference notificationPrivacy = TextSecurePreferences.getNotificationPrivacy(context); SingleRecipientNotificationBuilder builder = new SingleRecipientNotificationBuilder(context, notificationPrivacy); List notifications = notificationState.getNotifications(); Recipient recipient = notifications.get(0).getRecipient(); + boolean shouldAlert = signal && (isReminder || Stream.of(notifications).anyMatch(item -> item.getNotifiedTimestamp() == 0)); int notificationId; if (Build.VERSION.SDK_INT >= 23) { @@ -369,7 +389,7 @@ public class DefaultMessageNotifier implements MessageNotifier { notifications.get(0).getText(), notifications.get(0).getSlideDeck()); builder.setContentIntent(notifications.get(0).getPendingIntent(context)); builder.setDeleteIntent(notificationState.getDeleteIntent(context)); - builder.setOnlyAlertOnce(!signal); + builder.setOnlyAlertOnce(!shouldAlert); builder.setSortKey(String.valueOf(Long.MAX_VALUE - notifications.get(0).getTimestamp())); long timestamp = notifications.get(0).getTimestamp(); @@ -418,6 +438,8 @@ public class DefaultMessageNotifier implements MessageNotifier { Notification notification = builder.build(); NotificationManagerCompat.from(context).notify(notificationId, notification); Log.i(TAG, "Posted notification."); + + return shouldAlert; } private static void sendMultipleThreadNotification(@NonNull Context context, @@ -434,11 +456,12 @@ public class DefaultMessageNotifier implements MessageNotifier { NotificationPrivacyPreference notificationPrivacy = TextSecurePreferences.getNotificationPrivacy(context); MultipleRecipientNotificationBuilder builder = new MultipleRecipientNotificationBuilder(context, notificationPrivacy); List notifications = notificationState.getNotifications(); + boolean shouldAlert = signal && Stream.of(notifications).anyMatch(item -> item.getNotifiedTimestamp() == 0); builder.setMessageCount(notificationState.getMessageCount(), notificationState.getThreadCount()); builder.setMostRecentSender(notifications.get(0).getIndividualRecipient()); builder.setDeleteIntent(notificationState.getDeleteIntent(context)); - builder.setOnlyAlertOnce(!signal); + builder.setOnlyAlertOnce(!shouldAlert); if (Build.VERSION.SDK_INT >= 23) { builder.setGroup(NOTIFICATION_GROUP); @@ -531,6 +554,7 @@ public class DefaultMessageNotifier implements MessageNotifier { boolean isUnreadMessage = cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.READ)) == 0; boolean hasUnreadReactions = cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.REACTIONS_UNREAD)) == 1; long lastReactionRead = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.REACTIONS_LAST_SEEN)); + long notifiedTimestamp = record.getNotifiedTimestamp(); if (threadId != -1) { threadRecipients = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); @@ -564,7 +588,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, record.isJoined(), canReply)); + notificationState.addNotification(new NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, receivedTimestamp, slideDeck, false, record.isJoined(), canReply, notifiedTimestamp)); } } @@ -599,7 +623,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, record.isJoined(), false)); + notificationState.addNotification(new NotificationItem(id, mms, reactionSender, conversationRecipient, threadRecipients, threadId, body, reaction.getDateReceived(), receivedTimestamp, null, true, record.isJoined(), false, 0)); } } } 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 7cbb786e7..a1d5a124a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationItem.java @@ -23,12 +23,13 @@ public class NotificationItem { @Nullable private final Recipient threadRecipient; private final long threadId; @Nullable private final CharSequence text; - private final long notificationTimestamp; + private final long timestamp; private final long messageReceivedTimestamp; @Nullable private final SlideDeck slideDeck; private final boolean jumpToMessage; private final boolean isJoin; private final boolean canReply; + private final long notifiedTimestamp; public NotificationItem(long id, boolean mms, @@ -37,12 +38,13 @@ public class NotificationItem { @Nullable Recipient threadRecipient, long threadId, @Nullable CharSequence text, - long notificationTimestamp, + long timestamp, long messageReceivedTimestamp, @Nullable SlideDeck slideDeck, boolean jumpToMessage, boolean isJoin, - boolean canReply) + boolean canReply, + long notifiedTimestamp) { this.id = id; this.mms = mms; @@ -51,12 +53,13 @@ public class NotificationItem { this.threadRecipient = threadRecipient; this.text = text; this.threadId = threadId; - this.notificationTimestamp = notificationTimestamp; + this.timestamp = timestamp; this.messageReceivedTimestamp = messageReceivedTimestamp; this.slideDeck = slideDeck; this.jumpToMessage = jumpToMessage; this.isJoin = isJoin; this.canReply = canReply; + this.notifiedTimestamp = notifiedTimestamp; } public @NonNull Recipient getRecipient() { @@ -72,7 +75,7 @@ public class NotificationItem { } public long getTimestamp() { - return notificationTimestamp; + return timestamp; } public long getThreadId() { @@ -119,4 +122,8 @@ public class NotificationItem { public boolean canReply() { return canReply; } + + public long getNotifiedTimestamp() { + return notifiedTimestamp; + } }