diff --git a/app/src/main/java/app/revanced/manager/MainActivity.kt b/app/src/main/java/app/revanced/manager/MainActivity.kt index 982e7437..5516a34e 100644 --- a/app/src/main/java/app/revanced/manager/MainActivity.kt +++ b/app/src/main/java/app/revanced/manager/MainActivity.kt @@ -25,7 +25,6 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.navigation import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute -import app.revanced.manager.ui.model.SelectedApp import app.revanced.manager.ui.model.navigation.AppSelector import app.revanced.manager.ui.model.navigation.ComplexParameter import app.revanced.manager.ui.model.navigation.Dashboard @@ -141,9 +140,7 @@ private fun ReVancedManager() { onPatchClick = { packageName -> navController.navigateComplex( SelectedAppInfo, - SelectedAppInfo.ViewModelParams( - SelectedApp.Search(packageName, null) // TODO - ) + SelectedAppInfo.ViewModelParams(packageName) ) }, onBackClick = navController::popBackStack, @@ -156,15 +153,15 @@ private fun ReVancedManager() { onSelect = { packageName -> navController.navigateComplex( SelectedAppInfo, - SelectedAppInfo.ViewModelParams( - SelectedApp.Search(packageName, null) // TODO - ) + SelectedAppInfo.ViewModelParams(packageName) ) }, - onStorageSelect = { app -> + onStorageSelect = { packageName, localFile -> navController.navigateComplex( SelectedAppInfo, - SelectedAppInfo.ViewModelParams(app) + SelectedAppInfo.ViewModelParams( + packageName, localFile + ) ) }, onBackClick = navController::popBackStack @@ -214,23 +211,25 @@ private fun ReVancedManager() { ) } }, - onPatchSelectorClick = { app, patches, options -> + onPatchSelectorClick = { packageName, version, patches, options -> navController.navigateComplex( SelectedAppInfo.PatchesSelector, SelectedAppInfo.PatchesSelector.ViewModelParams( - app, - patches, - options + packageName, + version, + currentSelection = patches, + options = options, ) ) }, - onRequiredOptions = { app, patches, options -> + onRequiredOptions = { packageName, version, patches, options -> navController.navigateComplex( SelectedAppInfo.RequiredOptions, SelectedAppInfo.PatchesSelector.ViewModelParams( - app, - patches, - options + packageName, + version, + currentSelection = patches, + options = options, ) ) }, 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 8d101604..c63ecf55 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 @@ -8,6 +8,7 @@ import app.revanced.manager.util.PatchSelection import kotlinx.parcelize.Parcelize import kotlinx.parcelize.RawValue import kotlinx.serialization.Serializable +import java.io.File interface ComplexParameter @@ -27,7 +28,8 @@ data class Update(val downloadOnScreenEntry: Boolean = false) data object SelectedAppInfo : ComplexParameter { @Parcelize data class ViewModelParams( - val app: SelectedApp, + val packageName: String, + val localFile: File? = null, val patches: PatchSelection? = null ) : Parcelable @@ -38,7 +40,9 @@ data object SelectedAppInfo : ComplexParameter data object PatchesSelector : ComplexParameter { @Parcelize data class ViewModelParams( - val app: SelectedApp, + val packageName: String, + val version: String?, + val restrictToVersion: Boolean = false, val currentSelection: PatchSelection?, val options: @RawValue Options, ) : Parcelable diff --git a/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt index ff2ec6e7..f989d06c 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt @@ -44,23 +44,23 @@ import app.revanced.manager.ui.component.LazyColumnWithScrollbar import app.revanced.manager.ui.component.LoadingIndicator import app.revanced.manager.ui.component.NonSuggestedVersionDialog import app.revanced.manager.ui.component.SearchView -import app.revanced.manager.ui.model.SelectedApp import app.revanced.manager.ui.viewmodel.AppSelectorViewModel import app.revanced.manager.util.APK_MIMETYPE import app.revanced.manager.util.EventEffect import app.revanced.manager.util.transparentListItemColors import org.koin.androidx.compose.koinViewModel +import java.io.File @OptIn(ExperimentalMaterial3Api::class) @Composable fun AppSelectorScreen( - onSelect: (String) -> Unit, - onStorageSelect: (SelectedApp.Local) -> Unit, + onSelect: (packageName: String) -> Unit, + onStorageSelect: (packageName: String, File) -> Unit, onBackClick: () -> Unit, vm: AppSelectorViewModel = koinViewModel() ) { EventEffect(flow = vm.storageSelectionFlow) { - onStorageSelect(it) + onStorageSelect(it.first, it.second) } val pickApkLauncher = 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 6cadd902..05ceecd4 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 @@ -36,7 +36,6 @@ 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.SelectedApp import app.revanced.manager.ui.model.SelectedVersion import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel import app.revanced.manager.util.Options @@ -49,8 +48,8 @@ import org.koin.compose.koinInject @OptIn(ExperimentalMaterial3Api::class) @Composable fun SelectedAppInfoScreen( - onPatchSelectorClick: (SelectedApp, PatchSelection?, Options) -> Unit, - onRequiredOptions: (SelectedApp, PatchSelection?, Options) -> Unit, + 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, onSourceClick: (packageName: String, version: String?) -> Unit, @@ -62,8 +61,8 @@ fun SelectedAppInfoScreen( val networkConnected = remember { networkInfo.isConnected() } val networkMetered = remember { !networkInfo.isUnmetered() } - val packageName = vm.selectedApp.packageName - val version = vm.selectedApp.version + val packageName = vm.input.packageName + val version = "123" // TODO val bundles by vm.bundleInfoFlow.collectAsStateWithLifecycle(emptyList()) val allowIncompatiblePatches by vm.prefs.disablePatchVersionCompatCheck.getAsState() @@ -74,16 +73,16 @@ fun SelectedAppInfoScreen( } val selectedPatchCount = patches.values.sumOf { it.size } -// val patches2 by remember { -// derivedStateOf { -// vm.patchSelection(bundles, allowIncompatiblePatches).collectAsStateWithLifecycle() -// } -// } - val composableScope = rememberCoroutineScope() val error by vm.errorFlow.collectAsStateWithLifecycle(null) + val selectedVersion by vm.selectedVersion.collectAsStateWithLifecycle() + val resolvedVersion by vm.resolvedVersion.collectAsStateWithLifecycle(null) + + val selectedSource by vm.selectedSource.collectAsStateWithLifecycle() + + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) Scaffold( @@ -115,7 +114,8 @@ fun SelectedAppInfoScreen( composableScope.launch { if (!vm.hasSetRequiredOptions(patches)) { onRequiredOptions( - vm.selectedApp, + vm.packageName, + version, vm.getCustomPatches(bundles, allowIncompatiblePatches), vm.options ) @@ -151,10 +151,11 @@ fun SelectedAppInfoScreen( stringResource( R.string.patch_selector_item_description, selectedPatchCount - ) + "\n⚠\uFE0F 3 incompatible", + ), onClick = { onPatchSelectorClick( - vm.selectedApp, + vm.packageName, + version, vm.getCustomPatches( bundles, allowIncompatiblePatches @@ -164,43 +165,39 @@ fun SelectedAppInfoScreen( } ) - if (vm.selectedApp !is SelectedApp.Local || !(vm.selectedApp as SelectedApp.Local).temporary) { - val selectedVersion by vm.selectedVersion.collectAsStateWithLifecycle(SelectedVersion.Auto) - val resolvedVersion by vm.resolvedVersion.collectAsStateWithLifecycle(null) - + if (vm.input.localFile == null) { val version = resolvedVersion ?: "Any available version" 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, -// "Auto (${requiredVersion ?: stringResource(R.string.selected_app_meta_any_version)})", // ⚠️ 1 Patch incompatible onClick = { onVersionClick(packageName, patches, selectedVersion) }, ) } PageItem( R.string.apk_source_selector_item, - when (val app = vm.selectedApp) { - 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) - }, +// 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.selectedApp.let { it is SelectedApp.Local && it.temporary }, // Disable for APK from storage + enabled = vm.input.localFile == null, // Disable for APK from storage ) error?.let { Text( @@ -214,8 +211,8 @@ fun SelectedAppInfoScreen( modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { - val needsInternet = - vm.selectedApp.let { it is SelectedApp.Search || it is SelectedApp.Download } + val needsInternet = false +// vm.input.app.let { it is SelectedApp.Search || it is SelectedApp.Download } when { !needsInternet -> {} 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 da6db396..61ab22a7 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 @@ -2,7 +2,6 @@ package app.revanced.manager.ui.screen import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -15,27 +14,16 @@ import androidx.compose.material3.ListItem import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.revanced.manager.R -import app.revanced.manager.data.room.apps.downloaded.DownloadedApp -import app.revanced.manager.data.room.apps.installed.InstallType -import app.revanced.manager.data.room.apps.installed.InstalledApp -import app.revanced.manager.network.downloader.LoadedDownloaderPlugin -import app.revanced.manager.ui.component.AlertDialogExtended import app.revanced.manager.ui.component.AppTopBar -import app.revanced.manager.ui.component.LoadingIndicator import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton -import app.revanced.manager.ui.model.SelectedApp import app.revanced.manager.ui.model.SelectedVersion import app.revanced.manager.ui.viewmodel.VersionSelectorViewModel -import app.revanced.manager.util.enabled import app.revanced.manager.util.transparentListItemColors @OptIn(ExperimentalMaterial3Api::class) @@ -128,132 +116,3 @@ private fun VersionOption( ) } - -@Composable -fun AppSourceSelectorDialog( - plugins: List, - installedApp: Pair?, - downloadedApps: List, - searchApp: SelectedApp.Search, - activeSearchJob: String?, - hasRoot: Boolean, - requiredVersion: String?, - onDismissRequest: () -> Unit, - onSelectPlugin: (LoadedDownloaderPlugin) -> Unit, - onSelect: (SelectedApp) -> Unit, - onSelectDownloaded: (DownloadedApp) -> Unit = {}, -) { - val canSelect = activeSearchJob == null - - AlertDialogExtended( - onDismissRequest = onDismissRequest, - confirmButton = { - TextButton(onClick = onDismissRequest) { - Text(stringResource(R.string.cancel)) - } - }, - title = { Text(stringResource(R.string.app_source_dialog_title)) }, - textHorizontalPadding = PaddingValues(horizontal = 0.dp), - text = { - Column { - HorizontalDivider() - LazyColumn { - item(key = "auto") { - val hasPlugins = plugins.isNotEmpty() - ListItem( - modifier = Modifier - .clickable(enabled = canSelect && hasPlugins) { onSelect(searchApp) } - .enabled(hasPlugins), - headlineContent = { Text(stringResource(R.string.app_source_dialog_option_auto) + " (Recommended)") }, - supportingContent = { - Text( - "Automatically choose a suitable source" -// if (hasPlugins) -// stringResource(R.string.app_source_dialog_option_auto_description) -//// "Automatically choose a suitable source" -// else -// stringResource(R.string.app_source_dialog_option_auto_unavailable) - ) - }, - colors = transparentListItemColors - ) - } - - installedApp?.let { (app, meta) -> - item(key = "installed") { - val (usable, text) = when { -// Mounted apps must be unpatched before patching, which cannot be done without root access. - meta?.installType == InstallType.MOUNT && !hasRoot -> false to stringResource( - R.string.app_source_dialog_option_installed_no_root - ) -// Patching already patched apps is not allowed because patches expect unpatched apps. - meta?.installType == InstallType.DEFAULT -> false to stringResource( - R.string.already_patched - ) -// Version does not match suggested version. - requiredVersion != null && app.version != requiredVersion -> false to "Does not match the selected version" -// stringResource( -// R.string.app_source_dialog_option_installed_version_not_suggested, -// app.version -// ) - - else -> true to null - } - ListItem( - modifier = Modifier - .clickable(enabled = canSelect && usable) { onSelect(app) } - .enabled(usable), - overlineContent = { Text("Installed") }, - headlineContent = { Text(app.version) }, - supportingContent = text?.let { { Text(text) } }, - colors = transparentListItemColors - ) - } - } - - items(downloadedApps, key = { it.version }) { app -> - val (usable, text) = when { -// Version does not match suggested version. - requiredVersion != null && app.version != requiredVersion -> false to "Does not match the selected version" -// stringResource( -// R.string.app_source_dialog_option_installed_version_not_suggested, -// app.version -// ) - - else -> true to null // "Downloaded using downloader plugin" - } - ListItem( - modifier = Modifier - .clickable(enabled = usable) { onSelectDownloaded(app) } - .enabled(usable), - overlineContent = { Text("Downloaded") }, - headlineContent = { Text(app.version) }, - supportingContent = text?.let { { Text(text) } }, - colors = transparentListItemColors - ) - } - - items(plugins, key = { "plugin_${it.packageName}" }) { plugin -> - ListItem( - modifier = Modifier.clickable(enabled = canSelect) { - onSelectPlugin(plugin) - }, - overlineContent = { Text("Plugin") }, - headlineContent = { - Text( - plugin.name, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - }, - trailingContent = (@Composable { LoadingIndicator() }).takeIf { activeSearchJob == plugin.packageName }, - colors = transparentListItemColors - ) - } - } - HorizontalDivider() - } - } - ) -} - 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 538b85e3..185a07d2 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 @@ -31,7 +31,7 @@ class AppSelectorViewModel( private val app: Application, private val pm: PM, fs: Filesystem, - private val patchBundleRepository: PatchBundleRepository, + patchBundleRepository: PatchBundleRepository, savedStateHandle: SavedStateHandle, ) : ViewModel() { private val inputFile = savedStateHandle.saveable(key = "inputFile") { @@ -42,7 +42,7 @@ class AppSelectorViewModel( } val appList = pm.appList - private val storageSelectionChannel = Channel() + private val storageSelectionChannel = Channel>() val storageSelectionFlow = storageSelectionChannel.receiveAsFlow() val suggestedAppVersions = patchBundleRepository.suggestedVersions.flowOn(Dispatchers.Default) @@ -66,11 +66,7 @@ class AppSelectorViewModel( return@launch } - if (patchBundleRepository.isVersionAllowed(selectedApp.packageName, selectedApp.version)) { - storageSelectionChannel.send(selectedApp) - } else { - nonSuggestedVersionDialogSubject = selectedApp - } + storageSelectionChannel.send(selectedApp) } private fun loadSelectedFile(uri: Uri) = @@ -80,12 +76,7 @@ class AppSelectorViewModel( Files.copy(stream, toPath()) pm.getPackageInfo(this)?.let { packageInfo -> - SelectedApp.Local( - packageName = packageInfo.packageName, - version = packageInfo.versionName!!, - file = this, - temporary = true - ) + Pair(packageInfo.packageName, this) } } } 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 67c3f238..b901285f 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 @@ -51,8 +51,8 @@ class PatchesSelectorViewModel(input: SelectedAppInfo.PatchesSelector.ViewModelP private val savedStateHandle: SavedStateHandle = get() private val prefs: PreferencesManager = get() - private val packageName = input.app.packageName - val appVersion = input.app.version + private val packageName = input.packageName + val appVersion = input.version var selectionWarningEnabled by mutableStateOf(true) private set @@ -62,7 +62,7 @@ class PatchesSelectorViewModel(input: SelectedAppInfo.PatchesSelector.ViewModelP val allowIncompatiblePatches = get().disablePatchVersionCompatCheck.getBlocking() val bundlesFlow = - get().scopedBundleInfoFlow(packageName, input.app.version) + get().scopedBundleInfoFlow(packageName, input.version) init { viewModelScope.launch { 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 6624aa51..3541492e 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 @@ -50,7 +50,7 @@ import org.koin.core.component.get @OptIn(SavedStateHandleSaveableApi::class, PluginHostApi::class) class SelectedAppInfoViewModel( - input: SelectedAppInfo.ViewModelParams + val input: SelectedAppInfo.ViewModelParams ) : ViewModel(), KoinComponent { private val bundleRepository: PatchBundleRepository = get() private val selectionRepository: PatchSelectionRepository = get() @@ -61,7 +61,7 @@ class SelectedAppInfoViewModel( private val savedStateHandle: SavedStateHandle = get() val prefs: PreferencesManager = get() val plugins = pluginsRepository.loadedPluginsFlow - val packageName = input.app.packageName + val packageName = input.packageName private val persistConfiguration = input.patches == null @@ -105,7 +105,13 @@ class SelectedAppInfoViewModel( // All patches for package - val bundles = bundleRepository.scopedBundleInfoFlow(packageName, null) + @OptIn(ExperimentalCoroutinesApi::class) + val bundles = selectedVersion.flatMapLatest { selectedVersion -> + val version = if (selectedVersion is SelectedVersion.Specific) + selectedVersion.version + else null + bundleRepository.scopedBundleInfoFlow(packageName, version) + } // Selection derived from selectionFlow val patchSelection = combine( @@ -139,6 +145,98 @@ class SelectedAppInfoViewModel( } + + val bundleInfoFlow by derivedStateOf { + bundleRepository.scopedBundleInfoFlow(packageName, null) + } + + var options: Options by savedStateHandle.saveable { + viewModelScope.launch { + if (!persistConfiguration) return@launch // TODO: save options for patched apps. + val bundlePatches = bundleInfoFlow.first() + .associate { it.uid to it.patches.associateBy { patch -> patch.name } } + + options = withContext(Dispatchers.Default) { + optionsRepository.getOptions(packageName, bundlePatches) + } + } + + mutableStateOf(emptyMap()) + } + private set + + + + + + var installedAppData: Pair? by mutableStateOf(null) + private set + + private var _selectedApp by savedStateHandle.saveable { + mutableStateOf(null) + } + + var selectedAppInfo: PackageInfo? by mutableStateOf(null) + private set + + var selectedApp + get() = _selectedApp + set(value) { + _selectedApp = value + invalidateSelectedAppInfo() + } + + + + + + + + // 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) + } + + fun getOptionsFiltered(bundles: List) = options.filtered(bundles) + suspend fun hasSetRequiredOptions(patchSelection: PatchSelection) = bundleInfoFlow + .first() + .requiredOptionsSet( + allowIncompatible = prefs.disablePatchVersionCompatCheck.get(), + isSelected = { bundle, patch -> patch.name in patchSelection[bundle.uid]!! }, + optionsForPatch = { bundle, patch -> options[bundle.uid]?.get(patch.name) }, + ) + + suspend fun getPatcherParams(): Patcher.ViewModelParams { + val allowIncompatible = prefs.disablePatchVersionCompatCheck.get() + val bundles = bundleInfoFlow.first() + return Patcher.ViewModelParams( + SelectedApp.Installed(packageName, version = "123"), // TODO + getPatches(bundles, allowIncompatible), + getOptionsFiltered(bundles) + ) + } + + 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() @@ -167,100 +265,6 @@ class SelectedAppInfoViewModel( } } - var options: Options by savedStateHandle.saveable { - viewModelScope.launch { - if (!persistConfiguration) return@launch // TODO: save options for patched apps. - val bundlePatches = bundleInfoFlow.first() - .associate { it.uid to it.patches.associateBy { patch -> patch.name } } - - options = withContext(Dispatchers.Default) { - optionsRepository.getOptions(packageName, bundlePatches) - } - } - - mutableStateOf(emptyMap()) - } - private set - - - - - - - var installedAppData: Pair? by mutableStateOf(null) - private set - - private var _selectedApp by savedStateHandle.saveable { - mutableStateOf(input.app) - } - - var selectedAppInfo: PackageInfo? by mutableStateOf(null) - private set - - var selectedApp - get() = _selectedApp - set(value) { - _selectedApp = value - invalidateSelectedAppInfo() - } - - - val bundleInfoFlow by derivedStateOf { - bundleRepository.scopedBundleInfoFlow(packageName, selectedApp.version) - } - - - - - - // 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 - } - } - - - private fun invalidateSelectedAppInfo() = viewModelScope.launch { - selectedAppInfo = when (val app = selectedApp) { - is SelectedApp.Local -> withContext(Dispatchers.IO) { pm.getPackageInfo(app.file) } - else -> withContext(Dispatchers.IO) { pm.getPackageInfo(packageName) } -// else -> null - } - } - - fun getOptionsFiltered(bundles: List) = options.filtered(bundles) - suspend fun hasSetRequiredOptions(patchSelection: PatchSelection) = bundleInfoFlow - .first() - .requiredOptionsSet( - allowIncompatible = prefs.disablePatchVersionCompatCheck.get(), - isSelected = { bundle, patch -> patch.name in patchSelection[bundle.uid]!! }, - optionsForPatch = { bundle, patch -> options[bundle.uid]?.get(patch.name) }, - ) - - suspend fun getPatcherParams(): Patcher.ViewModelParams { - val allowIncompatible = prefs.disablePatchVersionCompatCheck.get() - val bundles = bundleInfoFlow.first() - return Patcher.ViewModelParams( - selectedApp, - getPatches(bundles, allowIncompatible), - getOptionsFiltered(bundles) - ) - } - - fun getPatches(bundles: List, allowIncompatible: Boolean) = - oldSelectionState.patches(bundles, allowIncompatible) - - fun getCustomPatches( - bundles: List, - allowIncompatible: Boolean - ): PatchSelection? = - (oldSelectionState as? SelectionState.Customized)?.patches(bundles, allowIncompatible) - - enum class Error(@param:StringRes val resourceId: Int) { NoPlugins(R.string.downloader_no_plugins_available) }