From 189712c5dc2df937e1bb6b4f2a6a9f59b9ddaac8 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sun, 17 Sep 2023 13:07:57 +0200 Subject: [PATCH 1/9] feat: default patches selection --- .../ui/screen/PatchesSelectorScreen.kt | 3 +- .../ui/viewmodel/PatchesSelectorViewModel.kt | 84 +++++++++++++++++-- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 722e65e1b6..610d62956e 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -107,7 +107,8 @@ fun PatchesSelectorScreen( onClick = { composableScope.launch { // TODO: only allow this if all required options have been set. - onPatchClick(vm.getAndSaveSelection(), vm.getOptions()) + onPatchClick(vm.getSelection(), vm.getOptions()) + // onPatchClick(vm.getAndSaveSelection(), vm.getOptions()) } } ) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index b5c6b67065..d5d9091e11 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -68,8 +68,9 @@ class PatchesSelectorViewModel( } } + /* private val selectedPatches: SnapshotStatePatchesSelection by savedStateHandle.saveable( - saver = patchesSelectionSaver, + saver = userPatchesSelectionSasver, init = { val map: SnapshotStatePatchesSelection = mutableStateMapOf() viewModelScope.launch(Dispatchers.Default) { @@ -94,12 +95,54 @@ class PatchesSelectorViewModel( } } return@saveable map - }) + })*/ + private val previousPatchSelection: SnapshotStateMap> = mutableStateMapOf() + init { + viewModelScope.launch(Dispatchers.Default) { + // val bundles = bundlesFlow.first() + val unfilteredSelection = input.patchesSelection ?: selectionRepository.getSelection(input.selectedApp.packageName) + val filteredSelection = unfilteredSelection.mapValues { (_, value) -> value.toSet() } + /* + val filteredSelection = unfilteredSelection.mapValues { (uid, patches) -> + // Filter out patches that don't exist. + val filteredPatches = bundles.singleOrNull { it.uid == uid } + ?.let { bundle -> + val allPatches = bundle.all.map { it.name } + patches.filter { allPatches.contains(it) } + } + ?: patches + filteredPatches.toSet() + }*/ + + withContext(Dispatchers.Main) { + previousPatchSelection.putAll(filteredSelection) + } + } + } + + private val userSelection: SnapshotStateUserPatchesSelection by savedStateHandle.saveable( + saver = userPatchesSelectionSaver, + init = ::mutableStateMapOf + ) + private val patchOptions: SnapshotStateOptions by savedStateHandle.saveable( saver = optionsSaver, init = ::mutableStateMapOf ) + private val selectors: List = listOf( + { bundle, patch -> + userSelection[bundle]?.get(patch.name) + }, + { bundle, patch -> + // previousPatchSelection[bundle]?.contains(patch.name) + null + }, + { _, patch -> + patch.include + } + ) + /** * Show the patch options dialog for this patch. */ @@ -111,18 +154,35 @@ class PatchesSelectorViewModel( private set private fun getOrCreateSelection(bundle: Int) = - selectedPatches.getOrPut(bundle, ::mutableStateSetOf) + userSelection.getOrPut(bundle, ::mutableStateMapOf) fun isSelected(bundle: Int, patch: PatchInfo) = - selectedPatches[bundle]?.contains(patch.name) ?: false + selectors.firstNotNullOf { fn -> fn(bundle, patch) } fun togglePatch(bundle: Int, patch: PatchInfo) { - val name = patch.name val patches = getOrCreateSelection(bundle) - if (patches.contains(name)) patches.remove(name) else patches.add(name) + patches[patch.name] = !isSelected(bundle, patch) } + suspend fun getSelection(): PatchesSelection { + val bundles = bundlesFlow.first() + val removeUnsupported = !allowExperimental.get() + + return bundles.associate { bundle -> + val included = bundle.all.filter { isSelected(bundle.uid, it) }.map { it.name }.toMutableSet() + + if (removeUnsupported) { + val unsupported = bundle.unsupported.map { it.name }.toSet() + included.removeAll(unsupported) + } + + bundle.uid to included + } + } + + // TODO: reimplement this. + /* suspend fun getAndSaveSelection(): PatchesSelection = selectedPatches.also { withContext(Dispatchers.Default) { @@ -138,6 +198,7 @@ class PatchesSelectorViewModel( this[it.uid]?.removeAll(it.unsupported.map { patch -> patch.name }.toSet()) } } + */ fun getOptions(): Options = patchOptions fun getOptions(bundle: Int, patch: PatchInfo) = patchOptions[bundle]?.get(patch.name) @@ -188,8 +249,8 @@ class PatchesSelectorViewModel( ) ) - private val patchesSelectionSaver: Saver = - snapshotStateMapSaver(valueSaver = snapshotStateSetSaver()) + private val userPatchesSelectionSaver: Saver = + snapshotStateMapSaver(valueSaver = snapshotStateMapSaver()) } data class BundleInfo( @@ -210,4 +271,9 @@ private typealias SnapshotStateOptions = SnapshotStateMap> \ No newline at end of file +private typealias SnapshotStatePatchesSelection = SnapshotStateMap> + +private typealias UserPatchesSelection = Map> +private typealias SnapshotStateUserPatchesSelection = SnapshotStateMap> + +private typealias Selector = (Int, PatchInfo) -> Boolean? \ No newline at end of file From e79ce8b03bbfec251ace2d3bac1a47b634c12991 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sun, 17 Sep 2023 15:58:12 +0200 Subject: [PATCH 2/9] re-add the ability to save selection --- .../data/room/selection/SelectionDao.kt | 3 + .../repository/PatchSelectionRepository.kt | 4 + .../ui/screen/PatchesSelectorScreen.kt | 45 ++++- .../ui/viewmodel/PatchesSelectorViewModel.kt | 172 +++++++++--------- app/src/main/res/values/strings.xml | 1 + 5 files changed, 138 insertions(+), 87 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/data/room/selection/SelectionDao.kt b/app/src/main/java/app/revanced/manager/data/room/selection/SelectionDao.kt index 2e288d9793..630c5d66e8 100644 --- a/app/src/main/java/app/revanced/manager/data/room/selection/SelectionDao.kt +++ b/app/src/main/java/app/revanced/manager/data/room/selection/SelectionDao.kt @@ -35,6 +35,9 @@ abstract class SelectionDao { @Query("DELETE FROM patch_selections WHERE patch_bundle = :uid") abstract suspend fun clearForPatchBundle(uid: Int) + @Query("DELETE FROM patch_selections WHERE package_name = :packageName") + abstract suspend fun clearForPackage(packageName: String) + @Query("DELETE FROM patch_selections") abstract suspend fun reset() diff --git a/app/src/main/java/app/revanced/manager/domain/repository/PatchSelectionRepository.kt b/app/src/main/java/app/revanced/manager/domain/repository/PatchSelectionRepository.kt index cade429164..c34e5efd6b 100644 --- a/app/src/main/java/app/revanced/manager/domain/repository/PatchSelectionRepository.kt +++ b/app/src/main/java/app/revanced/manager/domain/repository/PatchSelectionRepository.kt @@ -25,6 +25,10 @@ class PatchSelectionRepository(db: AppDatabase) { ) }) + suspend fun clearSelection(packageName: String) { + dao.clearForPackage(packageName) + } + suspend fun reset() = dao.reset() suspend fun export(bundleUid: Int): SerializedSelection = dao.exportSelection(bundleUid) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 610d62956e..88d326d45b 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -16,11 +16,14 @@ import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.outlined.HelpOutline +import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.Restore import androidx.compose.material.icons.outlined.Search import androidx.compose.material.icons.outlined.Settings import androidx.compose.material3.AlertDialog import androidx.compose.material3.Checkbox +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.FilterChip @@ -36,7 +39,10 @@ import androidx.compose.material3.TextButton import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.stringResource @@ -49,6 +55,7 @@ import app.revanced.manager.patcher.patch.PatchInfo import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.patches.OptionItem import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel +import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.BaseSelectionMode import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_SUPPORTED import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_UNIVERSAL import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW_UNSUPPORTED @@ -91,8 +98,35 @@ fun PatchesSelectorScreen( title = stringResource(R.string.select_patches), onBackClick = onBackClick, actions = { - IconButton(onClick = { }) { - Icon(Icons.Outlined.HelpOutline, stringResource(R.string.help)) + IconButton(onClick = vm::reset) { + Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) + } + + var dropdownActive by rememberSaveable { + mutableStateOf(false) + } + // This part should probably be changed + IconButton(onClick = { dropdownActive = true }) { + Icon(Icons.Outlined.MoreVert, stringResource(R.string.more)) + DropdownMenu( + expanded = dropdownActive, + onDismissRequest = { dropdownActive = false } + ) { + DropdownMenuItem( + text = { + val str = + if (vm.baseSelectionMode == BaseSelectionMode.DEFAULT) + "Use previous selection" else "Discard previous selection" + + Text(str) + }, + onClick = { + dropdownActive = false + vm.switchBaseSelectionMode() + }, + enabled = vm.hasPreviousSelection + ) + } } IconButton(onClick = { }) { Icon(Icons.Outlined.Search, stringResource(R.string.search)) @@ -105,10 +139,11 @@ fun PatchesSelectorScreen( text = { Text(stringResource(R.string.patch)) }, icon = { Icon(Icons.Default.Build, null) }, onClick = { + // TODO: only allow this if all required options have been set. composableScope.launch { - // TODO: only allow this if all required options have been set. - onPatchClick(vm.getSelection(), vm.getOptions()) - // onPatchClick(vm.getAndSaveSelection(), vm.getOptions()) + val selection = vm.getSelection() + vm.saveSelection(selection).join() + onPatchClick(selection, vm.getOptions()) } } ) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index d5d9091e11..5e7f2d9c10 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -1,18 +1,22 @@ package app.revanced.manager.ui.viewmodel +import android.app.Application +import android.content.Context import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.Saver -import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi import androidx.lifecycle.viewmodel.compose.saveable +import app.revanced.manager.R import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.PatchSelectionRepository import app.revanced.manager.domain.repository.PatchBundleRepository @@ -22,10 +26,8 @@ import app.revanced.manager.util.Options import app.revanced.manager.util.PatchesSelection import app.revanced.manager.util.SnapshotStateSet import app.revanced.manager.util.flatMapLatestAndCombine -import app.revanced.manager.util.mutableStateSetOf import app.revanced.manager.util.saver.snapshotStateMapSaver -import app.revanced.manager.util.saver.snapshotStateSetSaver -import app.revanced.manager.util.toMutableStateSet +import app.revanced.manager.util.toast import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map @@ -39,9 +41,12 @@ import org.koin.core.component.get class PatchesSelectorViewModel( val input: Destination.PatchesSelector ) : ViewModel(), KoinComponent { + private val app: Application = get() private val selectionRepository: PatchSelectionRepository = get() private val savedStateHandle: SavedStateHandle = get() + private val packageName = input.selectedApp.packageName + val allowExperimental = get().allowExperimental val bundlesFlow = get().sources.flatMapLatestAndCombine( combiner = { it.filterNotNull() } @@ -54,7 +59,7 @@ class PatchesSelectorViewModel( val unsupported = mutableListOf() val universal = mutableListOf() - bundle.patches.filter { it.compatibleWith(input.selectedApp.packageName) }.forEach { + bundle.patches.filter { it.compatibleWith(packageName) }.forEach { val targetList = when { it.compatiblePackages == null -> universal it.supportsVersion(input.selectedApp.version) -> supported @@ -68,58 +73,21 @@ class PatchesSelectorViewModel( } } - /* - private val selectedPatches: SnapshotStatePatchesSelection by savedStateHandle.saveable( - saver = userPatchesSelectionSasver, - init = { - val map: SnapshotStatePatchesSelection = mutableStateMapOf() - viewModelScope.launch(Dispatchers.Default) { - val bundles = bundlesFlow.first() - val filteredSelection = - (input.patchesSelection - ?: selectionRepository.getSelection(input.selectedApp.packageName)) - .mapValues { (uid, patches) -> - // Filter out patches that don't exist. - val filteredPatches = bundles.singleOrNull { it.uid == uid } - ?.let { bundle -> - val allPatches = bundle.all.map { it.name } - patches.filter { allPatches.contains(it) } - } - ?: patches - - filteredPatches.toMutableStateSet() - } - - withContext(Dispatchers.Main) { - map.putAll(filteredSelection) - } - } - return@saveable map - })*/ + var baseSelectionMode by mutableStateOf(BaseSelectionMode.DEFAULT) + private set + private val previousPatchSelection: SnapshotStateMap> = mutableStateMapOf() + init { - viewModelScope.launch(Dispatchers.Default) { - // val bundles = bundlesFlow.first() - val unfilteredSelection = input.patchesSelection ?: selectionRepository.getSelection(input.selectedApp.packageName) - val filteredSelection = unfilteredSelection.mapValues { (_, value) -> value.toSet() } - /* - val filteredSelection = unfilteredSelection.mapValues { (uid, patches) -> - // Filter out patches that don't exist. - val filteredPatches = bundles.singleOrNull { it.uid == uid } - ?.let { bundle -> - val allPatches = bundle.all.map { it.name } - patches.filter { allPatches.contains(it) } - } - ?: patches - filteredPatches.toSet() - }*/ - - withContext(Dispatchers.Main) { - previousPatchSelection.putAll(filteredSelection) - } - } + viewModelScope.launch(Dispatchers.Default) { loadPreviousSelection() } } + val hasPreviousSelection by derivedStateOf { + previousPatchSelection.filterValues(Set::isNotEmpty).isNotEmpty() + } + + private var hasModifiedSelection = false + private val userSelection: SnapshotStateUserPatchesSelection by savedStateHandle.saveable( saver = userPatchesSelectionSaver, init = ::mutableStateMapOf @@ -130,18 +98,21 @@ class PatchesSelectorViewModel( init = ::mutableStateMapOf ) - private val selectors: List = listOf( - { bundle, patch -> - userSelection[bundle]?.get(patch.name) - }, - { bundle, patch -> - // previousPatchSelection[bundle]?.contains(patch.name) - null - }, - { _, patch -> - patch.include - } - ) + private val selectors by derivedStateOf> { + arrayOf( + { bundle, patch -> + // Explicit selection + userSelection[bundle]?.get(patch.name) + }, + when (baseSelectionMode) { + BaseSelectionMode.DEFAULT -> ({ _, patch -> patch.include }) + + BaseSelectionMode.PREVIOUS -> ({ bundle, patch -> + previousPatchSelection[bundle]?.contains(patch.name) ?: false + }) + } + ) + } /** * Show the patch options dialog for this patch. @@ -150,9 +121,27 @@ class PatchesSelectorViewModel( val compatibleVersions = mutableStateListOf() - var filter by mutableStateOf(SHOW_SUPPORTED or SHOW_UNSUPPORTED) + var filter by mutableStateOf(SHOW_SUPPORTED or SHOW_UNIVERSAL or SHOW_UNSUPPORTED) private set + private suspend fun loadPreviousSelection() { + val selection = (input.patchesSelection ?: selectionRepository.getSelection( + packageName + )).mapValues { (_, value) -> value.toSet() } + + withContext(Dispatchers.Main) { + previousPatchSelection.putAll(selection) + } + } + + fun switchBaseSelectionMode() = viewModelScope.launch { + baseSelectionMode = if (baseSelectionMode == BaseSelectionMode.DEFAULT) { + BaseSelectionMode.PREVIOUS + } else { + BaseSelectionMode.DEFAULT + } + } + private fun getOrCreateSelection(bundle: Int) = userSelection.getOrPut(bundle, ::mutableStateMapOf) @@ -162,15 +151,25 @@ class PatchesSelectorViewModel( fun togglePatch(bundle: Int, patch: PatchInfo) { val patches = getOrCreateSelection(bundle) + hasModifiedSelection = true patches[patch.name] = !isSelected(bundle, patch) } + fun reset() { + patchOptions.clear() + baseSelectionMode = BaseSelectionMode.DEFAULT + userSelection.clear() + hasModifiedSelection = false + app.toast(app.getString(R.string.patch_selection_reset_toast)) + } + suspend fun getSelection(): PatchesSelection { val bundles = bundlesFlow.first() val removeUnsupported = !allowExperimental.get() return bundles.associate { bundle -> - val included = bundle.all.filter { isSelected(bundle.uid, it) }.map { it.name }.toMutableSet() + val included = + bundle.all.filter { isSelected(bundle.uid, it) }.map { it.name }.toMutableSet() if (removeUnsupported) { val unsupported = bundle.unsupported.map { it.name }.toSet() @@ -181,24 +180,17 @@ class PatchesSelectorViewModel( } } - // TODO: reimplement this. - /* - suspend fun getAndSaveSelection(): PatchesSelection = - selectedPatches.also { - withContext(Dispatchers.Default) { - selectionRepository.updateSelection(input.selectedApp.packageName, it) - } - }.mapValues { it.value.toMutableSet() }.apply { - if (allowExperimental.get()) { - return@apply - } + suspend fun saveSelection(selection: PatchesSelection) = + viewModelScope.launch(Dispatchers.Default) { + when { + hasModifiedSelection -> selectionRepository.updateSelection(packageName, selection) + baseSelectionMode == BaseSelectionMode.DEFAULT -> selectionRepository.clearSelection( + packageName + ) - // Filter out unsupported patches that may have gotten selected through the database if the setting is not enabled. - bundlesFlow.first().forEach { - this[it.uid]?.removeAll(it.unsupported.map { patch -> patch.name }.toSet()) + else -> {} } } - */ fun getOptions(): Options = patchOptions fun getOptions(bundle: Int, patch: PatchInfo) = patchOptions[bundle]?.get(patch.name) @@ -253,6 +245,22 @@ class PatchesSelectorViewModel( snapshotStateMapSaver(valueSaver = snapshotStateMapSaver()) } + /** + * An enum for controlling the behavior of the selector. + */ + enum class BaseSelectionMode { + /** + * Selection is determined by the [PatchInfo.include] field. + */ + DEFAULT, + + /** + * Selection is determined by what the user selected previously. + * Any patch that is not part of the previous selection will be deselected. + */ + PREVIOUS + } + data class BundleInfo( val name: String, val uid: Int, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2905507777..07e684a4dd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -143,6 +143,7 @@ Unsupported app Unsupported patches Universal patches + Patch selection and options has been reset to recommended defaults Supported Universal Unsupported From 58e4264fc6d398311934a1989cd7395abdceb0d2 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sun, 17 Sep 2023 21:47:54 +0200 Subject: [PATCH 3/9] selection warning dialog --- .../domain/manager/PreferencesManager.kt | 3 + .../manager/ui/component/Countdown.kt | 26 +++++ .../ui/screen/PatchesSelectorScreen.kt | 102 +++++++++++++++++- .../ui/viewmodel/PatchesSelectorViewModel.kt | 48 ++++++++- app/src/main/res/values/strings.xml | 4 + 5 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/app/revanced/manager/ui/component/Countdown.kt diff --git a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt index 34d7af60e0..c27015c40f 100644 --- a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt +++ b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt @@ -21,4 +21,7 @@ class PreferencesManager( val showAutoUpdatesDialog = booleanPreference("show_auto_updates_dialog", true) val managerAutoUpdates = booleanPreference("manager_auto_updates", false) + + val disableSelectionWarning = booleanPreference("disable_selection_warning", false) + val selectionWarningCountdown = booleanPreference("selection_warning_countdown", false) } diff --git a/app/src/main/java/app/revanced/manager/ui/component/Countdown.kt b/app/src/main/java/app/revanced/manager/ui/component/Countdown.kt new file mode 100644 index 0000000000..0fb23c2728 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/Countdown.kt @@ -0,0 +1,26 @@ +package app.revanced.manager.ui.component + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import kotlinx.coroutines.delay + +@Composable +fun Countdown(start: Int, content: @Composable (Int) -> Unit) { + var timer by rememberSaveable(start) { + mutableStateOf(start) + } + LaunchedEffect(timer) { + if (timer == 0) { + return@LaunchedEffect + } + + delay(1000L) + timer -= 1 + } + + content(timer) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 88d326d45b..44d50fe209 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.Restore import androidx.compose.material.icons.outlined.Search import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material.icons.outlined.WarningAmber import androidx.compose.material3.AlertDialog import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenu @@ -38,21 +39,26 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.revanced.manager.R +import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.patcher.patch.PatchInfo import app.revanced.manager.ui.component.AppTopBar +import app.revanced.manager.ui.component.Countdown import app.revanced.manager.ui.component.patches.OptionItem import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.BaseSelectionMode @@ -62,6 +68,7 @@ import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel.Companion.SHOW import app.revanced.manager.util.Options import app.revanced.manager.util.PatchesSelection import kotlinx.coroutines.launch +import org.koin.compose.rememberKoinInject @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable @@ -92,6 +99,13 @@ fun PatchesSelectorScreen( ) } + vm.pendingSelectionAction?.let { + SelectionWarningDialog( + onCancel = vm::dismissSelectionWarning, + onConfirm = vm::confirmSelectionWarning + ) + } + Scaffold( topBar = { AppTopBar( @@ -245,7 +259,15 @@ fun PatchesSelectorScreen( bundle.uid, patch ), - onToggle = { vm.togglePatch(bundle.uid, patch) }, + onToggle = { + if (vm.selectionWarningEnabled) { + vm.pendingSelectionAction = { + vm.togglePatch(bundle.uid, patch) + } + } else { + vm.togglePatch(bundle.uid, patch) + } + }, supported = supported ) } @@ -285,6 +307,84 @@ fun PatchesSelectorScreen( } } +@Composable +fun SelectionWarningDialog( + onCancel: () -> Unit, + onConfirm: (Boolean) -> Unit +) { + val prefs: PreferencesManager = rememberKoinInject() + var dismissPermanently by rememberSaveable { + mutableStateOf(false) + } + + AlertDialog( + onDismissRequest = onCancel, + confirmButton = { + val seenTimer by prefs.selectionWarningCountdown.getAsState() + + Countdown(start = if (seenTimer) 0 else 3) { timer -> + LaunchedEffect(timer) { + if (timer == 0) prefs.selectionWarningCountdown.update(true) + } + + TextButton( + onClick = { onConfirm(dismissPermanently) }, + enabled = timer == 0 + ) { + val text = + if (timer == 0) stringResource(R.string.continue_) else stringResource( + R.string.selection_warning_continue_countdown, timer + ) + Text(text) + } + } + }, + dismissButton = { + TextButton(onClick = onCancel) { + Text(stringResource(R.string.cancel)) + } + }, + icon = { + Icon(Icons.Outlined.WarningAmber, null) + }, + title = { + Text( + text = stringResource(R.string.selection_warning_title), + style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center), + color = MaterialTheme.colorScheme.onSurface, + ) + }, + text = { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.Start + ) { + Text( + text = stringResource(R.string.selection_warning_description), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(0.dp), + modifier = Modifier.clickable { + dismissPermanently = !dismissPermanently + } + ) { + Checkbox( + checked = dismissPermanently, + onCheckedChange = { + dismissPermanently = it + } + ) + Text("Do not show this again") + } + } + } + ) +} + @Composable fun PatchItem( patch: PatchInfo, diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index 5e7f2d9c10..c2c6779a10 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -1,7 +1,6 @@ package app.revanced.manager.ui.viewmodel import android.app.Application -import android.content.Context import androidx.compose.runtime.Stable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -44,9 +43,15 @@ class PatchesSelectorViewModel( private val app: Application = get() private val selectionRepository: PatchSelectionRepository = get() private val savedStateHandle: SavedStateHandle = get() + private val prefs: PreferencesManager = get() private val packageName = input.selectedApp.packageName + var pendingSelectionAction by mutableStateOf<(() -> Unit)?>(null) + + var selectionWarningEnabled by mutableStateOf(true) + private set + val allowExperimental = get().allowExperimental val bundlesFlow = get().sources.flatMapLatestAndCombine( combiner = { it.filterNotNull() } @@ -73,6 +78,30 @@ class PatchesSelectorViewModel( } } + init { + viewModelScope.launch { + if (prefs.disableSelectionWarning.get()) { + selectionWarningEnabled = false + return@launch + } + + val experimental = allowExperimental.get() + fun BundleInfo.hasDefaultPatches(): Boolean { + return if (experimental) { + all.any { patch -> patch.include } + } else { + listOf(supported, universal).flatten().any { + it.include + } + } + } + + // Don't show the warning if there are no default patches. + selectionWarningEnabled = bundlesFlow.first().any(BundleInfo::hasDefaultPatches) + } + } + + var baseSelectionMode by mutableStateOf(BaseSelectionMode.DEFAULT) private set @@ -155,6 +184,23 @@ class PatchesSelectorViewModel( patches[patch.name] = !isSelected(bundle, patch) } + fun confirmSelectionWarning(dismissPermanently: Boolean) { + selectionWarningEnabled = false + + pendingSelectionAction?.invoke() + pendingSelectionAction = null + + if (!dismissPermanently) return + + viewModelScope.launch { + prefs.disableSelectionWarning.update(true) + } + } + + fun dismissSelectionWarning() { + pendingSelectionAction = null + } + fun reset() { patchOptions.clear() baseSelectionMode = BaseSelectionMode.DEFAULT diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 07e684a4dd..7dabc87192 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -144,6 +144,9 @@ Unsupported patches Universal patches Patch selection and options has been reset to recommended defaults + Stop using defaults? + You may encounter issues when not using the default patch selection and options. + Continue (%ds) Supported Universal Unsupported @@ -224,6 +227,7 @@ collapse More + Continue Donate Website GitHub From 3386330d3f951464feb80aade4b4bc1b6bd7696b Mon Sep 17 00:00:00 2001 From: Ax333l Date: Thu, 21 Sep 2023 21:42:27 +0200 Subject: [PATCH 4/9] improve naming and add string resource --- .../ui/screen/PatchesSelectorScreen.kt | 6 ++-- .../ui/viewmodel/PatchesSelectorViewModel.kt | 28 +++++++------------ app/src/main/res/values/strings.xml | 1 + 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 44d50fe209..c449d44d00 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -320,9 +320,9 @@ fun SelectionWarningDialog( AlertDialog( onDismissRequest = onCancel, confirmButton = { - val seenTimer by prefs.selectionWarningCountdown.getAsState() + val userHasSeenTimer by prefs.selectionWarningCountdown.getAsState() - Countdown(start = if (seenTimer) 0 else 3) { timer -> + Countdown(start = if (userHasSeenTimer) 0 else 3) { timer -> LaunchedEffect(timer) { if (timer == 0) prefs.selectionWarningCountdown.update(true) } @@ -378,7 +378,7 @@ fun SelectionWarningDialog( dismissPermanently = it } ) - Text("Do not show this again") + Text(stringResource(R.string.permanent_dismiss)) } } } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index c2c6779a10..8a8f71c174 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -23,7 +23,6 @@ import app.revanced.manager.patcher.patch.PatchInfo import app.revanced.manager.ui.destination.Destination import app.revanced.manager.util.Options import app.revanced.manager.util.PatchesSelection -import app.revanced.manager.util.SnapshotStateSet import app.revanced.manager.util.flatMapLatestAndCombine import app.revanced.manager.util.saver.snapshotStateMapSaver import app.revanced.manager.util.toast @@ -117,22 +116,23 @@ class PatchesSelectorViewModel( private var hasModifiedSelection = false - private val userSelection: SnapshotStateUserPatchesSelection by savedStateHandle.saveable( + private val userSelection: SnapshotUserPatchesSelection by savedStateHandle.saveable( saver = userPatchesSelectionSaver, init = ::mutableStateMapOf ) - private val patchOptions: SnapshotStateOptions by savedStateHandle.saveable( + private val patchOptions: SnapshotOptions by savedStateHandle.saveable( saver = optionsSaver, init = ::mutableStateMapOf ) private val selectors by derivedStateOf> { arrayOf( + // Patches that were explicitly selected { bundle, patch -> - // Explicit selection userSelection[bundle]?.get(patch.name) }, + // The fallback selection. when (baseSelectionMode) { BaseSelectionMode.DEFAULT -> ({ _, patch -> patch.include }) @@ -279,7 +279,7 @@ class PatchesSelectorViewModel( private fun SnapshotStateMap>.getOrCreate(key: K) = getOrPut(key, ::mutableStateMapOf) - private val optionsSaver: Saver = snapshotStateMapSaver( + private val optionsSaver: Saver = snapshotStateMapSaver( // Patch name -> Options valueSaver = snapshotStateMapSaver( // Option key -> Option value @@ -287,7 +287,7 @@ class PatchesSelectorViewModel( ) ) - private val userPatchesSelectionSaver: Saver = + private val userPatchesSelectionSaver: Saver = snapshotStateMapSaver(valueSaver = snapshotStateMapSaver()) } @@ -317,17 +317,9 @@ class PatchesSelectorViewModel( ) } -/** - * [Options] but with observable collection types. - */ -private typealias SnapshotStateOptions = SnapshotStateMap>> - -/** - * [PatchesSelection] but with observable collection types. - */ -private typealias SnapshotStatePatchesSelection = SnapshotStateMap> - +private typealias Selector = (Int, PatchInfo) -> Boolean? private typealias UserPatchesSelection = Map> -private typealias SnapshotStateUserPatchesSelection = SnapshotStateMap> -private typealias Selector = (Int, PatchInfo) -> Boolean? \ No newline at end of file +// Versions of other types, but utilizing observable collection types instead. +private typealias SnapshotOptions = SnapshotStateMap>> +private typealias SnapshotUserPatchesSelection = SnapshotStateMap> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7dabc87192..1a8c664d29 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -228,6 +228,7 @@ More Continue + Do not show this again Donate Website GitHub From 2080444d7d0fa59cdd0c525b142d907b3cbb310c Mon Sep 17 00:00:00 2001 From: Ax333l Date: Tue, 26 Sep 2023 20:30:35 +0200 Subject: [PATCH 5/9] adjust color --- .../revanced/manager/ui/screen/PatchesSelectorScreen.kt | 2 +- .../manager/ui/viewmodel/PatchesSelectorViewModel.kt | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index c449d44d00..0e80e87155 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -335,7 +335,7 @@ fun SelectionWarningDialog( if (timer == 0) stringResource(R.string.continue_) else stringResource( R.string.selection_warning_continue_countdown, timer ) - Text(text) + Text(text, color = MaterialTheme.colorScheme.error) } } }, diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index 8a8f71c174..14ecf7fd4c 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -87,12 +87,13 @@ class PatchesSelectorViewModel( val experimental = allowExperimental.get() fun BundleInfo.hasDefaultPatches(): Boolean { return if (experimental) { - all.any { patch -> patch.include } + all.asSequence() } else { - listOf(supported, universal).flatten().any { - it.include + sequence { + yieldAll(supported) + yieldAll(universal) } - } + }.any { it.include } } // Don't show the warning if there are no default patches. From d54b49445a869a8313683f57e2d7a78c52e5a14b Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sun, 1 Oct 2023 18:36:30 +0200 Subject: [PATCH 6/9] better names and use string resources --- .../manager/domain/manager/PreferencesManager.kt | 2 +- .../manager/ui/screen/PatchesSelectorScreen.kt | 12 ++++++------ app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt index c27015c40f..5c08ba179c 100644 --- a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt +++ b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt @@ -23,5 +23,5 @@ class PreferencesManager( val managerAutoUpdates = booleanPreference("manager_auto_updates", false) val disableSelectionWarning = booleanPreference("disable_selection_warning", false) - val selectionWarningCountdown = booleanPreference("selection_warning_countdown", false) + val enableSelectionWarningCountdown = booleanPreference("enable_selection_warning_countdown", true) } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 0e80e87155..8e9f1852e6 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -128,11 +128,11 @@ fun PatchesSelectorScreen( ) { DropdownMenuItem( text = { - val str = + val id = if (vm.baseSelectionMode == BaseSelectionMode.DEFAULT) - "Use previous selection" else "Discard previous selection" + R.string.menu_opt_selection_mode_previous else R.string.menu_opt_selection_mode_default - Text(str) + Text(stringResource(id)) }, onClick = { dropdownActive = false @@ -320,11 +320,11 @@ fun SelectionWarningDialog( AlertDialog( onDismissRequest = onCancel, confirmButton = { - val userHasSeenTimer by prefs.selectionWarningCountdown.getAsState() + val enableCountdown by prefs.enableSelectionWarningCountdown.getAsState() - Countdown(start = if (userHasSeenTimer) 0 else 3) { timer -> + Countdown(start = if (enableCountdown) 3 else 0) { timer -> LaunchedEffect(timer) { - if (timer == 0) prefs.selectionWarningCountdown.update(true) + if (timer == 0) prefs.enableSelectionWarningCountdown.update(false) } TextButton( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a21d6f91fa..58c4b940a0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -143,6 +143,8 @@ Unsupported app Unsupported patches Universal patches + Use default selection + Use previous selection Patch selection and options has been reset to recommended defaults Stop using defaults? You may encounter issues when not using the default patch selection and options. From c4d931e34273605e9df3fa7d2e509efdcb2e74a0 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sun, 1 Oct 2023 18:38:30 +0200 Subject: [PATCH 7/9] formatting --- .../revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index 14ecf7fd4c..c4f5b1171f 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -101,7 +101,6 @@ class PatchesSelectorViewModel( } } - var baseSelectionMode by mutableStateOf(BaseSelectionMode.DEFAULT) private set From 19d6413150ee56ba8f30b32e66185050f1909635 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sun, 1 Oct 2023 18:44:20 +0200 Subject: [PATCH 8/9] better type name --- .../ui/viewmodel/PatchesSelectorViewModel.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index c4f5b1171f..ab5e1faf04 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -116,8 +116,8 @@ class PatchesSelectorViewModel( private var hasModifiedSelection = false - private val userSelection: SnapshotUserPatchesSelection by savedStateHandle.saveable( - saver = userPatchesSelectionSaver, + private val explicitPatchesSelection: SnapshotExplicitPatchesSelection by savedStateHandle.saveable( + saver = explicitPatchesSelectionSaver, init = ::mutableStateMapOf ) @@ -130,7 +130,7 @@ class PatchesSelectorViewModel( arrayOf( // Patches that were explicitly selected { bundle, patch -> - userSelection[bundle]?.get(patch.name) + explicitPatchesSelection[bundle]?.get(patch.name) }, // The fallback selection. when (baseSelectionMode) { @@ -172,7 +172,7 @@ class PatchesSelectorViewModel( } private fun getOrCreateSelection(bundle: Int) = - userSelection.getOrPut(bundle, ::mutableStateMapOf) + explicitPatchesSelection.getOrPut(bundle, ::mutableStateMapOf) fun isSelected(bundle: Int, patch: PatchInfo) = selectors.firstNotNullOf { fn -> fn(bundle, patch) } @@ -204,7 +204,7 @@ class PatchesSelectorViewModel( fun reset() { patchOptions.clear() baseSelectionMode = BaseSelectionMode.DEFAULT - userSelection.clear() + explicitPatchesSelection.clear() hasModifiedSelection = false app.toast(app.getString(R.string.patch_selection_reset_toast)) } @@ -287,7 +287,7 @@ class PatchesSelectorViewModel( ) ) - private val userPatchesSelectionSaver: Saver = + private val explicitPatchesSelectionSaver: Saver = snapshotStateMapSaver(valueSaver = snapshotStateMapSaver()) } @@ -318,8 +318,8 @@ class PatchesSelectorViewModel( } private typealias Selector = (Int, PatchInfo) -> Boolean? -private typealias UserPatchesSelection = Map> +private typealias ExplicitPatchesSelection = Map> // Versions of other types, but utilizing observable collection types instead. private typealias SnapshotOptions = SnapshotStateMap>> -private typealias SnapshotUserPatchesSelection = SnapshotStateMap> \ No newline at end of file +private typealias SnapshotExplicitPatchesSelection = SnapshotStateMap> \ No newline at end of file From 0a6dc9c0ad02d1fac4d305845b80784295c56bc0 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sun, 1 Oct 2023 20:54:22 +0200 Subject: [PATCH 9/9] rename previous variable --- .../manager/ui/viewmodel/PatchesSelectorViewModel.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index ab5e1faf04..44cead31ba 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -104,14 +104,14 @@ class PatchesSelectorViewModel( var baseSelectionMode by mutableStateOf(BaseSelectionMode.DEFAULT) private set - private val previousPatchSelection: SnapshotStateMap> = mutableStateMapOf() + private val previousPatchesSelection: SnapshotStateMap> = mutableStateMapOf() init { viewModelScope.launch(Dispatchers.Default) { loadPreviousSelection() } } val hasPreviousSelection by derivedStateOf { - previousPatchSelection.filterValues(Set::isNotEmpty).isNotEmpty() + previousPatchesSelection.filterValues(Set::isNotEmpty).isNotEmpty() } private var hasModifiedSelection = false @@ -137,7 +137,7 @@ class PatchesSelectorViewModel( BaseSelectionMode.DEFAULT -> ({ _, patch -> patch.include }) BaseSelectionMode.PREVIOUS -> ({ bundle, patch -> - previousPatchSelection[bundle]?.contains(patch.name) ?: false + previousPatchesSelection[bundle]?.contains(patch.name) ?: false }) } ) @@ -159,7 +159,7 @@ class PatchesSelectorViewModel( )).mapValues { (_, value) -> value.toSet() } withContext(Dispatchers.Main) { - previousPatchSelection.putAll(selection) + previousPatchesSelection.putAll(selection) } }