Fix reaction notification data inconsistencies.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
This commit is contained in:
parent
006eebb09e
commit
c9d1fb8533
6 changed files with 82 additions and 4 deletions
|
@ -319,6 +319,8 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||||
setReactions(db, messageId, updatedList);
|
setReactions(db, messageId, updatedList);
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
|
} catch (NoSuchMessageException e) {
|
||||||
|
Log.w(TAG, "No message for provided id", e);
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
@ -338,6 +340,8 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||||
setReactions(db, messageId, updatedList);
|
setReactions(db, messageId, updatedList);
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
|
} catch (NoSuchMessageException e) {
|
||||||
|
Log.w(TAG, "No message for provided id", e);
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
@ -543,14 +547,15 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
||||||
return Optional.absent();
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setReactions(@NonNull SQLiteDatabase db, long messageId, @NonNull ReactionList reactionList) {
|
private void setReactions(@NonNull SQLiteDatabase db, long messageId, @NonNull ReactionList reactionList) throws NoSuchMessageException {
|
||||||
ContentValues values = new ContentValues(1);
|
ContentValues values = new ContentValues();
|
||||||
|
boolean isOutgoing = getMessageRecord(messageId).isOutgoing();
|
||||||
boolean hasReactions = reactionList.getReactionsCount() != 0;
|
boolean hasReactions = reactionList.getReactionsCount() != 0;
|
||||||
|
|
||||||
values.put(REACTIONS, reactionList.getReactionsList().isEmpty() ? null : reactionList.toByteArray());
|
values.put(REACTIONS, reactionList.getReactionsList().isEmpty() ? null : reactionList.toByteArray());
|
||||||
values.put(REACTIONS_UNREAD, hasReactions ? 1 : 0);
|
values.put(REACTIONS_UNREAD, hasReactions ? 1 : 0);
|
||||||
|
|
||||||
if (hasReactions) {
|
if (isOutgoing && hasReactions) {
|
||||||
values.put(NOTIFIED, 0);
|
values.put(NOTIFIED, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -889,6 +889,7 @@ public class MmsDatabase extends MessageDatabase {
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
|
|
||||||
contentValues.put(NOTIFIED, 1);
|
contentValues.put(NOTIFIED, 1);
|
||||||
|
contentValues.put(REACTIONS_LAST_SEEN, System.currentTimeMillis());
|
||||||
|
|
||||||
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
|
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
|
||||||
}
|
}
|
||||||
|
|
|
@ -465,6 +465,7 @@ public class SmsDatabase extends MessageDatabase {
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
|
|
||||||
contentValues.put(NOTIFIED, 1);
|
contentValues.put(NOTIFIED, 1);
|
||||||
|
contentValues.put(REACTIONS_LAST_SEEN, System.currentTimeMillis());
|
||||||
|
|
||||||
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
|
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
import net.sqlcipher.database.SQLiteDatabase;
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
import net.sqlcipher.database.SQLiteOpenHelper;
|
import net.sqlcipher.database.SQLiteOpenHelper;
|
||||||
|
@ -45,6 +46,7 @@ import org.thoughtcrime.securesms.database.SqlCipherDatabaseHook;
|
||||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
|
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
||||||
|
@ -69,6 +71,7 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -174,8 +177,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||||
private static final int MP4_GIF_SUPPORT = 93;
|
private static final int MP4_GIF_SUPPORT = 93;
|
||||||
private static final int BLUR_AVATARS = 94;
|
private static final int BLUR_AVATARS = 94;
|
||||||
private static final int CLEAN_STORAGE_IDS_WITHOUT_INFO = 95;
|
private static final int CLEAN_STORAGE_IDS_WITHOUT_INFO = 95;
|
||||||
|
private static final int CLEAN_REACTION_NOTIFICATIONS = 96;
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 95;
|
private static final int DATABASE_VERSION = 96;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -1330,6 +1334,68 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||||
Log.i(TAG, "There were " + count + " bad rows that had their storageID removed due to not having any other identifier.");
|
Log.i(TAG, "There were " + count + " bad rows that had their storageID removed due to not having any other identifier.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < CLEAN_REACTION_NOTIFICATIONS) {
|
||||||
|
ContentValues values = new ContentValues(1);
|
||||||
|
values.put("notified", 1);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
count += db.update("sms", values, "notified = 0 AND read = 1 AND reactions_unread = 1 AND NOT ((type & 31) = 23 AND (type & 10485760) AND (type & 131072 = 0))", null);
|
||||||
|
count += db.update("mms", values, "notified = 0 AND read = 1 AND reactions_unread = 1 AND NOT ((msg_box & 31) = 23 AND (msg_box & 10485760) AND (msg_box & 131072 = 0))", null);
|
||||||
|
Log.d(TAG, "Resetting notified for " + count + " read incoming messages that were incorrectly flipped when receiving reactions");
|
||||||
|
|
||||||
|
List<Long> smsIds = new ArrayList<>();
|
||||||
|
|
||||||
|
try (Cursor cursor = db.query("sms", new String[]{"_id", "reactions", "notified_timestamp"}, "notified = 0 AND reactions_unread = 1", null, null, null, null)) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
byte[] reactions = cursor.getBlob(cursor.getColumnIndexOrThrow("reactions"));
|
||||||
|
long notifiedTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow("notified_timestamp"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
boolean hasReceiveLaterThanNotified = ReactionList.parseFrom(reactions)
|
||||||
|
.getReactionsList()
|
||||||
|
.stream()
|
||||||
|
.anyMatch(r -> r.getReceivedTime() > notifiedTimestamp);
|
||||||
|
if (!hasReceiveLaterThanNotified) {
|
||||||
|
smsIds.add(cursor.getLong(cursor.getColumnIndexOrThrow("_id")));
|
||||||
|
}
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
Log.e(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smsIds.size() > 0) {
|
||||||
|
Log.d(TAG, "Updating " + smsIds.size() + " records in sms");
|
||||||
|
db.execSQL("UPDATE sms SET reactions_last_seen = notified_timestamp WHERE _id in (" + Util.join(smsIds, ",") + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Long> mmsIds = new ArrayList<>();
|
||||||
|
|
||||||
|
try (Cursor cursor = db.query("mms", new String[]{"_id", "reactions", "notified_timestamp"}, "notified = 0 AND reactions_unread = 1", null, null, null, null)) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
byte[] reactions = cursor.getBlob(cursor.getColumnIndexOrThrow("reactions"));
|
||||||
|
long notifiedTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow("notified_timestamp"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
boolean hasReceiveLaterThanNotified = ReactionList.parseFrom(reactions)
|
||||||
|
.getReactionsList()
|
||||||
|
.stream()
|
||||||
|
.anyMatch(r -> r.getReceivedTime() > notifiedTimestamp);
|
||||||
|
if (!hasReceiveLaterThanNotified) {
|
||||||
|
mmsIds.add(cursor.getLong(cursor.getColumnIndexOrThrow("_id")));
|
||||||
|
}
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
Log.e(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mmsIds.size() > 0) {
|
||||||
|
Log.d(TAG, "Updating " + mmsIds.size() + " records in mms");
|
||||||
|
db.execSQL("UPDATE mms SET reactions_last_seen = notified_timestamp WHERE _id in (" + Util.join(mmsIds, ",") + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
|
|
@ -35,6 +35,8 @@ public class DeleteNotificationReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
if (ids == null || mms == null || ids.length != mms.length) return;
|
if (ids == null || mms == null || ids.length != mms.length) return;
|
||||||
|
|
||||||
|
PendingResult finisher = goAsync();
|
||||||
|
|
||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
for (int i = 0; i < ids.length; i++) {
|
for (int i = 0; i < ids.length; i++) {
|
||||||
if (!mms[i]) {
|
if (!mms[i]) {
|
||||||
|
@ -43,6 +45,7 @@ public class DeleteNotificationReceiver extends BroadcastReceiver {
|
||||||
DatabaseFactory.getMmsDatabase(context).markAsNotified(ids[i]);
|
DatabaseFactory.getMmsDatabase(context).markAsNotified(ids[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finisher.finish();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ public class MarkReadReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
NotificationCancellationHelper.cancelLegacy(context, intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1));
|
NotificationCancellationHelper.cancelLegacy(context, intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1));
|
||||||
|
|
||||||
|
PendingResult finisher = goAsync();
|
||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
List<MarkedMessageInfo> messageIdsCollection = new LinkedList<>();
|
List<MarkedMessageInfo> messageIdsCollection = new LinkedList<>();
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ public class MarkReadReceiver extends BroadcastReceiver {
|
||||||
process(context, messageIdsCollection);
|
process(context, messageIdsCollection);
|
||||||
|
|
||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context);
|
||||||
|
finisher.finish();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue