mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2025-05-19 07:26:39 +08:00
Compare commits
4 Commits
compose/re
...
compose/an
Author | SHA1 | Date | |
---|---|---|---|
04774d9079 | |||
53c939d005 | |||
28acf78156 | |||
251b9eef69 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,4 +9,3 @@
|
|||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
.kotlin/
|
|
||||||
|
@ -12,7 +12,6 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
@ -30,7 +29,6 @@ import app.revanced.manager.ui.theme.Theme
|
|||||||
import app.revanced.manager.ui.viewmodel.MainViewModel
|
import app.revanced.manager.ui.viewmodel.MainViewModel
|
||||||
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
|
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
|
||||||
import app.revanced.manager.util.EventEffect
|
import app.revanced.manager.util.EventEffect
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.koin.androidx.compose.navigation.koinNavViewModel
|
import org.koin.androidx.compose.navigation.koinNavViewModel
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
@ -141,20 +139,14 @@ private fun ReVancedManager(vm: MainViewModel) {
|
|||||||
val parentBackStackEntry = navController.navGraphEntry(it)
|
val parentBackStackEntry = navController.navGraphEntry(it)
|
||||||
val data =
|
val data =
|
||||||
parentBackStackEntry.getComplexArg<SelectedApplicationInfo.ViewModelParams>()
|
parentBackStackEntry.getComplexArg<SelectedApplicationInfo.ViewModelParams>()
|
||||||
val viewModel =
|
|
||||||
koinNavViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) {
|
|
||||||
parametersOf(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectedAppInfoScreen(
|
SelectedAppInfoScreen(
|
||||||
onBackClick = navController::popBackStack,
|
onBackClick = navController::popBackStack,
|
||||||
onPatchClick = {
|
onPatchClick = { app, patches, options ->
|
||||||
it.lifecycleScope.launch {
|
navController.navigateComplex(
|
||||||
navController.navigateComplex(
|
Patcher,
|
||||||
Patcher,
|
Patcher.ViewModelParams(app, patches, options)
|
||||||
viewModel.getPatcherParams()
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onPatchSelectorClick = { app, patches, options ->
|
onPatchSelectorClick = { app, patches, options ->
|
||||||
navController.navigateComplex(
|
navController.navigateComplex(
|
||||||
@ -166,17 +158,9 @@ private fun ReVancedManager(vm: MainViewModel) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onRequiredOptions = { app, patches, options ->
|
vm = koinNavViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) {
|
||||||
navController.navigateComplex(
|
parametersOf(data)
|
||||||
SelectedApplicationInfo.RequiredOptions,
|
}
|
||||||
SelectedApplicationInfo.PatchesSelector.ViewModelParams(
|
|
||||||
app,
|
|
||||||
patches,
|
|
||||||
options
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
vm = viewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,28 +180,6 @@ private fun ReVancedManager(vm: MainViewModel) {
|
|||||||
vm = koinViewModel { parametersOf(data) }
|
vm = koinViewModel { parametersOf(data) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<SelectedApplicationInfo.RequiredOptions> {
|
|
||||||
val data =
|
|
||||||
it.getComplexArg<SelectedApplicationInfo.PatchesSelector.ViewModelParams>()
|
|
||||||
val selectedAppInfoVm = koinNavViewModel<SelectedAppInfoViewModel>(
|
|
||||||
viewModelStoreOwner = navController.navGraphEntry(it)
|
|
||||||
)
|
|
||||||
|
|
||||||
RequiredOptionsScreen(
|
|
||||||
onBackClick = navController::popBackStack,
|
|
||||||
onContinue = { patches, options ->
|
|
||||||
selectedAppInfoVm.updateConfiguration(patches, options)
|
|
||||||
it.lifecycleScope.launch {
|
|
||||||
navController.navigateComplex(
|
|
||||||
Patcher,
|
|
||||||
selectedAppInfoVm.getPatcherParams()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
vm = koinViewModel { parametersOf(data) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
navigation<Settings>(startDestination = Settings.Main) {
|
navigation<Settings>(startDestination = Settings.Main) {
|
||||||
|
@ -8,7 +8,6 @@ import androidx.compose.foundation.LocalIndication
|
|||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@ -142,19 +141,13 @@ private inline fun <T : Any> WithOptionEditor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun <T : Any> OptionItem(
|
fun <T : Any> OptionItem(option: Option<T>, value: T?, setValue: (T?) -> Unit) {
|
||||||
option: Option<T>,
|
|
||||||
value: T?,
|
|
||||||
setValue: (T?) -> Unit,
|
|
||||||
) {
|
|
||||||
val editor = remember(option.type, option.presets) {
|
val editor = remember(option.type, option.presets) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val baseOptionEditor =
|
val baseOptionEditor =
|
||||||
optionEditors.getOrDefault(option.type, UnknownTypeEditor) as OptionEditor<T>
|
optionEditors.getOrDefault(option.type, UnknownTypeEditor) as OptionEditor<T>
|
||||||
|
|
||||||
if (option.type != typeOf<Boolean>() && option.presets != null) PresetOptionEditor(
|
if (option.type != typeOf<Boolean>() && option.presets != null) PresetOptionEditor(baseOptionEditor)
|
||||||
baseOptionEditor
|
|
||||||
)
|
|
||||||
else baseOptionEditor
|
else baseOptionEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,15 +155,7 @@ fun <T : Any> OptionItem(
|
|||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable(onClick = ::clickAction),
|
modifier = Modifier.clickable(onClick = ::clickAction),
|
||||||
headlineContent = { Text(option.title) },
|
headlineContent = { Text(option.title) },
|
||||||
supportingContent = {
|
supportingContent = { Text(option.description) },
|
||||||
Column {
|
|
||||||
Text(option.description)
|
|
||||||
if (option.required && value == null) Text(
|
|
||||||
stringResource(R.string.option_required),
|
|
||||||
color = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
trailingContent = { ListItemTrailingContent() }
|
trailingContent = { ListItemTrailingContent() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
package app.revanced.manager.ui.component.settings
|
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
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.res.stringResource
|
|
||||||
import app.revanced.manager.domain.manager.base.Preference
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun SafeguardBooleanItem(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
preference: Preference<Boolean>,
|
|
||||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
|
||||||
@StringRes headline: Int,
|
|
||||||
@StringRes description: Int,
|
|
||||||
@StringRes confirmationText: Int
|
|
||||||
) {
|
|
||||||
val value by preference.getAsState()
|
|
||||||
var showSafeguardWarning by rememberSaveable {
|
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showSafeguardWarning) {
|
|
||||||
SafeguardConfirmationDialog(
|
|
||||||
onDismiss = { showSafeguardWarning = false },
|
|
||||||
onConfirm = {
|
|
||||||
coroutineScope.launch { preference.update(!value) }
|
|
||||||
showSafeguardWarning = false
|
|
||||||
},
|
|
||||||
body = stringResource(confirmationText)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BooleanItem(
|
|
||||||
modifier = modifier,
|
|
||||||
value = value,
|
|
||||||
onValueChange = {
|
|
||||||
if (it != preference.default) {
|
|
||||||
showSafeguardWarning = true
|
|
||||||
} else {
|
|
||||||
coroutineScope.launch { preference.update(it) }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
headline = headline,
|
|
||||||
description = description
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package app.revanced.manager.ui.component.settings
|
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.WarningAmber
|
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import app.revanced.manager.R
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun SafeguardConfirmationDialog(
|
|
||||||
onDismiss: () -> Unit,
|
|
||||||
onConfirm: () -> Unit,
|
|
||||||
body: String,
|
|
||||||
) {
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = onDismiss,
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(onClick = onConfirm) {
|
|
||||||
Text(stringResource(R.string.yes))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = onDismiss) {
|
|
||||||
Text(stringResource(R.string.no))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon = {
|
|
||||||
Icon(Icons.Outlined.WarningAmber, null)
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(id = R.string.warning),
|
|
||||||
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(body)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
@ -34,23 +34,20 @@ data class BundleInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object Extensions {
|
companion object Extensions {
|
||||||
inline fun Iterable<BundleInfo>.toPatchSelection(
|
inline fun Iterable<BundleInfo>.toPatchSelection(allowUnsupported: Boolean, condition: (Int, PatchInfo) -> Boolean): PatchSelection = this.associate { bundle ->
|
||||||
allowUnsupported: Boolean,
|
val patches =
|
||||||
condition: (Int, PatchInfo) -> Boolean
|
bundle.patchSequence(allowUnsupported)
|
||||||
): PatchSelection = this.associate { bundle ->
|
.mapNotNullTo(mutableSetOf()) { patch ->
|
||||||
val patches =
|
patch.name.takeIf {
|
||||||
bundle.patchSequence(allowUnsupported)
|
condition(
|
||||||
.mapNotNullTo(mutableSetOf()) { patch ->
|
bundle.uid,
|
||||||
patch.name.takeIf {
|
patch
|
||||||
condition(
|
)
|
||||||
bundle.uid,
|
}
|
||||||
patch
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bundle.uid to patches
|
bundle.uid to patches
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PatchBundleRepository.bundleInfoFlow(packageName: String, version: String?) =
|
fun PatchBundleRepository.bundleInfoFlow(packageName: String, version: String?) =
|
||||||
sources.flatMapLatestAndCombine(
|
sources.flatMapLatestAndCombine(
|
||||||
@ -81,28 +78,6 @@ data class BundleInfo(
|
|||||||
BundleInfo(source.getName(), source.uid, supported, unsupported, universal)
|
BundleInfo(source.getName(), source.uid, supported, unsupported, universal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Algorithm for determining whether all required options have been set.
|
|
||||||
*/
|
|
||||||
inline fun Iterable<BundleInfo>.requiredOptionsSet(
|
|
||||||
crossinline isSelected: (BundleInfo, PatchInfo) -> Boolean,
|
|
||||||
crossinline optionsForPatch: (BundleInfo, PatchInfo) -> Map<String, Any?>?
|
|
||||||
) = all bundle@{ bundle ->
|
|
||||||
bundle
|
|
||||||
.all
|
|
||||||
.filter { isSelected(bundle, it) }
|
|
||||||
.all patch@{
|
|
||||||
if (it.options.isNullOrEmpty()) return@patch true
|
|
||||||
val opts by lazy { optionsForPatch(bundle, it).orEmpty() }
|
|
||||||
|
|
||||||
it.options.all option@{ option ->
|
|
||||||
if (!option.required || option.default != null) return@option true
|
|
||||||
|
|
||||||
option.key in opts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,9 +42,6 @@ data object SelectedApplicationInfo : ComplexParameter<SelectedApplicationInfo.V
|
|||||||
val options: @RawValue Options,
|
val options: @RawValue Options,
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data object RequiredOptions : ComplexParameter<PatchesSelector.ViewModelParams>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -109,6 +109,7 @@ fun DashboardScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
var showAndroid11Dialog by rememberSaveable { mutableStateOf(false) }
|
var showAndroid11Dialog by rememberSaveable { mutableStateOf(false) }
|
||||||
val installAppsPermissionLauncher =
|
val installAppsPermissionLauncher =
|
||||||
rememberLauncherForActivityResult(RequestInstallAppsContract) { granted ->
|
rememberLauncherForActivityResult(RequestInstallAppsContract) { granted ->
|
||||||
@ -120,7 +121,7 @@ fun DashboardScreen(
|
|||||||
showAndroid11Dialog = false
|
showAndroid11Dialog = false
|
||||||
},
|
},
|
||||||
onContinue = {
|
onContinue = {
|
||||||
installAppsPermissionLauncher.launch(androidContext.packageName)
|
installAppsPermissionLauncher.launch(context.packageName)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -238,7 +239,6 @@ fun DashboardScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val showBatteryOptimizationsWarning by vm.showBatteryOptimizationsWarningFlow.collectAsStateWithLifecycle(false)
|
|
||||||
Notifications(
|
Notifications(
|
||||||
if (!Aapt.supportsDevice()) {
|
if (!Aapt.supportsDevice()) {
|
||||||
{
|
{
|
||||||
@ -250,7 +250,7 @@ fun DashboardScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else null,
|
} else null,
|
||||||
if (showBatteryOptimizationsWarning) {
|
if (vm.showBatteryOptimizationsWarning) {
|
||||||
{
|
{
|
||||||
NotificationCard(
|
NotificationCard(
|
||||||
isWarning = true,
|
isWarning = true,
|
||||||
|
@ -67,7 +67,7 @@ fun PatchesSelectorScreen(
|
|||||||
mutableStateOf(null)
|
mutableStateOf(null)
|
||||||
}
|
}
|
||||||
var showBottomSheet by rememberSaveable { mutableStateOf(false) }
|
var showBottomSheet by rememberSaveable { mutableStateOf(false) }
|
||||||
val showSaveButton by remember {
|
val showPatchButton by remember {
|
||||||
derivedStateOf { vm.selectionIsValid(bundles) }
|
derivedStateOf { vm.selectionIsValid(bundles) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +298,7 @@ fun PatchesSelectorScreen(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
if (!showSaveButton) return@Scaffold
|
if (!showPatchButton) return@Scaffold
|
||||||
|
|
||||||
HapticExtendedFloatingActionButton(
|
HapticExtendedFloatingActionButton(
|
||||||
text = { Text(stringResource(R.string.save)) },
|
text = { Text(stringResource(R.string.save)) },
|
||||||
@ -311,6 +311,7 @@ fun PatchesSelectorScreen(
|
|||||||
expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp
|
expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp
|
||||||
?: true,
|
?: true,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
// TODO: only allow this if all required options have been set.
|
||||||
onSave(vm.getCustomSelection(), vm.getOptions())
|
onSave(vm.getCustomSelection(), vm.getOptions())
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -463,7 +464,7 @@ private fun PatchItem(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ListHeader(
|
private fun ListHeader(
|
||||||
title: String,
|
title: String,
|
||||||
onHelpClick: (() -> Unit)? = null
|
onHelpClick: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
package app.revanced.manager.ui.screen
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.AutoFixHigh
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.ScrollableTabRow
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import app.revanced.manager.R
|
|
||||||
import app.revanced.manager.patcher.patch.Option
|
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
|
||||||
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
|
||||||
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
|
||||||
import app.revanced.manager.ui.component.haptics.HapticTab
|
|
||||||
import app.revanced.manager.ui.component.patches.OptionItem
|
|
||||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.requiredOptionsSet
|
|
||||||
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel
|
|
||||||
import app.revanced.manager.util.Options
|
|
||||||
import app.revanced.manager.util.PatchSelection
|
|
||||||
import app.revanced.manager.util.isScrollingUp
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun RequiredOptionsScreen(
|
|
||||||
onContinue: (PatchSelection?, Options) -> Unit,
|
|
||||||
onBackClick: () -> Unit,
|
|
||||||
vm: PatchesSelectorViewModel
|
|
||||||
) {
|
|
||||||
val list by vm.requiredOptsPatches.collectAsStateWithLifecycle(emptyList())
|
|
||||||
|
|
||||||
val pagerState = rememberPagerState(
|
|
||||||
initialPage = 0,
|
|
||||||
initialPageOffsetFraction = 0f
|
|
||||||
) {
|
|
||||||
list.size
|
|
||||||
}
|
|
||||||
val patchLazyListStates = remember(list) { List(list.size, ::LazyListState) }
|
|
||||||
val bundles by vm.bundlesFlow.collectAsStateWithLifecycle(emptyList())
|
|
||||||
val showContinueButton by remember {
|
|
||||||
derivedStateOf {
|
|
||||||
bundles.requiredOptionsSet(
|
|
||||||
isSelected = { bundle, patch -> vm.isSelected(bundle.uid, patch) },
|
|
||||||
optionsForPatch = { bundle, patch -> vm.getOptions(bundle.uid, patch) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val composableScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
Scaffold(
|
|
||||||
topBar = {
|
|
||||||
AppTopBar(
|
|
||||||
title = stringResource(R.string.required_options_screen),
|
|
||||||
onBackClick = onBackClick
|
|
||||||
)
|
|
||||||
},
|
|
||||||
floatingActionButton = {
|
|
||||||
if (!showContinueButton) return@Scaffold
|
|
||||||
|
|
||||||
HapticExtendedFloatingActionButton(
|
|
||||||
text = { Text(stringResource(R.string.patch)) },
|
|
||||||
icon = {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.AutoFixHigh,
|
|
||||||
stringResource(R.string.patch)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp
|
|
||||||
?: true,
|
|
||||||
onClick = {
|
|
||||||
onContinue(vm.getCustomSelection(), vm.getOptions())
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) { paddingValues ->
|
|
||||||
Column(
|
|
||||||
Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(paddingValues)
|
|
||||||
) {
|
|
||||||
if (list.isEmpty()) return@Column
|
|
||||||
else if (list.size > 1) ScrollableTabRow(
|
|
||||||
selectedTabIndex = pagerState.currentPage,
|
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
|
|
||||||
) {
|
|
||||||
list.forEachIndexed { index, (bundle, _) ->
|
|
||||||
HapticTab(
|
|
||||||
selected = pagerState.currentPage == index,
|
|
||||||
onClick = {
|
|
||||||
composableScope.launch {
|
|
||||||
pagerState.animateScrollToPage(
|
|
||||||
index
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
text = { Text(bundle.name) },
|
|
||||||
selectedContentColor = MaterialTheme.colorScheme.primary,
|
|
||||||
unselectedContentColor = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HorizontalPager(
|
|
||||||
state = pagerState,
|
|
||||||
userScrollEnabled = true,
|
|
||||||
pageContent = { index ->
|
|
||||||
// Avoid crashing if the lists have not been fully initialized yet.
|
|
||||||
if (index > list.lastIndex || list.size != patchLazyListStates.size) return@HorizontalPager
|
|
||||||
val (bundle, patches) = list[index]
|
|
||||||
|
|
||||||
LazyColumnWithScrollbar(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
state = patchLazyListStates[index]
|
|
||||||
) {
|
|
||||||
items(patches, key = { it.name }) {
|
|
||||||
ListHeader(it.name)
|
|
||||||
|
|
||||||
val values = vm.getOptions(bundle.uid, it)
|
|
||||||
it.options?.forEach { option ->
|
|
||||||
val key = option.key
|
|
||||||
val value =
|
|
||||||
if (values == null || key !in values) option.default else values[key]
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
OptionItem(
|
|
||||||
option = option as Option<Any>,
|
|
||||||
value = value,
|
|
||||||
setValue = { new ->
|
|
||||||
vm.setOption(bundle.uid, it, key, new)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,9 +14,9 @@ import androidx.compose.material.icons.automirrored.outlined.ArrowRight
|
|||||||
import androidx.compose.material.icons.filled.AutoFixHigh
|
import androidx.compose.material.icons.filled.AutoFixHigh
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -32,6 +32,7 @@ import app.revanced.manager.ui.component.AppTopBar
|
|||||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.LoadingIndicator
|
import app.revanced.manager.ui.component.LoadingIndicator
|
||||||
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
||||||
|
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
|
||||||
import app.revanced.manager.ui.model.SelectedApp
|
import app.revanced.manager.ui.model.SelectedApp
|
||||||
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
|
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
|
||||||
import app.revanced.manager.util.EventEffect
|
import app.revanced.manager.util.EventEffect
|
||||||
@ -40,14 +41,12 @@ import app.revanced.manager.util.PatchSelection
|
|||||||
import app.revanced.manager.util.enabled
|
import app.revanced.manager.util.enabled
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
import app.revanced.manager.util.transparentListItemColors
|
import app.revanced.manager.util.transparentListItemColors
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SelectedAppInfoScreen(
|
fun SelectedAppInfoScreen(
|
||||||
onPatchSelectorClick: (SelectedApp, PatchSelection?, Options) -> Unit,
|
onPatchSelectorClick: (SelectedApp, PatchSelection?, Options) -> Unit,
|
||||||
onRequiredOptions: (SelectedApp, PatchSelection?, Options) -> Unit,
|
onPatchClick: (SelectedApp, PatchSelection, Options) -> Unit,
|
||||||
onPatchClick: () -> Unit,
|
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
vm: SelectedAppInfoViewModel
|
vm: SelectedAppInfoViewModel
|
||||||
) {
|
) {
|
||||||
@ -55,14 +54,20 @@ fun SelectedAppInfoScreen(
|
|||||||
|
|
||||||
val packageName = vm.selectedApp.packageName
|
val packageName = vm.selectedApp.packageName
|
||||||
val version = vm.selectedApp.version
|
val version = vm.selectedApp.version
|
||||||
val bundles by vm.bundleInfoFlow.collectAsStateWithLifecycle(emptyList())
|
val bundles by remember(packageName, version) {
|
||||||
|
vm.bundlesRepo.bundleInfoFlow(packageName, version)
|
||||||
|
}.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||||
|
|
||||||
val allowIncompatiblePatches by vm.prefs.disablePatchVersionCompatCheck.getAsState()
|
val allowIncompatiblePatches by vm.prefs.disablePatchVersionCompatCheck.getAsState()
|
||||||
val patches = remember(bundles, allowIncompatiblePatches) {
|
val patches by remember {
|
||||||
vm.getPatches(bundles, allowIncompatiblePatches)
|
derivedStateOf {
|
||||||
|
vm.getPatches(bundles, allowIncompatiblePatches)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val selectedPatchCount = remember(patches) {
|
val selectedPatchCount by remember {
|
||||||
patches.values.sumOf { it.size }
|
derivedStateOf {
|
||||||
|
patches.values.sumOf { it.size }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val launcher = rememberLauncherForActivityResult(
|
val launcher = rememberLauncherForActivityResult(
|
||||||
@ -72,7 +77,6 @@ fun SelectedAppInfoScreen(
|
|||||||
EventEffect(flow = vm.launchActivityFlow) { intent ->
|
EventEffect(flow = vm.launchActivityFlow) { intent ->
|
||||||
launcher.launch(intent)
|
launcher.launch(intent)
|
||||||
}
|
}
|
||||||
val composableScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
val error by vm.errorFlow.collectAsStateWithLifecycle(null)
|
val error by vm.errorFlow.collectAsStateWithLifecycle(null)
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@ -99,19 +103,11 @@ fun SelectedAppInfoScreen(
|
|||||||
|
|
||||||
return@patchClick
|
return@patchClick
|
||||||
}
|
}
|
||||||
|
onPatchClick(
|
||||||
composableScope.launch {
|
vm.selectedApp,
|
||||||
if (!vm.hasSetRequiredOptions(patches)) {
|
patches,
|
||||||
onRequiredOptions(
|
vm.getOptionsFiltered(bundles)
|
||||||
vm.selectedApp,
|
)
|
||||||
vm.getCustomPatches(bundles, allowIncompatiblePatches),
|
|
||||||
vm.options
|
|
||||||
)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
onPatchClick()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ import app.revanced.manager.ui.component.ColumnWithScrollbar
|
|||||||
import app.revanced.manager.ui.component.GroupHeader
|
import app.revanced.manager.ui.component.GroupHeader
|
||||||
import app.revanced.manager.ui.component.settings.BooleanItem
|
import app.revanced.manager.ui.component.settings.BooleanItem
|
||||||
import app.revanced.manager.ui.component.settings.IntegerItem
|
import app.revanced.manager.ui.component.settings.IntegerItem
|
||||||
import app.revanced.manager.ui.component.settings.SafeguardBooleanItem
|
|
||||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||||
import app.revanced.manager.ui.viewmodel.AdvancedSettingsViewModel
|
import app.revanced.manager.ui.viewmodel.AdvancedSettingsViewModel
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
@ -105,33 +104,29 @@ fun AdvancedSettingsScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
GroupHeader(stringResource(R.string.safeguards))
|
GroupHeader(stringResource(R.string.safeguards))
|
||||||
SafeguardBooleanItem(
|
BooleanItem(
|
||||||
preference = vm.prefs.disablePatchVersionCompatCheck,
|
preference = vm.prefs.disablePatchVersionCompatCheck,
|
||||||
coroutineScope = vm.viewModelScope,
|
coroutineScope = vm.viewModelScope,
|
||||||
headline = R.string.patch_compat_check,
|
headline = R.string.patch_compat_check,
|
||||||
description = R.string.patch_compat_check_description,
|
description = R.string.patch_compat_check_description
|
||||||
confirmationText = R.string.patch_compat_check_confirmation
|
|
||||||
)
|
)
|
||||||
SafeguardBooleanItem(
|
BooleanItem(
|
||||||
preference = vm.prefs.disableUniversalPatchWarning,
|
preference = vm.prefs.disableUniversalPatchWarning,
|
||||||
coroutineScope = vm.viewModelScope,
|
coroutineScope = vm.viewModelScope,
|
||||||
headline = R.string.universal_patches_safeguard,
|
headline = R.string.universal_patches_safeguard,
|
||||||
description = R.string.universal_patches_safeguard_description,
|
description = R.string.universal_patches_safeguard_description
|
||||||
confirmationText = R.string.universal_patches_safeguard_confirmation
|
|
||||||
)
|
)
|
||||||
SafeguardBooleanItem(
|
BooleanItem(
|
||||||
preference = vm.prefs.suggestedVersionSafeguard,
|
preference = vm.prefs.suggestedVersionSafeguard,
|
||||||
coroutineScope = vm.viewModelScope,
|
coroutineScope = vm.viewModelScope,
|
||||||
headline = R.string.suggested_version_safeguard,
|
headline = R.string.suggested_version_safeguard,
|
||||||
description = R.string.suggested_version_safeguard_description,
|
description = R.string.suggested_version_safeguard_description
|
||||||
confirmationText = R.string.suggested_version_safeguard_confirmation
|
|
||||||
)
|
)
|
||||||
SafeguardBooleanItem(
|
BooleanItem(
|
||||||
preference = vm.prefs.disableSelectionWarning,
|
preference = vm.prefs.disableSelectionWarning,
|
||||||
coroutineScope = vm.viewModelScope,
|
coroutineScope = vm.viewModelScope,
|
||||||
headline = R.string.patch_selection_safeguard,
|
headline = R.string.patch_selection_safeguard,
|
||||||
description = R.string.patch_selection_safeguard_description,
|
description = R.string.patch_selection_safeguard_description
|
||||||
confirmationText = R.string.patch_selection_safeguard_confirmation
|
|
||||||
)
|
)
|
||||||
|
|
||||||
GroupHeader(stringResource(R.string.debugging))
|
GroupHeader(stringResource(R.string.debugging))
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package app.revanced.manager.ui.screen.settings
|
package app.revanced.manager.ui.screen.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -95,7 +96,7 @@ fun ContributorScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class, ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ContributorsCard(
|
fun ContributorsCard(
|
||||||
title: String,
|
title: String,
|
||||||
@ -130,7 +131,7 @@ fun ContributorsCard(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = processHeadlineText(title),
|
||||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Medium)
|
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Medium)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
@ -198,4 +199,11 @@ fun ContributorsCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processHeadlineText(repositoryName: String): String {
|
||||||
|
return repositoryName.replace("revanced/revanced-", "")
|
||||||
|
.replace("-", " ")
|
||||||
|
.split(" ").joinToString(" ") { if (it.length > 3) it else it.uppercase() }
|
||||||
|
.replaceFirstChar { it.uppercase() }
|
||||||
}
|
}
|
@ -24,9 +24,7 @@ import app.revanced.manager.network.api.ReVancedAPI
|
|||||||
import app.revanced.manager.util.PM
|
import app.revanced.manager.util.PM
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
import app.revanced.manager.util.uiSafe
|
import app.revanced.manager.util.uiSafe
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@ -58,19 +56,14 @@ class DashboardViewModel(
|
|||||||
|
|
||||||
var updatedManagerVersion: String? by mutableStateOf(null)
|
var updatedManagerVersion: String? by mutableStateOf(null)
|
||||||
private set
|
private set
|
||||||
val showBatteryOptimizationsWarningFlow = flow {
|
var showBatteryOptimizationsWarning by mutableStateOf(false)
|
||||||
while (true) {
|
private set
|
||||||
// There is no callback for this, so we have to poll it.
|
|
||||||
val result = !powerManager.isIgnoringBatteryOptimizations(app.packageName)
|
|
||||||
emit(result)
|
|
||||||
if (!result) return@flow
|
|
||||||
delay(500L)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
checkForManagerUpdates()
|
checkForManagerUpdates()
|
||||||
|
showBatteryOptimizationsWarning =
|
||||||
|
!powerManager.isIgnoringBatteryOptimizations(app.packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import app.revanced.manager.patcher.patch.PatchInfo
|
|||||||
import app.revanced.manager.ui.model.BundleInfo
|
import app.revanced.manager.ui.model.BundleInfo
|
||||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
|
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
|
||||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.toPatchSelection
|
import app.revanced.manager.ui.model.BundleInfo.Extensions.toPatchSelection
|
||||||
|
import app.revanced.manager.ui.model.SelectedApp
|
||||||
import app.revanced.manager.ui.model.navigation.SelectedApplicationInfo
|
import app.revanced.manager.ui.model.navigation.SelectedApplicationInfo
|
||||||
import app.revanced.manager.util.Options
|
import app.revanced.manager.util.Options
|
||||||
import app.revanced.manager.util.PatchSelection
|
import app.revanced.manager.util.PatchSelection
|
||||||
@ -36,14 +37,11 @@ import kotlinx.coroutines.launch
|
|||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
import kotlinx.collections.immutable.*
|
import kotlinx.collections.immutable.*
|
||||||
import kotlinx.coroutines.CoroutineStart
|
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
@Stable
|
||||||
@OptIn(SavedStateHandleSaveableApi::class)
|
@OptIn(SavedStateHandleSaveableApi::class)
|
||||||
class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.ViewModelParams) :
|
class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.ViewModelParams) : ViewModel(), KoinComponent {
|
||||||
ViewModel(), KoinComponent {
|
|
||||||
private val app: Application = get()
|
private val app: Application = get()
|
||||||
private val savedStateHandle: SavedStateHandle = get()
|
private val savedStateHandle: SavedStateHandle = get()
|
||||||
private val prefs: PreferencesManager = get()
|
private val prefs: PreferencesManager = get()
|
||||||
@ -116,22 +114,6 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
|
|||||||
selection.values.sumOf { it.size }
|
selection.values.sumOf { it.size }
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is for the required options screen.
|
|
||||||
private val requiredOptsPatchesDeferred = viewModelScope.async(start = CoroutineStart.LAZY) {
|
|
||||||
bundlesFlow.first().map { bundle ->
|
|
||||||
bundle to bundle.all.filter { patch ->
|
|
||||||
val opts by lazy {
|
|
||||||
getOptions(bundle.uid, patch).orEmpty()
|
|
||||||
}
|
|
||||||
isSelected(
|
|
||||||
bundle.uid,
|
|
||||||
patch
|
|
||||||
) && patch.options?.any { it.required && it.default == null && it.key !in opts } ?: false
|
|
||||||
}.toList()
|
|
||||||
}.filter { (_, patches) -> patches.isNotEmpty() }
|
|
||||||
}
|
|
||||||
val requiredOptsPatches = flow { emit(requiredOptsPatchesDeferred.await()) }
|
|
||||||
|
|
||||||
fun selectionIsValid(bundles: List<BundleInfo>) = bundles.any { bundle ->
|
fun selectionIsValid(bundles: List<BundleInfo>) = bundles.any { bundle ->
|
||||||
bundle.patchSequence(allowIncompatiblePatches).any { patch ->
|
bundle.patchSequence(allowIncompatiblePatches).any { patch ->
|
||||||
isSelected(bundle.uid, patch)
|
isSelected(bundle.uid, patch)
|
||||||
|
@ -9,7 +9,6 @@ import android.util.Log
|
|||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@ -30,16 +29,13 @@ import app.revanced.manager.domain.repository.PatchOptionsRepository
|
|||||||
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
||||||
import app.revanced.manager.network.downloader.LoadedDownloaderPlugin
|
import app.revanced.manager.network.downloader.LoadedDownloaderPlugin
|
||||||
import app.revanced.manager.network.downloader.ParceledDownloaderData
|
import app.revanced.manager.network.downloader.ParceledDownloaderData
|
||||||
import app.revanced.manager.patcher.patch.PatchInfo
|
|
||||||
import app.revanced.manager.plugin.downloader.GetScope
|
import app.revanced.manager.plugin.downloader.GetScope
|
||||||
import app.revanced.manager.plugin.downloader.PluginHostApi
|
import app.revanced.manager.plugin.downloader.PluginHostApi
|
||||||
import app.revanced.manager.plugin.downloader.UserInteractionException
|
import app.revanced.manager.plugin.downloader.UserInteractionException
|
||||||
import app.revanced.manager.ui.model.BundleInfo
|
import app.revanced.manager.ui.model.BundleInfo
|
||||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
|
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
|
||||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.toPatchSelection
|
import app.revanced.manager.ui.model.BundleInfo.Extensions.toPatchSelection
|
||||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.requiredOptionsSet
|
|
||||||
import app.revanced.manager.ui.model.SelectedApp
|
import app.revanced.manager.ui.model.SelectedApp
|
||||||
import app.revanced.manager.ui.model.navigation.Patcher
|
|
||||||
import app.revanced.manager.ui.model.navigation.SelectedApplicationInfo
|
import app.revanced.manager.ui.model.navigation.SelectedApplicationInfo
|
||||||
import app.revanced.manager.util.Options
|
import app.revanced.manager.util.Options
|
||||||
import app.revanced.manager.util.PM
|
import app.revanced.manager.util.PM
|
||||||
@ -67,6 +63,7 @@ class SelectedAppInfoViewModel(
|
|||||||
input: SelectedApplicationInfo.ViewModelParams
|
input: SelectedApplicationInfo.ViewModelParams
|
||||||
) : ViewModel(), KoinComponent {
|
) : ViewModel(), KoinComponent {
|
||||||
private val app: Application = get()
|
private val app: Application = get()
|
||||||
|
val bundlesRepo: PatchBundleRepository = get()
|
||||||
private val bundleRepository: PatchBundleRepository = get()
|
private val bundleRepository: PatchBundleRepository = get()
|
||||||
private val selectionRepository: PatchSelectionRepository = get()
|
private val selectionRepository: PatchSelectionRepository = get()
|
||||||
private val optionsRepository: PatchOptionsRepository = get()
|
private val optionsRepository: PatchOptionsRepository = get()
|
||||||
@ -177,10 +174,6 @@ class SelectedAppInfoViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val bundleInfoFlow by derivedStateOf {
|
|
||||||
bundleRepository.bundleInfoFlow(packageName, selectedApp.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showSourceSelector() {
|
fun showSourceSelector() {
|
||||||
dismissSourceSelector()
|
dismissSourceSelector()
|
||||||
showSourceSelector = true
|
showSourceSelector = true
|
||||||
@ -267,23 +260,6 @@ class SelectedAppInfoViewModel(
|
|||||||
selectedAppInfo = info
|
selectedAppInfo = info
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun hasSetRequiredOptions(patchSelection: PatchSelection) = bundleInfoFlow
|
|
||||||
.first()
|
|
||||||
.requiredOptionsSet(
|
|
||||||
isSelected = { bundle, patch -> patch.name in patchSelection[bundle.uid]!! },
|
|
||||||
optionsForPatch = { bundle, patch -> options[bundle.uid]?.get(patch.name) },
|
|
||||||
)
|
|
||||||
|
|
||||||
suspend fun getPatcherParams(): Patcher.ViewModelParams {
|
|
||||||
val allowUnsupported = prefs.disablePatchVersionCompatCheck.get()
|
|
||||||
val bundles = bundleInfoFlow.first()
|
|
||||||
return Patcher.ViewModelParams(
|
|
||||||
selectedApp,
|
|
||||||
getPatches(bundles, allowUnsupported),
|
|
||||||
getOptionsFiltered(bundles)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getOptionsFiltered(bundles: List<BundleInfo>) = options.filtered(bundles)
|
fun getOptionsFiltered(bundles: List<BundleInfo>) = options.filtered(bundles)
|
||||||
|
|
||||||
fun getPatches(bundles: List<BundleInfo>, allowUnsupported: Boolean) =
|
fun getPatches(bundles: List<BundleInfo>, allowUnsupported: Boolean) =
|
||||||
@ -296,7 +272,7 @@ class SelectedAppInfoViewModel(
|
|||||||
(selectionState as? SelectionState.Customized)?.patches(bundles, allowUnsupported)
|
(selectionState as? SelectionState.Customized)?.patches(bundles, allowUnsupported)
|
||||||
|
|
||||||
fun updateConfiguration(selection: PatchSelection?, options: Options) = viewModelScope.launch {
|
fun updateConfiguration(selection: PatchSelection?, options: Options) = viewModelScope.launch {
|
||||||
val bundles = bundleInfoFlow.first()
|
val bundles = bundlesRepo.bundleInfoFlow(packageName, selectedApp.version).first()
|
||||||
|
|
||||||
selectionState = selection?.let(SelectionState::Customized) ?: SelectionState.Default
|
selectionState = selection?.let(SelectionState::Customized) ?: SelectionState.Default
|
||||||
|
|
||||||
|
@ -88,16 +88,12 @@
|
|||||||
<string name="safeguards">Safeguards</string>
|
<string name="safeguards">Safeguards</string>
|
||||||
<string name="patch_compat_check">Disable version compatibility check</string>
|
<string name="patch_compat_check">Disable version compatibility check</string>
|
||||||
<string name="patch_compat_check_description">The check restricts patches to supported app versions</string>
|
<string name="patch_compat_check_description">The check restricts patches to supported app versions</string>
|
||||||
<string name="patch_compat_check_confirmation">Selecting incompatible patches can result in a broken app.\n\nDo you want to proceed anyways?</string>
|
|
||||||
<string name="suggested_version_safeguard">Require suggested app version</string>
|
<string name="suggested_version_safeguard">Require suggested app version</string>
|
||||||
<string name="suggested_version_safeguard_description">Enforce selection of the suggested app version</string>
|
<string name="suggested_version_safeguard_description">Enforce selection of the suggested app version</string>
|
||||||
<string name="suggested_version_safeguard_confirmation">Selecting an app that is not the suggested version may cause unexpected issues.\n\nDo you want to proceed anyways?</string>
|
|
||||||
<string name="patch_selection_safeguard">Allow changing patch selection</string>
|
<string name="patch_selection_safeguard">Allow changing patch selection</string>
|
||||||
<string name="patch_selection_safeguard_description">Do not prevent selecting or deselecting patches</string>
|
<string name="patch_selection_safeguard_description">Do not prevent selecting or deselecting patches</string>
|
||||||
<string name="patch_selection_safeguard_confirmation">Changing the selection of patches may cause unexpected issues.\n\nEnable anyways?</string>
|
|
||||||
<string name="universal_patches_safeguard">Disable universal patch warning</string>
|
<string name="universal_patches_safeguard">Disable universal patch warning</string>
|
||||||
<string name="universal_patches_safeguard_description">Disables the warning that appears when you try to select universal patches</string>
|
<string name="universal_patches_safeguard_description">Disables the warning that appears when you try to select universal patches</string>
|
||||||
<string name="universal_patches_safeguard_confirmation">Universal patches are not as well tested as those that target specific apps.\n\nEnable anyways?</string>
|
|
||||||
<string name="import_keystore">Import keystore</string>
|
<string name="import_keystore">Import keystore</string>
|
||||||
<string name="import_keystore_description">Import a custom keystore</string>
|
<string name="import_keystore_description">Import a custom keystore</string>
|
||||||
<string name="import_keystore_dialog_title">Enter keystore credentials</string>
|
<string name="import_keystore_dialog_title">Enter keystore credentials</string>
|
||||||
@ -146,8 +142,6 @@
|
|||||||
|
|
||||||
<string name="options">Options</string>
|
<string name="options">Options</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="yes">Yes</string>
|
|
||||||
<string name="no">No</string>
|
|
||||||
<string name="edit">Edit</string>
|
<string name="edit">Edit</string>
|
||||||
<string name="dialog_input_placeholder">Value</string>
|
<string name="dialog_input_placeholder">Value</string>
|
||||||
<string name="reset">Reset</string>
|
<string name="reset">Reset</string>
|
||||||
@ -365,8 +359,6 @@
|
|||||||
<string name="invalid_date">Invalid date</string>
|
<string name="invalid_date">Invalid date</string>
|
||||||
<string name="disable_battery_optimization">Disable battery optimization</string>
|
<string name="disable_battery_optimization">Disable battery optimization</string>
|
||||||
<string name="input_dialog_value_invalid">Invalid value</string>
|
<string name="input_dialog_value_invalid">Invalid value</string>
|
||||||
<string name="option_required">This option is required</string>
|
|
||||||
<string name="required_options_screen">Required options</string>
|
|
||||||
|
|
||||||
<string name="failed_to_check_updates">Failed to check for updates: %s</string>
|
<string name="failed_to_check_updates">Failed to check for updates: %s</string>
|
||||||
<string name="no_update_available">No update available</string>
|
<string name="no_update_available">No update available</string>
|
||||||
|
Reference in New Issue
Block a user