Update selected app info screen and version selector screen

This commit is contained in:
Robert
2025-12-30 21:19:22 +01:00
parent fda0e1697b
commit 9cdb8eafb3
8 changed files with 111 additions and 110 deletions

View File

@@ -211,35 +211,35 @@ private fun ReVancedManager() {
)
}
},
onPatchSelectorClick = { packageName, version, patches, options ->
onPatchSelectorClick = { packageName, version, patchSelection, options ->
navController.navigateComplex(
SelectedAppInfo.PatchesSelector,
SelectedAppInfo.PatchesSelector.ViewModelParams(
packageName,
version,
currentSelection = patches,
options = options,
patchSelection,
options,
)
)
},
onRequiredOptions = { packageName, version, patches, options ->
onRequiredOptions = { packageName, version, patchSelection, options ->
navController.navigateComplex(
SelectedAppInfo.RequiredOptions,
SelectedAppInfo.PatchesSelector.ViewModelParams(
packageName,
version,
currentSelection = patches,
options = options,
patchSelection,
options,
)
)
},
onVersionClick = { packageName, patchSelection, currentSelection ->
onVersionClick = { packageName, patchSelection, selectedVersion ->
navController.navigateComplex(
SelectedAppInfo.VersionSelector,
SelectedAppInfo.VersionSelector.ViewModelParams(
packageName,
patchSelection,
currentSelection,
selectedVersion,
)
)
},

View File

@@ -42,8 +42,7 @@ data object SelectedAppInfo : ComplexParameter<SelectedAppInfo.ViewModelParams>
data class ViewModelParams(
val packageName: String,
val version: String?,
val restrictToVersion: Boolean = false,
val currentSelection: PatchSelection?,
val patchSelection: PatchSelection?,
val options: @RawValue Options,
) : Parcelable
}
@@ -54,7 +53,7 @@ data object SelectedAppInfo : ComplexParameter<SelectedAppInfo.ViewModelParams>
data class ViewModelParams(
val packageName: String,
val patchSelection: PatchSelection,
val currentSelection: SelectedVersion,
val selectedVersion: SelectedVersion,
) : Parcelable
}

View File

@@ -19,7 +19,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
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
@@ -36,11 +35,13 @@ import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.component.NotificationCard
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
import app.revanced.manager.ui.model.SelectedSource
import app.revanced.manager.ui.model.SelectedVersion
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.enabled
import app.revanced.manager.util.patchCount
import app.revanced.manager.util.toast
import kotlinx.coroutines.launch
import org.koin.compose.koinInject
@@ -51,7 +52,7 @@ fun SelectedAppInfoScreen(
onPatchSelectorClick: (packageName: String, version: String?, PatchSelection?, Options) -> Unit,
onRequiredOptions: (packageName: String, version: String?, PatchSelection?, Options) -> Unit,
onPatchClick: () -> Unit,
onVersionClick: (packageName: String, patchSelection: PatchSelection, currentSelection: SelectedVersion) -> Unit,
onVersionClick: (packageName: String, patchSelection: PatchSelection, selectedVersion: SelectedVersion) -> Unit,
onSourceClick: (packageName: String, version: String?) -> Unit,
onBackClick: () -> Unit,
vm: SelectedAppInfoViewModel
@@ -61,18 +62,7 @@ fun SelectedAppInfoScreen(
val networkConnected = remember { networkInfo.isConnected() }
val networkMetered = remember { !networkInfo.isUnmetered() }
val packageName = vm.input.packageName
val version = "123" // TODO
val bundles by vm.bundleInfoFlow.collectAsStateWithLifecycle(emptyList())
val allowIncompatiblePatches by vm.prefs.disablePatchVersionCompatCheck.getAsState()
val patches by remember {
derivedStateOf {
vm.getPatches(bundles, allowIncompatiblePatches)
}
}
val selectedPatchCount = patches.values.sumOf { it.size }
val packageName = vm.packageName
val composableScope = rememberCoroutineScope()
val error by vm.errorFlow.collectAsStateWithLifecycle(null)
@@ -81,9 +71,14 @@ fun SelectedAppInfoScreen(
val resolvedVersion by vm.resolvedVersion.collectAsStateWithLifecycle(null)
val selectedSource by vm.selectedSource.collectAsStateWithLifecycle()
val resolvedSource by vm.resolvedSource.collectAsStateWithLifecycle(null)
val customSelection by vm.customSelection.collectAsStateWithLifecycle(null)
val fullPatchSelection by vm.patchSelection.collectAsStateWithLifecycle(emptyMap())
val patchCount = fullPatchSelection.patchCount
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val plugins by vm.plugins.collectAsStateWithLifecycle(emptyList())
Scaffold(
topBar = {
@@ -105,19 +100,18 @@ fun SelectedAppInfoScreen(
)
},
onClick = patchClick@{
if (selectedPatchCount == 0) {
if (patchCount == 0) {
context.toast(context.getString(R.string.no_patches_selected))
return@patchClick
}
composableScope.launch {
if (!vm.hasSetRequiredOptions(patches)) {
if (!vm.hasSetRequiredOptions(fullPatchSelection)) {
onRequiredOptions(
vm.packageName,
version,
vm.getCustomPatches(bundles, allowIncompatiblePatches),
vm.options
resolvedVersion,
customSelection,
vm.options,
)
return@launch
}
@@ -129,8 +123,6 @@ fun SelectedAppInfoScreen(
},
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { paddingValues ->
val plugins by vm.plugins.collectAsStateWithLifecycle(emptyList())
ColumnWithScrollbar(
modifier = Modifier
.fillMaxSize()
@@ -148,57 +140,42 @@ fun SelectedAppInfoScreen(
PageItem(
R.string.patch_selector_item,
stringResource(
R.string.patch_selector_item_description,
selectedPatchCount
),
stringResource(R.string.patch_selector_item_description, patchCount),
onClick = {
onPatchSelectorClick(
vm.packageName,
version,
vm.getCustomPatches(
bundles,
allowIncompatiblePatches
),
resolvedVersion,
customSelection,
vm.options
)
}
)
if (vm.input.localFile == null) {
val version = resolvedVersion ?: "Any available version"
val versionText = resolvedVersion ?: "Any available version"
val versionDescription = if (selectedVersion is SelectedVersion.Auto)
"Auto ($versionText)" // stringResource(R.string.selected_app_meta_auto_version, actualVersion)
else versionText
val description = if (selectedVersion is SelectedVersion.Auto)
"Auto ($version)" // stringResource(R.string.selected_app_meta_auto_version, actualVersion)
else version
PageItem(
R.string.version_selector_item,
description,
onClick = { onVersionClick(packageName, patches, selectedVersion) },
)
}
PageItem(
R.string.version_selector_item,
versionDescription,
onClick = {
onVersionClick(
packageName,
fullPatchSelection,
selectedVersion,
)
},
)
PageItem(
R.string.apk_source_selector_item,
// when (val app = vm.input.app) {
// is SelectedApp.Search -> "Auto (Downloaded APK)" // stringResource(R.string.apk_source_auto)
// is SelectedApp.Installed -> app.version + " (Installed)" // stringResource(R.string.apk_source_installed)
// is SelectedApp.Download -> plugins.find { it.packageName == app.data.pluginPackageName }?.name ?: app.data.pluginPackageName
// is SelectedApp.Local -> if (app.temporary) "${app.version} (Local File)" else "Downloaded APK"
//
//
//// stringResource(
//// R.string.apk_source_downloader,
//// plugins.find { it.packageName == app.data.pluginPackageName }?.name
//// ?: app.data.pluginPackageName
//// )
// // stringResource(R.string.apk_source_local)
// },
"Sourcing the source",
onClick = { onSourceClick(packageName, version) },
enabled = vm.input.localFile == null, // Disable for APK from storage
when (selectedSource) {
else -> "Sourcing the source"
},
onClick = { onSourceClick(packageName, versionText) },
)
error?.let {
Text(
stringResource(it.resourceId),
@@ -211,8 +188,7 @@ fun SelectedAppInfoScreen(
modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
val needsInternet = false
// vm.input.app.let { it is SelectedApp.Search || it is SelectedApp.Download }
val needsInternet = resolvedSource is SelectedSource.Plugin
when {
!needsInternet -> {}
@@ -240,7 +216,12 @@ fun SelectedAppInfoScreen(
}
@Composable
private fun PageItem(@StringRes title: Int, description: String, onClick: () -> Unit, enabled: Boolean = true) {
private fun PageItem(
@StringRes title: Int,
description: String,
onClick: () -> Unit,
enabled: Boolean = true
) {
ListItem(
modifier = Modifier
.clickable(enabled, onClick = onClick)

View File

@@ -60,35 +60,33 @@ fun VersionSelectorScreen(
title = { Text("Auto (Recommended)") },
description = { Text("Automatically select the best available version") }
)
if (versions.isNotEmpty())
HorizontalDivider()
LazyColumn {
items(versions, key = { it.first.version }) { version ->
VersionOption(
version = version.first,
isSelected = viewModel.selectedVersion == version.first,
onSelect = viewModel::selectVersion,
title = { Text(version.first.version) },
description = { Text(
"${version.second.let { if (it == 0) "No" else it }} incompatible patches")
}
)
}
}
HorizontalDivider()
VersionOption(
version = SelectedVersion.Any,
isSelected = viewModel.selectedVersion is SelectedVersion.Any,
onSelect = viewModel::selectVersion,
title = { Text("Any available version") },
description = { Text("Use any available version regardless of compatibility") }
)
if (versions.isNotEmpty()) {
LazyColumn {
items(versions, key = { it.first.version }) { version ->
VersionOption(
version = version.first,
isSelected = viewModel.selectedVersion == version.first,
onSelect = viewModel::selectVersion,
title = { Text(version.first.version) },
description = {
Text(
"${version.second.let { if (it == 0) "No" else it }} incompatible patches"
)
}
)
}
}
} else {
VersionOption(
version = SelectedVersion.Any,
isSelected = viewModel.selectedVersion is SelectedVersion.Any,
onSelect = viewModel::selectVersion,
title = { Text("Any available version") },
description = { Text("Use any available version regardless of compatibility") }
)
}
}
}
}

View File

@@ -66,6 +66,7 @@ class AppSelectorViewModel(
return@launch
}
// TODO: Disallow if 0 patches are compatible
storageSelectionChannel.send(selectedApp)
}

View File

@@ -88,7 +88,7 @@ class PatchesSelectorViewModel(input: SelectedAppInfo.PatchesSelector.ViewModelP
key = "selection",
stateSaver = selectionSaver,
) {
mutableStateOf(input.currentSelection?.toPersistentPatchSelection())
mutableStateOf(input.patchSelection?.toPersistentPatchSelection())
}
private val patchOptions: PersistentOptions by savedStateHandle.saveable(

View File

@@ -42,6 +42,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
@@ -59,7 +60,7 @@ class SelectedAppInfoViewModel(
private val installedAppRepository: InstalledAppRepository = get()
private val pm: PM = get()
private val savedStateHandle: SavedStateHandle = get()
val prefs: PreferencesManager = get()
private val prefs: PreferencesManager = get()
val plugins = pluginsRepository.loadedPluginsFlow
val packageName = input.packageName
private val persistConfiguration = input.patches == null
@@ -67,8 +68,8 @@ class SelectedAppInfoViewModel(
// User selection
private var selectionFlow = MutableStateFlow(
input.patches?.let {
SelectionState.Customized(input.patches)
input.patches?.let { selection ->
SelectionState.Customized(selection)
} ?: SelectionState.Default
)
@@ -106,7 +107,7 @@ class SelectedAppInfoViewModel(
// All patches for package
@OptIn(ExperimentalCoroutinesApi::class)
val bundles = selectedVersion.flatMapLatest { selectedVersion ->
val bundles = _selectedVersion.flatMapLatest { selectedVersion ->
val version = if (selectedVersion is SelectedVersion.Specific)
selectedVersion.version
else null
@@ -121,6 +122,13 @@ class SelectedAppInfoViewModel(
selection.patches(bundles, allowIncompatible = true)
}
val customSelection = combine(
selectionFlow,
bundles,
) { selection, bundles ->
(selection as? SelectionState.Customized)?.patches(bundles, allowIncompatible = true)
}
// Most compatible versions based on patch selection
@OptIn(ExperimentalCoroutinesApi::class)
val mostCompatibleVersions = patchSelection.flatMapLatest { patchSelection ->
@@ -144,6 +152,19 @@ class SelectedAppInfoViewModel(
}
}
// Resolve actual source from user selection
val resolvedSource = _selectedSource.map { source ->
// TODO
// when (source) {
// is SelectedSource.Auto -> null
// is SelectedSource.Installed -> null
// is SelectedSource.Downloaded -> null
// is SelectedSource.Plugin -> null
// }
source
}
val bundleInfoFlow by derivedStateOf {
@@ -168,7 +189,6 @@ class SelectedAppInfoViewModel(
var installedAppData: Pair<SelectedApp.Installed, InstalledApp?>? by mutableStateOf(null)
private set
@@ -222,7 +242,7 @@ class SelectedAppInfoViewModel(
val bundles = bundleInfoFlow.first()
return Patcher.ViewModelParams(
SelectedApp.Installed(packageName, version = "123"), // TODO
getPatches(bundles, allowIncompatible),
patchSelection.first(),
getOptionsFiltered(bundles)
)
}

View File

@@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
import app.revanced.manager.domain.repository.PatchBundleRepository
import app.revanced.manager.ui.model.SelectedVersion
import app.revanced.manager.ui.model.navigation.SelectedAppInfo
import app.revanced.manager.util.patchCount
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
@@ -16,7 +17,8 @@ class VersionSelectorViewModel(
) : ViewModel(), KoinComponent {
val patchBundleRepository: PatchBundleRepository = get()
val patchCount = input.patchSelection.values.sumOf { it.size }
val patchCount = input.patchSelection.patchCount
val availableVersions = patchBundleRepository.suggestedVersions(input.packageName, input.patchSelection)
.map { versions ->
@@ -29,7 +31,7 @@ class VersionSelectorViewModel(
}
var selectedVersion by mutableStateOf(input.currentSelection)
var selectedVersion by mutableStateOf(input.selectedVersion)
private set
fun selectVersion(version: SelectedVersion) {