mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 05:54:26 +02:00
not supporting parcelables is cringe
This commit is contained in:
parent
8a20d8cf9b
commit
251b9eef69
@ -126,6 +126,7 @@ dependencies {
|
||||
implementation(libs.compose.livedata)
|
||||
implementation(libs.compose.material.icons.extended)
|
||||
implementation(libs.compose.material3)
|
||||
implementation(libs.navigation.compose)
|
||||
|
||||
// Accompanist
|
||||
implementation(libs.accompanist.drawablepainter)
|
||||
@ -173,6 +174,7 @@ dependencies {
|
||||
// Koin
|
||||
implementation(libs.koin.android)
|
||||
implementation(libs.koin.compose)
|
||||
implementation(libs.koin.compose.navigation)
|
||||
implementation(libs.koin.workmanager)
|
||||
|
||||
// Compose Navigation
|
||||
|
@ -6,28 +6,31 @@ import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import app.revanced.manager.ui.destination.Destination
|
||||
import app.revanced.manager.ui.destination.SettingsDestination
|
||||
import app.revanced.manager.ui.screen.AppSelectorScreen
|
||||
import app.revanced.manager.ui.screen.DashboardScreen
|
||||
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
|
||||
import app.revanced.manager.ui.screen.PatcherScreen
|
||||
import app.revanced.manager.ui.screen.SelectedAppInfoScreen
|
||||
import app.revanced.manager.ui.screen.SettingsScreen
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.NavHost
|
||||
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.navigation.*
|
||||
import app.revanced.manager.ui.screen.*
|
||||
import app.revanced.manager.ui.screen.settings.*
|
||||
import app.revanced.manager.ui.screen.settings.update.ChangelogsScreen
|
||||
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
|
||||
import app.revanced.manager.ui.theme.ReVancedManagerTheme
|
||||
import app.revanced.manager.ui.theme.Theme
|
||||
import app.revanced.manager.ui.viewmodel.MainViewModel
|
||||
import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel
|
||||
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
|
||||
import app.revanced.manager.util.EventEffect
|
||||
import dev.olshevski.navigation.reimagined.AnimatedNavHost
|
||||
import dev.olshevski.navigation.reimagined.NavBackHandler
|
||||
import dev.olshevski.navigation.reimagined.navigate
|
||||
import dev.olshevski.navigation.reimagined.pop
|
||||
import dev.olshevski.navigation.reimagined.popUpTo
|
||||
import dev.olshevski.navigation.reimagined.rememberNavController
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.androidx.compose.navigation.koinNavViewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koin.androidx.compose.koinViewModel as getComposeViewModel
|
||||
import org.koin.androidx.viewmodel.ext.android.getViewModel as getAndroidViewModel
|
||||
@ -52,6 +55,200 @@ class MainActivity : ComponentActivity() {
|
||||
darkTheme = theme == Theme.SYSTEM && isSystemInDarkTheme() || theme == Theme.DARK,
|
||||
dynamicColor = dynamicColor
|
||||
) {
|
||||
ReVancedManager(vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReVancedManager(vm: MainViewModel) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
EventEffect(vm.appSelectFlow) { app ->
|
||||
// navController.navigate(SelectedApplicationInfo(app))
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Dashboard,
|
||||
) {
|
||||
composable<Dashboard> {
|
||||
DashboardScreen(
|
||||
onSettingsClick = { navController.navigate(Settings) },
|
||||
onAppSelectorClick = {
|
||||
println("before: ${navController.currentBackStackEntry?.id}")
|
||||
navController.navigate(AppSelector)
|
||||
println("after: ${navController.currentBackStackEntry?.id}")
|
||||
},
|
||||
onUpdateClick = {
|
||||
navController.navigate(Update())
|
||||
// navController.navigate(Destination.Settings(SettingsDestination.Update()))
|
||||
},
|
||||
onDownloaderPluginClick = {
|
||||
// navController.navigate(Destination.Settings(SettingsDestination.Downloads))
|
||||
navController.navigate(Settings.Downloads)
|
||||
},
|
||||
onAppClick = { installedApp ->
|
||||
navController.navigate(InstalledApplicationInfo(installedApp.currentPackageName))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<InstalledApplicationInfo> {
|
||||
val data = it.toRoute<InstalledApplicationInfo>()
|
||||
|
||||
InstalledAppInfoScreen(
|
||||
onPatchClick = vm::selectApp,
|
||||
onBackClick = navController::popBackStack,
|
||||
viewModel = getComposeViewModel { parametersOf(data.packageName) }
|
||||
)
|
||||
}
|
||||
|
||||
composable<AppSelector> {
|
||||
AppSelectorScreen(
|
||||
onSelect = vm::selectApp,
|
||||
onStorageSelect = vm::selectApp,
|
||||
onBackClick = navController::popBackStack
|
||||
)
|
||||
}
|
||||
|
||||
composable<Patcher> {
|
||||
PatcherScreen(
|
||||
onBackClick = {
|
||||
navController.navigate(route = Dashboard) {
|
||||
launchSingleTop = true
|
||||
popUpTo<Dashboard> {
|
||||
inclusive = false
|
||||
}
|
||||
}
|
||||
},
|
||||
vm = koinViewModel { parametersOf(it.toRoute<Patcher>()) }
|
||||
)
|
||||
}
|
||||
|
||||
navigation<SelectedApplicationInfo>(startDestination = SelectedApplicationInfo.Main) {
|
||||
composable<SelectedApplicationInfo.Main> {
|
||||
val parentBackStackEntry = navController.navGraphEntry(it)
|
||||
val data = parentBackStackEntry.toRoute<SelectedApplicationInfo>()
|
||||
|
||||
SelectedAppInfoScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
onPatchClick = { app, patches, options ->
|
||||
// navController.navigate(Patcher(app, patches, options))
|
||||
},
|
||||
onPatchSelectorClick = { app, patches, options ->
|
||||
/*
|
||||
navController.navigate(
|
||||
SelectedApplicationInfo.PatchesSelector(
|
||||
app,
|
||||
patches,
|
||||
options
|
||||
)
|
||||
)*/
|
||||
},
|
||||
vm = koinNavViewModel<SelectedAppInfoViewModel>(viewModelStoreOwner = parentBackStackEntry) {
|
||||
parametersOf(
|
||||
SelectedAppInfoViewModel.Params(
|
||||
data.selectedApp,
|
||||
data.patchSelection
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<SelectedApplicationInfo.PatchesSelector>(
|
||||
// typeMap = mapOf(typeOf<SelectedApplicationInfo.PatchesSelector>() to SelectedApplicationInfo.PatchesSelector.navType)
|
||||
) {
|
||||
val data = it.toRoute<SelectedApplicationInfo.PatchesSelector>()
|
||||
val selectedAppInfoVm = koinNavViewModel<SelectedAppInfoViewModel>(
|
||||
viewModelStoreOwner = navController.navGraphEntry(it)
|
||||
)
|
||||
|
||||
PatchesSelectorScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
onSave = { patches, options ->
|
||||
selectedAppInfoVm.updateConfiguration(patches, options)
|
||||
navController.popBackStack()
|
||||
},
|
||||
vm = koinViewModel {
|
||||
parametersOf(
|
||||
PatchesSelectorViewModel.Params(
|
||||
data.app,
|
||||
data.currentSelection,
|
||||
data.options,
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
navigation<Settings>(startDestination = Settings.Main) {
|
||||
composable<Settings.Main> {
|
||||
SettingsScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
navigate = navController::navigate
|
||||
)
|
||||
}
|
||||
|
||||
composable<Settings.General> {
|
||||
GeneralSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Advanced> {
|
||||
AdvancedSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Updates> {
|
||||
UpdatesSettingsScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
onChangelogClick = { navController.navigate(Settings.Changelogs) },
|
||||
onUpdateClick = { navController.navigate(Update()) }
|
||||
)
|
||||
}
|
||||
|
||||
composable<Settings.Downloads> {
|
||||
DownloadsSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.ImportExport> {
|
||||
ImportExportSettingsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.About> {
|
||||
AboutSettingsScreen(
|
||||
onBackClick = navController::popBackStack,
|
||||
navigate = navController::navigate
|
||||
)
|
||||
}
|
||||
|
||||
composable<Settings.Changelogs> {
|
||||
ChangelogsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Contributors> {
|
||||
ContributorScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.Licenses> {
|
||||
LicensesScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
|
||||
composable<Settings.DeveloperOptions> {
|
||||
DeveloperOptionsScreen(onBackClick = navController::popBackStack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NavController.navGraphEntry(entry: NavBackStackEntry) = remember(entry) {
|
||||
getBackStackEntry(entry.destination.parent!!)
|
||||
}
|
||||
|
||||
/*
|
||||
val navController =
|
||||
rememberNavController<Destination>(startDestination = Destination.Dashboard)
|
||||
NavBackHandler(navController)
|
||||
@ -124,7 +321,4 @@ class MainActivity : ComponentActivity() {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
@ -1,18 +1,15 @@
|
||||
package app.revanced.manager.data.room.apps.installed
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import app.revanced.manager.R
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
enum class InstallType(val stringResource: Int) {
|
||||
DEFAULT(R.string.default_install),
|
||||
MOUNT(R.string.mount_install)
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
@Entity(tableName = "installed_app")
|
||||
data class InstalledApp(
|
||||
@PrimaryKey
|
||||
@ -20,4 +17,4 @@ data class InstalledApp(
|
||||
@ColumnInfo(name = "original_package_name") val originalPackageName: String,
|
||||
@ColumnInfo(name = "version") val version: String,
|
||||
@ColumnInfo(name = "install_type") val installType: InstallType
|
||||
) : Parcelable
|
||||
)
|
@ -9,7 +9,7 @@ val viewModelModule = module {
|
||||
viewModelOf(::DashboardViewModel)
|
||||
viewModelOf(::SelectedAppInfoViewModel)
|
||||
viewModelOf(::PatchesSelectorViewModel)
|
||||
viewModelOf(::SettingsViewModel)
|
||||
viewModelOf(::GeneralSettingsViewModel)
|
||||
viewModelOf(::AdvancedSettingsViewModel)
|
||||
viewModelOf(::AppSelectorViewModel)
|
||||
viewModelOf(::PatcherViewModel)
|
||||
|
@ -13,8 +13,8 @@ sealed interface Destination : Parcelable {
|
||||
@Parcelize
|
||||
data object Dashboard : Destination
|
||||
|
||||
@Parcelize
|
||||
data class InstalledApplicationInfo(val installedApp: InstalledApp) : Destination
|
||||
// @Parcelize
|
||||
// data class InstalledApplicationInfo(val installedApp: InstalledApp) : Destination
|
||||
|
||||
@Parcelize
|
||||
data object AppSelector : Destination
|
||||
|
@ -0,0 +1,116 @@
|
||||
package app.revanced.manager.ui.model.navigation
|
||||
|
||||
import app.revanced.manager.ui.model.SelectedApp
|
||||
import app.revanced.manager.util.Options
|
||||
import app.revanced.manager.util.PatchSelection
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/*
|
||||
sealed interface Destination : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
data object Dashboard : Destination
|
||||
|
||||
@Parcelize
|
||||
data class InstalledApplicationInfo(val installedApp: InstalledApp) : Destination
|
||||
|
||||
@Parcelize
|
||||
data object AppSelector : Destination
|
||||
|
||||
@Parcelize
|
||||
data class Settings(val startDestination: SettingsDestination = SettingsDestination.Settings) : Destination
|
||||
|
||||
@Parcelize
|
||||
data class SelectedApplicationInfo(val selectedApp: SelectedApp, val patchSelection: PatchSelection? = null) : Destination
|
||||
|
||||
@Parcelize
|
||||
data class Patcher(val selectedApp: SelectedApp, val selectedPatches: PatchSelection, val options: @RawValue Options) : Destination
|
||||
|
||||
}*/
|
||||
|
||||
@Serializable
|
||||
object Dashboard
|
||||
|
||||
@Serializable
|
||||
object AppSelector
|
||||
|
||||
@Serializable
|
||||
data class InstalledApplicationInfo(val packageName: String)
|
||||
|
||||
@Serializable
|
||||
data class Update(val downloadOnScreenEntry: Boolean = false)
|
||||
|
||||
@Serializable
|
||||
data class SelectedApplicationInfo(
|
||||
val h: Int = 0,
|
||||
// val selectedApp: SelectedApp,
|
||||
// val patchSelection: PatchSelection? = null
|
||||
) {
|
||||
val patchSelection: PatchSelection? get() = TODO()
|
||||
val selectedApp: SelectedApp get() = TODO()
|
||||
|
||||
@Serializable
|
||||
object Main
|
||||
|
||||
@Serializable
|
||||
data class PatchesSelector(
|
||||
val h: Int,
|
||||
//val app: SelectedApp,
|
||||
// val currentSelection: PatchSelection?,
|
||||
// val options: Options
|
||||
) {
|
||||
val app: SelectedApp get() = TODO()
|
||||
val currentSelection: PatchSelection? get() = TODO()
|
||||
val options: Options get() = TODO()
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Patcher(
|
||||
val h: Int = 0,
|
||||
//val selectedApp: SelectedApp,
|
||||
// val selectedPatches: PatchSelection,
|
||||
// val options: Options
|
||||
) {
|
||||
val selectedApp: SelectedApp get() = TODO()
|
||||
val options: Options get() = TODO()
|
||||
val selectedPatches: PatchSelection get() = TODO()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Settings {
|
||||
sealed interface Destination
|
||||
|
||||
@Serializable
|
||||
data object Main : Destination
|
||||
|
||||
@Serializable
|
||||
data object General : Destination
|
||||
|
||||
@Serializable
|
||||
data object Advanced : Destination
|
||||
|
||||
@Serializable
|
||||
data object Updates : Destination
|
||||
|
||||
@Serializable
|
||||
data object Downloads : Destination
|
||||
|
||||
@Serializable
|
||||
data object ImportExport : Destination
|
||||
|
||||
@Serializable
|
||||
data object About : Destination
|
||||
|
||||
@Serializable
|
||||
data object Changelogs : Destination
|
||||
|
||||
@Serializable
|
||||
data object Contributors : Destination
|
||||
|
||||
@Serializable
|
||||
data object Licenses : Destination
|
||||
|
||||
@Serializable
|
||||
data object DeveloperOptions : Destination
|
||||
}
|
@ -77,10 +77,12 @@ fun InstalledAppInfoScreen(
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
) {
|
||||
AppInfo(viewModel.appInfo) {
|
||||
Text(viewModel.installedApp.version, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium)
|
||||
val installedApp = viewModel.installedApp ?: return@ColumnWithScrollbar
|
||||
|
||||
if (viewModel.installedApp.installType == InstallType.MOUNT) {
|
||||
AppInfo(viewModel.appInfo) {
|
||||
Text(installedApp.version, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium)
|
||||
|
||||
if (installedApp.installType == InstallType.MOUNT) {
|
||||
Text(
|
||||
text = if (viewModel.isMounted) {
|
||||
stringResource(R.string.mounted)
|
||||
@ -104,7 +106,7 @@ fun InstalledAppInfoScreen(
|
||||
onClick = viewModel::launch
|
||||
)
|
||||
|
||||
when (viewModel.installedApp.installType) {
|
||||
when (installedApp.installType) {
|
||||
InstallType.DEFAULT -> SegmentedButton(
|
||||
icon = Icons.Outlined.Delete,
|
||||
text = stringResource(R.string.uninstall),
|
||||
@ -133,9 +135,9 @@ fun InstalledAppInfoScreen(
|
||||
icon = Icons.Outlined.Update,
|
||||
text = stringResource(R.string.repatch),
|
||||
onClick = {
|
||||
onPatchClick(viewModel.installedApp.originalPackageName)
|
||||
onPatchClick(installedApp.originalPackageName)
|
||||
},
|
||||
enabled = viewModel.installedApp.installType != InstallType.MOUNT || viewModel.rootInstaller.hasRootAccess()
|
||||
enabled = installedApp.installType != InstallType.MOUNT || viewModel.rootInstaller.hasRootAccess()
|
||||
)
|
||||
}
|
||||
|
||||
@ -158,19 +160,19 @@ fun InstalledAppInfoScreen(
|
||||
|
||||
SettingsListItem(
|
||||
headlineContent = stringResource(R.string.package_name),
|
||||
supportingContent = viewModel.installedApp.currentPackageName
|
||||
supportingContent = installedApp.currentPackageName
|
||||
)
|
||||
|
||||
if (viewModel.installedApp.originalPackageName != viewModel.installedApp.currentPackageName) {
|
||||
if (installedApp.originalPackageName != installedApp.currentPackageName) {
|
||||
SettingsListItem(
|
||||
headlineContent = stringResource(R.string.original_package_name),
|
||||
supportingContent = viewModel.installedApp.originalPackageName
|
||||
supportingContent = installedApp.originalPackageName
|
||||
)
|
||||
}
|
||||
|
||||
SettingsListItem(
|
||||
headlineContent = stringResource(R.string.install_type),
|
||||
supportingContent = stringResource(viewModel.installedApp.installType.stringResource)
|
||||
supportingContent = stringResource(installedApp.installType.stringResource)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import org.koin.core.parameter.parametersOf
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SelectedAppInfoScreen(
|
||||
onPatchSelectorClick: (SelectedApp, PatchSelection?, Options) -> Unit,
|
||||
onPatchClick: (SelectedApp, PatchSelection, Options) -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
vm: SelectedAppInfoViewModel
|
||||
@ -82,15 +83,8 @@ fun SelectedAppInfoScreen(
|
||||
launcher.launch(intent)
|
||||
}
|
||||
|
||||
val navController =
|
||||
rememberNavController<SelectedAppInfoDestination>(startDestination = SelectedAppInfoDestination.Main)
|
||||
|
||||
NavBackHandler(controller = navController)
|
||||
|
||||
AnimatedNavHost(controller = navController) { destination ->
|
||||
val error by vm.errorFlow.collectAsStateWithLifecycle(null)
|
||||
when (destination) {
|
||||
is SelectedAppInfoDestination.Main -> Scaffold(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppTopBar(
|
||||
title = stringResource(R.string.app_info),
|
||||
@ -167,6 +161,15 @@ fun SelectedAppInfoScreen(
|
||||
selectedPatchCount
|
||||
),
|
||||
onClick = {
|
||||
onPatchSelectorClick(
|
||||
vm.selectedApp,
|
||||
vm.getCustomPatches(
|
||||
bundles,
|
||||
allowIncompatiblePatches
|
||||
),
|
||||
vm.options
|
||||
)
|
||||
/*
|
||||
navController.navigate(
|
||||
SelectedAppInfoDestination.PatchesSelector(
|
||||
vm.selectedApp,
|
||||
@ -176,7 +179,7 @@ fun SelectedAppInfoScreen(
|
||||
),
|
||||
vm.options
|
||||
)
|
||||
)
|
||||
)*/
|
||||
}
|
||||
)
|
||||
PageItem(
|
||||
@ -205,8 +208,10 @@ fun SelectedAppInfoScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is SelectedAppInfoDestination.PatchesSelector -> PatchesSelectorScreen(
|
||||
/*
|
||||
PatchesSelectorScreen(
|
||||
onSave = { patches, options ->
|
||||
vm.updateConfiguration(patches, options, bundles)
|
||||
navController.pop()
|
||||
@ -222,9 +227,7 @@ fun SelectedAppInfoScreen(
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@Composable
|
||||
private fun PageItem(@StringRes title: Int, description: String, onClick: () -> Unit) {
|
||||
|
@ -13,21 +13,75 @@ import app.revanced.manager.R
|
||||
import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||
import app.revanced.manager.ui.destination.SettingsDestination
|
||||
import app.revanced.manager.ui.screen.settings.*
|
||||
import app.revanced.manager.ui.screen.settings.update.ChangelogsScreen
|
||||
import app.revanced.manager.ui.screen.settings.update.UpdateScreen
|
||||
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
|
||||
import app.revanced.manager.ui.viewmodel.SettingsViewModel
|
||||
import dev.olshevski.navigation.reimagined.*
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import app.revanced.manager.ui.model.navigation.Settings
|
||||
|
||||
private val settingsSections = listOf(
|
||||
Triple(
|
||||
R.string.general,
|
||||
R.string.general_description,
|
||||
Icons.Outlined.Settings
|
||||
) to Settings.General,
|
||||
Triple(
|
||||
R.string.updates,
|
||||
R.string.updates_description,
|
||||
Icons.Outlined.Update
|
||||
) to Settings.Updates,
|
||||
Triple(
|
||||
R.string.downloads,
|
||||
R.string.downloads_description,
|
||||
Icons.Outlined.Download
|
||||
) to Settings.Downloads,
|
||||
Triple(
|
||||
R.string.import_export,
|
||||
R.string.import_export_description,
|
||||
Icons.Outlined.SwapVert
|
||||
) to Settings.ImportExport,
|
||||
Triple(
|
||||
R.string.advanced,
|
||||
R.string.advanced_description,
|
||||
Icons.Outlined.Tune
|
||||
) to Settings.Advanced,
|
||||
Triple(
|
||||
R.string.about,
|
||||
R.string.app_name,
|
||||
Icons.Outlined.Info
|
||||
) to Settings.About,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(onBackClick: () -> Unit, navigate: (Settings.Destination) -> Unit) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppTopBar(
|
||||
title = stringResource(R.string.settings),
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
ColumnWithScrollbar(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
settingsSections.forEach { (titleDescIcon, destination) ->
|
||||
SettingsListItem(
|
||||
modifier = Modifier.clickable { navigate(destination) },
|
||||
headlineContent = stringResource(titleDescIcon.first),
|
||||
supportingContent = stringResource(titleDescIcon.second),
|
||||
leadingContent = { Icon(titleDescIcon.third, null) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
onBackClick: () -> Unit,
|
||||
startDestination: SettingsDestination,
|
||||
startDestination: Settings.Destination,
|
||||
viewModel: SettingsViewModel = koinViewModel()
|
||||
) {
|
||||
val navController = rememberNavController(startDestination)
|
||||
@ -156,4 +210,4 @@ fun SettingsScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.manager.ui.screen.settings.update
|
||||
package app.revanced.manager.ui.screen
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.spring
|
@ -37,6 +37,7 @@ import app.revanced.manager.network.dto.ReVancedSocial
|
||||
import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||
import app.revanced.manager.ui.model.navigation.Settings
|
||||
import app.revanced.manager.ui.viewmodel.AboutViewModel
|
||||
import app.revanced.manager.ui.viewmodel.AboutViewModel.Companion.getSocialIcon
|
||||
import app.revanced.manager.util.openUrl
|
||||
@ -47,9 +48,7 @@ import org.koin.androidx.compose.koinViewModel
|
||||
@Composable
|
||||
fun AboutSettingsScreen(
|
||||
onBackClick: () -> Unit,
|
||||
onContributorsClick: () -> Unit,
|
||||
onLicensesClick: () -> Unit,
|
||||
onDeveloperOptionsClick: () -> Unit,
|
||||
navigate: (Settings.Destination) -> Unit,
|
||||
viewModel: AboutViewModel = koinViewModel()
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
@ -114,17 +113,17 @@ fun AboutSettingsScreen(
|
||||
Triple(
|
||||
stringResource(R.string.contributors),
|
||||
stringResource(R.string.contributors_description),
|
||||
third = onContributorsClick
|
||||
third = { navigate(Settings.Contributors) }
|
||||
),
|
||||
Triple(
|
||||
stringResource(R.string.developer_options),
|
||||
stringResource(R.string.developer_options_description),
|
||||
third = onDeveloperOptionsClick
|
||||
third = { navigate(Settings.DeveloperOptions) }
|
||||
),
|
||||
Triple(
|
||||
stringResource(R.string.opensource_licenses),
|
||||
stringResource(R.string.opensource_licenses_description),
|
||||
third = onLicensesClick
|
||||
third = { navigate(Settings.Licenses) }
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
@ -32,14 +31,15 @@ import app.revanced.manager.ui.component.haptics.HapticRadioButton
|
||||
import app.revanced.manager.ui.component.settings.BooleanItem
|
||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||
import app.revanced.manager.ui.theme.Theme
|
||||
import app.revanced.manager.ui.viewmodel.SettingsViewModel
|
||||
import app.revanced.manager.ui.viewmodel.GeneralSettingsViewModel
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun GeneralSettingsScreen(
|
||||
onBackClick: () -> Unit,
|
||||
viewModel: SettingsViewModel
|
||||
viewModel: GeneralSettingsViewModel = koinViewModel()
|
||||
) {
|
||||
val prefs = viewModel.prefs
|
||||
val coroutineScope = viewModel.viewModelScope
|
||||
|
@ -6,7 +6,7 @@ import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.ui.theme.Theme
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SettingsViewModel(
|
||||
class GeneralSettingsViewModel(
|
||||
val prefs: PreferencesManager
|
||||
) : ViewModel() {
|
||||
fun setTheme(theme: Theme) = viewModelScope.launch {
|
@ -32,15 +32,17 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class InstalledAppInfoViewModel(
|
||||
val installedApp: InstalledApp
|
||||
packageName: String
|
||||
) : ViewModel(), KoinComponent {
|
||||
private val app: Application by inject()
|
||||
private val context: Application by inject()
|
||||
private val pm: PM by inject()
|
||||
private val installedAppRepository: InstalledAppRepository by inject()
|
||||
val rootInstaller: RootInstaller by inject()
|
||||
|
||||
lateinit var onBackClick: () -> Unit
|
||||
|
||||
var installedApp: InstalledApp? by mutableStateOf(null)
|
||||
private set
|
||||
var appInfo: PackageInfo? by mutableStateOf(null)
|
||||
private set
|
||||
var appliedPatches: PatchSelection? by mutableStateOf(null)
|
||||
@ -49,38 +51,48 @@ class InstalledAppInfoViewModel(
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
isMounted = rootInstaller.isAppMounted(installedApp.currentPackageName)
|
||||
installedApp = installedAppRepository.get(packageName)?.also {
|
||||
isMounted = rootInstaller.isAppMounted(it.currentPackageName)
|
||||
appInfo = withContext(Dispatchers.IO) {
|
||||
pm.getPackageInfo(it.currentPackageName)
|
||||
}
|
||||
appliedPatches = withContext(Dispatchers.IO) {
|
||||
installedAppRepository.getAppliedPatches(it.currentPackageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun launch() = pm.launch(installedApp.currentPackageName)
|
||||
fun launch() = installedApp?.currentPackageName?.let(pm::launch)
|
||||
|
||||
fun mountOrUnmount() = viewModelScope.launch {
|
||||
val pkgName = installedApp?.currentPackageName ?: return@launch
|
||||
try {
|
||||
if (isMounted)
|
||||
rootInstaller.unmount(installedApp.currentPackageName)
|
||||
rootInstaller.unmount(pkgName)
|
||||
else
|
||||
rootInstaller.mount(installedApp.currentPackageName)
|
||||
rootInstaller.mount(pkgName)
|
||||
} catch (e: Exception) {
|
||||
if (isMounted) {
|
||||
app.toast(app.getString(R.string.failed_to_unmount, e.simpleMessage()))
|
||||
context.toast(context.getString(R.string.failed_to_unmount, e.simpleMessage()))
|
||||
Log.e(tag, "Failed to unmount", e)
|
||||
} else {
|
||||
app.toast(app.getString(R.string.failed_to_mount, e.simpleMessage()))
|
||||
context.toast(context.getString(R.string.failed_to_mount, e.simpleMessage()))
|
||||
Log.e(tag, "Failed to mount", e)
|
||||
}
|
||||
} finally {
|
||||
isMounted = rootInstaller.isAppMounted(installedApp.currentPackageName)
|
||||
isMounted = rootInstaller.isAppMounted(pkgName)
|
||||
}
|
||||
}
|
||||
|
||||
fun uninstall() {
|
||||
when (installedApp.installType) {
|
||||
InstallType.DEFAULT -> pm.uninstallPackage(installedApp.currentPackageName)
|
||||
val app = installedApp ?: return
|
||||
when (app.installType) {
|
||||
InstallType.DEFAULT -> pm.uninstallPackage(app.currentPackageName)
|
||||
|
||||
InstallType.MOUNT -> viewModelScope.launch {
|
||||
rootInstaller.uninstall(installedApp.currentPackageName)
|
||||
installedAppRepository.delete(installedApp)
|
||||
rootInstaller.uninstall(app.currentPackageName)
|
||||
installedAppRepository.delete(app)
|
||||
onBackClick()
|
||||
}
|
||||
}
|
||||
@ -97,34 +109,22 @@ class InstalledAppInfoViewModel(
|
||||
|
||||
if (extraStatus == PackageInstaller.STATUS_SUCCESS) {
|
||||
viewModelScope.launch {
|
||||
installedAppRepository.delete(installedApp)
|
||||
installedApp?.let {
|
||||
installedAppRepository.delete(it)
|
||||
}
|
||||
onBackClick()
|
||||
}
|
||||
} else if (extraStatus != PackageInstaller.STATUS_FAILURE_ABORTED) {
|
||||
app.toast(app.getString(R.string.uninstall_app_fail, extraStatusMessage))
|
||||
this@InstalledAppInfoViewModel.context.toast(this@InstalledAppInfoViewModel.context.getString(R.string.uninstall_app_fail, extraStatusMessage))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
appInfo = withContext(Dispatchers.IO) {
|
||||
pm.getPackageInfo(installedApp.currentPackageName)
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
appliedPatches = withContext(Dispatchers.IO) {
|
||||
installedAppRepository.getAppliedPatches(installedApp.currentPackageName)
|
||||
}
|
||||
}
|
||||
|
||||
}.also {
|
||||
ContextCompat.registerReceiver(
|
||||
app,
|
||||
uninstallBroadcastReceiver,
|
||||
context,
|
||||
it,
|
||||
IntentFilter(UninstallService.APP_UNINSTALL_ACTION),
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
)
|
||||
@ -132,6 +132,6 @@ class InstalledAppInfoViewModel(
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
app.unregisterReceiver(uninstallBroadcastReceiver)
|
||||
context.unregisterReceiver(uninstallBroadcastReceiver)
|
||||
}
|
||||
}
|
@ -47,6 +47,7 @@ import app.revanced.manager.ui.model.State
|
||||
import app.revanced.manager.ui.model.Step
|
||||
import app.revanced.manager.ui.model.StepCategory
|
||||
import app.revanced.manager.ui.model.StepProgressProvider
|
||||
import app.revanced.manager.ui.model.navigation.Patcher
|
||||
import app.revanced.manager.util.PM
|
||||
import app.revanced.manager.util.saveableVar
|
||||
import app.revanced.manager.util.saver.snapshotStateListSaver
|
||||
@ -69,10 +70,11 @@ import org.koin.core.component.inject
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.time.Duration
|
||||
import java.util.UUID
|
||||
|
||||
@OptIn(SavedStateHandleSaveableApi::class, PluginHostApi::class)
|
||||
class PatcherViewModel(
|
||||
private val input: Destination.Patcher
|
||||
private val input: Patcher
|
||||
) : ViewModel(), KoinComponent, StepProgressProvider, InstallerModel {
|
||||
private val app: Application by inject()
|
||||
private val fs: Filesystem by inject()
|
||||
|
@ -33,6 +33,7 @@ import app.revanced.manager.plugin.downloader.GetScope
|
||||
import app.revanced.manager.plugin.downloader.PluginHostApi
|
||||
import app.revanced.manager.plugin.downloader.UserInteractionException
|
||||
import app.revanced.manager.ui.model.BundleInfo
|
||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.bundleInfoFlow
|
||||
import app.revanced.manager.ui.model.BundleInfo.Extensions.toPatchSelection
|
||||
import app.revanced.manager.ui.model.SelectedApp
|
||||
import app.revanced.manager.util.Options
|
||||
@ -110,7 +111,10 @@ class SelectedAppInfoViewModel(input: Params) : ViewModel(), KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
val requiredVersion = combine(prefs.suggestedVersionSafeguard.flow, bundleRepository.suggestedVersions) { suggestedVersionSafeguard, suggestedVersions ->
|
||||
val requiredVersion = combine(
|
||||
prefs.suggestedVersionSafeguard.flow,
|
||||
bundleRepository.suggestedVersions
|
||||
) { suggestedVersionSafeguard, suggestedVersions ->
|
||||
if (!suggestedVersionSafeguard) return@combine null
|
||||
|
||||
suggestedVersions[input.app.packageName]
|
||||
@ -264,17 +268,15 @@ class SelectedAppInfoViewModel(input: Params) : ViewModel(), KoinComponent {
|
||||
): PatchSelection? =
|
||||
(selectionState as? SelectionState.Customized)?.patches(bundles, allowUnsupported)
|
||||
|
||||
fun updateConfiguration(
|
||||
selection: PatchSelection?,
|
||||
options: Options,
|
||||
bundles: List<BundleInfo>
|
||||
) {
|
||||
fun updateConfiguration(selection: PatchSelection?, options: Options) = viewModelScope.launch {
|
||||
val bundles = bundlesRepo.bundleInfoFlow(packageName, selectedApp.version).first()
|
||||
|
||||
selectionState = selection?.let(SelectionState::Customized) ?: SelectionState.Default
|
||||
|
||||
val filteredOptions = options.filtered(bundles)
|
||||
this.options = filteredOptions
|
||||
this@SelectedAppInfoViewModel.options = filteredOptions
|
||||
|
||||
if (!persistConfiguration) return
|
||||
if (!persistConfiguration) return@launch
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
selection?.let { selectionRepository.updateSelection(packageName, it) }
|
||||
?: selectionRepository.clearSelection(packageName)
|
||||
|
@ -3,11 +3,6 @@ package app.revanced.manager.util
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.icu.number.Notation
|
||||
import android.icu.number.NumberFormatter
|
||||
import android.icu.number.Precision
|
||||
import android.icu.text.CompactDecimalFormat
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.MainThread
|
||||
@ -54,13 +49,14 @@ import kotlinx.datetime.format.MonthNames
|
||||
import kotlinx.datetime.format.char
|
||||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import kotlinx.serialization.Contextual
|
||||
import java.util.Locale
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
typealias PatchSelection = Map<Int, Set<String>>
|
||||
typealias Options = Map<Int, Map<String, Map<String, Any?>>>
|
||||
typealias Options = Map<Int, Map<String, Map<String, @Contextual Any?>>>
|
||||
|
||||
val Context.isDebuggable get() = 0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE
|
||||
|
||||
|
@ -9,6 +9,7 @@ appcompat = "1.7.0"
|
||||
preferences-datastore = "1.1.1"
|
||||
work-runtime = "2.10.0"
|
||||
compose-bom = "2024.12.01"
|
||||
navigation = "2.8.4"
|
||||
accompanist = "0.34.0"
|
||||
placeholder = "1.1.2"
|
||||
reorderable = "1.5.2"
|
||||
@ -59,6 +60,7 @@ compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", versi
|
||||
compose-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" }
|
||||
compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
||||
compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
|
||||
navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
||||
|
||||
# Coil
|
||||
coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
|
||||
@ -87,6 +89,7 @@ revanced-library = { group = "app.revanced", name = "revanced-library", version.
|
||||
# Koin
|
||||
koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin-version" }
|
||||
koin-compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin-version-compose" }
|
||||
koin-compose-navigation = { group = "io.insert-koin", name = "koin-androidx-compose-navigation", version.ref = "koin-version-compose" }
|
||||
koin-workmanager = { group = "io.insert-koin", name = "koin-androidx-workmanager", version.ref = "koin-version" }
|
||||
|
||||
# Compose Navigation
|
||||
|
Loading…
x
Reference in New Issue
Block a user