@Test public void findKeyDifference_someOverlap() { KeyDifferenceResult result = StorageSyncHelper.findKeyDifference(keyListOf(1, 2, 3), keyListOf(2, 3, 4)); assertContentsEqual(keyListOf(1), result.getRemoteOnlyKeys()); assertContentsEqual(keyListOf(4), result.getLocalOnlyKeys()); assertFalse(result.hasTypeMismatches()); }
public static @NonNull KeyDifferenceResult findKeyDifference(@NonNull Collection<StorageId> remoteKeys, @NonNull Collection<StorageId> localKeys) { Map<String, StorageId> remoteByRawId = Stream.of(remoteKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); Map<String, StorageId> localByRawId = Stream.of(localKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); boolean hasTypeMismatch = remoteByRawId.size() != remoteKeys.size() || localByRawId.size() != localKeys.size(); Set<String> remoteOnlyRawIds = SetUtil.difference(remoteByRawId.keySet(), localByRawId.keySet()); Set<String> localOnlyRawIds = SetUtil.difference(localByRawId.keySet(), remoteByRawId.keySet()); Set<String> sharedRawIds = SetUtil.intersection(localByRawId.keySet(), remoteByRawId.keySet()); for (String rawId : sharedRawIds) { StorageId remote = Objects.requireNonNull(remoteByRawId.get(rawId)); StorageId local = Objects.requireNonNull(localByRawId.get(rawId)); if (remote.getType() != local.getType()) { remoteOnlyRawIds.remove(rawId); localOnlyRawIds.remove(rawId); hasTypeMismatch = true; } } List<StorageId> remoteOnlyKeys = Stream.of(remoteOnlyRawIds).map(remoteByRawId::get).toList(); List<StorageId> localOnlyKeys = Stream.of(localOnlyRawIds).map(localByRawId::get).toList(); return new KeyDifferenceResult(remoteOnlyKeys, localOnlyKeys, hasTypeMismatch); }
StorageSyncHelper { public static @NonNull KeyDifferenceResult findKeyDifference(@NonNull Collection<StorageId> remoteKeys, @NonNull Collection<StorageId> localKeys) { Map<String, StorageId> remoteByRawId = Stream.of(remoteKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); Map<String, StorageId> localByRawId = Stream.of(localKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); boolean hasTypeMismatch = remoteByRawId.size() != remoteKeys.size() || localByRawId.size() != localKeys.size(); Set<String> remoteOnlyRawIds = SetUtil.difference(remoteByRawId.keySet(), localByRawId.keySet()); Set<String> localOnlyRawIds = SetUtil.difference(localByRawId.keySet(), remoteByRawId.keySet()); Set<String> sharedRawIds = SetUtil.intersection(localByRawId.keySet(), remoteByRawId.keySet()); for (String rawId : sharedRawIds) { StorageId remote = Objects.requireNonNull(remoteByRawId.get(rawId)); StorageId local = Objects.requireNonNull(localByRawId.get(rawId)); if (remote.getType() != local.getType()) { remoteOnlyRawIds.remove(rawId); localOnlyRawIds.remove(rawId); hasTypeMismatch = true; } } List<StorageId> remoteOnlyKeys = Stream.of(remoteOnlyRawIds).map(remoteByRawId::get).toList(); List<StorageId> localOnlyKeys = Stream.of(localOnlyRawIds).map(localByRawId::get).toList(); return new KeyDifferenceResult(remoteOnlyKeys, localOnlyKeys, hasTypeMismatch); } }
StorageSyncHelper { public static @NonNull KeyDifferenceResult findKeyDifference(@NonNull Collection<StorageId> remoteKeys, @NonNull Collection<StorageId> localKeys) { Map<String, StorageId> remoteByRawId = Stream.of(remoteKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); Map<String, StorageId> localByRawId = Stream.of(localKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); boolean hasTypeMismatch = remoteByRawId.size() != remoteKeys.size() || localByRawId.size() != localKeys.size(); Set<String> remoteOnlyRawIds = SetUtil.difference(remoteByRawId.keySet(), localByRawId.keySet()); Set<String> localOnlyRawIds = SetUtil.difference(localByRawId.keySet(), remoteByRawId.keySet()); Set<String> sharedRawIds = SetUtil.intersection(localByRawId.keySet(), remoteByRawId.keySet()); for (String rawId : sharedRawIds) { StorageId remote = Objects.requireNonNull(remoteByRawId.get(rawId)); StorageId local = Objects.requireNonNull(localByRawId.get(rawId)); if (remote.getType() != local.getType()) { remoteOnlyRawIds.remove(rawId); localOnlyRawIds.remove(rawId); hasTypeMismatch = true; } } List<StorageId> remoteOnlyKeys = Stream.of(remoteOnlyRawIds).map(remoteByRawId::get).toList(); List<StorageId> localOnlyKeys = Stream.of(localOnlyRawIds).map(localByRawId::get).toList(); return new KeyDifferenceResult(remoteOnlyKeys, localOnlyKeys, hasTypeMismatch); }  }
StorageSyncHelper { public static @NonNull KeyDifferenceResult findKeyDifference(@NonNull Collection<StorageId> remoteKeys, @NonNull Collection<StorageId> localKeys) { Map<String, StorageId> remoteByRawId = Stream.of(remoteKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); Map<String, StorageId> localByRawId = Stream.of(localKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); boolean hasTypeMismatch = remoteByRawId.size() != remoteKeys.size() || localByRawId.size() != localKeys.size(); Set<String> remoteOnlyRawIds = SetUtil.difference(remoteByRawId.keySet(), localByRawId.keySet()); Set<String> localOnlyRawIds = SetUtil.difference(localByRawId.keySet(), remoteByRawId.keySet()); Set<String> sharedRawIds = SetUtil.intersection(localByRawId.keySet(), remoteByRawId.keySet()); for (String rawId : sharedRawIds) { StorageId remote = Objects.requireNonNull(remoteByRawId.get(rawId)); StorageId local = Objects.requireNonNull(localByRawId.get(rawId)); if (remote.getType() != local.getType()) { remoteOnlyRawIds.remove(rawId); localOnlyRawIds.remove(rawId); hasTypeMismatch = true; } } List<StorageId> remoteOnlyKeys = Stream.of(remoteOnlyRawIds).map(remoteByRawId::get).toList(); List<StorageId> localOnlyKeys = Stream.of(localOnlyRawIds).map(localByRawId::get).toList(); return new KeyDifferenceResult(remoteOnlyKeys, localOnlyKeys, hasTypeMismatch); }  static @NonNull Optional<LocalWriteResult> buildStorageUpdatesForLocal(long currentManifestVersion,
                                                                                @NonNull List<StorageId> currentLocalKeys,
                                                                                @NonNull List<RecipientSettings> updates,
                                                                                @NonNull List<RecipientSettings> inserts,
                                                                                @NonNull List<RecipientSettings> deletes,
                                                                                @NonNull Optional<SignalAccountRecord> accountUpdate,
                                                                                @NonNull Optional<SignalAccountRecord> accountInsert); static @NonNull KeyDifferenceResult findKeyDifference(@NonNull Collection<StorageId> remoteKeys,
                                                               @NonNull Collection<StorageId> localKeys); static @NonNull MergeResult resolveConflict(@NonNull Collection<SignalStorageRecord> remoteOnlyRecords,
                                                     @NonNull Collection<SignalStorageRecord> localOnlyRecords); static @NonNull WriteOperationResult createWriteOperation(long currentManifestVersion,
                                                                   @NonNull List<StorageId> currentLocalStorageKeys,
                                                                   @NonNull MergeResult mergeResult); static @NonNull byte[] generateKey(); static boolean profileKeyChanged(RecordUpdate<SignalContactRecord> update); static Optional<SignalAccountRecord> getPendingAccountSyncUpdate(@NonNull Context context, @NonNull Recipient self); static Optional<SignalAccountRecord> getPendingAccountSyncInsert(@NonNull Context context, @NonNull Recipient self); static SignalStorageRecord buildAccountRecord(@NonNull Context context, @NonNull Recipient self); static void applyAccountStorageSyncUpdates(@NonNull Context context, Optional<StorageSyncHelper.RecordUpdate<SignalAccountRecord>> update); static void applyAccountStorageSyncUpdates(@NonNull Context context, @NonNull StorageId storageId, @NonNull SignalAccountRecord update, boolean fetchProfile); static void scheduleSyncForDataChange(); static void scheduleRoutineSync(); }
StorageSyncHelper { public static @NonNull KeyDifferenceResult findKeyDifference(@NonNull Collection<StorageId> remoteKeys, @NonNull Collection<StorageId> localKeys) { Map<String, StorageId> remoteByRawId = Stream.of(remoteKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); Map<String, StorageId> localByRawId = Stream.of(localKeys).collect(Collectors.toMap(id -> Base64.encodeBytes(id.getRaw()), id -> id)); boolean hasTypeMismatch = remoteByRawId.size() != remoteKeys.size() || localByRawId.size() != localKeys.size(); Set<String> remoteOnlyRawIds = SetUtil.difference(remoteByRawId.keySet(), localByRawId.keySet()); Set<String> localOnlyRawIds = SetUtil.difference(localByRawId.keySet(), remoteByRawId.keySet()); Set<String> sharedRawIds = SetUtil.intersection(localByRawId.keySet(), remoteByRawId.keySet()); for (String rawId : sharedRawIds) { StorageId remote = Objects.requireNonNull(remoteByRawId.get(rawId)); StorageId local = Objects.requireNonNull(localByRawId.get(rawId)); if (remote.getType() != local.getType()) { remoteOnlyRawIds.remove(rawId); localOnlyRawIds.remove(rawId); hasTypeMismatch = true; } } List<StorageId> remoteOnlyKeys = Stream.of(remoteOnlyRawIds).map(remoteByRawId::get).toList(); List<StorageId> localOnlyKeys = Stream.of(localOnlyRawIds).map(localByRawId::get).toList(); return new KeyDifferenceResult(remoteOnlyKeys, localOnlyKeys, hasTypeMismatch); }  static @NonNull Optional<LocalWriteResult> buildStorageUpdatesForLocal(long currentManifestVersion,
                                                                                @NonNull List<StorageId> currentLocalKeys,
                                                                                @NonNull List<RecipientSettings> updates,
                                                                                @NonNull List<RecipientSettings> inserts,
                                                                                @NonNull List<RecipientSettings> deletes,
                                                                                @NonNull Optional<SignalAccountRecord> accountUpdate,
                                                                                @NonNull Optional<SignalAccountRecord> accountInsert); static @NonNull KeyDifferenceResult findKeyDifference(@NonNull Collection<StorageId> remoteKeys,
                                                               @NonNull Collection<StorageId> localKeys); static @NonNull MergeResult resolveConflict(@NonNull Collection<SignalStorageRecord> remoteOnlyRecords,
                                                     @NonNull Collection<SignalStorageRecord> localOnlyRecords); static @NonNull WriteOperationResult createWriteOperation(long currentManifestVersion,
                                                                   @NonNull List<StorageId> currentLocalStorageKeys,
                                                                   @NonNull MergeResult mergeResult); static @NonNull byte[] generateKey(); static boolean profileKeyChanged(RecordUpdate<SignalContactRecord> update); static Optional<SignalAccountRecord> getPendingAccountSyncUpdate(@NonNull Context context, @NonNull Recipient self); static Optional<SignalAccountRecord> getPendingAccountSyncInsert(@NonNull Context context, @NonNull Recipient self); static SignalStorageRecord buildAccountRecord(@NonNull Context context, @NonNull Recipient self); static void applyAccountStorageSyncUpdates(@NonNull Context context, Optional<StorageSyncHelper.RecordUpdate<SignalAccountRecord>> update); static void applyAccountStorageSyncUpdates(@NonNull Context context, @NonNull StorageId storageId, @NonNull SignalAccountRecord update, boolean fetchProfile); static void scheduleSyncForDataChange(); static void scheduleRoutineSync();  }