diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 95d641bffc..dd86b5f4ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -214,7 +214,6 @@ public class ApplicationContext extends Application implements AppForegroundObse .addNonBlocking(this::ensureProfileUploaded) .addNonBlocking(() -> AppDependencies.getExpireStoriesManager().scheduleIfNecessary()) .addNonBlocking(BackupRepository::maybeFixAnyDanglingUploadProgress) - .addNonBlocking(BackupRepository::maybeFixAnyDanglingLocalExportProgress) .addPostRender(() -> AppDependencies.getDeletedCallEventManager().scheduleIfNecessary()) .addPostRender(() -> RateLimitUtil.retryAllRateLimitedMessages(this)) .addPostRender(this::initializeExpiringMessageManager) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/LocalExportProgress.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/LocalExportProgress.kt new file mode 100644 index 0000000000..d05a288542 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/LocalExportProgress.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress + +object LocalExportProgress { + val internalEncryptedProgress = MutableStateFlow(LocalBackupCreationProgress()) + val internalPlaintextProgress = MutableStateFlow(LocalBackupCreationProgress()) + + val encryptedProgress: StateFlow = internalEncryptedProgress + val plaintextProgress: StateFlow = internalPlaintextProgress + + fun setEncryptedProgress(progress: LocalBackupCreationProgress) { + internalEncryptedProgress.value = progress + } + + fun setPlaintextProgress(progress: LocalBackupCreationProgress) { + internalPlaintextProgress.value = progress + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index abe2f1d56f..9d481adb75 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -72,7 +72,6 @@ import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.backup.ArchiveUploadProgress import org.thoughtcrime.securesms.backup.DeletionState -import org.thoughtcrime.securesms.backup.isIdle import org.thoughtcrime.securesms.backup.v2.BackupRepository.copyAttachmentToArchive import org.thoughtcrime.securesms.backup.v2.BackupRepository.exportForDebugging import org.thoughtcrime.securesms.backup.v2.importer.ChatItemArchiveImporter @@ -115,7 +114,6 @@ import org.thoughtcrime.securesms.jobs.BackupMessagesJob import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob import org.thoughtcrime.securesms.jobs.CancelRestoreMediaJob import org.thoughtcrime.securesms.jobs.CreateReleaseChannelJob -import org.thoughtcrime.securesms.jobs.LocalArchiveJob import org.thoughtcrime.securesms.jobs.LocalBackupJob import org.thoughtcrime.securesms.jobs.MultiDeviceKeysUpdateJob import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob @@ -132,7 +130,6 @@ import org.thoughtcrime.securesms.keyvalue.KeyValueStore import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.isDecisionPending import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState -import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository import org.thoughtcrime.securesms.net.SignalNetwork import org.thoughtcrime.securesms.notifications.NotificationChannels @@ -592,14 +589,6 @@ object BackupRepository { SignalStore.backup.snoozeDownloadNotifier() } - @JvmStatic - fun maybeFixAnyDanglingLocalExportProgress() { - if (!SignalStore.backup.newLocalBackupProgress.isIdle && AppDependencies.jobManager.find { it.factoryKey == LocalArchiveJob.KEY }.isEmpty()) { - Log.w(TAG, "Found stale local backup progress with no active job. Resetting to idle.") - SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(idle = LocalBackupCreationProgress.Idle()) - } - } - @JvmStatic fun maybeFixAnyDanglingUploadProgress() { if (SignalStore.account.isLinkedDevice) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/local/LocalArchiver.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/local/LocalArchiver.kt index f3bd3ecace..50bf61f0e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/local/LocalArchiver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/local/LocalArchiver.kt @@ -22,6 +22,7 @@ import org.signal.core.util.readFully import org.signal.core.util.toJson import org.signal.libsignal.crypto.Aes256Ctr32 import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.backup.LocalExportProgress import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.database.SignalDatabase @@ -72,10 +73,10 @@ object LocalArchiver { Log.i(TAG, "Listing all current files") val allFiles = filesFileSystem.allFiles { completed, total -> - SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.INITIALIZING, frameExportCount = completed.toLong(), frameTotalCount = total.toLong())) + LocalExportProgress.setEncryptedProgress(LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.INITIALIZING, frameExportCount = completed.toLong(), frameTotalCount = total.toLong()))) } stopwatch.split("files-list") - SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.INITIALIZING)) + LocalExportProgress.setEncryptedProgress(LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.INITIALIZING))) val mediaNames: MutableSet = Collections.synchronizedSet(HashSet()) @@ -387,7 +388,7 @@ object LocalArchiver { } private fun post(progress: LocalBackupCreationProgress) { - SignalStore.backup.newLocalBackupProgress = progress + LocalExportProgress.setEncryptedProgress(progress) } } @@ -442,7 +443,7 @@ object LocalArchiver { } private fun post(progress: LocalBackupCreationProgress) { - SignalStore.backup.newLocalPlaintextBackupProgress = progress + LocalExportProgress.setPlaintextProgress(progress) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/local/LocalBackupsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/local/LocalBackupsViewModel.kt index 5e1da84b25..8439ceb849 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/local/LocalBackupsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/local/LocalBackupsViewModel.kt @@ -18,6 +18,7 @@ import org.signal.core.ui.util.StorageUtil import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.BackupPassphrase +import org.thoughtcrime.securesms.backup.LocalExportProgress import org.thoughtcrime.securesms.components.settings.app.backups.remote.BackupKeyCredentialManagerHandler import org.thoughtcrime.securesms.components.settings.app.backups.remote.BackupKeySaveState import org.thoughtcrime.securesms.dependencies.AppDependencies @@ -74,7 +75,7 @@ class LocalBackupsViewModel : ViewModel(), BackupKeyCredentialManagerHandler { } viewModelScope.launch { - SignalStore.backup.newLocalBackupProgressFlow.collect { progress -> + LocalExportProgress.encryptedProgress.collect { progress -> internalSettingsState.update { it.copy(progress = progress) } } } @@ -108,7 +109,7 @@ class LocalBackupsViewModel : ViewModel(), BackupKeyCredentialManagerHandler { } fun onBackupStarted() { - SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.NONE)) + LocalExportProgress.setEncryptedProgress(LocalBackupCreationProgress(exporting = LocalBackupCreationProgress.Exporting(phase = LocalBackupCreationProgress.ExportPhase.NONE))) } fun turnOffAndDelete(context: Context) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsState.kt index eae6b2015d..d30e305e6c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsState.kt @@ -1,6 +1,6 @@ package org.thoughtcrime.securesms.components.settings.app.chats -import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.backup.LocalExportProgress import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress data class ChatsSettingsState( @@ -14,7 +14,7 @@ data class ChatsSettingsState( val userUnregistered: Boolean, val clientDeprecated: Boolean, val isPlaintextExportEnabled: Boolean, - val plaintextExportProgress: LocalBackupCreationProgress = SignalStore.backup.newLocalPlaintextBackupProgress, + val plaintextExportProgress: LocalBackupCreationProgress = LocalExportProgress.plaintextProgress.value, val chatExportState: ChatExportState = ChatExportState.None, val includeMediaInExport: Boolean = false ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt index f6ad07c928..e11a3064fd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsViewModel.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.thoughtcrime.securesms.backup.LocalExportProgress import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFoldersRepository import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.LocalBackupJob @@ -44,7 +45,7 @@ class ChatsSettingsViewModel @JvmOverloads constructor( init { viewModelScope.launch { - SignalStore.backup.newLocalPlaintextBackupProgressFlow.collect { progress -> + LocalExportProgress.plaintextProgress.collect { progress -> store.update { it.copy( plaintextExportProgress = progress, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt index 5d0b7ef9b1..6acde0ca07 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt @@ -40,6 +40,7 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKey import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.backup.ArchiveUploadProgress +import org.thoughtcrime.securesms.backup.LocalExportProgress import org.thoughtcrime.securesms.backup.v2.ArchiveValidator import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.backup.v2.DebugBackupMetadata @@ -92,7 +93,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { init { viewModelScope.launch { - SignalStore.backup.newLocalPlaintextBackupProgressFlow.collect { progress -> + LocalExportProgress.plaintextProgress.collect { progress -> _state.value = _state.value.copy(plaintextProgress = progress) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalArchiveJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalArchiveJob.kt index bfc0c0c64d..8e94e982ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalArchiveJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalArchiveJob.kt @@ -11,6 +11,7 @@ import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.BackupFileIOError import org.thoughtcrime.securesms.backup.FullBackupExporter.BackupCanceledException +import org.thoughtcrime.securesms.backup.LocalExportProgress import org.thoughtcrime.securesms.backup.v2.local.ArchiveFileSystem import org.thoughtcrime.securesms.backup.v2.local.LocalArchiver import org.thoughtcrime.securesms.backup.v2.local.SnapshotFileSystem @@ -92,7 +93,7 @@ class LocalArchiveJob internal constructor(parameters: Parameters) : Job(paramet val progressScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) progressScope.launch { - SignalStore.backup.newLocalBackupProgressFlow.collect { progress -> + LocalExportProgress.encryptedProgress.collect { progress -> updateNotification(progress, notification) } } @@ -153,11 +154,11 @@ class LocalArchiveJob internal constructor(parameters: Parameters) : Job(paramet } override fun onFailure() { - SignalStore.backup.newLocalBackupProgress = LocalBackupCreationProgress(idle = LocalBackupCreationProgress.Idle()) + LocalExportProgress.setEncryptedProgress(LocalBackupCreationProgress(idle = LocalBackupCreationProgress.Idle())) } private fun setProgress(progress: LocalBackupCreationProgress, notification: NotificationController?) { - SignalStore.backup.newLocalBackupProgress = progress + LocalExportProgress.setEncryptedProgress(progress) updateNotification(progress, notification) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalPlaintextArchiveJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalPlaintextArchiveJob.kt index cfe718d565..a9fa5dbbbd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalPlaintextArchiveJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalPlaintextArchiveJob.kt @@ -10,12 +10,12 @@ import kotlinx.coroutines.launch import org.signal.core.util.Stopwatch import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.backup.LocalExportProgress import org.thoughtcrime.securesms.backup.v2.local.LocalArchiver import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.JsonJobData -import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress import org.thoughtcrime.securesms.notifications.NotificationChannels import org.thoughtcrime.securesms.service.GenericForegroundService @@ -104,7 +104,7 @@ class LocalPlaintextArchiveJob internal constructor( val progressScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) progressScope.launch { - SignalStore.backup.newLocalPlaintextBackupProgressFlow.collect { progress -> + LocalExportProgress.plaintextProgress.collect { progress -> updateNotification(progress, notification) } } @@ -146,14 +146,14 @@ class LocalPlaintextArchiveJob internal constructor( override fun onFailure() { zipFile?.delete() - val current = SignalStore.backup.newLocalPlaintextBackupProgress + val current = LocalExportProgress.plaintextProgress.value if (current.canceled == null && current.failed == null) { - SignalStore.backup.newLocalPlaintextBackupProgress = LocalBackupCreationProgress(failed = LocalBackupCreationProgress.Failed()) + LocalExportProgress.setPlaintextProgress(LocalBackupCreationProgress(failed = LocalBackupCreationProgress.Failed())) } } private fun setProgress(progress: LocalBackupCreationProgress, notification: NotificationController?) { - SignalStore.backup.newLocalPlaintextBackupProgress = progress + LocalExportProgress.setPlaintextProgress(progress) updateNotification(progress, notification) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt index 6edfc08e7a..35167fc2f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.NoRemoteArchiveGarbageCollecti import org.thoughtcrime.securesms.jobmanager.impl.RestoreAttachmentConstraintObserver import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState import org.thoughtcrime.securesms.keyvalue.protos.BackupDownloadNotifierState -import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress import org.thoughtcrime.securesms.util.Environment import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential import org.whispersystems.signalservice.api.archive.GetArchiveCdnCredentialsResponse @@ -105,9 +104,6 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { private const val KEY_NEW_LOCAL_BACKUPS_DIRECTORY = "backup.new_local_backups_directory" private const val KEY_NEW_LOCAL_BACKUPS_LAST_BACKUP_TIME = "backup.new_local_backups_last_backup_time" private const val KEY_NEW_LOCAL_BACKUPS_SELECTED_SNAPSHOT_TIMESTAMP = "backup.new_local_backups_selected_snapshot_timestamp" - private const val KEY_NEW_LOCAL_BACKUPS_CREATION_PROGRESS = "backup.new_local_backups_creation_progress" - private const val KEY_NEW_LOCAL_PLAINTEXT_BACKUPS_CREATION_PROGRESS = "backup.new_local_plaintext_backups_creation_progress" - private const val KEY_LOCAL_RESTORE_ACCOUNT_ENTROPY_POOL = "backup.local_restore_account_entropy_pool" private const val KEY_UPLOAD_BANNER_VISIBLE = "backup.upload_banner_visible" @@ -478,20 +474,6 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { var newLocalBackupsEnabled: Boolean by newLocalBackupsEnabledValue val newLocalBackupsEnabledFlow: Flow by lazy { newLocalBackupsEnabledValue.toFlow() } - /** - * Progress values for local backup progress. - */ - private val newLocalBackupProgressValue = protoValue(KEY_NEW_LOCAL_BACKUPS_CREATION_PROGRESS, LocalBackupCreationProgress(), LocalBackupCreationProgress.ADAPTER) - var newLocalBackupProgress: LocalBackupCreationProgress by newLocalBackupProgressValue - val newLocalBackupProgressFlow: Flow by lazy { newLocalBackupProgressValue.toFlow() } - - /** - * Progress values for local plaintext backup progress. - */ - private val newLocalPlaintextBackupProgressValue = protoValue(KEY_NEW_LOCAL_PLAINTEXT_BACKUPS_CREATION_PROGRESS, LocalBackupCreationProgress(), LocalBackupCreationProgress.ADAPTER) - var newLocalPlaintextBackupProgress: LocalBackupCreationProgress by newLocalPlaintextBackupProgressValue - val newLocalPlaintextBackupProgressFlow: Flow by lazy { newLocalPlaintextBackupProgressValue.toFlow() } - /**IT * The directory URI path selected for new local backups. */