feat: settings screen

This commit is contained in:
CnC-Robert 2023-05-18 13:38:02 +02:00
parent 1e2fadb2e1
commit caf8faf563
No known key found for this signature in database
GPG Key ID: C58ED617AEA8CB68
16 changed files with 605 additions and 13 deletions

View File

@ -56,10 +56,10 @@ dependencies {
implementation("androidx.compose.material3:material3")
// Accompanist
//val accompanistVersion = "0.30.1"
val accompanistVersion = "0.30.1"
//implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
//implementation("com.google.accompanist:accompanist-placeholder-material:$accompanistVersion")
//implementation("com.google.accompanist:accompanist-drawablepainter:$accompanistVersion")
implementation("com.google.accompanist:accompanist-drawablepainter:$accompanistVersion")
//implementation("com.google.accompanist:accompanist-flowlayout:$accompanistVersion")
//implementation("com.google.accompanist:accompanist-permissions:$accompanistVersion")

View File

@ -10,6 +10,7 @@ import app.revanced.manager.compose.domain.manager.PreferencesManager
import app.revanced.manager.compose.ui.destination.Destination
import app.revanced.manager.compose.ui.screen.AppSelectorScreen
import app.revanced.manager.compose.ui.screen.DashboardScreen
import app.revanced.manager.compose.ui.screen.SettingsScreen
import app.revanced.manager.compose.ui.theme.ReVancedManagerTheme
import app.revanced.manager.compose.ui.theme.Theme
import app.revanced.manager.compose.util.PM
@ -52,11 +53,17 @@ class MainActivity : ComponentActivity() {
) { destination ->
when (destination) {
is Destination.Dashboard -> { DashboardScreen(
is Destination.Dashboard -> DashboardScreen(
onSettingsClick = { navController.navigate(Destination.Settings) },
onAppSelectorClick = { navController.navigate(Destination.AppSelector) }
) }
)
is Destination.Settings -> SettingsScreen(
onBackClick = { navController.pop() }
)
is Destination.AppSelector -> AppSelectorScreen(
onAppClick = { },
onBackClick = { navController.pop() }
)

View File

@ -16,6 +16,7 @@ class ManagerApplication: Application() {
preferencesModule,
repositoryModule,
serviceModule,
viewModelModule
)
}
}

View File

@ -0,0 +1,10 @@
package app.revanced.manager.compose.di
import app.revanced.manager.compose.ui.viewmodel.*
import org.koin.androidx.viewmodel.dsl.viewModelOf
import org.koin.dsl.module
val viewModelModule = module {
//viewModelOf(::PatchesSelectorViewModel)
viewModelOf(::SettingsViewModel)
}

View File

@ -0,0 +1,21 @@
package app.revanced.manager.compose.ui.component
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun GroupHeader(
title: String,
modifier: Modifier = Modifier
) {
Text(
text = title,
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(16.dp).then(modifier)
)
}

View File

@ -11,4 +11,7 @@ sealed interface Destination: Parcelable {
@Parcelize
object AppSelector: Destination
@Parcelize
object Settings: Destination
}

View File

@ -0,0 +1,26 @@
package app.revanced.manager.compose.ui.destination
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
sealed interface SettingsDestination: Parcelable {
@Parcelize
object Settings: SettingsDestination
@Parcelize
object General: SettingsDestination
@Parcelize
object Updates: SettingsDestination
@Parcelize
object Downloads: SettingsDestination
@Parcelize
object ImportExport: SettingsDestination
@Parcelize
object About: SettingsDestination
}

View File

@ -0,0 +1,138 @@
package app.revanced.manager.compose.ui.screen
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Download
import androidx.compose.material.icons.outlined.ImportExport
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.Update
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import app.revanced.manager.compose.R
import app.revanced.manager.compose.ui.component.AppTopBar
import app.revanced.manager.compose.ui.destination.SettingsDestination
import app.revanced.manager.compose.ui.screen.settings.AboutSettingsScreen
import app.revanced.manager.compose.ui.screen.settings.DownloadsSettingsScreen
import app.revanced.manager.compose.ui.screen.settings.GeneralSettingsScreen
import app.revanced.manager.compose.ui.screen.settings.ImportExportSettingsScreen
import app.revanced.manager.compose.ui.screen.settings.UpdatesSettingsScreen
import app.revanced.manager.compose.ui.viewmodel.SettingsViewModel
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.rememberNavController
import org.koin.androidx.compose.getViewModel
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
@Composable
fun SettingsScreen(
onBackClick: () -> Unit,
viewModel: SettingsViewModel = getViewModel()
) {
val navController = rememberNavController<SettingsDestination>(startDestination = SettingsDestination.Settings)
NavBackHandler(navController)
AnimatedNavHost(
controller = navController
) { destination ->
when (destination) {
is SettingsDestination.General -> GeneralSettingsScreen(
onBackClick = { navController.pop() },
viewModel = viewModel
)
is SettingsDestination.Updates -> UpdatesSettingsScreen(
onBackClick = { navController.pop() }
)
is SettingsDestination.Downloads -> DownloadsSettingsScreen(
onBackClick = { navController.pop() }
)
is SettingsDestination.ImportExport -> ImportExportSettingsScreen(
onBackClick = { navController.pop() }
)
is SettingsDestination.About -> AboutSettingsScreen(
onBackClick = { navController.pop() }
)
is SettingsDestination.Settings -> {
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.settings),
onBackClick = onBackClick,
actions = {
}
)
}
) { paddingValues ->
Column(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
ListItem(
modifier = Modifier.clickable { navController.navigate(SettingsDestination.General) },
leadingContent = { Icon(Icons.Outlined.Settings, null) },
headlineContent = { Text(stringResource(R.string.general)) },
supportingContent = { Text(stringResource(R.string.general_description)) }
)
ListItem(
modifier = Modifier.clickable { navController.navigate(SettingsDestination.Updates) },
leadingContent = { Icon(Icons.Outlined.Update, null) },
headlineContent = { Text(stringResource(R.string.updates)) },
supportingContent = { Text(stringResource(R.string.updates_description)) }
)
ListItem(
modifier = Modifier.clickable { navController.navigate(SettingsDestination.Downloads) },
leadingContent = { Icon(Icons.Outlined.Download, null) },
headlineContent = { Text(stringResource(R.string.downloads)) },
supportingContent = { Text(stringResource(R.string.downloads_description)) }
)
ListItem(
modifier = Modifier.clickable { navController.navigate(SettingsDestination.ImportExport) },
leadingContent = { Icon(Icons.Outlined.ImportExport, null) },
headlineContent = { Text(stringResource(R.string.import_export)) },
supportingContent = { Text(stringResource(R.string.import_export_description)) }
)
ListItem(
modifier = Modifier.clickable { navController.navigate(SettingsDestination.About) },
leadingContent = { Icon(Icons.Outlined.Info, null) },
headlineContent = { Text(stringResource(R.string.about)) },
supportingContent = { Text(stringResource(R.string.about_description)) }
)
}
}
}
}
}
}

View File

@ -0,0 +1,93 @@
package app.revanced.manager.compose.ui.screen.settings
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import app.revanced.manager.compose.R
import app.revanced.manager.compose.ui.component.AppTopBar
import com.google.accompanist.drawablepainter.rememberDrawablePainter
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AboutSettingsScreen(
onBackClick: () -> Unit
) {
val context = LocalContext.current
val icon = rememberDrawablePainter(context.packageManager.getApplicationIcon(context.packageName))
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.about),
onBackClick = onBackClick
)
}
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.verticalScroll(rememberScrollState())
) {
Column(
modifier = Modifier.fillMaxWidth().padding(vertical = 15.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Image(painter = icon, contentDescription = null)
Text(stringResource(R.string.app_name), style = MaterialTheme.typography.titleLarge)
Text("Version 1.0.0 (100000000)", style = MaterialTheme.typography.labelMedium)
Row(
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
FilledTonalButton(onClick = { /*TODO*/ }) {
Text("Website")
}
FilledTonalButton(onClick = { /*TODO*/ }) {
Text("Donate")
}
}
Row(
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
OutlinedButton(onClick = { /*TODO*/ }) {
Text("GitHub")
}
OutlinedButton(onClick = { /*TODO*/ }) {
Text("Contact")
}
OutlinedButton(onClick = { /*TODO*/ }) {
Text("License")
}
}
}
ListItem(
modifier = Modifier.clickable { },
headlineContent = { Text(stringResource(R.string.contributors)) },
supportingContent = { Text(stringResource(R.string.contributors_description)) }
)
}
}
}

View File

@ -0,0 +1,35 @@
package app.revanced.manager.compose.ui.screen.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import app.revanced.manager.compose.R
import app.revanced.manager.compose.ui.component.AppTopBar
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DownloadsSettingsScreen(
onBackClick: () -> Unit
) {
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.downloads),
onBackClick = onBackClick
)
}
) { paddingValues ->
Column(
modifier = Modifier.fillMaxSize().padding(paddingValues).verticalScroll(rememberScrollState())
) {
}
}
}

View File

@ -0,0 +1,120 @@
package app.revanced.manager.compose.ui.screen.settings
import android.os.Build
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import app.revanced.manager.compose.R
import app.revanced.manager.compose.domain.manager.PreferencesManager
import app.revanced.manager.compose.ui.component.AppTopBar
import app.revanced.manager.compose.ui.component.GroupHeader
import app.revanced.manager.compose.ui.theme.Theme
import app.revanced.manager.compose.ui.viewmodel.SettingsViewModel
import org.koin.compose.koinInject
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GeneralSettingsScreen(
onBackClick: () -> Unit,
viewModel: SettingsViewModel
) {
val prefs = viewModel.prefs
var showThemePicker by rememberSaveable { mutableStateOf(false) }
if (showThemePicker) {
ThemePicker(
onDismiss = { showThemePicker = false },
onConfirm = { viewModel.setTheme(it) }
)
}
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.general),
onBackClick = onBackClick
)
}
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.verticalScroll(rememberScrollState())
) {
GroupHeader(stringResource(R.string.appearance))
ListItem(
modifier = Modifier.clickable { showThemePicker = true },
headlineContent = { Text(stringResource(R.string.theme)) },
supportingContent = { Text(stringResource(R.string.theme_description)) },
trailingContent = { Button({ showThemePicker = true }) { Text(stringResource(prefs.theme.displayName)) } }
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ListItem(
modifier = Modifier.clickable { prefs.dynamicColor = !prefs.dynamicColor },
headlineContent = { Text(stringResource(R.string.dynamic_color)) },
supportingContent = { Text(stringResource(R.string.dynamic_color_description)) },
trailingContent = { Switch(checked = prefs.dynamicColor, onCheckedChange = { prefs.dynamicColor = it }) }
)
}
}
}
}
@Composable
fun ThemePicker(
onDismiss: () -> Unit,
onConfirm: (Theme) -> Unit,
prefs: PreferencesManager = koinInject()
) {
var selectedTheme by rememberSaveable { mutableStateOf(prefs.theme) }
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.theme)) },
text = {
Column {
Theme.values().forEach {
Row(
modifier = Modifier.fillMaxWidth().clickable { selectedTheme = it },
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = selectedTheme == it, onClick = { selectedTheme = it })
Text(stringResource(it.displayName))
}
}
}
},
confirmButton = {
Button(onClick = {
onConfirm(selectedTheme)
onDismiss()
}) {
Text(stringResource(R.string.apply))
}
}
)
}

View File

@ -0,0 +1,52 @@
package app.revanced.manager.compose.ui.screen.settings
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import app.revanced.manager.compose.R
import app.revanced.manager.compose.ui.component.AppTopBar
import app.revanced.manager.compose.ui.component.GroupHeader
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ImportExportSettingsScreen(
onBackClick: () -> Unit
) {
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.import_export),
onBackClick = onBackClick
)
}
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.verticalScroll(rememberScrollState())
) {
GroupHeader(stringResource(R.string.signing))
ListItem(
modifier = Modifier.clickable { },
headlineContent = { Text(stringResource(R.string.import_keystore)) },
supportingContent = { Text(stringResource(R.string.import_keystore_descripion)) }
)
ListItem(
modifier = Modifier.clickable { },
headlineContent = { Text(stringResource(R.string.export_keystore)) },
supportingContent = { Text(stringResource(R.string.export_keystore_description)) }
)
}
}
}

View File

@ -0,0 +1,35 @@
package app.revanced.manager.compose.ui.screen.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import app.revanced.manager.compose.R
import app.revanced.manager.compose.ui.component.AppTopBar
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UpdatesSettingsScreen(
onBackClick: () -> Unit
) {
Scaffold(
topBar = {
AppTopBar(
title = stringResource(R.string.updates),
onBackClick = onBackClick
)
}
) { paddingValues ->
Column(
modifier = Modifier.fillMaxSize().padding(paddingValues).verticalScroll(rememberScrollState())
) {
}
}
}

View File

@ -9,7 +9,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.core.view.WindowCompat
import app.revanced.manager.compose.R
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
@ -60,8 +62,8 @@ fun ReVancedManagerTheme(
)
}
enum class Theme(val displayName: String) {
SYSTEM("System"),
LIGHT("Light"),
DARK("Dark");
enum class Theme(val displayName: Int) {
SYSTEM(R.string.system),
LIGHT(R.string.light),
DARK(R.string.dark);
}

View File

@ -0,0 +1,15 @@
package app.revanced.manager.compose.ui.viewmodel
import androidx.lifecycle.ViewModel
import app.revanced.manager.compose.domain.manager.PreferencesManager
import app.revanced.manager.compose.ui.theme.Theme
class SettingsViewModel(
val prefs: PreferencesManager
): ViewModel() {
fun setTheme(theme: Theme) {
prefs.theme = theme
}
}

View File

@ -8,21 +8,55 @@
<string name="dashboard">Dashboard</string>
<string name="settings">Settings</string>
<string name="about">About</string>
<string name="select_app">Select an app</string>
<string name="select_patches">Select patches</string>
<string name="general">General</string>
<string name="general_description">General settings</string>
<string name="updates">Updates</string>
<string name="updates_description">Updates for ReVanced Manager</string>
<string name="downloads">Downloads</string>
<string name="downloads_description">Manage downloaded content</string>
<string name="import_export">Import &amp; export</string>
<string name="import_export_description">Import and export settings</string>
<string name="about">About</string>
<string name="about_description">About ReVanced</string>
<string name="contributors">Contributors</string>
<string name="contributors_description">View the contributors of ReVanced</string>
<string name="dynamic_color">Dynamic color</string>
<string name="dynamic_color_description">Adapt colors to the wallpaper</string>
<string name="theme">Theme</string>
<string name="theme_description">Choose between light or dark theme</string>
<string name="import_keystore">Import keystore</string>
<string name="import_keystore_descripion">Import a custom keystore</string>
<string name="export_keystore">Export keystore</string>
<string name="export_keystore_description">Export the current keystore</string>
<string name="search_apps">Search apps…</string>
<string name="loading_body">Loading…</string>
<string name="downloading_patches">Downloading patch bundle…</string>
<string name="downloading_integrations">Downloading Integrations…</string>
<string name="contributors">Contributors</string>
<string name="options">Options</string>
<string name="ok">OK</string>
<string name="patch">Patch</string>
<string name="select_from_storage">Select from storage</string>
<string name="search">Search</string>
<string name="apply">Apply</string>
<string name="help">Help</string>
<string name="back">Back</string>
<string name="add">Add</string>
<string name="system">System</string>
<string name="light">Light</string>
<string name="dark">Dark</string>
<string name="appearance">Appearance</string>
<string name="dynamic_color">Dynamic color</string>
<string name="theme">Theme</string>
<string name="signing">Signing</string>
<string name="storage">Storage</string>
<string name="tab_apps">Apps</string>
<string name="tab_sources">Sources</string>
<string name="no_sources_set">No sources set</string>
<string name="no_patched_apps_found">No patched apps found</string>
<string name="unsupported_app">Unsupported app</string>
<string name="app_not_supported">Some of the patches do not support this app version (%s). The patches only support the following versions: %s.</string>
</resources>