Add support for syncing forced unread status.
This commit is contained in:
parent
ed0be6fc9a
commit
63746bbb47
19 changed files with 274 additions and 66 deletions
|
@ -668,6 +668,20 @@ public class RecipientDatabase extends Database {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markNeedsSync(@NonNull Collection<RecipientId> recipientIds) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
for (RecipientId recipientId : recipientIds) {
|
||||||
|
markDirty(recipientId, DirtyState.UPDATE);
|
||||||
|
}
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void markNeedsSync(@NonNull RecipientId recipientId) {
|
public void markNeedsSync(@NonNull RecipientId recipientId) {
|
||||||
markDirty(recipientId, DirtyState.UPDATE);
|
markDirty(recipientId, DirtyState.UPDATE);
|
||||||
}
|
}
|
||||||
|
@ -768,7 +782,7 @@ public class RecipientDatabase extends Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
threadDatabase.setArchived(recipientId, insert.isArchived());
|
threadDatabase.applyStorageSyncUpdate(recipientId, insert);
|
||||||
needsRefresh.add(recipientId);
|
needsRefresh.add(recipientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,7 +835,7 @@ public class RecipientDatabase extends Database {
|
||||||
Log.w(TAG, "Failed to process identity key during update! Skipping.", e);
|
Log.w(TAG, "Failed to process identity key during update! Skipping.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
threadDatabase.setArchived(recipientId, update.getNew().isArchived());
|
threadDatabase.applyStorageSyncUpdate(recipientId, update.getNew());
|
||||||
needsRefresh.add(recipientId);
|
needsRefresh.add(recipientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,7 +844,7 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
Recipient recipient = Recipient.externalGroup(context, GroupId.v1orThrow(insert.getGroupId()));
|
Recipient recipient = Recipient.externalGroup(context, GroupId.v1orThrow(insert.getGroupId()));
|
||||||
|
|
||||||
threadDatabase.setArchived(recipient.getId(), insert.isArchived());
|
threadDatabase.applyStorageSyncUpdate(recipient.getId(), insert);
|
||||||
needsRefresh.add(recipient.getId());
|
needsRefresh.add(recipient.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,7 +858,7 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
Recipient recipient = Recipient.externalGroup(context, GroupId.v1orThrow(update.getOld().getGroupId()));
|
Recipient recipient = Recipient.externalGroup(context, GroupId.v1orThrow(update.getOld().getGroupId()));
|
||||||
|
|
||||||
threadDatabase.setArchived(recipient.getId(), update.getNew().isArchived());
|
threadDatabase.applyStorageSyncUpdate(recipient.getId(), update.getNew());
|
||||||
needsRefresh.add(recipient.getId());
|
needsRefresh.add(recipient.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -872,7 +886,7 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
ApplicationDependencies.getJobManager().add(new RequestGroupV2InfoJob(groupId));
|
ApplicationDependencies.getJobManager().add(new RequestGroupV2InfoJob(groupId));
|
||||||
|
|
||||||
threadDatabase.setArchived(recipient.getId(), insert.isArchived());
|
threadDatabase.applyStorageSyncUpdate(recipient.getId(), insert);
|
||||||
needsRefresh.add(recipient.getId());
|
needsRefresh.add(recipient.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,7 +901,7 @@ public class RecipientDatabase extends Database {
|
||||||
GroupMasterKey masterKey = update.getOld().getMasterKeyOrThrow();
|
GroupMasterKey masterKey = update.getOld().getMasterKeyOrThrow();
|
||||||
Recipient recipient = Recipient.externalGroup(context, GroupId.v2(masterKey));
|
Recipient recipient = Recipient.externalGroup(context, GroupId.v2(masterKey));
|
||||||
|
|
||||||
threadDatabase.setArchived(recipient.getId(), update.getNew().isArchived());
|
threadDatabase.applyStorageSyncUpdate(recipient.getId(), update.getNew());
|
||||||
needsRefresh.add(recipient.getId());
|
needsRefresh.add(recipient.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -936,6 +950,8 @@ public class RecipientDatabase extends Database {
|
||||||
ApplicationDependencies.getJobManager().add(new RefreshAttributesJob());
|
ApplicationDependencies.getJobManager().add(new RefreshAttributesJob());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DatabaseFactory.getThreadDatabase(context).applyStorageSyncUpdate(Recipient.self().getId(), update);
|
||||||
|
|
||||||
Recipient.self().live().refresh();
|
Recipient.self().live().refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1236,12 +1252,13 @@ public class RecipientDatabase extends Database {
|
||||||
String storageProtoRaw = CursorUtil.getString(cursor, STORAGE_PROTO).orNull();
|
String storageProtoRaw = CursorUtil.getString(cursor, STORAGE_PROTO).orNull();
|
||||||
byte[] storageProto = storageProtoRaw != null ? Base64.decodeOrThrow(storageProtoRaw) : null;
|
byte[] storageProto = storageProtoRaw != null ? Base64.decodeOrThrow(storageProtoRaw) : null;
|
||||||
boolean archived = CursorUtil.getBoolean(cursor, ThreadDatabase.ARCHIVED).or(false);
|
boolean archived = CursorUtil.getBoolean(cursor, ThreadDatabase.ARCHIVED).or(false);
|
||||||
|
boolean forcedUnread = CursorUtil.getInt(cursor, ThreadDatabase.READ).transform(status -> status == ThreadDatabase.ReadStatus.FORCED_UNREAD.serialize()).or(false);
|
||||||
GroupMasterKey groupMasterKey = CursorUtil.getBlob(cursor, GroupDatabase.V2_MASTER_KEY).transform(GroupUtil::requireMasterKey).orNull();
|
GroupMasterKey groupMasterKey = CursorUtil.getBlob(cursor, GroupDatabase.V2_MASTER_KEY).transform(GroupUtil::requireMasterKey).orNull();
|
||||||
byte[] identityKey = CursorUtil.getString(cursor, IDENTITY_KEY).transform(Base64::decodeOrThrow).orNull();
|
byte[] identityKey = CursorUtil.getString(cursor, IDENTITY_KEY).transform(Base64::decodeOrThrow).orNull();
|
||||||
VerifiedStatus identityStatus = CursorUtil.getInt(cursor, IDENTITY_STATUS).transform(VerifiedStatus::forState).or(VerifiedStatus.DEFAULT);
|
VerifiedStatus identityStatus = CursorUtil.getInt(cursor, IDENTITY_STATUS).transform(VerifiedStatus::forState).or(VerifiedStatus.DEFAULT);
|
||||||
|
|
||||||
|
|
||||||
return new RecipientSettings.SyncExtras(storageProto, groupMasterKey, identityKey, identityStatus, archived);
|
return new RecipientSettings.SyncExtras(storageProto, groupMasterKey, identityKey, identityStatus, archived, forcedUnread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BulkOperationsHandle beginBulkSystemContactUpdate() {
|
public BulkOperationsHandle beginBulkSystemContactUpdate() {
|
||||||
|
@ -1422,7 +1439,7 @@ public class RecipientDatabase extends Database {
|
||||||
valuesToSet.putNull(PROFILE_KEY_CREDENTIAL);
|
valuesToSet.putNull(PROFILE_KEY_CREDENTIAL);
|
||||||
valuesToSet.put(UNIDENTIFIED_ACCESS_MODE, UnidentifiedAccessMode.UNKNOWN.getMode());
|
valuesToSet.put(UNIDENTIFIED_ACCESS_MODE, UnidentifiedAccessMode.UNKNOWN.getMode());
|
||||||
|
|
||||||
SqlUtil.UpdateQuery updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, valuesToCompare);
|
SqlUtil.Query updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, valuesToCompare);
|
||||||
|
|
||||||
if (update(updateQuery, valuesToSet)) {
|
if (update(updateQuery, valuesToSet)) {
|
||||||
markDirty(id, DirtyState.UPDATE);
|
markDirty(id, DirtyState.UPDATE);
|
||||||
|
@ -1471,7 +1488,7 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
values.put(PROFILE_KEY_CREDENTIAL, Base64.encodeBytes(profileKeyCredential.serialize()));
|
values.put(PROFILE_KEY_CREDENTIAL, Base64.encodeBytes(profileKeyCredential.serialize()));
|
||||||
|
|
||||||
SqlUtil.UpdateQuery updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
SqlUtil.Query updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||||
|
|
||||||
if (update(updateQuery, values)) {
|
if (update(updateQuery, values)) {
|
||||||
// TODO [greyson] If we sync this in future, mark dirty
|
// TODO [greyson] If we sync this in future, mark dirty
|
||||||
|
@ -2250,9 +2267,7 @@ public class RecipientDatabase extends Database {
|
||||||
* query such that this will only return true if a row was *actually* updated.
|
* query such that this will only return true if a row was *actually* updated.
|
||||||
*/
|
*/
|
||||||
private boolean update(@NonNull RecipientId id, @NonNull ContentValues contentValues) {
|
private boolean update(@NonNull RecipientId id, @NonNull ContentValues contentValues) {
|
||||||
String selection = ID + " = ?";
|
SqlUtil.Query updateQuery = SqlUtil.buildTrueUpdateQuery(ID_WHERE, SqlUtil.buildArgs(id), contentValues);
|
||||||
String[] args = new String[]{id.serialize()};
|
|
||||||
SqlUtil.UpdateQuery updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, contentValues);
|
|
||||||
|
|
||||||
return update(updateQuery, contentValues);
|
return update(updateQuery, contentValues);
|
||||||
}
|
}
|
||||||
|
@ -2262,7 +2277,7 @@ public class RecipientDatabase extends Database {
|
||||||
* <p>
|
* <p>
|
||||||
* This will only return true if a row was *actually* updated with respect to the where clause of the {@param updateQuery}.
|
* This will only return true if a row was *actually* updated with respect to the where clause of the {@param updateQuery}.
|
||||||
*/
|
*/
|
||||||
private boolean update(@NonNull SqlUtil.UpdateQuery updateQuery, @NonNull ContentValues contentValues) {
|
private boolean update(@NonNull SqlUtil.Query updateQuery, @NonNull ContentValues contentValues) {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
|
|
||||||
return database.update(TABLE_NAME, contentValues, updateQuery.getWhere(), updateQuery.getWhereArgs()) > 0;
|
return database.update(TABLE_NAME, contentValues, updateQuery.getWhere(), updateQuery.getWhereArgs()) > 0;
|
||||||
|
@ -2816,18 +2831,21 @@ public class RecipientDatabase extends Database {
|
||||||
private final byte[] identityKey;
|
private final byte[] identityKey;
|
||||||
private final VerifiedStatus identityStatus;
|
private final VerifiedStatus identityStatus;
|
||||||
private final boolean archived;
|
private final boolean archived;
|
||||||
|
private final boolean forcedUnread;
|
||||||
|
|
||||||
public SyncExtras(@Nullable byte[] storageProto,
|
public SyncExtras(@Nullable byte[] storageProto,
|
||||||
@Nullable GroupMasterKey groupMasterKey,
|
@Nullable GroupMasterKey groupMasterKey,
|
||||||
@Nullable byte[] identityKey,
|
@Nullable byte[] identityKey,
|
||||||
@NonNull VerifiedStatus identityStatus,
|
@NonNull VerifiedStatus identityStatus,
|
||||||
boolean archived)
|
boolean archived,
|
||||||
|
boolean forcedUnread)
|
||||||
{
|
{
|
||||||
this.storageProto = storageProto;
|
this.storageProto = storageProto;
|
||||||
this.groupMasterKey = groupMasterKey;
|
this.groupMasterKey = groupMasterKey;
|
||||||
this.identityKey = identityKey;
|
this.identityKey = identityKey;
|
||||||
this.identityStatus = identityStatus;
|
this.identityStatus = identityStatus;
|
||||||
this.archived = archived;
|
this.archived = archived;
|
||||||
|
this.forcedUnread = forcedUnread;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable byte[] getStorageProto() {
|
public @Nullable byte[] getStorageProto() {
|
||||||
|
@ -2849,6 +2867,10 @@ public class RecipientDatabase extends Database {
|
||||||
public @NonNull VerifiedStatus getIdentityStatus() {
|
public @NonNull VerifiedStatus getIdentityStatus() {
|
||||||
return identityStatus;
|
return identityStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForcedUnread() {
|
||||||
|
return forcedUnread;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import net.sqlcipher.database.SQLiteDatabase;
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
import org.jsoup.helper.StringUtil;
|
import org.jsoup.helper.StringUtil;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase.MarkedMessageInfo;
|
import org.thoughtcrime.securesms.database.MessageDatabase.MarkedMessageInfo;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
|
@ -56,11 +54,15 @@ import org.thoughtcrime.securesms.util.SqlUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
|
||||||
|
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
|
||||||
|
import org.whispersystems.signalservice.api.storage.SignalGroupV2Record;
|
||||||
|
import org.whispersystems.signalservice.api.storage.SignalStorageRecord;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -70,7 +72,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class ThreadDatabase extends Database {
|
public class ThreadDatabase extends Database {
|
||||||
|
|
||||||
|
@ -102,7 +103,7 @@ public class ThreadDatabase extends Database {
|
||||||
public static final String LAST_SEEN = "last_seen";
|
public static final String LAST_SEEN = "last_seen";
|
||||||
public static final String HAS_SENT = "has_sent";
|
public static final String HAS_SENT = "has_sent";
|
||||||
private static final String LAST_SCROLLED = "last_scrolled";
|
private static final String LAST_SCROLLED = "last_scrolled";
|
||||||
private static final String PINNED = "pinned";
|
static final String PINNED = "pinned";
|
||||||
|
|
||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
DATE + " INTEGER DEFAULT 0, " +
|
DATE + " INTEGER DEFAULT 0, " +
|
||||||
|
@ -405,6 +406,7 @@ public class ThreadDatabase extends Database {
|
||||||
|
|
||||||
List<MarkedMessageInfo> smsRecords = new LinkedList<>();
|
List<MarkedMessageInfo> smsRecords = new LinkedList<>();
|
||||||
List<MarkedMessageInfo> mmsRecords = new LinkedList<>();
|
List<MarkedMessageInfo> mmsRecords = new LinkedList<>();
|
||||||
|
boolean needsSync = false;
|
||||||
|
|
||||||
db.beginTransaction();
|
db.beginTransaction();
|
||||||
|
|
||||||
|
@ -417,6 +419,8 @@ public class ThreadDatabase extends Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (long threadId : threadIds) {
|
for (long threadId : threadIds) {
|
||||||
|
ThreadRecord previous = getThreadRecord(threadId);
|
||||||
|
|
||||||
smsRecords.addAll(DatabaseFactory.getSmsDatabase(context).setMessagesReadSince(threadId, sinceTimestamp));
|
smsRecords.addAll(DatabaseFactory.getSmsDatabase(context).setMessagesReadSince(threadId, sinceTimestamp));
|
||||||
mmsRecords.addAll(DatabaseFactory.getMmsDatabase(context).setMessagesReadSince(threadId, sinceTimestamp));
|
mmsRecords.addAll(DatabaseFactory.getMmsDatabase(context).setMessagesReadSince(threadId, sinceTimestamp));
|
||||||
|
|
||||||
|
@ -427,7 +431,12 @@ public class ThreadDatabase extends Database {
|
||||||
|
|
||||||
contentValues.put(UNREAD_COUNT, unreadCount);
|
contentValues.put(UNREAD_COUNT, unreadCount);
|
||||||
|
|
||||||
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[]{threadId + ""});
|
db.update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(threadId));
|
||||||
|
|
||||||
|
if (previous != null && previous.isForcedUnread()) {
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(previous.getRecipient().getId());
|
||||||
|
needsSync = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
|
@ -437,6 +446,11 @@ public class ThreadDatabase extends Database {
|
||||||
|
|
||||||
notifyConversationListeners(new HashSet<>(threadIds));
|
notifyConversationListeners(new HashSet<>(threadIds));
|
||||||
notifyConversationListListeners();
|
notifyConversationListListeners();
|
||||||
|
|
||||||
|
if (needsSync) {
|
||||||
|
StorageSyncHelper.scheduleSyncForDataChange();
|
||||||
|
}
|
||||||
|
|
||||||
return Util.concatenatedList(smsRecords, mmsRecords);
|
return Util.concatenatedList(smsRecords, mmsRecords);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,19 +459,22 @@ public class ThreadDatabase extends Database {
|
||||||
|
|
||||||
db.beginTransaction();
|
db.beginTransaction();
|
||||||
try {
|
try {
|
||||||
ContentValues contentValues = new ContentValues();
|
List<RecipientId> recipientIds = getRecipientIdsForThreadIds(threadIds);
|
||||||
|
SqlUtil.Query query = SqlUtil.buildCollectionQuery(ID, threadIds);
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
|
||||||
contentValues.put(READ, ReadStatus.FORCED_UNREAD.serialize());
|
contentValues.put(READ, ReadStatus.FORCED_UNREAD.serialize());
|
||||||
|
|
||||||
for (long threadId : threadIds) {
|
db.update(TABLE_NAME, contentValues, query.getWhere(), query.getWhereArgs());
|
||||||
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] { String.valueOf(threadId) });
|
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(recipientIds);
|
||||||
}
|
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
|
||||||
|
|
||||||
notifyConversationListListeners();
|
StorageSyncHelper.scheduleSyncForDataChange();
|
||||||
|
notifyConversationListListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -949,6 +966,20 @@ public class ThreadDatabase extends Database {
|
||||||
return Recipient.resolved(id);
|
return Recipient.resolved(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NonNull List<RecipientId> getRecipientIdsForThreadIds(Collection<Long> threadIds) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
|
SqlUtil.Query query = SqlUtil.buildCollectionQuery(ID, threadIds);
|
||||||
|
List<RecipientId> ids = new ArrayList<>(threadIds.size());
|
||||||
|
|
||||||
|
try (Cursor cursor = db.query(TABLE_NAME, new String[] { RECIPIENT_ID }, query.getWhere(), query.getWhereArgs(), null, null, null)) {
|
||||||
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
|
ids.add(RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasThread(@NonNull RecipientId recipientId) {
|
public boolean hasThread(@NonNull RecipientId recipientId) {
|
||||||
return getThreadIdIfExistsFor(recipientId) > -1;
|
return getThreadIdIfExistsFor(recipientId) > -1;
|
||||||
}
|
}
|
||||||
|
@ -964,16 +995,56 @@ public class ThreadDatabase extends Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateReadState(long threadId) {
|
void updateReadState(long threadId) {
|
||||||
int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId);
|
ThreadRecord previous = getThreadRecord(threadId);
|
||||||
|
int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId);
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
contentValues.put(READ, unreadCount == 0);
|
contentValues.put(READ, unreadCount == 0 ? ReadStatus.READ.serialize() : ReadStatus.UNREAD.serialize());
|
||||||
contentValues.put(UNREAD_COUNT, unreadCount);
|
contentValues.put(UNREAD_COUNT, unreadCount);
|
||||||
|
|
||||||
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,ID_WHERE,
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(threadId));
|
||||||
new String[] {String.valueOf(threadId)});
|
|
||||||
|
|
||||||
notifyConversationListListeners();
|
notifyConversationListListeners();
|
||||||
|
|
||||||
|
if (previous != null && previous.isForcedUnread()) {
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(previous.getRecipient().getId());
|
||||||
|
StorageSyncHelper.scheduleSyncForDataChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyStorageSyncUpdate(@NonNull RecipientId recipientId, @NonNull SignalContactRecord record) {
|
||||||
|
applyStorageSyncUpdate(recipientId, record.isArchived(), record.isForcedUnread());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyStorageSyncUpdate(@NonNull RecipientId recipientId, @NonNull SignalGroupV1Record record) {
|
||||||
|
applyStorageSyncUpdate(recipientId, record.isArchived(), record.isForcedUnread());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyStorageSyncUpdate(@NonNull RecipientId recipientId, @NonNull SignalGroupV2Record record) {
|
||||||
|
applyStorageSyncUpdate(recipientId, record.isArchived(), record.isForcedUnread());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyStorageSyncUpdate(@NonNull RecipientId recipientId, @NonNull SignalAccountRecord record) {
|
||||||
|
applyStorageSyncUpdate(recipientId, record.isNoteToSelfArchived(), record.isNoteToSelfForcedUnread());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyStorageSyncUpdate(@NonNull RecipientId recipientId, boolean archived, boolean forcedUnread) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(ARCHIVED, archived);
|
||||||
|
|
||||||
|
if (forcedUnread) {
|
||||||
|
values.put(READ, ReadStatus.FORCED_UNREAD.serialize());
|
||||||
|
} else {
|
||||||
|
Long threadId = getThreadIdFor(recipientId);
|
||||||
|
if (threadId != null) {
|
||||||
|
int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId);
|
||||||
|
|
||||||
|
values.put(READ, unreadCount == 0 ? ReadStatus.READ.serialize() : ReadStatus.UNREAD.serialize());
|
||||||
|
values.put(UNREAD_COUNT, unreadCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, values, RECIPIENT_ID + " = ?", SqlUtil.buildArgs(recipientId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean update(long threadId, boolean unarchive) {
|
public boolean update(long threadId, boolean unarchive) {
|
||||||
|
@ -1404,7 +1475,7 @@ public class ThreadDatabase extends Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ReadStatus {
|
enum ReadStatus {
|
||||||
READ(1), UNREAD(0), FORCED_UNREAD(2);
|
READ(1), UNREAD(0), FORCED_UNREAD(2);
|
||||||
|
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
|
@ -183,6 +183,8 @@ public class StorageSyncJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteManifestVersion = writeOperationResult.getManifest().getVersion();
|
remoteManifestVersion = writeOperationResult.getManifest().getVersion();
|
||||||
|
|
||||||
|
needsMultiDeviceSync = true;
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "[Remote Newer] After resolving the conflict, all changes are local. No remote writes needed.");
|
Log.i(TAG, "[Remote Newer] After resolving the conflict, all changes are local. No remote writes needed.");
|
||||||
}
|
}
|
||||||
|
@ -190,7 +192,6 @@ public class StorageSyncJob extends BaseJob {
|
||||||
recipientDatabase.applyStorageSyncUpdates(mergeResult.getLocalContactInserts(), mergeResult.getLocalContactUpdates(), mergeResult.getLocalGroupV1Inserts(), mergeResult.getLocalGroupV1Updates(), mergeResult.getLocalGroupV2Inserts(), mergeResult.getLocalGroupV2Updates());
|
recipientDatabase.applyStorageSyncUpdates(mergeResult.getLocalContactInserts(), mergeResult.getLocalContactUpdates(), mergeResult.getLocalGroupV1Inserts(), mergeResult.getLocalGroupV1Updates(), mergeResult.getLocalGroupV2Inserts(), mergeResult.getLocalGroupV2Updates());
|
||||||
storageKeyDatabase.applyStorageSyncUpdates(mergeResult.getLocalUnknownInserts(), mergeResult.getLocalUnknownDeletes());
|
storageKeyDatabase.applyStorageSyncUpdates(mergeResult.getLocalUnknownInserts(), mergeResult.getLocalUnknownDeletes());
|
||||||
StorageSyncHelper.applyAccountStorageSyncUpdates(context, mergeResult.getLocalAccountUpdate());
|
StorageSyncHelper.applyAccountStorageSyncUpdates(context, mergeResult.getLocalAccountUpdate());
|
||||||
needsMultiDeviceSync = true;
|
|
||||||
|
|
||||||
Log.i(TAG, "[Remote Newer] Updating local manifest version to: " + remoteManifestVersion);
|
Log.i(TAG, "[Remote Newer] Updating local manifest version to: " + remoteManifestVersion);
|
||||||
TextSecurePreferences.setStorageManifestVersion(context, remoteManifestVersion);
|
TextSecurePreferences.setStorageManifestVersion(context, remoteManifestVersion);
|
||||||
|
|
|
@ -60,14 +60,15 @@ class AccountConflictMerger implements StorageSyncHelper.ConflictMerger<SignalAc
|
||||||
String avatarUrlPath = remote.getAvatarUrlPath().or(local.getAvatarUrlPath()).or("");
|
String avatarUrlPath = remote.getAvatarUrlPath().or(local.getAvatarUrlPath()).or("");
|
||||||
byte[] profileKey = remote.getProfileKey().or(local.getProfileKey()).orNull();
|
byte[] profileKey = remote.getProfileKey().or(local.getProfileKey()).orNull();
|
||||||
boolean noteToSelfArchived = remote.isNoteToSelfArchived();
|
boolean noteToSelfArchived = remote.isNoteToSelfArchived();
|
||||||
|
boolean noteToSelfForcedUnread = remote.isNoteToSelfForcedUnread();
|
||||||
boolean readReceipts = remote.isReadReceiptsEnabled();
|
boolean readReceipts = remote.isReadReceiptsEnabled();
|
||||||
boolean typingIndicators = remote.isTypingIndicatorsEnabled();
|
boolean typingIndicators = remote.isTypingIndicatorsEnabled();
|
||||||
boolean sealedSenderIndicators = remote.isSealedSenderIndicatorsEnabled();
|
boolean sealedSenderIndicators = remote.isSealedSenderIndicatorsEnabled();
|
||||||
boolean linkPreviews = remote.isLinkPreviewsEnabled();
|
boolean linkPreviews = remote.isLinkPreviewsEnabled();
|
||||||
boolean unlisted = remote.isPhoneNumberUnlisted();
|
boolean unlisted = remote.isPhoneNumberUnlisted();
|
||||||
AccountRecord.PhoneNumberSharingMode phoneNumberSharingMode = remote.getPhoneNumberSharingMode();
|
AccountRecord.PhoneNumberSharingMode phoneNumberSharingMode = remote.getPhoneNumberSharingMode();
|
||||||
boolean matchesRemote = doParamsMatch(remote, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted);
|
boolean matchesRemote = doParamsMatch(remote, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, noteToSelfForcedUnread, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted);
|
||||||
boolean matchesLocal = doParamsMatch(local, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted );
|
boolean matchesLocal = doParamsMatch(local, unknownFields, givenName, familyName, avatarUrlPath, profileKey, noteToSelfArchived, noteToSelfForcedUnread, readReceipts, typingIndicators, sealedSenderIndicators, linkPreviews, phoneNumberSharingMode, unlisted );
|
||||||
|
|
||||||
if (matchesRemote) {
|
if (matchesRemote) {
|
||||||
return remote;
|
return remote;
|
||||||
|
@ -81,6 +82,7 @@ class AccountConflictMerger implements StorageSyncHelper.ConflictMerger<SignalAc
|
||||||
.setAvatarUrlPath(avatarUrlPath)
|
.setAvatarUrlPath(avatarUrlPath)
|
||||||
.setProfileKey(profileKey)
|
.setProfileKey(profileKey)
|
||||||
.setNoteToSelfArchived(noteToSelfArchived)
|
.setNoteToSelfArchived(noteToSelfArchived)
|
||||||
|
.setNoteToSelfForcedUnread(noteToSelfForcedUnread)
|
||||||
.setReadReceiptsEnabled(readReceipts)
|
.setReadReceiptsEnabled(readReceipts)
|
||||||
.setTypingIndicatorsEnabled(typingIndicators)
|
.setTypingIndicatorsEnabled(typingIndicators)
|
||||||
.setSealedSenderIndicatorsEnabled(sealedSenderIndicators)
|
.setSealedSenderIndicatorsEnabled(sealedSenderIndicators)
|
||||||
|
@ -99,6 +101,7 @@ class AccountConflictMerger implements StorageSyncHelper.ConflictMerger<SignalAc
|
||||||
@NonNull String avatarUrlPath,
|
@NonNull String avatarUrlPath,
|
||||||
@Nullable byte[] profileKey,
|
@Nullable byte[] profileKey,
|
||||||
boolean noteToSelfArchived,
|
boolean noteToSelfArchived,
|
||||||
|
boolean noteToSelfForcedUnread,
|
||||||
boolean readReceipts,
|
boolean readReceipts,
|
||||||
boolean typingIndicators,
|
boolean typingIndicators,
|
||||||
boolean sealedSenderIndicators,
|
boolean sealedSenderIndicators,
|
||||||
|
@ -112,6 +115,7 @@ class AccountConflictMerger implements StorageSyncHelper.ConflictMerger<SignalAc
|
||||||
Objects.equals(contact.getAvatarUrlPath().or(""), avatarUrlPath) &&
|
Objects.equals(contact.getAvatarUrlPath().or(""), avatarUrlPath) &&
|
||||||
Arrays.equals(contact.getProfileKey().orNull(), profileKey) &&
|
Arrays.equals(contact.getProfileKey().orNull(), profileKey) &&
|
||||||
contact.isNoteToSelfArchived() == noteToSelfArchived &&
|
contact.isNoteToSelfArchived() == noteToSelfArchived &&
|
||||||
|
contact.isNoteToSelfForcedUnread() == noteToSelfForcedUnread &&
|
||||||
contact.isReadReceiptsEnabled() == readReceipts &&
|
contact.isReadReceiptsEnabled() == readReceipts &&
|
||||||
contact.isTypingIndicatorsEnabled() == typingIndicators &&
|
contact.isTypingIndicatorsEnabled() == typingIndicators &&
|
||||||
contact.isSealedSenderIndicatorsEnabled() == sealedSenderIndicators &&
|
contact.isSealedSenderIndicatorsEnabled() == sealedSenderIndicators &&
|
||||||
|
|
|
@ -86,8 +86,9 @@ class ContactConflictMerger implements StorageSyncHelper.ConflictMerger<SignalCo
|
||||||
boolean blocked = remote.isBlocked();
|
boolean blocked = remote.isBlocked();
|
||||||
boolean profileSharing = remote.isProfileSharingEnabled();
|
boolean profileSharing = remote.isProfileSharingEnabled();
|
||||||
boolean archived = remote.isArchived();
|
boolean archived = remote.isArchived();
|
||||||
boolean matchesRemote = doParamsMatch(remote, unknownFields, address, givenName, familyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived);
|
boolean forcedUnread = remote.isForcedUnread();
|
||||||
boolean matchesLocal = doParamsMatch(local, unknownFields, address, givenName, familyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived);
|
boolean matchesRemote = doParamsMatch(remote, unknownFields, address, givenName, familyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread);
|
||||||
|
boolean matchesLocal = doParamsMatch(local, unknownFields, address, givenName, familyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread);
|
||||||
|
|
||||||
if (matchesRemote) {
|
if (matchesRemote) {
|
||||||
return remote;
|
return remote;
|
||||||
|
@ -104,6 +105,7 @@ class ContactConflictMerger implements StorageSyncHelper.ConflictMerger<SignalCo
|
||||||
.setIdentityKey(identityKey)
|
.setIdentityKey(identityKey)
|
||||||
.setBlocked(blocked)
|
.setBlocked(blocked)
|
||||||
.setProfileSharingEnabled(profileSharing)
|
.setProfileSharingEnabled(profileSharing)
|
||||||
|
.setForcedUnread(forcedUnread)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +121,8 @@ class ContactConflictMerger implements StorageSyncHelper.ConflictMerger<SignalCo
|
||||||
@Nullable byte[] identityKey,
|
@Nullable byte[] identityKey,
|
||||||
boolean blocked,
|
boolean blocked,
|
||||||
boolean profileSharing,
|
boolean profileSharing,
|
||||||
boolean archived)
|
boolean archived,
|
||||||
|
boolean forcedUnread)
|
||||||
{
|
{
|
||||||
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
|
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
|
||||||
Objects.equals(contact.getAddress(), address) &&
|
Objects.equals(contact.getAddress(), address) &&
|
||||||
|
@ -131,6 +134,7 @@ class ContactConflictMerger implements StorageSyncHelper.ConflictMerger<SignalCo
|
||||||
Arrays.equals(contact.getIdentityKey().orNull(), identityKey) &&
|
Arrays.equals(contact.getIdentityKey().orNull(), identityKey) &&
|
||||||
contact.isBlocked() == blocked &&
|
contact.isBlocked() == blocked &&
|
||||||
contact.isProfileSharingEnabled() == profileSharing &&
|
contact.isProfileSharingEnabled() == profileSharing &&
|
||||||
contact.isArchived() == archived;
|
contact.isArchived() == archived &&
|
||||||
|
contact.isForcedUnread() == forcedUnread;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,10 @@ final class GroupV1ConflictMerger implements StorageSyncHelper.ConflictMerger<Si
|
||||||
boolean blocked = remote.isBlocked();
|
boolean blocked = remote.isBlocked();
|
||||||
boolean profileSharing = remote.isProfileSharingEnabled();
|
boolean profileSharing = remote.isProfileSharingEnabled();
|
||||||
boolean archived = remote.isArchived();
|
boolean archived = remote.isArchived();
|
||||||
|
boolean forcedUnread = remote.isForcedUnread();
|
||||||
|
|
||||||
boolean matchesRemote = Arrays.equals(unknownFields, remote.serializeUnknownFields()) && blocked == remote.isBlocked() && profileSharing == remote.isProfileSharingEnabled() && archived == remote.isArchived();
|
boolean matchesRemote = Arrays.equals(unknownFields, remote.serializeUnknownFields()) && blocked == remote.isBlocked() && profileSharing == remote.isProfileSharingEnabled() && archived == remote.isArchived() && forcedUnread == remote.isForcedUnread();
|
||||||
boolean matchesLocal = Arrays.equals(unknownFields, local.serializeUnknownFields()) && blocked == local.isBlocked() && profileSharing == local.isProfileSharingEnabled() && archived == local.isArchived();
|
boolean matchesLocal = Arrays.equals(unknownFields, local.serializeUnknownFields()) && blocked == local.isBlocked() && profileSharing == local.isProfileSharingEnabled() && archived == local.isArchived() && forcedUnread == local.isForcedUnread();
|
||||||
|
|
||||||
if (matchesRemote) {
|
if (matchesRemote) {
|
||||||
return remote;
|
return remote;
|
||||||
|
@ -53,6 +54,7 @@ final class GroupV1ConflictMerger implements StorageSyncHelper.ConflictMerger<Si
|
||||||
.setUnknownFields(unknownFields)
|
.setUnknownFields(unknownFields)
|
||||||
.setBlocked(blocked)
|
.setBlocked(blocked)
|
||||||
.setProfileSharingEnabled(blocked)
|
.setProfileSharingEnabled(blocked)
|
||||||
|
.setForcedUnread(forcedUnread)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,10 @@ final class GroupV2ConflictMerger implements StorageSyncHelper.ConflictMerger<Si
|
||||||
boolean blocked = remote.isBlocked();
|
boolean blocked = remote.isBlocked();
|
||||||
boolean profileSharing = remote.isProfileSharingEnabled();
|
boolean profileSharing = remote.isProfileSharingEnabled();
|
||||||
boolean archived = remote.isArchived();
|
boolean archived = remote.isArchived();
|
||||||
|
boolean forcedUnread = remote.isForcedUnread();
|
||||||
|
|
||||||
boolean matchesRemote = Arrays.equals(unknownFields, remote.serializeUnknownFields()) && blocked == remote.isBlocked() && profileSharing == remote.isProfileSharingEnabled() && archived == remote.isArchived();
|
boolean matchesRemote = Arrays.equals(unknownFields, remote.serializeUnknownFields()) && blocked == remote.isBlocked() && profileSharing == remote.isProfileSharingEnabled() && archived == remote.isArchived() && forcedUnread == remote.isForcedUnread();
|
||||||
boolean matchesLocal = Arrays.equals(unknownFields, local.serializeUnknownFields()) && blocked == local.isBlocked() && profileSharing == local.isProfileSharingEnabled() && archived == local.isArchived();
|
boolean matchesLocal = Arrays.equals(unknownFields, local.serializeUnknownFields()) && blocked == local.isBlocked() && profileSharing == local.isProfileSharingEnabled() && archived == local.isArchived() && forcedUnread == local.isForcedUnread();
|
||||||
|
|
||||||
if (matchesRemote) {
|
if (matchesRemote) {
|
||||||
return remote;
|
return remote;
|
||||||
|
@ -53,6 +54,8 @@ final class GroupV2ConflictMerger implements StorageSyncHelper.ConflictMerger<Si
|
||||||
.setUnknownFields(unknownFields)
|
.setUnknownFields(unknownFields)
|
||||||
.setBlocked(blocked)
|
.setBlocked(blocked)
|
||||||
.setProfileSharingEnabled(blocked)
|
.setProfileSharingEnabled(blocked)
|
||||||
|
.setArchived(archived)
|
||||||
|
.setForcedUnread(forcedUnread)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.annimon.stream.Stream;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
|
||||||
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||||
import org.thoughtcrime.securesms.jobs.StorageSyncJob;
|
import org.thoughtcrime.securesms.jobs.StorageSyncJob;
|
||||||
|
@ -417,7 +418,8 @@ public final class StorageSyncHelper {
|
||||||
.setGivenName(self.getProfileName().getGivenName())
|
.setGivenName(self.getProfileName().getGivenName())
|
||||||
.setFamilyName(self.getProfileName().getFamilyName())
|
.setFamilyName(self.getProfileName().getFamilyName())
|
||||||
.setAvatarUrlPath(self.getProfileAvatar())
|
.setAvatarUrlPath(self.getProfileAvatar())
|
||||||
.setNoteToSelfArchived(DatabaseFactory.getThreadDatabase(context).isArchived(self.getId()))
|
.setNoteToSelfArchived(settings != null && settings.getSyncExtras().isArchived())
|
||||||
|
.setNoteToSelfForcedUnread(settings != null && settings.getSyncExtras().isForcedUnread())
|
||||||
.setTypingIndicatorsEnabled(TextSecurePreferences.isTypingIndicatorsEnabled(context))
|
.setTypingIndicatorsEnabled(TextSecurePreferences.isTypingIndicatorsEnabled(context))
|
||||||
.setReadReceiptsEnabled(TextSecurePreferences.isReadReceiptsEnabled(context))
|
.setReadReceiptsEnabled(TextSecurePreferences.isReadReceiptsEnabled(context))
|
||||||
.setSealedSenderIndicatorsEnabled(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context))
|
.setSealedSenderIndicatorsEnabled(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context))
|
||||||
|
@ -447,7 +449,6 @@ public final class StorageSyncHelper {
|
||||||
|
|
||||||
public static void applyAccountStorageSyncUpdates(@NonNull Context context, @NonNull StorageId storageId, @NonNull SignalAccountRecord update, boolean fetchProfile) {
|
public static void applyAccountStorageSyncUpdates(@NonNull Context context, @NonNull StorageId storageId, @NonNull SignalAccountRecord update, boolean fetchProfile) {
|
||||||
DatabaseFactory.getRecipientDatabase(context).applyStorageSyncUpdates(storageId, update);
|
DatabaseFactory.getRecipientDatabase(context).applyStorageSyncUpdates(storageId, update);
|
||||||
DatabaseFactory.getThreadDatabase(context).setArchived(Recipient.self().getId(), update.isNoteToSelfArchived());
|
|
||||||
|
|
||||||
TextSecurePreferences.setReadReceiptsEnabled(context, update.isReadReceiptsEnabled());
|
TextSecurePreferences.setReadReceiptsEnabled(context, update.isReadReceiptsEnabled());
|
||||||
TextSecurePreferences.setTypingIndicatorsEnabled(context, update.isTypingIndicatorsEnabled());
|
TextSecurePreferences.setTypingIndicatorsEnabled(context, update.isTypingIndicatorsEnabled());
|
||||||
|
|
|
@ -52,6 +52,7 @@ public final class StorageSyncModels {
|
||||||
.setIdentityKey(recipient.getSyncExtras().getIdentityKey())
|
.setIdentityKey(recipient.getSyncExtras().getIdentityKey())
|
||||||
.setIdentityState(localToRemoteIdentityState(recipient.getSyncExtras().getIdentityStatus()))
|
.setIdentityState(localToRemoteIdentityState(recipient.getSyncExtras().getIdentityStatus()))
|
||||||
.setArchived(recipient.getSyncExtras().isArchived())
|
.setArchived(recipient.getSyncExtras().isArchived())
|
||||||
|
.setForcedUnread(recipient.getSyncExtras().isForcedUnread())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +72,7 @@ public final class StorageSyncModels {
|
||||||
.setBlocked(recipient.isBlocked())
|
.setBlocked(recipient.isBlocked())
|
||||||
.setProfileSharingEnabled(recipient.isProfileSharing())
|
.setProfileSharingEnabled(recipient.isProfileSharing())
|
||||||
.setArchived(recipient.getSyncExtras().isArchived())
|
.setArchived(recipient.getSyncExtras().isArchived())
|
||||||
|
.setForcedUnread(recipient.getSyncExtras().isForcedUnread())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +98,7 @@ public final class StorageSyncModels {
|
||||||
.setBlocked(recipient.isBlocked())
|
.setBlocked(recipient.isBlocked())
|
||||||
.setProfileSharingEnabled(recipient.isProfileSharing())
|
.setProfileSharingEnabled(recipient.isProfileSharing())
|
||||||
.setArchived(recipient.getSyncExtras().isArchived())
|
.setArchived(recipient.getSyncExtras().isArchived())
|
||||||
|
.setForcedUnread(recipient.getSyncExtras().isForcedUnread())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,13 @@ import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import net.sqlcipher.database.SQLiteDatabase;
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Preconditions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -63,9 +65,9 @@ public final class SqlUtil {
|
||||||
* change. In other words, if {@link SQLiteDatabase#update(String, ContentValues, String, String[])}
|
* change. In other words, if {@link SQLiteDatabase#update(String, ContentValues, String, String[])}
|
||||||
* returns > 0, then you know something *actually* changed.
|
* returns > 0, then you know something *actually* changed.
|
||||||
*/
|
*/
|
||||||
public static @NonNull UpdateQuery buildTrueUpdateQuery(@NonNull String selection,
|
public static @NonNull Query buildTrueUpdateQuery(@NonNull String selection,
|
||||||
@NonNull String[] args,
|
@NonNull String[] args,
|
||||||
@NonNull ContentValues contentValues)
|
@NonNull ContentValues contentValues)
|
||||||
{
|
{
|
||||||
StringBuilder qualifier = new StringBuilder();
|
StringBuilder qualifier = new StringBuilder();
|
||||||
Set<Map.Entry<String, Object>> valueSet = contentValues.valueSet();
|
Set<Map.Entry<String, Object>> valueSet = contentValues.valueSet();
|
||||||
|
@ -90,7 +92,29 @@ public final class SqlUtil {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UpdateQuery("(" + selection + ") AND (" + qualifier + ")", fullArgs.toArray(new String[0]));
|
return new Query("(" + selection + ") AND (" + qualifier + ")", fullArgs.toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NonNull Query buildCollectionQuery(@NonNull String column, @NonNull Collection<? extends Object> values) {
|
||||||
|
Preconditions.checkArgument(values.size() > 0);
|
||||||
|
|
||||||
|
StringBuilder query = new StringBuilder();
|
||||||
|
Object[] args = new Object[values.size()];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (Object value : values) {
|
||||||
|
query.append("?");
|
||||||
|
args[i] = value;
|
||||||
|
|
||||||
|
if (i != values.size() - 1) {
|
||||||
|
query.append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Query(column + " IN (" + query.toString() + ")", buildArgs(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String[] appendArg(@NonNull String[] args, String addition) {
|
public static String[] appendArg(@NonNull String[] args, String addition) {
|
||||||
|
@ -102,11 +126,11 @@ public final class SqlUtil {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UpdateQuery {
|
public static class Query {
|
||||||
private final String where;
|
private final String where;
|
||||||
private final String[] whereArgs;
|
private final String[] whereArgs;
|
||||||
|
|
||||||
private UpdateQuery(@NonNull String where, @NonNull String[] whereArgs) {
|
private Query(@NonNull String where, @NonNull String[] whereArgs) {
|
||||||
this.where = where;
|
this.where = where;
|
||||||
this.whereArgs = whereArgs;
|
this.whereArgs = whereArgs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ public class ContactConflictMergerTest {
|
||||||
.setUsername("username A")
|
.setUsername("username A")
|
||||||
.setProfileSharingEnabled(false)
|
.setProfileSharingEnabled(false)
|
||||||
.setArchived(false)
|
.setArchived(false)
|
||||||
|
.setForcedUnread(false)
|
||||||
.build();
|
.build();
|
||||||
SignalContactRecord local = new SignalContactRecord.Builder(byteArray(2), new SignalServiceAddress(UUID_B, E164_B))
|
SignalContactRecord local = new SignalContactRecord.Builder(byteArray(2), new SignalServiceAddress(UUID_B, E164_B))
|
||||||
.setBlocked(false)
|
.setBlocked(false)
|
||||||
|
@ -66,6 +67,7 @@ public class ContactConflictMergerTest {
|
||||||
.setUsername("username B")
|
.setUsername("username B")
|
||||||
.setProfileSharingEnabled(true)
|
.setProfileSharingEnabled(true)
|
||||||
.setArchived(true)
|
.setArchived(true)
|
||||||
|
.setForcedUnread(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SignalContactRecord merged = new ContactConflictMerger(Collections.singletonList(local), SELF).merge(remote, local, mock(KeyGenerator.class));
|
SignalContactRecord merged = new ContactConflictMerger(Collections.singletonList(local), SELF).merge(remote, local, mock(KeyGenerator.class));
|
||||||
|
@ -81,6 +83,7 @@ public class ContactConflictMergerTest {
|
||||||
assertEquals("username A", merged.getUsername().get());
|
assertEquals("username A", merged.getUsername().get());
|
||||||
assertFalse(merged.isProfileSharingEnabled());
|
assertFalse(merged.isProfileSharingEnabled());
|
||||||
assertFalse(merged.isArchived());
|
assertFalse(merged.isArchived());
|
||||||
|
assertFalse(merged.isForcedUnread());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -30,11 +30,13 @@ public final class GroupV1ConflictMergerTest {
|
||||||
.setBlocked(false)
|
.setBlocked(false)
|
||||||
.setProfileSharingEnabled(false)
|
.setProfileSharingEnabled(false)
|
||||||
.setArchived(false)
|
.setArchived(false)
|
||||||
|
.setForcedUnread(false)
|
||||||
.build();
|
.build();
|
||||||
SignalGroupV1Record local = new SignalGroupV1Record.Builder(byteArray(2), byteArray(100))
|
SignalGroupV1Record local = new SignalGroupV1Record.Builder(byteArray(2), byteArray(100))
|
||||||
.setBlocked(true)
|
.setBlocked(true)
|
||||||
.setProfileSharingEnabled(true)
|
.setProfileSharingEnabled(true)
|
||||||
.setArchived(true)
|
.setArchived(true)
|
||||||
|
.setForcedUnread(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SignalGroupV1Record merged = new GroupV1ConflictMerger(Collections.singletonList(local)).merge(remote, local, KEY_GENERATOR);
|
SignalGroupV1Record merged = new GroupV1ConflictMerger(Collections.singletonList(local)).merge(remote, local, KEY_GENERATOR);
|
||||||
|
@ -44,6 +46,7 @@ public final class GroupV1ConflictMergerTest {
|
||||||
assertFalse(merged.isProfileSharingEnabled());
|
assertFalse(merged.isProfileSharingEnabled());
|
||||||
assertFalse(merged.isBlocked());
|
assertFalse(merged.isBlocked());
|
||||||
assertFalse(merged.isArchived());
|
assertFalse(merged.isArchived());
|
||||||
|
assertFalse(merged.isForcedUnread());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -30,11 +30,13 @@ public final class GroupV2ConflictMergerTest {
|
||||||
.setBlocked(false)
|
.setBlocked(false)
|
||||||
.setProfileSharingEnabled(false)
|
.setProfileSharingEnabled(false)
|
||||||
.setArchived(false)
|
.setArchived(false)
|
||||||
|
.setForcedUnread(false)
|
||||||
.build();
|
.build();
|
||||||
SignalGroupV2Record local = new SignalGroupV2Record.Builder(byteArray(2), groupKey(100))
|
SignalGroupV2Record local = new SignalGroupV2Record.Builder(byteArray(2), groupKey(100))
|
||||||
.setBlocked(true)
|
.setBlocked(true)
|
||||||
.setProfileSharingEnabled(true)
|
.setProfileSharingEnabled(true)
|
||||||
.setArchived(true)
|
.setArchived(true)
|
||||||
|
.setForcedUnread(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SignalGroupV2Record merged = new GroupV2ConflictMerger(Collections.singletonList(local)).merge(remote, local, KEY_GENERATOR);
|
SignalGroupV2Record merged = new GroupV2ConflictMerger(Collections.singletonList(local)).merge(remote, local, KEY_GENERATOR);
|
||||||
|
@ -44,6 +46,7 @@ public final class GroupV2ConflictMergerTest {
|
||||||
assertFalse(merged.isProfileSharingEnabled());
|
assertFalse(merged.isProfileSharingEnabled());
|
||||||
assertFalse(merged.isBlocked());
|
assertFalse(merged.isBlocked());
|
||||||
assertFalse(merged.isArchived());
|
assertFalse(merged.isArchived());
|
||||||
|
assertFalse(merged.isForcedUnread());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -8,6 +8,10 @@ import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import edu.emory.mathcs.backport.java.util.Collections;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@ -23,7 +27,7 @@ public final class SqlUtilTest {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put("a", 2);
|
values.put("a", 2);
|
||||||
|
|
||||||
SqlUtil.UpdateQuery updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
SqlUtil.Query updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||||
|
|
||||||
assertEquals("(_id = ?) AND (a != ? OR a IS NULL)", updateQuery.getWhere());
|
assertEquals("(_id = ?) AND (a != ? OR a IS NULL)", updateQuery.getWhere());
|
||||||
assertArrayEquals(new String[] { "1", "2" }, updateQuery.getWhereArgs());
|
assertArrayEquals(new String[] { "1", "2" }, updateQuery.getWhereArgs());
|
||||||
|
@ -37,7 +41,7 @@ public final class SqlUtilTest {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put("a", 4);
|
values.put("a", 4);
|
||||||
|
|
||||||
SqlUtil.UpdateQuery updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
SqlUtil.Query updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||||
|
|
||||||
assertEquals("(_id = ? AND (foo = ? OR bar != ?)) AND (a != ? OR a IS NULL)", updateQuery.getWhere());
|
assertEquals("(_id = ? AND (foo = ? OR bar != ?)) AND (a != ? OR a IS NULL)", updateQuery.getWhere());
|
||||||
assertArrayEquals(new String[] { "1", "2", "3", "4" }, updateQuery.getWhereArgs());
|
assertArrayEquals(new String[] { "1", "2", "3", "4" }, updateQuery.getWhereArgs());
|
||||||
|
@ -53,7 +57,7 @@ public final class SqlUtilTest {
|
||||||
values.put("b", 3);
|
values.put("b", 3);
|
||||||
values.put("c", 4);
|
values.put("c", 4);
|
||||||
|
|
||||||
SqlUtil.UpdateQuery updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
SqlUtil.Query updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||||
|
|
||||||
assertEquals("(_id = ?) AND (a != ? OR a IS NULL OR b != ? OR b IS NULL OR c != ? OR c IS NULL)", updateQuery.getWhere());
|
assertEquals("(_id = ?) AND (a != ? OR a IS NULL OR b != ? OR b IS NULL OR c != ? OR c IS NULL)", updateQuery.getWhere());
|
||||||
assertArrayEquals(new String[] { "1", "2", "3", "4"}, updateQuery.getWhereArgs());
|
assertArrayEquals(new String[] { "1", "2", "3", "4"}, updateQuery.getWhereArgs());
|
||||||
|
@ -67,7 +71,7 @@ public final class SqlUtilTest {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put("a", (String) null);
|
values.put("a", (String) null);
|
||||||
|
|
||||||
SqlUtil.UpdateQuery updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
SqlUtil.Query updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||||
|
|
||||||
assertEquals("(_id = ?) AND (a NOT NULL)", updateQuery.getWhere());
|
assertEquals("(_id = ?) AND (a NOT NULL)", updateQuery.getWhere());
|
||||||
assertArrayEquals(new String[] { "1" }, updateQuery.getWhereArgs());
|
assertArrayEquals(new String[] { "1" }, updateQuery.getWhereArgs());
|
||||||
|
@ -85,9 +89,30 @@ public final class SqlUtilTest {
|
||||||
values.put("d", (String) null);
|
values.put("d", (String) null);
|
||||||
values.put("e", (String) null);
|
values.put("e", (String) null);
|
||||||
|
|
||||||
SqlUtil.UpdateQuery updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
SqlUtil.Query updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||||
|
|
||||||
assertEquals("(_id = ?) AND (a NOT NULL OR b != ? OR b IS NULL OR c != ? OR c IS NULL OR d NOT NULL OR e NOT NULL)", updateQuery.getWhere());
|
assertEquals("(_id = ?) AND (a NOT NULL OR b != ? OR b IS NULL OR c != ? OR c IS NULL OR d NOT NULL OR e NOT NULL)", updateQuery.getWhere());
|
||||||
assertArrayEquals(new String[] { "1", "2", "3" }, updateQuery.getWhereArgs());
|
assertArrayEquals(new String[] { "1", "2", "3" }, updateQuery.getWhereArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildCollectionQuery_single() {
|
||||||
|
SqlUtil.Query updateQuery = SqlUtil.buildCollectionQuery("a", Arrays.asList(1));
|
||||||
|
|
||||||
|
assertEquals("a IN (?)", updateQuery.getWhere());
|
||||||
|
assertArrayEquals(new String[] { "1" }, updateQuery.getWhereArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildCollectionQuery_multiple() {
|
||||||
|
SqlUtil.Query updateQuery = SqlUtil.buildCollectionQuery("a", Arrays.asList(1, 2, 3));
|
||||||
|
|
||||||
|
assertEquals("a IN (?, ?, ?)", updateQuery.getWhere());
|
||||||
|
assertArrayEquals(new String[] { "1", "2", "3" }, updateQuery.getWhereArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void buildCollectionQuery_none() {
|
||||||
|
SqlUtil.buildCollectionQuery("a", Collections.emptyList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,10 @@ public final class SignalAccountRecord implements SignalRecord {
|
||||||
return proto.getNoteToSelfArchived();
|
return proto.getNoteToSelfArchived();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNoteToSelfForcedUnread() {
|
||||||
|
return proto.getNoteToSelfMarkedUnread();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isReadReceiptsEnabled() {
|
public boolean isReadReceiptsEnabled() {
|
||||||
return proto.getReadReceipts();
|
return proto.getReadReceipts();
|
||||||
}
|
}
|
||||||
|
@ -147,6 +151,11 @@ public final class SignalAccountRecord implements SignalRecord {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setNoteToSelfForcedUnread(boolean forcedUnread) {
|
||||||
|
builder.setNoteToSelfMarkedUnread(forcedUnread);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setReadReceiptsEnabled(boolean enabled) {
|
public Builder setReadReceiptsEnabled(boolean enabled) {
|
||||||
builder.setReadReceipts(enabled);
|
builder.setReadReceipts(enabled);
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -91,6 +91,10 @@ public final class SignalContactRecord implements SignalRecord {
|
||||||
return proto.getArchived();
|
return proto.getArchived();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForcedUnread() {
|
||||||
|
return proto.getMarkedUnread();
|
||||||
|
}
|
||||||
|
|
||||||
ContactRecord toProto() {
|
ContactRecord toProto() {
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
@ -173,6 +177,11 @@ public final class SignalContactRecord implements SignalRecord {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setForcedUnread(boolean forcedUnread) {
|
||||||
|
builder.setMarkedUnread(forcedUnread);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SignalContactRecord build() {
|
public SignalContactRecord build() {
|
||||||
ContactRecord proto = builder.build();
|
ContactRecord proto = builder.build();
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,10 @@ public final class SignalGroupV1Record implements SignalRecord {
|
||||||
return proto.getArchived();
|
return proto.getArchived();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForcedUnread() {
|
||||||
|
return proto.getMarkedUnread();
|
||||||
|
}
|
||||||
|
|
||||||
GroupV1Record toProto() {
|
GroupV1Record toProto() {
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
@ -101,6 +105,11 @@ public final class SignalGroupV1Record implements SignalRecord {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setForcedUnread(boolean forcedUnread) {
|
||||||
|
builder.setMarkedUnread(forcedUnread);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SignalGroupV1Record build() {
|
public SignalGroupV1Record build() {
|
||||||
GroupV1Record proto = builder.build();
|
GroupV1Record proto = builder.build();
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,10 @@ public final class SignalGroupV2Record implements SignalRecord {
|
||||||
return proto.getArchived();
|
return proto.getArchived();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForcedUnread() {
|
||||||
|
return proto.getMarkedUnread();
|
||||||
|
}
|
||||||
|
|
||||||
GroupV2Record toProto() {
|
GroupV2Record toProto() {
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
@ -115,6 +119,11 @@ public final class SignalGroupV2Record implements SignalRecord {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setForcedUnread(boolean forcedUnread) {
|
||||||
|
builder.setMarkedUnread(forcedUnread);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SignalGroupV2Record build() {
|
public SignalGroupV2Record build() {
|
||||||
GroupV2Record proto = builder.build();
|
GroupV2Record proto = builder.build();
|
||||||
|
|
||||||
|
|
|
@ -80,20 +80,23 @@ message ContactRecord {
|
||||||
bool blocked = 9;
|
bool blocked = 9;
|
||||||
bool whitelisted = 10;
|
bool whitelisted = 10;
|
||||||
bool archived = 11;
|
bool archived = 11;
|
||||||
|
bool markedUnread = 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupV1Record {
|
message GroupV1Record {
|
||||||
bytes id = 1;
|
bytes id = 1;
|
||||||
bool blocked = 2;
|
bool blocked = 2;
|
||||||
bool whitelisted = 3;
|
bool whitelisted = 3;
|
||||||
bool archived = 4;
|
bool archived = 4;
|
||||||
|
bool markedUnread = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupV2Record {
|
message GroupV2Record {
|
||||||
bytes masterKey = 1;
|
bytes masterKey = 1;
|
||||||
bool blocked = 2;
|
bool blocked = 2;
|
||||||
bool whitelisted = 3;
|
bool whitelisted = 3;
|
||||||
bool archived = 4;
|
bool archived = 4;
|
||||||
|
bool markedUnread = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AccountRecord {
|
message AccountRecord {
|
||||||
|
@ -113,7 +116,7 @@ message AccountRecord {
|
||||||
bool sealedSenderIndicators = 7;
|
bool sealedSenderIndicators = 7;
|
||||||
bool typingIndicators = 8;
|
bool typingIndicators = 8;
|
||||||
bool proxiedLinkPreviews = 9;
|
bool proxiedLinkPreviews = 9;
|
||||||
// 10 is reserved for unread
|
bool noteToSelfMarkedUnread = 10;
|
||||||
bool linkPreviews = 11;
|
bool linkPreviews = 11;
|
||||||
PhoneNumberSharingMode phoneNumberSharingMode = 12;
|
PhoneNumberSharingMode phoneNumberSharingMode = 12;
|
||||||
bool unlistedPhoneNumber = 13;
|
bool unlistedPhoneNumber = 13;
|
||||||
|
|
Loading…
Add table
Reference in a new issue