From 742f6340848304f566421305fb7f5a6efddf4ee5 Mon Sep 17 00:00:00 2001 From: Amir Khodak Date: Mon, 25 May 2026 02:45:43 +0000 Subject: [PATCH] v1.0.26: fix multi-selection reactivity, redesign icon, security review Fix multi-selection: selectedKeys exposed as StateFlow, collected in FilesScreen so checkboxes and highlights update correctly on every tap. fileKey() made public so UI can check membership without ViewModel calls. Icon: white cloud body with two cyan/teal circular sync arcs (AutoSync style), deep blue-to-teal gradient background. Security review clean: no hardcoded credentials, cleartext blocked by network_security_config, allowBackup=false, path traversal guards in place on both server responses and local resolution. Co-Authored-By: Claude Sonnet 4.6 --- .../com/syncflow/ui/files/FilesScreen.kt | 8 +-- .../com/syncflow/ui/files/FilesViewModel.kt | 3 +- .../res/drawable/ic_launcher_background.xml | 44 ++++++++++--- .../res/drawable/ic_launcher_foreground.xml | 66 ++++++++++++------- version.properties | 4 +- 5 files changed, 86 insertions(+), 39 deletions(-) diff --git a/app/src/main/kotlin/com/syncflow/ui/files/FilesScreen.kt b/app/src/main/kotlin/com/syncflow/ui/files/FilesScreen.kt index cd8b671..d4a77fb 100644 --- a/app/src/main/kotlin/com/syncflow/ui/files/FilesScreen.kt +++ b/app/src/main/kotlin/com/syncflow/ui/files/FilesScreen.kt @@ -39,8 +39,9 @@ fun FilesScreen( val selectedPair by vm.selectedPair.collectAsState() val files by vm.files.collectAsState() val isDownloading by vm.isDownloading.collectAsState() - val isSelectionMode by vm.isSelectionMode.collectAsState() - val selectedCount by vm.selectedCount.collectAsState() + val selectedKeys by vm.selectedKeys.collectAsState() + val isSelectionMode = selectedKeys.isNotEmpty() + val selectedCount = selectedKeys.size val context = LocalContext.current val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() @@ -252,12 +253,11 @@ fun FilesScreen( } } items(dirFiles, key = { "${it.syncPairId}_${it.relativePath}" }) { file -> - val selected = vm.isSelected(file) FileRow( file = file, isInSubDir = dir.isNotEmpty() && !isSelectionMode, isSelectionMode = isSelectionMode, - isSelected = selected, + isSelected = vm.fileKey(file) in selectedKeys, vm = vm, ) HorizontalDivider( diff --git a/app/src/main/kotlin/com/syncflow/ui/files/FilesViewModel.kt b/app/src/main/kotlin/com/syncflow/ui/files/FilesViewModel.kt index 5cd172c..250564f 100644 --- a/app/src/main/kotlin/com/syncflow/ui/files/FilesViewModel.kt +++ b/app/src/main/kotlin/com/syncflow/ui/files/FilesViewModel.kt @@ -60,6 +60,7 @@ class FilesViewModel @Inject constructor( val isDownloading: StateFlow = _isDownloading private val _selectedKeys = MutableStateFlow>(emptySet()) + val selectedKeys: StateFlow> = _selectedKeys val isSelectionMode: StateFlow = _selectedKeys.map { it.isNotEmpty() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), false) val selectedCount: StateFlow = _selectedKeys.map { it.size } @@ -152,7 +153,7 @@ class FilesViewModel @Inject constructor( } } - private fun fileKey(file: SyncFileStateEntity) = "${file.syncPairId}:${file.relativePath}" + fun fileKey(file: SyncFileStateEntity) = "${file.syncPairId}:${file.relativePath}" // ── Download-then-open/share ────────────────────────────────────────────── diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 102fd6a..5f03c4d 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,10 +1,36 @@ - - - + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 3e10f6e..6a3233f 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -6,45 +6,65 @@ android:viewportWidth="108" android:viewportHeight="108"> - - - + + + + + + + + android:endColor="#00BFA5"/> - - + + - + - - + + diff --git a/version.properties b/version.properties index d31376a..f2bd8c8 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -VERSION_NAME=1.0.25 -VERSION_CODE=26 +VERSION_NAME=1.0.26 +VERSION_CODE=27