mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-11 13:46:17 +00:00
Show selected source in overview
This commit is contained in:
@@ -7,6 +7,6 @@ import kotlinx.parcelize.Parcelize
|
||||
sealed class SelectedSource : Parcelable {
|
||||
data object Auto : SelectedSource()
|
||||
data object Installed : SelectedSource()
|
||||
data class Downloaded(val path: String) : SelectedSource()
|
||||
data class Downloaded(val path: String, val version: String) : SelectedSource()
|
||||
data class Plugin(val plugin: String) : SelectedSource() // TODO
|
||||
}
|
||||
@@ -77,6 +77,8 @@ fun SelectedAppInfoScreen(
|
||||
val fullPatchSelection by vm.patchSelection.collectAsStateWithLifecycle(emptyMap())
|
||||
val patchCount = fullPatchSelection.patchCount
|
||||
|
||||
val incompatibleCount by vm.incompatiblePatchCount.collectAsStateWithLifecycle(0)
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val plugins by vm.plugins.collectAsStateWithLifecycle(emptyList())
|
||||
|
||||
@@ -151,6 +153,15 @@ fun SelectedAppInfoScreen(
|
||||
}
|
||||
)
|
||||
|
||||
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)
|
||||
@@ -170,9 +181,7 @@ fun SelectedAppInfoScreen(
|
||||
|
||||
PageItem(
|
||||
R.string.apk_source_selector_item,
|
||||
when (selectedSource) {
|
||||
else -> "Sourcing the source"
|
||||
},
|
||||
selectedSource.toString(),
|
||||
onClick = { onSourceClick(packageName, versionText, selectedSource) },
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package app.revanced.manager.ui.screen
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Save
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -13,7 +15,9 @@ import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton
|
||||
import app.revanced.manager.ui.model.SelectedSource
|
||||
@@ -28,6 +32,8 @@ fun SourceSelectorScreen(
|
||||
onSave: (source: SelectedSource) -> Unit,
|
||||
viewModel: SourceSelectorViewModel,
|
||||
) {
|
||||
val downloadedApps by viewModel.downloadedApps.collectAsStateWithLifecycle(emptyList())
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppTopBar(
|
||||
@@ -47,56 +53,71 @@ fun SourceSelectorScreen(
|
||||
modifier = Modifier.padding(paddingValues)
|
||||
) {
|
||||
SourceOption(
|
||||
source = SelectedSource.Auto,
|
||||
isSelected = viewModel.selectedSource == SelectedSource.Auto,
|
||||
onSelect = viewModel::selectSource,
|
||||
onSelect = { viewModel.selectSource(SelectedSource.Auto) },
|
||||
headlineContent = { Text("Auto (Recommended)") },
|
||||
supportingContent = { Text("Automatically select the best available source") }
|
||||
)
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
SourceOption(
|
||||
source = SelectedSource.Installed,
|
||||
isSelected = viewModel.selectedSource == SelectedSource.Installed,
|
||||
onSelect = viewModel::selectSource,
|
||||
headlineContent = { Text("20.14.43") },
|
||||
supportingContent = { Text("Split APK's are not supported") },
|
||||
overlineContent = { Text("Installed") },
|
||||
enabled = false,
|
||||
isSelected = viewModel.selectedSource == SelectedSource.Plugin("any"),
|
||||
onSelect = { viewModel.selectSource(SelectedSource.Plugin("any")) },
|
||||
headlineContent = { Text("Any available downloader") },
|
||||
)
|
||||
|
||||
HorizontalDivider()
|
||||
viewModel.installedVersion?.let { installedVersion ->
|
||||
HorizontalDivider()
|
||||
|
||||
SourceOption(
|
||||
source = SelectedSource.Downloaded("path"),
|
||||
isSelected = viewModel.selectedSource == SelectedSource.Downloaded("path"),
|
||||
onSelect = viewModel::selectSource,
|
||||
headlineContent = { Text("20.14.43") },
|
||||
// supportingContent = { Text("") },
|
||||
overlineContent = { Text("Downloaded") },
|
||||
SourceOption(
|
||||
isSelected = viewModel.selectedSource == SelectedSource.Installed,
|
||||
onSelect = { viewModel.selectSource(SelectedSource.Installed) },
|
||||
headlineContent = { Text(installedVersion) },
|
||||
overlineContent = { Text("Installed") },
|
||||
enabled = viewModel.input.version?.let { it == installedVersion } ?: true
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
if (downloadedApps.isNotEmpty()) {
|
||||
HorizontalDivider()
|
||||
|
||||
LazyColumn {
|
||||
items(downloadedApps, key = { it.version }) { app ->
|
||||
SourceOption(
|
||||
isSelected = (viewModel.selectedSource as? SelectedSource.Downloaded)?.version == app.version,
|
||||
onSelect = { viewModel.selectDownloadedApp(app) },
|
||||
headlineContent = { Text(app.version) },
|
||||
overlineContent = { Text("Downloaded") },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
SourceOption(
|
||||
source = SelectedSource.Plugin("plugin-id"),
|
||||
isSelected = viewModel.selectedSource == SelectedSource.Plugin("plugin-id"),
|
||||
onSelect = viewModel::selectSource,
|
||||
onSelect = { viewModel.selectSource(SelectedSource.Plugin("plugin-id")) },
|
||||
headlineContent = { Text("APKMirror Downloader") },
|
||||
overlineContent = { Text("Plugin") },
|
||||
)
|
||||
|
||||
SourceOption(
|
||||
isSelected = viewModel.selectedSource == SelectedSource.Plugin("another-plugin-id"),
|
||||
onSelect = { viewModel.selectSource(SelectedSource.Plugin("another-plugin-id")) },
|
||||
headlineContent = { Text("Another Plugin") },
|
||||
overlineContent = { Text("Plugin") },
|
||||
supportingContent = { Text("Untrusted") },
|
||||
enabled = false,
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SourceOption(
|
||||
source: SelectedSource,
|
||||
isSelected: Boolean,
|
||||
onSelect: (SelectedSource) -> Unit,
|
||||
onSelect: () -> Unit,
|
||||
headlineContent: @Composable (() -> Unit),
|
||||
supportingContent: @Composable (() -> Unit)? = null,
|
||||
overlineContent: @Composable (() -> Unit)? = null,
|
||||
@@ -104,7 +125,7 @@ private fun SourceOption(
|
||||
) {
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
.clickable(enabled) { onSelect(source) }
|
||||
.clickable(enabled) { onSelect() }
|
||||
.enabled(enabled),
|
||||
leadingContent = {
|
||||
RadioButton(
|
||||
|
||||
@@ -36,6 +36,7 @@ fun VersionSelectorScreen(
|
||||
viewModel: VersionSelectorViewModel,
|
||||
) {
|
||||
val versions by viewModel.availableVersions.collectAsStateWithLifecycle(emptyList())
|
||||
val downloadedVersions by viewModel.downloadedVersions.collectAsStateWithLifecycle(emptyList())
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -64,24 +65,35 @@ fun VersionSelectorScreen(
|
||||
version = SelectedVersion.Auto,
|
||||
isSelected = viewModel.selectedVersion is SelectedVersion.Auto,
|
||||
onSelect = viewModel::selectVersion,
|
||||
title = { Text("Auto (Recommended)") },
|
||||
description = { Text("Automatically select the best available version") }
|
||||
headlineContent = { Text("Auto (Recommended)") },
|
||||
supportingContent = { Text("Automatically select the best available version") }
|
||||
)
|
||||
HorizontalDivider()
|
||||
|
||||
if (versions.isNotEmpty()) {
|
||||
LazyColumn {
|
||||
items(versions, key = { it.first.version }) { version ->
|
||||
val isDownloaded = downloadedVersions.contains(version.first.version)
|
||||
val isInstalled = viewModel.installedAppVersion == version.first.version
|
||||
|
||||
val overlineText = when {
|
||||
isDownloaded && isInstalled -> "Downloaded, Installed"
|
||||
isDownloaded -> "Downloaded"
|
||||
isInstalled -> "Installed"
|
||||
else -> null
|
||||
}
|
||||
|
||||
VersionOption(
|
||||
version = version.first,
|
||||
isSelected = viewModel.selectedVersion == version.first,
|
||||
onSelect = viewModel::selectVersion,
|
||||
title = { Text(version.first.version) },
|
||||
description = {
|
||||
headlineContent = { Text(version.first.version) },
|
||||
supportingContent = {
|
||||
Text(
|
||||
"${version.second.let { if (it == 0) "No" else it }} incompatible patches"
|
||||
)
|
||||
}
|
||||
},
|
||||
overlineContent = overlineText?.let { { Text(it) } }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -90,8 +102,8 @@ fun VersionSelectorScreen(
|
||||
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") }
|
||||
headlineContent = { Text("Any available version") },
|
||||
supportingContent = { Text("Use any available version regardless of compatibility") }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -103,8 +115,9 @@ private fun VersionOption(
|
||||
version: SelectedVersion,
|
||||
isSelected: Boolean,
|
||||
onSelect: (SelectedVersion) -> Unit,
|
||||
title: @Composable (() -> Unit),
|
||||
description: @Composable (() -> Unit)? = null,
|
||||
headlineContent: @Composable (() -> Unit),
|
||||
supportingContent: @Composable (() -> Unit)? = null,
|
||||
overlineContent: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
@@ -115,8 +128,9 @@ private fun VersionOption(
|
||||
onClick = null
|
||||
)
|
||||
},
|
||||
headlineContent = title,
|
||||
supportingContent = description,
|
||||
headlineContent = headlineContent,
|
||||
supportingContent = supportingContent,
|
||||
trailingContent = overlineContent,
|
||||
colors = transparentListItemColors
|
||||
)
|
||||
}
|
||||
|
||||
@@ -146,9 +146,21 @@ class SelectedAppInfoViewModel(
|
||||
when (selected) {
|
||||
is SelectedVersion.Specific -> selected.version
|
||||
is SelectedVersion.Any -> null
|
||||
is SelectedVersion.Auto -> {
|
||||
mostCompatible?.maxByOrNull { it.value }?.key
|
||||
}
|
||||
is SelectedVersion.Auto -> mostCompatible?.maxWithOrNull(
|
||||
compareBy<Map.Entry<String, Int>> { it.value }
|
||||
.thenBy { it.key }
|
||||
)?.key
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val scopedBundles = resolvedVersion.flatMapLatest { version ->
|
||||
bundleRepository.scopedBundleInfoFlow(packageName, version)
|
||||
}
|
||||
|
||||
val incompatiblePatchCount = scopedBundles.map { bundles ->
|
||||
bundles.sumOf { bundle ->
|
||||
bundle.incompatible.size
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package app.revanced.manager.ui.viewmodel
|
||||
|
||||
import android.content.pm.PackageInfo
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.revanced.manager.data.room.apps.downloaded.DownloadedApp
|
||||
import app.revanced.manager.domain.repository.DownloadedAppRepository
|
||||
import app.revanced.manager.ui.model.SelectedSource
|
||||
import app.revanced.manager.ui.model.navigation.SelectedAppInfo
|
||||
import app.revanced.manager.util.PM
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
@@ -20,14 +21,8 @@ class SourceSelectorViewModel(
|
||||
private val downloadedAppRepository: DownloadedAppRepository = get()
|
||||
private val pm: PM = get()
|
||||
|
||||
// val downloadedApps = downloadedAppRepository.get(input.packageName)
|
||||
// .map {
|
||||
// it.map {
|
||||
// SelectedSource.Downloaded(
|
||||
//
|
||||
// )
|
||||
// }.sortedByDescending { app -> app.version }
|
||||
// }
|
||||
val downloadedApps = downloadedAppRepository.get(input.packageName)
|
||||
.map { it.sortedByDescending { app -> app.version } }
|
||||
|
||||
var selectedSource by mutableStateOf(input.selectedSource)
|
||||
private set
|
||||
@@ -36,14 +31,20 @@ class SourceSelectorViewModel(
|
||||
selectedSource = source
|
||||
}
|
||||
|
||||
fun selectDownloadedApp(app: DownloadedApp) {
|
||||
val file = downloadedAppRepository.getApkFileForApp(app)
|
||||
|
||||
selectedSource = SelectedSource.Downloaded(file.path, app.version)
|
||||
}
|
||||
|
||||
var installedApp: PackageInfo? = null
|
||||
var installedVersion by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
installedApp = pm.getPackageInfo(input.packageName)
|
||||
val packageInfo = pm.getPackageInfo(input.packageName)
|
||||
|
||||
installedVersion = packageInfo?.versionName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,34 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.revanced.manager.domain.repository.DownloadedAppRepository
|
||||
import app.revanced.manager.domain.repository.InstalledAppRepository
|
||||
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.PM
|
||||
import app.revanced.manager.util.patchCount
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
|
||||
class VersionSelectorViewModel(
|
||||
val input: SelectedAppInfo.VersionSelector.ViewModelParams
|
||||
) : ViewModel(), KoinComponent {
|
||||
val patchBundleRepository: PatchBundleRepository = get()
|
||||
private val patchBundleRepository: PatchBundleRepository = get()
|
||||
private val downloadedAppsRepository: DownloadedAppRepository = get()
|
||||
private val installedAppRepository: InstalledAppRepository = get()
|
||||
private val pm: PM = get()
|
||||
|
||||
val patchCount = input.patchSelection.patchCount
|
||||
|
||||
val downloadedVersions = downloadedAppsRepository.get(input.packageName)
|
||||
.map { apps ->
|
||||
apps.map { it.version }
|
||||
}
|
||||
|
||||
val availableVersions = patchBundleRepository.suggestedVersions(input.packageName, input.patchSelection)
|
||||
.map { versions ->
|
||||
versions.orEmpty()
|
||||
@@ -29,6 +42,19 @@ class VersionSelectorViewModel(
|
||||
)
|
||||
}
|
||||
|
||||
var installedAppVersion by mutableStateOf<String?>(null)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val currentApp = pm.getPackageInfo(input.packageName)
|
||||
val patchedApp = installedAppRepository.get(input.packageName)
|
||||
|
||||
// Skip if installed app is patched
|
||||
if (patchedApp?.currentPackageName == input.packageName) return@launch
|
||||
|
||||
installedAppVersion = currentApp?.versionName
|
||||
}
|
||||
}
|
||||
|
||||
var selectedVersion by mutableStateOf(input.selectedVersion)
|
||||
private set
|
||||
|
||||
Reference in New Issue
Block a user