From 9cdb8eafb3413c10d379c5d827ddc549345ff70c Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 30 Dec 2025 21:19:22 +0100 Subject: [PATCH] Update selected app info screen and version selector screen --- .../java/app/revanced/manager/MainActivity.kt | 16 +-- .../manager/ui/model/navigation/Nav.kt | 5 +- .../ui/screen/SelectedAppInfoScreen.kt | 107 +++++++----------- .../ui/screen/VersionSelectorScreen.kt | 52 ++++----- .../ui/viewmodel/AppSelectorViewModel.kt | 1 + .../ui/viewmodel/PatchesSelectorViewModel.kt | 2 +- .../ui/viewmodel/SelectedAppInfoViewModel.kt | 32 +++++- .../ui/viewmodel/VersionSelectorViewModel.kt | 6 +- 8 files changed, 111 insertions(+), 110 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/MainActivity.kt b/app/src/main/java/app/revanced/manager/MainActivity.kt index 5516a34e..82e75260 100644 --- a/app/src/main/java/app/revanced/manager/MainActivity.kt +++ b/app/src/main/java/app/revanced/manager/MainActivity.kt @@ -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, ) ) }, diff --git a/app/src/main/java/app/revanced/manager/ui/model/navigation/Nav.kt b/app/src/main/java/app/revanced/manager/ui/model/navigation/Nav.kt index c63ecf55..88d9f6cb 100644 --- a/app/src/main/java/app/revanced/manager/ui/model/navigation/Nav.kt +++ b/app/src/main/java/app/revanced/manager/ui/model/navigation/Nav.kt @@ -42,8 +42,7 @@ data object SelectedAppInfo : ComplexParameter 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 data class ViewModelParams( val packageName: String, val patchSelection: PatchSelection, - val currentSelection: SelectedVersion, + val selectedVersion: SelectedVersion, ) : Parcelable } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt index 05ceecd4..0ee08999 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt @@ -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) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/VersionSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/VersionSelectorScreen.kt index 61ab22a7..afa42291 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/VersionSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/VersionSelectorScreen.kt @@ -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") } + ) + } } } } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt index 185a07d2..cf9f1a57 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt @@ -66,6 +66,7 @@ class AppSelectorViewModel( return@launch } + // TODO: Disallow if 0 patches are compatible storageSelectionChannel.send(selectedApp) } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index b901285f..8dc7fa6c 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -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( diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/SelectedAppInfoViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/SelectedAppInfoViewModel.kt index 3541492e..4a56fa57 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/SelectedAppInfoViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/SelectedAppInfoViewModel.kt @@ -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? 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) ) } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/VersionSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/VersionSelectorViewModel.kt index 0cc6766a..0363f6c5 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/VersionSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/VersionSelectorViewModel.kt @@ -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) {