Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add required options screen #2378

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 46 additions & 8 deletions app/src/main/java/app/revanced/manager/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
Expand All @@ -29,6 +30,7 @@ import app.revanced.manager.ui.theme.Theme
import app.revanced.manager.ui.viewmodel.MainViewModel
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
import app.revanced.manager.util.EventEffect
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
import org.koin.androidx.compose.navigation.koinNavViewModel
import org.koin.core.parameter.parametersOf
Expand Down Expand Up @@ -139,14 +141,20 @@ private fun ReVancedManager(vm: MainViewModel) {
val parentBackStackEntry = navController.navGraphEntry(it)
val data =
parentBackStackEntry.getComplexArg<SelectedApplicationInfo.ViewModelParams>()
val viewModel =
koinNavViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) {
parametersOf(data)
}

SelectedAppInfoScreen(
onBackClick = navController::popBackStack,
onPatchClick = { app, patches, options ->
navController.navigateComplex(
Patcher,
Patcher.ViewModelParams(app, patches, options)
)
onPatchClick = {
it.lifecycleScope.launch {
navController.navigateComplex(
Patcher,
viewModel.getPatcherParams()
)
}
},
onPatchSelectorClick = { app, patches, options ->
navController.navigateComplex(
Expand All @@ -158,9 +166,17 @@ private fun ReVancedManager(vm: MainViewModel) {
)
)
},
vm = koinNavViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) {
parametersOf(data)
}
onRequiredOptions = { app, patches, options ->
navController.navigateComplex(
SelectedApplicationInfo.RequiredOptions,
SelectedApplicationInfo.PatchesSelector.ViewModelParams(
app,
patches,
options
)
)
},
vm = viewModel
)
}

Expand All @@ -180,6 +196,28 @@ private fun ReVancedManager(vm: MainViewModel) {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
Expand Down Expand Up @@ -141,21 +142,35 @@ private inline fun <T : Any> WithOptionEditor(
}

@Composable
fun <T : Any> OptionItem(option: Option<T>, value: T?, setValue: (T?) -> Unit) {
fun <T : Any> OptionItem(
option: Option<T>,
value: T?,
setValue: (T?) -> Unit,
) {
val editor = remember(option.type, option.presets) {
@Suppress("UNCHECKED_CAST")
val baseOptionEditor =
optionEditors.getOrDefault(option.type, UnknownTypeEditor) as OptionEditor<T>

if (option.type != typeOf<Boolean>() && option.presets != null) PresetOptionEditor(baseOptionEditor)
if (option.type != typeOf<Boolean>() && option.presets != null) PresetOptionEditor(
baseOptionEditor
)
else baseOptionEditor
}

WithOptionEditor(editor, option, value, setValue) {
ListItem(
modifier = Modifier.clickable(onClick = ::clickAction),
headlineContent = { Text(option.title) },
supportingContent = { Text(option.description) },
supportingContent = {
Column {
Text(option.description)
if (option.required && value == null) Text(
stringResource(R.string.option_required),
color = MaterialTheme.colorScheme.error
)
}
},
trailingContent = { ListItemTrailingContent() }
)
}
Expand Down
49 changes: 37 additions & 12 deletions app/src/main/java/app/revanced/manager/ui/model/BundleInfo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,23 @@ data class BundleInfo(
}

companion object Extensions {
inline fun Iterable<BundleInfo>.toPatchSelection(allowUnsupported: Boolean, condition: (Int, PatchInfo) -> Boolean): PatchSelection = this.associate { bundle ->
val patches =
bundle.patchSequence(allowUnsupported)
.mapNotNullTo(mutableSetOf()) { patch ->
patch.name.takeIf {
condition(
bundle.uid,
patch
)
}
inline fun Iterable<BundleInfo>.toPatchSelection(
allowUnsupported: Boolean,
condition: (Int, PatchInfo) -> Boolean
): PatchSelection = this.associate { bundle ->
val patches =
bundle.patchSequence(allowUnsupported)
.mapNotNullTo(mutableSetOf()) { patch ->
patch.name.takeIf {
condition(
bundle.uid,
patch
)
}
}

bundle.uid to patches
}
bundle.uid to patches
}

fun PatchBundleRepository.bundleInfoFlow(packageName: String, version: String?) =
sources.flatMapLatestAndCombine(
Expand Down Expand Up @@ -78,6 +81,28 @@ data class BundleInfo(
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
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ data object SelectedApplicationInfo : ComplexParameter<SelectedApplicationInfo.V
val options: @RawValue Options,
) : Parcelable
}

@Serializable
data object RequiredOptions : ComplexParameter<PatchesSelector.ViewModelParams>
}

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fun PatchesSelectorScreen(
mutableStateOf(null)
}
var showBottomSheet by rememberSaveable { mutableStateOf(false) }
val showPatchButton by remember {
val showSaveButton by remember {
derivedStateOf { vm.selectionIsValid(bundles) }
}

Expand Down Expand Up @@ -298,7 +298,7 @@ fun PatchesSelectorScreen(
)
},
floatingActionButton = {
if (!showPatchButton) return@Scaffold
if (!showSaveButton) return@Scaffold

HapticExtendedFloatingActionButton(
text = { Text(stringResource(R.string.save)) },
Expand All @@ -311,7 +311,6 @@ fun PatchesSelectorScreen(
expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp
?: true,
onClick = {
// TODO: only allow this if all required options have been set.
onSave(vm.getCustomSelection(), vm.getOptions())
}
)
Expand Down Expand Up @@ -464,7 +463,7 @@ private fun PatchItem(
)

@Composable
private fun ListHeader(
fun ListHeader(
title: String,
onHelpClick: (() -> Unit)? = null
) {
Expand Down
Loading
Loading