mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-19 17:23:58 +00:00
Update selected app info screen and version selector screen
This commit is contained in:
@@ -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,
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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") }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ class AppSelectorViewModel(
|
||||
return@launch
|
||||
}
|
||||
|
||||
// TODO: Disallow if 0 patches are compatible
|
||||
storageSelectionChannel.send(selectedApp)
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user