mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
feat(patch-selector): default patches selection (#1272)
This commit is contained in:
parent
ca3c9af3b8
commit
f78b56ef0a
@ -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()
|
||||
|
||||
|
@ -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 enableSelectionWarningCountdown = booleanPreference("enable_selection_warning_countdown", true)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
@ -16,11 +16,15 @@ 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.material.icons.outlined.WarningAmber
|
||||
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
|
||||
@ -35,26 +39,36 @@ 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
|
||||
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
|
||||
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
|
||||
@ -85,12 +99,49 @@ fun PatchesSelectorScreen(
|
||||
)
|
||||
}
|
||||
|
||||
vm.pendingSelectionAction?.let {
|
||||
SelectionWarningDialog(
|
||||
onCancel = vm::dismissSelectionWarning,
|
||||
onConfirm = vm::confirmSelectionWarning
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppTopBar(
|
||||
title = stringResource(R.string.select_patches),
|
||||
onBackClick = onBackClick,
|
||||
actions = {
|
||||
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 id =
|
||||
if (vm.baseSelectionMode == BaseSelectionMode.DEFAULT)
|
||||
R.string.menu_opt_selection_mode_previous else R.string.menu_opt_selection_mode_default
|
||||
|
||||
Text(stringResource(id))
|
||||
},
|
||||
onClick = {
|
||||
dropdownActive = false
|
||||
vm.switchBaseSelectionMode()
|
||||
},
|
||||
enabled = vm.hasPreviousSelection
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { }) {
|
||||
Icon(Icons.Outlined.Search, stringResource(R.string.search))
|
||||
}
|
||||
@ -102,9 +153,11 @@ fun PatchesSelectorScreen(
|
||||
text = { Text(stringResource(R.string.patch)) },
|
||||
icon = { Icon(Icons.Default.Build, null) },
|
||||
onClick = {
|
||||
composableScope.launch {
|
||||
// TODO: only allow this if all required options have been set.
|
||||
onPatchClick(vm.getAndSaveSelection(), vm.getOptions())
|
||||
composableScope.launch {
|
||||
val selection = vm.getSelection()
|
||||
vm.saveSelection(selection).join()
|
||||
onPatchClick(selection, vm.getOptions())
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -206,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
|
||||
)
|
||||
}
|
||||
@ -246,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 enableCountdown by prefs.enableSelectionWarningCountdown.getAsState()
|
||||
|
||||
Countdown(start = if (enableCountdown) 3 else 0) { timer ->
|
||||
LaunchedEffect(timer) {
|
||||
if (timer == 0) prefs.enableSelectionWarningCountdown.update(false)
|
||||
}
|
||||
|
||||
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, color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
}
|
||||
},
|
||||
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(stringResource(R.string.permanent_dismiss))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PatchItem(
|
||||
patch: PatchInfo,
|
||||
|
@ -1,18 +1,21 @@
|
||||
package app.revanced.manager.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
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
|
||||
@ -20,12 +23,9 @@ 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.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,8 +39,17 @@ 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 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<PreferencesManager>().allowExperimental
|
||||
val bundlesFlow = get<PatchBundleRepository>().sources.flatMapLatestAndCombine(
|
||||
@ -54,7 +63,7 @@ class PatchesSelectorViewModel(
|
||||
val unsupported = mutableListOf<PatchInfo>()
|
||||
val universal = mutableListOf<PatchInfo>()
|
||||
|
||||
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,38 +77,72 @@ class PatchesSelectorViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private val selectedPatches: SnapshotStatePatchesSelection by savedStateHandle.saveable(
|
||||
saver = patchesSelectionSaver,
|
||||
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()
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
if (prefs.disableSelectionWarning.get()) {
|
||||
selectionWarningEnabled = false
|
||||
return@launch
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
map.putAll(filteredSelection)
|
||||
val experimental = allowExperimental.get()
|
||||
fun BundleInfo.hasDefaultPatches(): Boolean {
|
||||
return if (experimental) {
|
||||
all.asSequence()
|
||||
} else {
|
||||
sequence {
|
||||
yieldAll(supported)
|
||||
yieldAll(universal)
|
||||
}
|
||||
}.any { it.include }
|
||||
}
|
||||
|
||||
// Don't show the warning if there are no default patches.
|
||||
selectionWarningEnabled = bundlesFlow.first().any(BundleInfo::hasDefaultPatches)
|
||||
}
|
||||
}
|
||||
return@saveable map
|
||||
})
|
||||
private val patchOptions: SnapshotStateOptions by savedStateHandle.saveable(
|
||||
|
||||
var baseSelectionMode by mutableStateOf(BaseSelectionMode.DEFAULT)
|
||||
private set
|
||||
|
||||
private val previousPatchesSelection: SnapshotStateMap<Int, Set<String>> = mutableStateMapOf()
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.Default) { loadPreviousSelection() }
|
||||
}
|
||||
|
||||
val hasPreviousSelection by derivedStateOf {
|
||||
previousPatchesSelection.filterValues(Set<String>::isNotEmpty).isNotEmpty()
|
||||
}
|
||||
|
||||
private var hasModifiedSelection = false
|
||||
|
||||
private val explicitPatchesSelection: SnapshotExplicitPatchesSelection by savedStateHandle.saveable(
|
||||
saver = explicitPatchesSelectionSaver,
|
||||
init = ::mutableStateMapOf
|
||||
)
|
||||
|
||||
private val patchOptions: SnapshotOptions by savedStateHandle.saveable(
|
||||
saver = optionsSaver,
|
||||
init = ::mutableStateMapOf
|
||||
)
|
||||
|
||||
private val selectors by derivedStateOf<Array<Selector>> {
|
||||
arrayOf(
|
||||
// Patches that were explicitly selected
|
||||
{ bundle, patch ->
|
||||
explicitPatchesSelection[bundle]?.get(patch.name)
|
||||
},
|
||||
// The fallback selection.
|
||||
when (baseSelectionMode) {
|
||||
BaseSelectionMode.DEFAULT -> ({ _, patch -> patch.include })
|
||||
|
||||
BaseSelectionMode.PREVIOUS -> ({ bundle, patch ->
|
||||
previousPatchesSelection[bundle]?.contains(patch.name) ?: false
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the patch options dialog for this patch.
|
||||
*/
|
||||
@ -107,35 +150,91 @@ class PatchesSelectorViewModel(
|
||||
|
||||
val compatibleVersions = mutableStateListOf<String>()
|
||||
|
||||
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) {
|
||||
previousPatchesSelection.putAll(selection)
|
||||
}
|
||||
}
|
||||
|
||||
fun switchBaseSelectionMode() = viewModelScope.launch {
|
||||
baseSelectionMode = if (baseSelectionMode == BaseSelectionMode.DEFAULT) {
|
||||
BaseSelectionMode.PREVIOUS
|
||||
} else {
|
||||
BaseSelectionMode.DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOrCreateSelection(bundle: Int) =
|
||||
selectedPatches.getOrPut(bundle, ::mutableStateSetOf)
|
||||
explicitPatchesSelection.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)
|
||||
hasModifiedSelection = true
|
||||
patches[patch.name] = !isSelected(bundle, patch)
|
||||
}
|
||||
|
||||
suspend fun getAndSaveSelection(): PatchesSelection =
|
||||
selectedPatches.also {
|
||||
withContext(Dispatchers.Default) {
|
||||
selectionRepository.updateSelection(input.selectedApp.packageName, it)
|
||||
fun confirmSelectionWarning(dismissPermanently: Boolean) {
|
||||
selectionWarningEnabled = false
|
||||
|
||||
pendingSelectionAction?.invoke()
|
||||
pendingSelectionAction = null
|
||||
|
||||
if (!dismissPermanently) return
|
||||
|
||||
viewModelScope.launch {
|
||||
prefs.disableSelectionWarning.update(true)
|
||||
}
|
||||
}.mapValues { it.value.toMutableSet() }.apply {
|
||||
if (allowExperimental.get()) {
|
||||
return@apply
|
||||
}
|
||||
|
||||
// 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())
|
||||
fun dismissSelectionWarning() {
|
||||
pendingSelectionAction = null
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
patchOptions.clear()
|
||||
baseSelectionMode = BaseSelectionMode.DEFAULT
|
||||
explicitPatchesSelection.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()
|
||||
|
||||
if (removeUnsupported) {
|
||||
val unsupported = bundle.unsupported.map { it.name }.toSet()
|
||||
included.removeAll(unsupported)
|
||||
}
|
||||
|
||||
bundle.uid to included
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveSelection(selection: PatchesSelection) =
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
when {
|
||||
hasModifiedSelection -> selectionRepository.updateSelection(packageName, selection)
|
||||
baseSelectionMode == BaseSelectionMode.DEFAULT -> selectionRepository.clearSelection(
|
||||
packageName
|
||||
)
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,7 +279,7 @@ class PatchesSelectorViewModel(
|
||||
private fun <K, K2, V> SnapshotStateMap<K, SnapshotStateMap<K2, V>>.getOrCreate(key: K) =
|
||||
getOrPut(key, ::mutableStateMapOf)
|
||||
|
||||
private val optionsSaver: Saver<SnapshotStateOptions, Options> = snapshotStateMapSaver(
|
||||
private val optionsSaver: Saver<SnapshotOptions, Options> = snapshotStateMapSaver(
|
||||
// Patch name -> Options
|
||||
valueSaver = snapshotStateMapSaver(
|
||||
// Option key -> Option value
|
||||
@ -188,8 +287,24 @@ class PatchesSelectorViewModel(
|
||||
)
|
||||
)
|
||||
|
||||
private val patchesSelectionSaver: Saver<SnapshotStatePatchesSelection, PatchesSelection> =
|
||||
snapshotStateMapSaver(valueSaver = snapshotStateSetSaver())
|
||||
private val explicitPatchesSelectionSaver: Saver<SnapshotExplicitPatchesSelection, ExplicitPatchesSelection> =
|
||||
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(
|
||||
@ -202,12 +317,9 @@ class PatchesSelectorViewModel(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Options] but with observable collection types.
|
||||
*/
|
||||
private typealias SnapshotStateOptions = SnapshotStateMap<Int, SnapshotStateMap<String, SnapshotStateMap<String, Any?>>>
|
||||
private typealias Selector = (Int, PatchInfo) -> Boolean?
|
||||
private typealias ExplicitPatchesSelection = Map<Int, Map<String, Boolean>>
|
||||
|
||||
/**
|
||||
* [PatchesSelection] but with observable collection types.
|
||||
*/
|
||||
private typealias SnapshotStatePatchesSelection = SnapshotStateMap<Int, SnapshotStateSet<String>>
|
||||
// Versions of other types, but utilizing observable collection types instead.
|
||||
private typealias SnapshotOptions = SnapshotStateMap<Int, SnapshotStateMap<String, SnapshotStateMap<String, Any?>>>
|
||||
private typealias SnapshotExplicitPatchesSelection = SnapshotStateMap<Int, SnapshotStateMap<String, Boolean>>
|
@ -143,6 +143,12 @@
|
||||
<string name="unsupported_app">Unsupported app</string>
|
||||
<string name="unsupported_patches">Unsupported patches</string>
|
||||
<string name="universal_patches">Universal patches</string>
|
||||
<string name="menu_opt_selection_mode_default">Use default selection</string>
|
||||
<string name="menu_opt_selection_mode_previous">Use previous selection</string>
|
||||
<string name="patch_selection_reset_toast">Patch selection and options has been reset to recommended defaults</string>
|
||||
<string name="selection_warning_title">Stop using defaults?</string>
|
||||
<string name="selection_warning_description">You may encounter issues when not using the default patch selection and options.</string>
|
||||
<string name="selection_warning_continue_countdown">Continue (%ds)</string>
|
||||
<string name="supported">Supported</string>
|
||||
<string name="universal">Universal</string>
|
||||
<string name="unsupported">Unsupported</string>
|
||||
@ -223,6 +229,8 @@
|
||||
<string name="collapse_content">collapse</string>
|
||||
|
||||
<string name="more">More</string>
|
||||
<string name="continue_">Continue</string>
|
||||
<string name="permanent_dismiss">Do not show this again</string>
|
||||
<string name="donate">Donate</string>
|
||||
<string name="website">Website</string>
|
||||
<string name="github">GitHub</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user