Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ class ExportObfuscatedCopyViewModelImpl @OptIn(DelicateKaliumApi::class) @Inject
latestCreatedBackup = BackupAndRestoreState.CreatedBackup(
result.backupFilePath,
result.backupFileName,
result.backupFileSize,
false
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -39,7 +41,8 @@ data class BackupAndRestoreState(
backupCreationProgress = BackupCreationProgress.InProgress(),
restorePasswordValidation = PasswordValidation.NotVerified,
passwordValidation = ValidatePasswordResult.Valid,
lastBackupData = null
lastBackupData = null,
backupFileFormat = BackupFileFormat.ANDROID,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
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
Expand All @@ -38,15 +37,17 @@
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
import com.wire.kalium.logic.feature.backup.RestoreBackupResult.BackupRestoreFailure.IncompatibleBackup
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
Expand All @@ -63,7 +64,8 @@
class BackupAndRestoreViewModel
@Inject constructor(
private val importBackup: RestoreBackupUseCase,
private val createBackupFile: CreateBackupUseCase,
private val importMpBackup: RestoreMPBackupUseCase,
private val createMpBackupFile: CreateMPBackupUseCase,
private val verifyBackup: VerifyBackupUseCase,
private val validatePassword: ValidatePasswordUseCase,
private val kaliumFileSystem: KaliumFileSystem,
Expand Down Expand Up @@ -111,13 +113,12 @@
updateCreationProgress(PROGRESS_50)
delay(SMALL_DELAY)

when (val result = createBackupFile(createBackupPasswordState.text.toString())) {
when (val result = createMpBackupFile(createBackupPasswordState.text.toString())) {
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()
Expand Down Expand Up @@ -170,27 +171,25 @@
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)
}
}

Expand All @@ -199,6 +198,15 @@
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

Check warning on line 206 in app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt#L203-L206

Added lines #L203 - L206 were not covered by tests
)
"Invalid user ID"

Check warning on line 208 in app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt#L208

Added line #L208 was not covered by tests
}
}

AnonymousAnalyticsManagerImpl.sendEvent(event = AnalyticsEvent.BackupRestoreFailed)
Expand All @@ -207,30 +215,6 @@
}
}

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),
Expand All @@ -240,21 +224,8 @@
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)
Expand Down Expand Up @@ -317,6 +288,30 @@
}
}

private fun restoreBackup(backupFilePath: Path, password: String?) = viewModelScope.launch {
val result = when (state.backupFileFormat) {
BackupFileFormat.ANDROID -> importBackup(backupFilePath, password)
BackupFileFormat.MULTIPLATFORM -> importMpBackup(backupFilePath, password)

Check warning on line 294 in app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/settings/backup/BackupAndRestoreViewModel.kt#L294

Added line #L294 was not covered by tests
}
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))
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1343,7 +1343,7 @@ In group conversations, the group admin can overwrite this setting.</string>
<string name="backup_label_conversation_restored">Conversations have been restored</string>
<string name="backup_label_loading_conversations">Loading conversations</string>
<string name="backup_dialog_restore_backup_title">Restore from backup </string>
<string name="backup_dialog_restore_backup_message">The backup contents will replace the conversation history on this device. You can only restore history from a backup of the same platform.</string>
<string name="backup_dialog_restore_backup_message">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.</string>
<string name="backup_dialog_choose_backup_file_option">Choose Backup File</string>
<string name="backup_label_enter_password">Enter password</string>
<string name="backup_dialog_restore_backup_password_message">This backup is password protected.</string>
Expand Down
Loading
Loading