From 9fc2b4fdefeca35d0e026074e50e552bb929252b Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 7 Jan 2026 20:51:39 +0100 Subject: [PATCH] Show selected source in overview --- .../manager/patcher/worker/PatcherWorker.kt | 23 +++--- .../revanced/manager/ui/model/SelectedApp.kt | 35 -------- .../manager/ui/model/SelectedSource.kt | 1 + .../ui/screen/SelectedAppInfoScreen.kt | 65 +++++++++------ .../manager/ui/screen/SourceSelectorScreen.kt | 6 +- .../ui/viewmodel/SelectedAppInfoViewModel.kt | 79 ++++++++----------- 6 files changed, 90 insertions(+), 119 deletions(-) delete mode 100644 app/src/main/java/app/revanced/manager/ui/model/SelectedApp.kt diff --git a/app/src/main/java/app/revanced/manager/patcher/worker/PatcherWorker.kt b/app/src/main/java/app/revanced/manager/patcher/worker/PatcherWorker.kt index 070af2cb..6ffb0a58 100644 --- a/app/src/main/java/app/revanced/manager/patcher/worker/PatcherWorker.kt +++ b/app/src/main/java/app/revanced/manager/patcher/worker/PatcherWorker.kt @@ -158,16 +158,15 @@ class PatcherWorker( args.version, prefs.suggestedVersionSafeguard.get(), !prefs.disablePatchVersionCompatCheck.get(), - onDownload = { progress -> - args.onEvent( - ProgressEvent.Progress( - stepId = StepId.DownloadAPK, - current = progress.first, - total = progress.second - ) + ) { progress -> + args.onEvent( + ProgressEvent.Progress( + stepId = StepId.DownloadAPK, + current = progress.first, + total = progress.second ) - } - ).also { args.setInputFile(it) } + ) + }.also { args.setInputFile(it) } val inputFile = when (val source = args.source) { is SelectedSource.Auto -> throw Exception("Auto source is not supported in worker.") @@ -212,6 +211,7 @@ class PatcherWorker( } is SelectedSource.Downloaded -> File(source.path) + is SelectedSource.Local -> File(source.path) is SelectedSource.Installed -> File(pm.getPackageInfo(args.packageName)!!.applicationInfo!!.sourceDir) } @@ -251,10 +251,7 @@ class PatcherWorker( Result.failure() } finally { patchedApk.delete() - // TODO -// if (args.source is SelectedApp.Local && args.source.temporary) { -// args.source.file.delete() -// } + if (args.source is SelectedSource.Local) File(args.source.path).delete() } } diff --git a/app/src/main/java/app/revanced/manager/ui/model/SelectedApp.kt b/app/src/main/java/app/revanced/manager/ui/model/SelectedApp.kt deleted file mode 100644 index 5d05c4ea..00000000 --- a/app/src/main/java/app/revanced/manager/ui/model/SelectedApp.kt +++ /dev/null @@ -1,35 +0,0 @@ -package app.revanced.manager.ui.model - -import android.os.Parcelable -import app.revanced.manager.network.downloader.ParceledDownloaderData -import kotlinx.parcelize.Parcelize -import java.io.File - -sealed interface SelectedApp : Parcelable { - val packageName: String - val version: String? - - @Parcelize - data class Download( - override val packageName: String, - override val version: String?, - val data: ParceledDownloaderData - ) : SelectedApp - - @Parcelize - data class Search(override val packageName: String, override val version: String?) : SelectedApp - - @Parcelize - data class Local( - override val packageName: String, - override val version: String, - val file: File, - val temporary: Boolean - ) : SelectedApp - - @Parcelize - data class Installed( - override val packageName: String, - override val version: String - ) : SelectedApp -} diff --git a/app/src/main/java/app/revanced/manager/ui/model/SelectedSource.kt b/app/src/main/java/app/revanced/manager/ui/model/SelectedSource.kt index aedb5bbd..9942cfca 100644 --- a/app/src/main/java/app/revanced/manager/ui/model/SelectedSource.kt +++ b/app/src/main/java/app/revanced/manager/ui/model/SelectedSource.kt @@ -8,5 +8,6 @@ sealed class SelectedSource : Parcelable { data object Auto : SelectedSource() data object Installed : SelectedSource() data class Downloaded(val path: String, val version: String) : SelectedSource() + data class Local(val path: String) : SelectedSource() data class Plugin(val packageName: String?) : SelectedSource() } \ No newline at end of file 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 4da4fcb5..d5785222 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 @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -80,7 +81,6 @@ fun SelectedAppInfoScreen( val incompatibleCount by vm.incompatiblePatchCount.collectAsStateWithLifecycle(0) val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - val plugins by vm.plugins.collectAsStateWithLifecycle(emptyList()) Scaffold( topBar = { @@ -150,18 +150,16 @@ fun SelectedAppInfoScreen( customSelection, vm.options ) - } + }, + extraDescription = if (incompatibleCount > 0) { { + Text( + "$incompatibleCount incompatible", + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodyMedium, + ) + } } else null, ) - if (incompatibleCount > 0) { - Text( - "$incompatibleCount incompatible", - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(horizontal = 16.dp) - ) - } - val versionText = resolvedVersion ?: "Any available version" val versionDescription = if (selectedVersion is SelectedVersion.Auto) "Auto ($versionText)" // stringResource(R.string.selected_app_meta_auto_version, actualVersion) @@ -179,19 +177,32 @@ fun SelectedAppInfoScreen( }, ) + val sourceText = when (val source = resolvedSource) { + is SelectedSource.Installed -> "Installed APK" + is SelectedSource.Downloaded -> "Downloaded APK" + is SelectedSource.Local -> "Local APK" + is SelectedSource.Plugin -> { + source.packageName ?: "Any available downloader" + } + else -> "Auto" + } + val sourceDescription = if (selectedSource is SelectedSource.Auto) + "Auto ($sourceText)" // stringResource(R.string.selected_app_meta_auto_version, actualVersion) + else sourceText + PageItem( R.string.apk_source_selector_item, - selectedSource.toString(), + sourceDescription, onClick = { onSourceClick(packageName, resolvedVersion, selectedSource) }, ) -// error?.let { -// Text( -// stringResource(it.resourceId), -// color = MaterialTheme.colorScheme.error, -// modifier = Modifier.padding(horizontal = 16.dp) -// ) -// } + error?.let { + Text( + stringResource(it.resourceId), + color = MaterialTheme.colorScheme.error, + modifier = Modifier.padding(horizontal = 16.dp) + ) + } if (resolvedSource is SelectedSource.Plugin) Column( modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp), @@ -225,7 +236,8 @@ private fun PageItem( @StringRes title: Int, description: String, onClick: () -> Unit, - enabled: Boolean = true + enabled: Boolean = true, + extraDescription: @Composable (ColumnScope.() -> Unit)? = null, ) { ListItem( modifier = Modifier @@ -239,11 +251,14 @@ private fun PageItem( ) }, supportingContent = { - Text( - description, - color = MaterialTheme.colorScheme.outline, - style = MaterialTheme.typography.bodyMedium - ) + Column { + Text( + description, + color = MaterialTheme.colorScheme.outline, + style = MaterialTheme.typography.bodyMedium + ) + extraDescription?.invoke(this) + } }, trailingContent = { Icon(Icons.AutoMirrored.Outlined.ArrowRight, null) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/SourceSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/SourceSelectorScreen.kt index 6cae108b..ffca89a4 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/SourceSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/SourceSelectorScreen.kt @@ -39,6 +39,9 @@ fun SourceSelectorScreen( val downloadedApps by viewModel.downloadedApps.collectAsStateWithLifecycle(emptyList()) val plugins by viewModel.plugins.collectAsStateWithLifecycle(emptyList()) + val version = viewModel.input.version + fun matchesVersion(appVersion: String) = version == null || version == appVersion + Scaffold( topBar = { AppTopBar( @@ -82,7 +85,7 @@ fun SourceSelectorScreen( onSelect = { viewModel.selectSource(SelectedSource.Installed) }, headlineContent = { Text(installedVersion) }, overlineContent = { Text("Installed") }, - enabled = viewModel.input.version?.let { it == installedVersion } ?: true + enabled = matchesVersion(installedVersion) ) } } @@ -95,6 +98,7 @@ fun SourceSelectorScreen( onSelect = { viewModel.selectDownloadedApp(app) }, headlineContent = { Text(app.version) }, overlineContent = { Text("Downloaded") }, + enabled = matchesVersion(app.version) ) } 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 daa8dbfd..a6375385 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 @@ -15,6 +15,7 @@ import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi import androidx.lifecycle.viewmodel.compose.saveable import app.revanced.manager.R import app.revanced.manager.domain.manager.PreferencesManager +import app.revanced.manager.domain.repository.DownloadedAppRepository import app.revanced.manager.domain.repository.DownloaderPluginRepository import app.revanced.manager.domain.repository.InstalledAppRepository import app.revanced.manager.domain.repository.PatchBundleRepository @@ -49,13 +50,14 @@ import org.koin.core.component.get @OptIn(SavedStateHandleSaveableApi::class, PluginHostApi::class) class SelectedAppInfoViewModel( - val input: SelectedAppInfo.ViewModelParams + private val input: SelectedAppInfo.ViewModelParams ) : ViewModel(), KoinComponent { private val bundleRepository: PatchBundleRepository = get() private val selectionRepository: PatchSelectionRepository = get() private val optionsRepository: PatchOptionsRepository = get() private val pluginsRepository: DownloaderPluginRepository = get() private val installedAppRepository: InstalledAppRepository = get() + private val downloadedAppRepository: DownloadedAppRepository = get() private val pm: PM = get() private val savedStateHandle: SavedStateHandle = get() private val prefs: PreferencesManager = get() @@ -101,16 +103,8 @@ class SelectedAppInfoViewModel( } - - // All patches for package - @OptIn(ExperimentalCoroutinesApi::class) - val bundles = _selectedVersion.flatMapLatest { selectedVersion -> - val version = if (selectedVersion is SelectedVersion.Specific) - selectedVersion.version - else null - bundleRepository.scopedBundleInfoFlow(packageName, version) - } + val bundles = bundleRepository.scopedBundleInfoFlow(packageName, null) // Selection derived from selectionFlow val patchSelection = combine( @@ -163,20 +157,29 @@ 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 -// } + val resolvedSource = combine( + _selectedSource, + resolvedVersion + ) { source, version -> + when (source) { + is SelectedSource.Installed -> source + is SelectedSource.Local -> source + is SelectedSource.Downloaded -> source + is SelectedSource.Plugin -> source + is SelectedSource.Auto -> { + val app = version?.let { + downloadedAppRepository.get(packageName, it) + } + val file = app?.let { + downloadedAppRepository.getApkFileForApp(it) + } - source + file?.let { SelectedSource.Downloaded(it.path, version) } + ?: SelectedSource.Plugin(null) + } + } } - - val bundleInfoFlow by derivedStateOf { bundleRepository.scopedBundleInfoFlow(packageName, null) } @@ -197,6 +200,16 @@ class SelectedAppInfoViewModel( private set + val errorFlow = combine( + plugins, + resolvedSource, + ) { pluginsList, source -> + when { + source is SelectedSource.Plugin && pluginsList.isEmpty() -> Error.NoPlugins + else -> null + } + } + // var installedAppData: Pair? by mutableStateOf(null) @@ -219,20 +232,6 @@ class SelectedAppInfoViewModel( - - - - // TODO: Remove - private var oldSelectionState: SelectionState by savedStateHandle.saveable { mutableStateOf(SelectionState.Default) } - - val errorFlow = combine(plugins, snapshotFlow { selectedApp }) { pluginsList, app -> - when { -// app is SelectedApp.Search && pluginsList.isEmpty() -> Error.NoPlugins - else -> null - } - } - - // TODO: Load from local file or downloaded app private fun invalidateSelectedAppInfo() = viewModelScope.launch { selectedAppInfo = pm.getPackageInfo(packageName) @@ -259,16 +258,6 @@ class SelectedAppInfoViewModel( ) } - fun getPatches(bundles: List, allowIncompatible: Boolean) = - oldSelectionState.patches(bundles, allowIncompatible) - - fun getCustomPatches( - bundles: List, - allowIncompatible: Boolean - ): PatchSelection? = - (oldSelectionState as? SelectionState.Customized)?.patches(bundles, allowIncompatible) - - init { invalidateSelectedAppInfo()