diff --git a/src/main/java/com/machiav3lli/backup/ui/compose/component/BatchPackageItem.kt b/src/main/java/com/machiav3lli/backup/ui/compose/component/BatchPackageItem.kt index b9bf2ca14..69764c06f 100644 --- a/src/main/java/com/machiav3lli/backup/ui/compose/component/BatchPackageItem.kt +++ b/src/main/java/com/machiav3lli/backup/ui/compose/component/BatchPackageItem.kt @@ -149,6 +149,7 @@ fun RestorePackageItem( onClick: (Package, Boolean, Boolean) -> Unit = { _: Package, _: Boolean, _: Boolean -> }, onBackupApkClick: (String, Boolean, Int) -> Unit = { _: String, _: Boolean, _: Int -> }, onBackupDataClick: (String, Boolean, Int) -> Unit = { _: String, _: Boolean, _: Int -> }, + searchQuery: String = "", ) { val packageItem by remember(item) { mutableStateOf(item) } val apkBC by apkBackupChecked @@ -239,9 +240,19 @@ fun RestorePackageItem( ) } } - item.backupsNewestFirst.forEachIndexed { index, item -> + // filter by note search, but keep the original indices of + // backupsNewestFirst, since they are used to address the + // backup to restore + val indexedBackups = + item.backupsNewestFirst.mapIndexed { index, backup -> index to backup } + val displayedBackups = + if (searchQuery.isEmpty()) indexedBackups + else indexedBackups + .filter { it.second.note.contains(searchQuery, ignoreCase = true) } + .ifEmpty { indexedBackups } + displayedBackups.forEach { (index, backup) -> RestoreBackupItem( - item = item, + item = backup, index = index, isApkChecked = index == apkBC, isDataChecked = index == dataBC, diff --git a/src/main/java/com/machiav3lli/backup/ui/compose/component/Recyclers.kt b/src/main/java/com/machiav3lli/backup/ui/compose/component/Recyclers.kt index dc7a8a44c..505407fff 100644 --- a/src/main/java/com/machiav3lli/backup/ui/compose/component/Recyclers.kt +++ b/src/main/java/com/machiav3lli/backup/ui/compose/component/Recyclers.kt @@ -104,6 +104,7 @@ fun BatchPackageRecycler( modifier: Modifier = Modifier, productsList: PersistentList?, restore: Boolean = false, + searchQuery: String = "", apkBackupCheckedList: SnapshotStateMap, dataBackupCheckedList: SnapshotStateMap, onBackupApkClick: (String, Boolean, Int) -> Unit = { _: String, _: Boolean, _: Int -> }, @@ -129,6 +130,7 @@ fun BatchPackageRecycler( onClick, onBackupApkClick, onBackupDataClick, + searchQuery, ) else BatchPackageItem( it, diff --git a/src/main/java/com/machiav3lli/backup/ui/pages/AppPage.kt b/src/main/java/com/machiav3lli/backup/ui/pages/AppPage.kt index 7ca7273ae..e5860ce0d 100644 --- a/src/main/java/com/machiav3lli/backup/ui/pages/AppPage.kt +++ b/src/main/java/com/machiav3lli/backup/ui/pages/AppPage.kt @@ -113,6 +113,7 @@ import com.machiav3lli.backup.ui.dialogs.RestoreDialogUI import com.machiav3lli.backup.ui.dialogs.StringInputDialogUI import com.machiav3lli.backup.utils.BackupShareUtils import com.machiav3lli.backup.utils.TraceUtils +import com.machiav3lli.backup.utils.applyNoteSearch import com.machiav3lli.backup.utils.extensions.koinNeoViewModel import com.machiav3lli.backup.utils.infoChips import com.machiav3lli.backup.viewmodels.AppVM @@ -121,6 +122,7 @@ import timber.log.Timber @Composable fun AppPage( packageName: String, + searchQuery: String = "", viewModel: AppVM = koinNeoViewModel(), onDismiss: () -> Unit, ) { @@ -502,7 +504,7 @@ fun AppPage( } } this.items( - items = pkg.backupsNewestFirst, + items = pkg.backupsNewestFirst.applyNoteSearch(searchQuery), span = { GridItemSpan(columns) }) { BackupItem( it, diff --git a/src/main/java/com/machiav3lli/backup/ui/pages/BatchPage.kt b/src/main/java/com/machiav3lli/backup/ui/pages/BatchPage.kt index 8e45dd5ba..0c265b8b9 100644 --- a/src/main/java/com/machiav3lli/backup/ui/pages/BatchPage.kt +++ b/src/main/java/com/machiav3lli/backup/ui/pages/BatchPage.kt @@ -228,6 +228,7 @@ fun BatchPage( .fillMaxSize(), productsList = state.filteredPackages, restore = !backupBoolean, + searchQuery = state.searchQuery, apkBackupCheckedList = viewModel.apkBackupCheckedList, dataBackupCheckedList = viewModel.dataBackupCheckedList, onBackupApkClick = { packageName: String, b: Boolean, i: Int -> diff --git a/src/main/java/com/machiav3lli/backup/ui/pages/HomePage.kt b/src/main/java/com/machiav3lli/backup/ui/pages/HomePage.kt index d02da9fca..a3354db4b 100644 --- a/src/main/java/com/machiav3lli/backup/ui/pages/HomePage.kt +++ b/src/main/java/com/machiav3lli/backup/ui/pages/HomePage.kt @@ -337,6 +337,7 @@ fun HomePage( AnimatedPane { AppPage( packageName = packageName, + searchQuery = mainState.searchQuery, onDismiss = onDismiss, ) } diff --git a/src/main/java/com/machiav3lli/backup/utils/FilterUtils.kt b/src/main/java/com/machiav3lli/backup/utils/FilterUtils.kt index b9f6c466a..a18ad2b3b 100644 --- a/src/main/java/com/machiav3lli/backup/utils/FilterUtils.kt +++ b/src/main/java/com/machiav3lli/backup/utils/FilterUtils.kt @@ -38,6 +38,7 @@ import com.machiav3lli.backup.R import com.machiav3lli.backup.Sort import com.machiav3lli.backup.UpdatedFilter import com.machiav3lli.backup.data.dbs.entity.AppExtras +import com.machiav3lli.backup.data.dbs.entity.Backup import com.machiav3lli.backup.data.entity.Package import com.machiav3lli.backup.data.entity.SortFilterModel import com.machiav3lli.backup.data.entity.SpecialFilter @@ -106,9 +107,18 @@ fun Collection.applySearch( ) ) .any { it.contains(query, ignoreCase = true) } - ) + ) || item.backupList.any { it.note.contains(query, ignoreCase = true) } } +/** + * Filters a list of backups by matching their note against the search query. + * Falls back to the full list when no backup's note matches + * (e.g. when the app was found by its name instead of a note). + */ +fun List.applyNoteSearch(query: String): List = + if (query.isEmpty()) this + else filter { it.note.contains(query, ignoreCase = true) }.ifEmpty { this } + fun Collection.applyFilter( filter: SortFilterModel, tagsMap: Map>,