diff --git a/app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt b/app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt index adb00a9d3f5..75d1d0d845d 100644 --- a/app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt +++ b/app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt @@ -362,6 +362,16 @@ class UseCaseModule { ): ObserveSecurityClassificationLabelUseCase = coreLogic.getSessionScope(currentAccount).observeSecurityClassificationLabel + @ViewModelScoped + @Provides + fun provideCreateMpBackupUseCase(@KaliumCoreLogic coreLogic: CoreLogic, @CurrentAccount currentAccount: UserId) = + coreLogic.getSessionScope(currentAccount).multiPlatformBackup.create + + @ViewModelScoped + @Provides + fun provideRestoreMpBackupUseCase(@KaliumCoreLogic coreLogic: CoreLogic, @CurrentAccount currentAccount: UserId) = + coreLogic.getSessionScope(currentAccount).multiPlatformBackup.restore + @ViewModelScoped @Provides fun provideUpdateApiVersionsScheduler(@KaliumCoreLogic coreLogic: CoreLogic) = diff --git a/app/src/main/kotlin/com/wire/android/di/accountScoped/BackupModule.kt b/app/src/main/kotlin/com/wire/android/di/accountScoped/BackupModule.kt index e86d5bb77b9..1ccc4b276e6 100644 --- a/app/src/main/kotlin/com/wire/android/di/accountScoped/BackupModule.kt +++ b/app/src/main/kotlin/com/wire/android/di/accountScoped/BackupModule.kt @@ -17,8 +17,10 @@ */ package com.wire.android.di.accountScoped +import com.wire.android.BuildConfig import com.wire.android.di.CurrentAccount import com.wire.android.di.KaliumCoreLogic +import com.wire.android.ui.home.settings.backup.MPBackupSettings import com.wire.kalium.logic.CoreLogic import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.feature.backup.BackupScope @@ -53,6 +55,13 @@ class BackupModule { fun provideRestoreBackupUseCase(backupScope: BackupScope) = backupScope.restore + @Provides + fun provideMpBackupSettings() = if (BuildConfig.ENABLE_CROSSPLATFORM_BACKUP) { + MPBackupSettings.Enabled + } else { + MPBackupSettings.Disabled + } + @OptIn(DelicateKaliumApi::class) @ViewModelScoped @Provides diff --git a/app/src/main/kotlin/com/wire/android/ui/debug/ExportObfuscatedCopyViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/debug/ExportObfuscatedCopyViewModel.kt index 47e966166c2..af280f1354e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/debug/ExportObfuscatedCopyViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/debug/ExportObfuscatedCopyViewModel.kt @@ -83,7 +83,6 @@ class ExportObfuscatedCopyViewModelImpl @OptIn(DelicateKaliumApi::class) @Inject latestCreatedBackup = BackupAndRestoreState.CreatedBackup( result.backupFilePath, result.backupFileName, - result.backupFileSize, false ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreState.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreState.kt index 3d0b662ad58..76ef31dd53b 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreState.kt @@ -19,6 +19,7 @@ package com.wire.android.ui.home.settings.backup import com.wire.kalium.logic.feature.auth.ValidatePasswordResult +import com.wire.kalium.logic.feature.backup.BackupFileFormat import okio.Path data class BackupAndRestoreState( @@ -27,10 +28,11 @@ data class BackupAndRestoreState( val restorePasswordValidation: PasswordValidation, val backupCreationProgress: BackupCreationProgress, val lastBackupData: Long?, - val passwordValidation: ValidatePasswordResult + val passwordValidation: ValidatePasswordResult, + val backupFileFormat: BackupFileFormat, ) { - data class CreatedBackup(val path: Path, val assetName: String, val assetSize: Long, val isEncrypted: Boolean) + data class CreatedBackup(val path: Path, val assetName: String, val isEncrypted: Boolean) companion object { val INITIAL_STATE = BackupAndRestoreState( @@ -39,7 +41,8 @@ data class BackupAndRestoreState( backupCreationProgress = BackupCreationProgress.InProgress(), restorePasswordValidation = PasswordValidation.NotVerified, passwordValidation = ValidatePasswordResult.Valid, - lastBackupData = null + lastBackupData = null, + backupFileFormat = BackupFileFormat.ANDROID, ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt index 357a47fb1d5..9e3a9c320e3 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt @@ -27,7 +27,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.wire.android.BuildConfig import com.wire.android.appLogger import com.wire.android.datastore.UserDataStore import com.wire.android.feature.analytics.AnonymousAnalyticsManagerImpl @@ -38,8 +37,10 @@ import com.wire.android.util.dispatchers.DispatcherProvider import com.wire.kalium.logic.data.asset.KaliumFileSystem import com.wire.kalium.logic.feature.auth.ValidatePasswordResult import com.wire.kalium.logic.feature.auth.ValidatePasswordUseCase +import com.wire.kalium.logic.feature.backup.BackupFileFormat import com.wire.kalium.logic.feature.backup.CreateBackupResult import com.wire.kalium.logic.feature.backup.CreateBackupUseCase +import com.wire.kalium.logic.feature.backup.CreateMPBackupUseCase import com.wire.kalium.logic.feature.backup.RestoreBackupResult import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.BackupIOFailure import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.DecryptionFailure @@ -47,6 +48,7 @@ import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFai import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.InvalidPassword import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.InvalidUserId import com.wire.kalium.logic.feature.backup.RestoreBackupUseCase +import com.wire.kalium.logic.feature.backup.RestoreMPBackupUseCase import com.wire.kalium.logic.feature.backup.VerifyBackupResult import com.wire.kalium.logic.feature.backup.VerifyBackupUseCase import com.wire.kalium.util.DateTimeUtil @@ -60,16 +62,18 @@ import javax.inject.Inject @Suppress("LongParameterList", "TooManyFunctions") @HiltViewModel -class BackupAndRestoreViewModel -@Inject constructor( +class BackupAndRestoreViewModel @Inject constructor( private val importBackup: RestoreBackupUseCase, + private val importMpBackup: RestoreMPBackupUseCase, private val createBackupFile: CreateBackupUseCase, + private val createMpBackupFile: CreateMPBackupUseCase, private val verifyBackup: VerifyBackupUseCase, private val validatePassword: ValidatePasswordUseCase, private val kaliumFileSystem: KaliumFileSystem, private val fileManager: FileManager, private val userDataStore: UserDataStore, - private val dispatcher: DispatcherProvider + private val dispatcher: DispatcherProvider, + private val mpBackupSettings: MPBackupSettings, ) : ViewModel() { val createBackupPasswordState: TextFieldState = TextFieldState() @@ -111,13 +115,20 @@ class BackupAndRestoreViewModel updateCreationProgress(PROGRESS_50) delay(SMALL_DELAY) - when (val result = createBackupFile(createBackupPasswordState.text.toString())) { + val password = createBackupPasswordState.text.toString() + + val result = if (mpBackupSettings is MPBackupSettings.Enabled) { + createMpBackupFile(password) + } else { + createBackupFile(password) + } + + when (result) { is CreateBackupResult.Success -> { state = state.copy(backupCreationProgress = BackupCreationProgress.Finished(result.backupFileName)) latestCreatedBackup = BackupAndRestoreState.CreatedBackup( result.backupFilePath, result.backupFileName, - result.backupFileSize, createBackupPasswordState.text.isNotEmpty() ) createBackupPasswordState.clearText() @@ -170,27 +181,25 @@ class BackupAndRestoreViewModel fun chooseBackupFileToRestore(uri: Uri) = viewModelScope.launch { latestImportedBackupTempPath = kaliumFileSystem.tempFilePath(TEMP_IMPORTED_BACKUP_FILE_NAME) fileManager.copyToPath(uri, latestImportedBackupTempPath) - checkIfBackupEncrypted(latestImportedBackupTempPath) + verifyBackupFile(latestImportedBackupTempPath) } private fun showPasswordDialog() { state = state.copy(restoreFileValidation = RestoreFileValidation.PasswordRequired) } - private suspend fun checkIfBackupEncrypted(importedBackupPath: Path) = withContext(dispatcher.main()) { + private suspend fun verifyBackupFile(importedBackupPath: Path) = withContext(dispatcher.main()) { when (val result = verifyBackup(importedBackupPath)) { is VerifyBackupResult.Success -> { - when (result) { - is VerifyBackupResult.Success.Encrypted -> showPasswordDialog() - is VerifyBackupResult.Success.NotEncrypted -> importDatabase(importedBackupPath) - VerifyBackupResult.Success.Web -> { - if (BuildConfig.DEVELOPER_FEATURES_ENABLED) { - importDatabase(importedBackupPath) - } else { - state = state.copy(restoreFileValidation = RestoreFileValidation.IncompatibleBackup) - AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreFailed) - } - } + state = state.copy(backupFileFormat = result.format) + if (result.isEncrypted) { + showPasswordDialog() + } else { + state = state.copy( + restoreFileValidation = RestoreFileValidation.ValidNonEncryptedBackup, + backupRestoreProgress = BackupRestoreProgress.InProgress(PROGRESS_75) + ) + restoreBackup(importedBackupPath, null) } } @@ -199,6 +208,15 @@ class BackupAndRestoreViewModel val errorMessage = when (result) { is VerifyBackupResult.Failure.Generic -> result.error.toString() VerifyBackupResult.Failure.InvalidBackupFile -> "No valid files found in the backup" + is VerifyBackupResult.Failure.UnsupportedVersion -> "Unsupported backup version: ${result.version}" + VerifyBackupResult.Failure.InvalidUserId -> { + state = state.copy( + backupRestoreProgress = BackupRestoreProgress.Failed, + restoreFileValidation = RestoreFileValidation.WrongBackup, + restorePasswordValidation = PasswordValidation.Valid + ) + "Invalid user ID" + } } AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreFailed) @@ -207,30 +225,6 @@ class BackupAndRestoreViewModel } } - private suspend fun importDatabase(importedBackupPath: Path) { - state = state.copy( - restoreFileValidation = RestoreFileValidation.ValidNonEncryptedBackup, - backupRestoreProgress = BackupRestoreProgress.InProgress(PROGRESS_75) - ) - when (val result = importBackup(importedBackupPath, null)) { - RestoreBackupResult.Success -> { - updateCreationProgress(PROGRESS_75) - delay(SMALL_DELAY) - state = state.copy(backupRestoreProgress = BackupRestoreProgress.Finished) - AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreSucceeded) - } - - is RestoreBackupResult.Failure -> { - appLogger.e("Error when restoring the backup db file caused by: ${result.failure.cause}") - state = state.copy( - restoreFileValidation = RestoreFileValidation.IncompatibleBackup, - backupRestoreProgress = BackupRestoreProgress.Failed - ) - AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreFailed) - } - } - } - fun restorePasswordProtectedBackup() = viewModelScope.launch(dispatcher.main()) { state = state.copy( backupRestoreProgress = BackupRestoreProgress.InProgress(PROGRESS_50), @@ -240,21 +234,8 @@ class BackupAndRestoreViewModel val fileValidationState = state.restoreFileValidation if (fileValidationState is RestoreFileValidation.PasswordRequired) { state = state.copy(restorePasswordValidation = PasswordValidation.Entered) - when (val result = importBackup(latestImportedBackupTempPath, restoreBackupPasswordState.text.toString())) { - RestoreBackupResult.Success -> { - state = state.copy( - backupRestoreProgress = BackupRestoreProgress.Finished, - restorePasswordValidation = PasswordValidation.Valid - ) - restoreBackupPasswordState.clearText() - AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreSucceeded) - } - is RestoreBackupResult.Failure -> { - mapBackupRestoreFailure(result.failure) - AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreFailed) - } - } + restoreBackup(latestImportedBackupTempPath, restoreBackupPasswordState.text.toString()) } else { state = state.copy(backupRestoreProgress = BackupRestoreProgress.Failed) AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreFailed) @@ -317,6 +298,30 @@ class BackupAndRestoreViewModel } } + private fun restoreBackup(backupFilePath: Path, password: String?) = viewModelScope.launch { + val result = when (state.backupFileFormat) { + BackupFileFormat.ANDROID -> importBackup(backupFilePath, password) + BackupFileFormat.MULTIPLATFORM -> importMpBackup(backupFilePath, password) + } + when (result) { + RestoreBackupResult.Success -> { + updateCreationProgress(PROGRESS_75) + delay(SMALL_DELAY) + state = state.copy( + backupRestoreProgress = BackupRestoreProgress.Finished, + restorePasswordValidation = PasswordValidation.Valid + ) + restoreBackupPasswordState.clearText() + AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreSucceeded) + } + + is RestoreBackupResult.Failure -> { + mapBackupRestoreFailure(result.failure) + AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreFailed) + } + } + } + private suspend fun updateCreationProgress(progress: Float) = withContext(dispatcher.main()) { state = state.copy(backupCreationProgress = BackupCreationProgress.InProgress(progress)) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/MPBackupSettings.kt b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/MPBackupSettings.kt new file mode 100644 index 00000000000..e540e3335ad --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/home/settings/backup/MPBackupSettings.kt @@ -0,0 +1,23 @@ +/* + * Wire + * Copyright (C) 2025 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.home.settings.backup + +sealed interface MPBackupSettings { + data object Disabled : MPBackupSettings + data object Enabled : MPBackupSettings +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9a433117d4d..e780233d504 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1343,7 +1343,7 @@ In group conversations, the group admin can overwrite this setting. Conversations have been restored Loading conversations Restore from backup - The backup contents will replace the conversation history on this device. You can only restore history from a backup of the same platform. + The existing history on this device remains and will be completed by the new backup. You can restore history from all your devices and different platforms but not from another account. Choose Backup File Enter password This backup is password protected. diff --git a/app/src/test/kotlin/com/wire/android/ui/home/settings/home/BackupAndRestoreViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/settings/home/BackupAndRestoreViewModelTest.kt index 2851aa7d144..18532c56be8 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/settings/home/BackupAndRestoreViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/settings/home/BackupAndRestoreViewModelTest.kt @@ -29,14 +29,17 @@ import com.wire.android.ui.home.settings.backup.BackupAndRestoreState import com.wire.android.ui.home.settings.backup.BackupAndRestoreViewModel import com.wire.android.ui.home.settings.backup.BackupCreationProgress import com.wire.android.ui.home.settings.backup.BackupRestoreProgress +import com.wire.android.ui.home.settings.backup.MPBackupSettings import com.wire.android.ui.home.settings.backup.PasswordValidation import com.wire.android.ui.home.settings.backup.RestoreFileValidation import com.wire.android.util.FileManager import com.wire.kalium.common.error.CoreFailure import com.wire.kalium.logic.feature.auth.ValidatePasswordResult import com.wire.kalium.logic.feature.auth.ValidatePasswordUseCase +import com.wire.kalium.logic.feature.backup.BackupFileFormat import com.wire.kalium.logic.feature.backup.CreateBackupResult import com.wire.kalium.logic.feature.backup.CreateBackupUseCase +import com.wire.kalium.logic.feature.backup.CreateMPBackupUseCase import com.wire.kalium.logic.feature.backup.RestoreBackupResult import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.BackupIOFailure import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.IncompatibleBackup @@ -44,6 +47,7 @@ import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFai import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.InvalidUserId import com.wire.kalium.logic.feature.backup.RestoreBackupResult.Failure import com.wire.kalium.logic.feature.backup.RestoreBackupUseCase +import com.wire.kalium.logic.feature.backup.RestoreMPBackupUseCase import com.wire.kalium.logic.feature.backup.VerifyBackupResult import com.wire.kalium.logic.feature.backup.VerifyBackupUseCase import io.mockk.MockKAnnotations @@ -107,7 +111,7 @@ class BackupAndRestoreViewModelTest { // Then assert(backupAndRestoreViewModel.state.backupCreationProgress is BackupCreationProgress.Finished) assertFalse(backupAndRestoreViewModel.latestCreatedBackup?.isEncrypted!!) - coVerify(exactly = 1) { arrangement.createBackupFile(password = emptyPassword) } + coVerify(exactly = 1) { arrangement.createMpBackupFile(password = emptyPassword) } } @Test @@ -127,7 +131,7 @@ class BackupAndRestoreViewModelTest { // Then assertInstanceOf(BackupCreationProgress.Finished::class.java, backupAndRestoreViewModel.state.backupCreationProgress) assertTrue(backupAndRestoreViewModel.latestCreatedBackup?.isEncrypted!!) - coVerify(exactly = 1) { arrangement.createBackupFile(password = password) } + coVerify(exactly = 1) { arrangement.createMpBackupFile(password = password) } } @Test @@ -151,7 +155,7 @@ class BackupAndRestoreViewModelTest { fun givenANonEmptyPassword_whenItIsInvalid_thenItUpdatePasswordValidationState() = runTest(dispatcher.default()) { // Given val password = "mayTh3ForceBeWIthYou" - val (arrangement, backupAndRestoreViewModel) = Arrangement() + val (_, backupAndRestoreViewModel) = Arrangement() .withInvalidPassword() .arrange() @@ -167,7 +171,7 @@ class BackupAndRestoreViewModelTest { fun givenANonEmptyPassword_whenItIsValid_thenItUpdatePasswordValidationState() = runTest(dispatcher.default()) { // Given val password = "mayTh3ForceBeWIthYou_" - val (arrangement, backupAndRestoreViewModel) = Arrangement() + val (_, backupAndRestoreViewModel) = Arrangement() .withValidPassword() .arrange() @@ -196,13 +200,13 @@ class BackupAndRestoreViewModelTest { // Then assertEquals(backupAndRestoreViewModel.state.backupCreationProgress, BackupCreationProgress.Failed) assert(backupAndRestoreViewModel.latestCreatedBackup == null) - coVerify(exactly = 1) { arrangement.createBackupFile(password = password) } + coVerify(exactly = 1) { arrangement.createMpBackupFile(password = password) } } @Test fun givenACreatedBackup_whenSharingIt_thenTheStateIsResetButKeepsTheLastBackupDate() = runTest { // Given - val storedBackup = BackupAndRestoreState.CreatedBackup("backupFilePath".toPath(), "backupName.zip", 100L, true) + val storedBackup = BackupAndRestoreState.CreatedBackup("backupFilePath".toPath(), "backupName.zip", true) val (arrangement, backupAndRestoreViewModel) = Arrangement() .withPreviouslyCreatedBackup(storedBackup) .withUpdateLastBackupData() @@ -235,7 +239,7 @@ class BackupAndRestoreViewModelTest { @Test fun givenACreatedBackup_whenSavingIt_thenTheStateIsResetButKeepsTheLastBackupDate() = runTest(dispatcher.default()) { // Given - val storedBackup = BackupAndRestoreState.CreatedBackup("backupFilePath".toPath(), "backupName.zip", 100L, true) + val storedBackup = BackupAndRestoreState.CreatedBackup("backupFilePath".toPath(), "backupName.zip", true) val (arrangement, backupAndRestoreViewModel) = Arrangement() .withPreviouslyCreatedBackup(storedBackup) .withUpdateLastBackupData() @@ -500,13 +504,19 @@ class BackupAndRestoreViewModelTest { withGetLastBackupDateSeconds() every { Uri.parse("some-backup") } returns mockUri coEvery { importBackup(any(), any()) } returns RestoreBackupResult.Success - coEvery { createBackupFile(any()) } returns CreateBackupResult.Success("".toPath(), 0L, "") - coEvery { verifyBackup(any()) } returns VerifyBackupResult.Success.Encrypted + coEvery { createMpBackupFile(any()) } returns CreateBackupResult.Success("".toPath(), "") + coEvery { verifyBackup(any()) } returns VerifyBackupResult.Success(BackupFileFormat.ANDROID, true) } @MockK lateinit var importBackup: RestoreBackupUseCase + @MockK + lateinit var importMpBackup: RestoreMPBackupUseCase + + @MockK + lateinit var createMpBackupFile: CreateMPBackupUseCase + @MockK lateinit var createBackupFile: CreateBackupUseCase @@ -526,24 +536,28 @@ class BackupAndRestoreViewModelTest { private val viewModel = BackupAndRestoreViewModel( importBackup = importBackup, - createBackupFile = createBackupFile, + importMpBackup = importMpBackup, + createMpBackupFile = createMpBackupFile, verifyBackup = verifyBackup, kaliumFileSystem = fakeKaliumFileSystem, dispatcher = dispatcher, fileManager = fileManager, validatePassword = validatePassword, - userDataStore = userDataStore + userDataStore = userDataStore, + createBackupFile = createBackupFile, + mpBackupSettings = MPBackupSettings.Enabled, ) fun withSuccessfulCreation(password: String) = apply { val backupFilePath = "some-file-path".toPath() - val backupSize = 1000L val backupName = "some-backup.zip" - coEvery { createBackupFile(eq(password)) } returns CreateBackupResult.Success(backupFilePath, backupSize, backupName) + coEvery { createMpBackupFile(eq(password)) } returns CreateBackupResult.Success(backupFilePath, backupName) } fun withFailedCreation(password: String) = apply { - coEvery { createBackupFile(eq(password)) } returns CreateBackupResult.Failure(CoreFailure.Unknown(IOException("Some db error"))) + coEvery { + createMpBackupFile(eq(password)) + } returns CreateBackupResult.Failure(CoreFailure.Unknown(IOException("Some db error"))) } fun withPreviouslyCreatedBackup(backup: BackupAndRestoreState.CreatedBackup) = apply { @@ -571,7 +585,10 @@ class BackupAndRestoreViewModelTest { } coEvery { verifyBackup(any()) } returns - if (isEncrypted) VerifyBackupResult.Success.Encrypted else VerifyBackupResult.Success.NotEncrypted + VerifyBackupResult.Success( + format = BackupFileFormat.ANDROID, + isEncrypted = isEncrypted + ) coEvery { importBackup(any(), any()) } returns RestoreBackupResult.Success } @@ -596,7 +613,10 @@ class BackupAndRestoreViewModelTest { } } - coEvery { verifyBackup(any()) } returns VerifyBackupResult.Success.NotEncrypted + coEvery { verifyBackup(any()) } returns VerifyBackupResult.Success( + format = BackupFileFormat.ANDROID, + isEncrypted = false + ) coEvery { importBackup(any(), any()) } returns error } diff --git a/buildSrc/src/main/kotlin/customization/FeatureConfigs.kt b/buildSrc/src/main/kotlin/customization/FeatureConfigs.kt index 171db140cbd..ecf6931481f 100644 --- a/buildSrc/src/main/kotlin/customization/FeatureConfigs.kt +++ b/buildSrc/src/main/kotlin/customization/FeatureConfigs.kt @@ -49,6 +49,7 @@ enum class FeatureConfigs(val value: String, val configType: ConfigType) { WEBSOCKET_ENABLED_BY_DEFAULT("websocket_enabled_by_default", ConfigType.BOOLEAN), TEAM_APP_LOCK("team_app_lock", ConfigType.BOOLEAN), TEAM_APP_LOCK_TIMEOUT("team_app_lock_timeout", ConfigType.INT), + ENABLE_CROSSPLATFORM_BACKUP("enable_crossplatform_backup", ConfigType.BOOLEAN), /** * Security/Cryptography stuff diff --git a/default.json b/default.json index 4e9e83bcc86..0bea757f92b 100644 --- a/default.json +++ b/default.json @@ -9,7 +9,8 @@ "paginated_conversation_list_enabled": true, "analytics_enabled": true, "analytics_app_key": "4483f7a58ae3e70b3780319c4ccb5c88a037be49", - "analytics_server_url": "https://countly.wire.com/" + "analytics_server_url": "https://countly.wire.com/", + "enable_crossplatform_backup": false }, "dev": { "application_id": "com.waz.zclient.dev", @@ -31,7 +32,8 @@ "default_backend_title": "wire-anta", "analytics_enabled": false, "analytics_app_key": "8ffae535f1836ed5f58fd5c8a11c00eca07c5438", - "analytics_server_url": "https://countly.wire.com/" + "analytics_server_url": "https://countly.wire.com/", + "enable_crossplatform_backup": true }, "staging": { "application_id": "com.waz.zclient.dev", @@ -52,7 +54,8 @@ "default_backend_title": "wire-staging", "analytics_enabled": true, "analytics_app_key": "8ffae535f1836ed5f58fd5c8a11c00eca07c5438", - "analytics_server_url": "https://countly.wire.com/" + "analytics_server_url": "https://countly.wire.com/", + "enable_crossplatform_backup": true }, "beta": { "application_id": "com.wire.android.internal", @@ -62,7 +65,8 @@ "development_api_enabled": false, "analytics_enabled": true, "analytics_app_key": "8ffae535f1836ed5f58fd5c8a11c00eca07c5438", - "analytics_server_url": "https://countly.wire.com/" + "analytics_server_url": "https://countly.wire.com/", + "enable_crossplatform_backup": true }, "internal": { "application_id": "com.wire.internal", @@ -73,7 +77,8 @@ "analytics_enabled": true, "analytics_app_key": "8ffae535f1836ed5f58fd5c8a11c00eca07c5438", "analytics_server_url": "https://countly.wire.com/", - "paginated_conversation_list_enabled": true + "paginated_conversation_list_enabled": true, + "enable_crossplatform_backup": true }, "fdroid": { "application_id": "com.wire", @@ -83,7 +88,8 @@ "development_api_enabled": false, "analytics_enabled": false, "analytics_app_key": "", - "analytics_server_url": "" + "analytics_server_url": "", + "enable_crossplatform_backup": false } }, "application_name": "Wire", diff --git a/kalium b/kalium index 0b8971aec4f..4f958e800c3 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 0b8971aec4f65ba20f9e459dea3b507d1820c8fa +Subproject commit 4f958e800c3bd606a6be6bbeb68e632352943f09