From e3ff7cbff63076a4f9212f263a6cf017006e2337 Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Wed, 30 Sep 2026 11:12:01 +0200 Subject: [PATCH 1/4] feat: Backup the app data properly, and backup tokens with Block Store --- Core | 2 +- app/src/main/AndroidManifest.xml | 2 + .../backup/SwissTransferFullBackupAgent.kt | 35 ++++++++++++ .../swisstransfer/ui/MainApplication.kt | 53 +++++++++++-------- app/src/main/res/xml/backup_rules.xml | 20 ++++--- .../main/res/xml/data_extraction_rules.xml | 38 ++++++++----- 6 files changed, 106 insertions(+), 44 deletions(-) create mode 100644 app/src/main/java/com/infomaniak/swisstransfer/backup/SwissTransferFullBackupAgent.kt diff --git a/Core b/Core index 12d648f83..157673f75 160000 --- a/Core +++ b/Core @@ -1 +1 @@ -Subproject commit 12d648f832cba5b1ee7554c9f135993033b7f40b +Subproject commit 157673f75373404f1e73a2ff20b347a75ec92600 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7464268d7..d181cb7e2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,9 +38,11 @@ . + */ +package com.infomaniak.swisstransfer.backup + +import android.app.backup.FullBackupDataOutput +import com.infomaniak.core.auth.backup.withBlockStoreCredentialsBackup +import com.infomaniak.core.common.backup.FullBackupAgent +import com.infomaniak.swisstransfer.ui.MainApplication + +class SwissTransferFullBackupAgent : FullBackupAgent(RestorationPolicy.AllBackedUpFiles) { + + override fun onCreate() { + super.onCreate() + MainApplication.configureSentry() + } + + override fun onFullBackup(data: FullBackupDataOutput) = withBlockStoreCredentialsBackup { + super.onFullBackup(data) + } +} diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/MainApplication.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/MainApplication.kt index c2909c467..6559203e6 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/MainApplication.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/MainApplication.kt @@ -18,6 +18,8 @@ package com.infomaniak.swisstransfer.ui import android.app.Application +import android.app.backup.BackupAgent +import android.content.Context import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import com.infomaniak.core.common.AssociatedUserDataCleanable @@ -93,8 +95,6 @@ class MainApplication : Application(), Configuration.Provider { override val workManagerConfiguration: Configuration get() = Configuration.Builder().setWorkerFactory(workerFactory).build() - private val applicationScope = CoroutineScope(Dispatchers.Default + CoroutineName("MainApplication")) - override fun onCreate() { super.onCreate() @@ -136,30 +136,37 @@ class MainApplication : Application(), Configuration.Provider { ) } - /** - * Reasons to discard Sentry events : - * - Application is in Debug mode - * - User deactivated Sentry tracking in DataManagement settings - * - The exception was a NetworkException or a [CancellationException] or a [KmpNetworkException], - * and we don't want to send them to Sentry - */ - private fun configureSentry() { - val isSentryEnabled: StateFlow = dataManagementDataStore.data - .map { prefs -> - prefs[IsSentryAuthorized.dataStoreKey] ?: DataManagementPreferencesDefaults.IsSentryAuthorizedDefault - } - .stateIn(applicationScope, SharingStarted.Eagerly, initialValue = false) - - this.configureSentry( - isDebug = BuildConfig.DEBUG, - isSentryTrackingEnabled = { isSentryEnabled.value }, - isFilteredException = { exception -> exception is KmpNetworkException }, - ) - } - companion object { @JvmStatic var userDataCleanableList: List = emptyList() private set + + private val applicationScope = CoroutineScope(Dispatchers.Default + CoroutineName("MainApplication")) + + context(context: BackupAgent) + fun configureSentry() { + context.configureSentry() + } + + /** + * Reasons to discard Sentry events : + * - Application is in Debug mode + * - User deactivated Sentry tracking in DataManagement settings + * - The exception was a NetworkException or a [CancellationException] or a [KmpNetworkException], + * and we don't want to send them to Sentry + */ + private fun Context.configureSentry() { + val isSentryEnabled: StateFlow = dataManagementDataStore.data + .map { prefs -> + prefs[IsSentryAuthorized.dataStoreKey] ?: DataManagementPreferencesDefaults.IsSentryAuthorizedDefault + } + .stateIn(applicationScope, SharingStarted.Eagerly, initialValue = false) + + configureSentry( + isDebug = BuildConfig.DEBUG, + isSentryTrackingEnabled = { isSentryEnabled.value }, + isFilteredException = { exception -> exception is KmpNetworkException }, + ) + } } } diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml index 04b95c673..981c6ab66 100644 --- a/app/src/main/res/xml/backup_rules.xml +++ b/app/src/main/res/xml/backup_rules.xml @@ -17,13 +17,17 @@ --> - - - - - + - - - + + + + + + + + + + + diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml index c2056997a..46181ad04 100644 --- a/app/src/main/res/xml/data_extraction_rules.xml +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -23,22 +23,36 @@ --> - - - - - + + + + + + - - - + + + + + + - - + + - - + + + + + + + + + + + + From 170f3a465d8f08a0930a45d7cae91be64c15c51f Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Wed, 30 Sep 2026 12:52:06 +0200 Subject: [PATCH 2/4] fix: Stop forwarding a ViewModel Fixes lint error. --- .../ExistingTransferFilesDetailsScreen.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/components/ExistingTransferFilesDetailsScreen.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/components/ExistingTransferFilesDetailsScreen.kt index 268d94e2f..348f7b6c0 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/components/ExistingTransferFilesDetailsScreen.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/components/ExistingTransferFilesDetailsScreen.kt @@ -127,10 +127,14 @@ fun ExistingTransferFilesDetailsScreen( val selectedFiles = fileList.filter { it.uid in selectedUids } ExistingTransferFilesDetailsBottomBar( selectedFiles = { selectedFiles }, - transferIdType = transferIdType, - filesDetailsViewModel = filesDetailsViewModel, + onDownloadSelectionClick = { + filesDetailsViewModel.triggerFilesSelectionDownload( + transferIdType = transferIdType, + selectedFiles = selectedFiles, + direction = transferDirection + ) + }, onCancelSelection = onCancelSelection, - direction = transferDirection, ) } } @@ -217,9 +221,7 @@ private fun ExistingTransferFilesDetailsTopBar( @Composable private fun ExistingTransferFilesDetailsBottomBar( selectedFiles: () -> List, - transferIdType: TransferIdType, - direction: TransferDirection, - filesDetailsViewModel: FilesDetailsViewModel, + onDownloadSelectionClick: () -> Unit, onCancelSelection: () -> Unit, ) { val writePermissionManager = rememberPermissionManagerState(PermissionType.WriteExternalStorage) @@ -229,7 +231,7 @@ private fun ExistingTransferFilesDetailsBottomBar( labelResId = R.string.buttonDownloadSelected, enabled = selectedFiles().isNotEmpty(), onClick = writePermissionManager.dropIfDenied { - filesDetailsViewModel.triggerFilesSelectionDownload(transferIdType, selectedFiles(), direction) + onDownloadSelectionClick() onCancelSelection() }, modifier = Modifier.weight(1f), From 427e3bbb4128cd6d3181c277ef0253f6416d9cdf Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Wed, 30 Sep 2026 13:33:15 +0200 Subject: [PATCH 3/4] chore: Address lint error by making composable movable --- .../ui/components/transfer/TransferItemList.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/components/transfer/TransferItemList.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/components/transfer/TransferItemList.kt index dc2b33b35..85934b826 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/components/transfer/TransferItemList.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/components/transfer/TransferItemList.kt @@ -32,6 +32,8 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.movableContentOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext @@ -64,6 +66,7 @@ fun TransferItemList( emptyState: @Composable () -> Unit, modifier: Modifier = Modifier, ) { + val movableEmptyState = remember(emptyState) { movableContentOf { emptyState() } } val headerContent = @Composable { Column(verticalArrangement = Arrangement.spacedBy(Margin.Mini)) { SmallOrMediumWindowScreenTitle(title = header.title) @@ -79,10 +82,10 @@ fun TransferItemList( if (header.showWhenEmpty()) { Column(modifier = Modifier.fillMaxHeight()) { Box(modifier = Modifier.padding(horizontal = Margin.Medium, vertical = Margin.Large)) { headerContent() } - Box(modifier = Modifier.weight(1.0f)) { emptyState() } + Box(modifier = Modifier.weight(1.0f)) { movableEmptyState() } } } else { - emptyState() + movableEmptyState() } } else { TransferItemList( From 58cc9f4fe27aea9a6b7463ee6ed2b0a260c6aa1a Mon Sep 17 00:00:00 2001 From: Louis CAD Date: Wed, 30 Sep 2026 13:33:41 +0200 Subject: [PATCH 4/4] chore: Suppress Compose lint errors --- .../ui/screen/main/settings/components/SettingItem.kt | 1 + .../ui/screen/main/transferdetails/TransferDownloadComposeUi.kt | 1 + .../ui/screen/main/transfers/TransfersScreenWrapper.kt | 2 ++ 3 files changed, 4 insertions(+) diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SettingItem.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SettingItem.kt index fc9f963a0..b3af72bcc 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SettingItem.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/settings/components/SettingItem.kt @@ -67,6 +67,7 @@ fun SettingItem( .fillMaxWidth() .heightIn(min = ITEM_MIN_HEIGHT) + @Suppress("ComposeModifierReused") // Fine because it's only used once at a time. onClick?.let { SharpRippleButton( modifier = itemModifier, diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDownloadComposeUi.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDownloadComposeUi.kt index 75de5870b..53962a43a 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDownloadComposeUi.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transferdetails/TransferDownloadComposeUi.kt @@ -289,6 +289,7 @@ class TransferDownloadComposeUi( @Composable private fun DownloadStatus( + @Suppress("SlotReused") // Irrelevant for our use case where the parameters change on each when branch. button: @Composable ( data: ButtonData, action: CallableState, diff --git a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transfers/TransfersScreenWrapper.kt b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transfers/TransfersScreenWrapper.kt index 777cdf19f..748153edb 100644 --- a/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transfers/TransfersScreenWrapper.kt +++ b/app/src/main/java/com/infomaniak/swisstransfer/ui/screen/main/transfers/TransfersScreenWrapper.kt @@ -66,6 +66,7 @@ import com.infomaniak.swisstransfer.ui.utils.isWindowSmall import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize +@Suppress("ComposeViewModelForwarding") @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun TransfersScreenWrapper( @@ -125,6 +126,7 @@ private fun ThreePaneScaffoldNavigator.HandleDeepLink( } } +@Suppress("ComposeViewModelForwarding") @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable private fun ListPane(