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