diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index ea416d26b..c1622e69a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -1283,6 +1283,7 @@ public class RecipientDatabase extends Database { values.put(USERNAME, TextUtils.isEmpty(username) ? null : username); values.put(PROFILE_SHARING, contact.isProfileSharingEnabled() ? "1" : "0"); values.put(BLOCKED, contact.isBlocked() ? "1" : "0"); + values.put(MUTE_UNTIL, contact.getMuteUntil()); values.put(STORAGE_SERVICE_ID, Base64.encodeBytes(contact.getId().getRaw())); values.put(DIRTY, DirtyState.CLEAN.getId()); @@ -1305,6 +1306,7 @@ public class RecipientDatabase extends Database { values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId()); values.put(PROFILE_SHARING, groupV1.isProfileSharingEnabled() ? "1" : "0"); values.put(BLOCKED, groupV1.isBlocked() ? "1" : "0"); + values.put(MUTE_UNTIL, groupV1.getMuteUntil()); values.put(STORAGE_SERVICE_ID, Base64.encodeBytes(groupV1.getId().getRaw())); values.put(DIRTY, DirtyState.CLEAN.getId()); @@ -1323,6 +1325,7 @@ public class RecipientDatabase extends Database { values.put(GROUP_TYPE, GroupType.SIGNAL_V2.getId()); values.put(PROFILE_SHARING, groupV2.isProfileSharingEnabled() ? "1" : "0"); values.put(BLOCKED, groupV2.isBlocked() ? "1" : "0"); + values.put(MUTE_UNTIL, groupV2.getMuteUntil()); values.put(STORAGE_SERVICE_ID, Base64.encodeBytes(groupV2.getId().getRaw())); values.put(DIRTY, DirtyState.CLEAN.getId()); @@ -1661,7 +1664,9 @@ public class RecipientDatabase extends Database { values.put(MUTE_UNTIL, until); if (update(id, values)) { Recipient.live(id).refresh(); + markDirty(id, DirtyState.UPDATE); } + StorageSyncHelper.scheduleSyncForDataChange(); } public void setSeenFirstInviteReminder(@NonNull RecipientId id) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJobV2.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJobV2.java index 65461ff3b..294d4b194 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJobV2.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJobV2.java @@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.migrations.StorageServiceMigrationJob; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.storage.AccountRecordProcessor; @@ -25,6 +26,7 @@ import org.thoughtcrime.securesms.storage.ContactRecordProcessor; import org.thoughtcrime.securesms.storage.GroupV1RecordProcessor; import org.thoughtcrime.securesms.storage.GroupV2RecordProcessor; import org.thoughtcrime.securesms.storage.StorageRecordProcessor; +import org.thoughtcrime.securesms.storage.StorageRecordUpdate; import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.thoughtcrime.securesms.storage.StorageSyncHelper.KeyDifferenceResult; import org.thoughtcrime.securesms.storage.StorageSyncHelper.LocalWriteResult; @@ -42,6 +44,7 @@ import org.whispersystems.signalservice.api.storage.SignalAccountRecord; 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.SignalRecord; import org.whispersystems.signalservice.api.storage.SignalStorageManifest; import org.whispersystems.signalservice.api.storage.SignalStorageRecord; import org.whispersystems.signalservice.api.storage.StorageId; @@ -107,6 +110,27 @@ import java.util.concurrent.TimeUnit; * converting local data into a format that can be compared with, merged, and eventually written * back to both local and remote data stores is tiresome. There's also lots of general bookkeeping, * error handling, cleanup scenarios, logging, etc. + * + * == Syncing a new field on an existing record == + * + * - Add the field the the respective proto + * - Update the respective model (i.e. {@link SignalContactRecord}) + * - Add getters + * - Update the builder + * - Update {@link SignalRecord#describeDiff(SignalRecord)}. + * - Update the respective record processor (i.e {@link ContactRecordProcessor}). You need to make + * sure that you're: + * - Merging the attributes, likely preferring remote + * - Adding to doParamsMatch() + * - Adding the parameter to the builder chain when creating a merged model + * - Update builder usage in StorageSyncModels + * - Handle the new data when writing to the local storage + * (i.e. {@link RecipientDatabase#applyStorageSyncContactUpdate(StorageRecordUpdate)}). + * - Make sure that whenever you change the field in the UI, we mark the row as dirty and call + * {@link StorageSyncHelper#scheduleSyncForDataChange()}. + * - If you're syncing a field that was otherwise already present in the UI, you'll probably want + * to enqueue a {@link StorageServiceMigrationJob} as an app migration to make sure it gets + * synced. */ public class StorageSyncJobV2 extends BaseJob { diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index 7c4ac63e1..a4659c58f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -40,7 +40,7 @@ public class ApplicationMigrations { private static final int LEGACY_CANONICAL_VERSION = 455; - public static final int CURRENT_VERSION = 30; + public static final int CURRENT_VERSION = 31; private static final class Version { static final int LEGACY = 1; @@ -71,6 +71,8 @@ public class ApplicationMigrations { static final int DAY_BY_DAY_STICKERS = 26; static final int BLOB_LOCATION = 27; static final int SYSTEM_NAME_SPLIT = 28; + // Versions 29, 30 accidentally skipped + static final int MUTE_SYNC = 31; } /** @@ -301,6 +303,10 @@ public class ApplicationMigrations { jobs.put(Version.SYSTEM_NAME_SPLIT, new DirectoryRefreshMigrationJob()); } + if (lastSeenVersion < Version.MUTE_SYNC) { + jobs.put(Version.MUTE_SYNC, new StorageServiceMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/StorageServiceMigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/StorageServiceMigrationJob.java index d1125b430..316087e60 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/StorageServiceMigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/StorageServiceMigrationJob.java @@ -11,6 +11,9 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceKeysUpdateJob; import org.thoughtcrime.securesms.jobs.StorageSyncJob; import org.thoughtcrime.securesms.util.TextSecurePreferences; +/** + * Just runs a storage sync. Useful if you've started syncing a new field to storage service. + */ public class StorageServiceMigrationJob extends MigrationJob { private static final String TAG = Log.tag(StorageServiceMigrationJob.class); diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java index 7f1b1ea14..91972f990 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java @@ -97,8 +97,9 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor