update settings

This commit is contained in:
X1nto 2022-02-27 12:59:11 +04:00
parent 2f9690ec04
commit 11ac0d0e75
9 changed files with 231 additions and 203 deletions

View File

@ -1,6 +1,7 @@
package com.vanced.manager.di
import android.app.Application
import android.content.SharedPreferences
import com.vanced.manager.core.downloader.impl.MicrogDownloader
import com.vanced.manager.core.downloader.impl.MusicDownloader
import com.vanced.manager.core.downloader.impl.VancedDownloader
@ -13,6 +14,7 @@ import com.vanced.manager.repository.MirrorRepository
import com.vanced.manager.ui.viewmodel.ConfigurationViewModel
import com.vanced.manager.ui.viewmodel.InstallViewModel
import com.vanced.manager.ui.viewmodel.MainViewModel
import com.vanced.manager.ui.viewmodel.SettingsViewModel
import org.koin.android.ext.koin.androidApplication
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
@ -22,8 +24,9 @@ val viewModelModule = module {
fun provideMainViewModel(
mainRepository: MainRepository,
mirrorRepository: MirrorRepository,
preferences: SharedPreferences,
app: Application,
) = MainViewModel(mainRepository, mirrorRepository, app)
) = MainViewModel(mainRepository, mirrorRepository, preferences, app)
fun provideInstallViewModel(
vancedDownloader: VancedDownloader,
@ -39,7 +42,12 @@ val viewModelModule = module {
return ConfigurationViewModel()
}
viewModel { provideMainViewModel(get(), get(), androidApplication()) }
fun provideSettingsViewModel(): SettingsViewModel {
return SettingsViewModel()
}
viewModel { provideMainViewModel(get(), get(), get(), androidApplication()) }
viewModel { provideInstallViewModel(get(), get(), get(), get(), get(), get()) }
viewModel { provideConfigurationViewModel() }
viewModel { provideSettingsViewModel() }
}

View File

@ -18,7 +18,6 @@ import com.vanced.manager.core.installer.service.AppInstallService
import com.vanced.manager.core.installer.service.AppUninstallService
import com.vanced.manager.ui.screen.*
import com.vanced.manager.ui.theme.ManagerTheme
import com.vanced.manager.ui.theme.isDark
import com.vanced.manager.ui.util.Screen
import com.vanced.manager.ui.util.animated
import com.vanced.manager.ui.viewmodel.InstallViewModel
@ -54,11 +53,10 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
mainViewModel.fetch()
setContent {
ManagerTheme {
val isDark = mainViewModel.appTheme.isDark()
ManagerTheme(darkMode = isDark) {
val surfaceColor = MaterialTheme.colorScheme.surface.animated
val isDark = isDark()
val systemUiController = rememberSystemUiController()
SideEffect {
@ -104,6 +102,9 @@ class MainActivity : ComponentActivity() {
SettingsScreen(
onToolbarBackButtonClick = {
backStack.pop()
},
onThemeChange = {
mainViewModel.appTheme = it
}
)
}

View File

@ -5,10 +5,7 @@ import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -61,3 +58,29 @@ inline fun LazyListScope.managerCategory(
}
content()
}
inline fun <K, V> LazyListScope.items(
items: Map<K, V>,
noinline key: ((key: K, value: V) -> Any)? = null,
crossinline itemContent: @Composable LazyItemScope.(key: K, value: V) -> Unit
) = items(
count = items.size,
key = if (key != null) { index ->
key(items.keys.elementAt(index), items.values.elementAt(index))
} else null
) { index ->
itemContent(items.keys.elementAt(index), items.values.elementAt(index))
}
inline fun <K, V> LazyListScope.itemsIndexed(
items: Map<K, V>,
noinline key: ((index: Int, key: K, value: V) -> Any)? = null,
crossinline itemContent: @Composable LazyItemScope.(index: Int, key: K, value: V) -> Unit
) = items(
count = items.size,
key = if (key != null) { index ->
key(index, items.keys.elementAt(index), items.values.elementAt(index))
} else null
) { index ->
itemContent(index, items.keys.elementAt(index), items.values.elementAt(index))
}

View File

@ -3,7 +3,6 @@ package com.vanced.manager.ui.component
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Checkbox
import androidx.compose.material.Switch
import androidx.compose.material3.MaterialTheme
@ -15,12 +14,17 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.vanced.manager.R
import com.vanced.manager.core.preferences.RadioButtonPreference
import com.vanced.manager.ui.resource.managerString
import com.vanced.manager.ui.theme.LargeShape
import com.vanced.manager.ui.theme.SmallShape
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
@JvmInline
value class EntryText(val text: String)
@JvmInline
value class EntryValue(val value: String)
@Composable
fun ManagerPreference(
preferenceTitle: String,
@ -74,21 +78,21 @@ fun ManagerSwitchPreference(
fun ManagerDialogPreference(
preferenceTitle: String,
preferenceDescription: String? = null,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
isDialogVisible: Boolean,
showDialog: Boolean,
onClick: () -> Unit,
onDismissRequest: () -> Unit,
confirmButton: @Composable () -> Unit,
dismissButton: @Composable () -> Unit = {},
trailing: @Composable () -> Unit = {},
content: @Composable () -> Unit
) {
ManagerPreference(
preferenceTitle = preferenceTitle,
preferenceDescription = preferenceDescription,
trailing = trailing,
onClick = onPreferenceClick
onClick = onClick
)
if (isDialogVisible) {
if (showDialog) {
ManagerDialog(
title = preferenceTitle,
onDismissRequest = onDismissRequest,
@ -103,13 +107,13 @@ fun ManagerDialogPreference(
fun ManagerSingleSelectDialogPreference(
preferenceTitle: String,
preferenceDescription: String,
isDialogVisible: Boolean,
currentSelectedKey: String,
buttons: List<RadioButtonPreference>,
showDialog: Boolean,
selected: EntryValue,
entries: Map<EntryText, EntryValue>,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
onClick: () -> Unit,
onDismissRequest: () -> Unit,
onItemClick: (itemKey: String) -> Unit,
onEntrySelect: (EntryValue) -> Unit,
onSave: () -> Unit,
) {
ManagerDialogPreference(
@ -127,20 +131,19 @@ fun ManagerSingleSelectDialogPreference(
}
},
onDismissRequest = onDismissRequest,
isDialogVisible = isDialogVisible,
onPreferenceClick = onPreferenceClick
showDialog = showDialog,
onClick = onClick
) {
LazyColumn(
modifier = Modifier.heightIn(max = 400.dp)
) {
items(buttons) { button ->
val (title, key) = button
items(entries) { entryText, entryValue ->
ListDialogRadiobuttonItem(
modifier = Modifier.fillMaxWidth(),
text = title,
selected = currentSelectedKey == key,
text = entryText.text,
selected = selected == entryValue,
onClick = {
onItemClick(key)
onEntrySelect(entryValue)
}
)
}
@ -152,13 +155,13 @@ fun ManagerSingleSelectDialogPreference(
fun ManagerMultiSelectDialogPreference(
preferenceTitle: String,
preferenceDescription: String,
isDialogVisible: Boolean,
currentSelectedKeys: List<String>,
buttons: List<RadioButtonPreference>,
showDialog: Boolean,
selected: List<EntryValue>,
entries: Map<EntryText, EntryValue>,
trailing: @Composable () -> Unit = {},
onPreferenceClick: () -> Unit,
onClick: () -> Unit,
onDismissRequest: () -> Unit,
onItemCheckChange: (isChecked: Boolean, itemKey: String) -> Unit,
onEntriesSelect: (List<EntryValue>) -> Unit,
onSave: () -> Unit,
) {
ManagerDialogPreference(
@ -166,29 +169,33 @@ fun ManagerMultiSelectDialogPreference(
preferenceDescription = preferenceDescription,
trailing = trailing,
confirmButton = {
androidx.compose.material.TextButton(onClick = onSave) {
TextButton(onClick = onSave) {
ManagerText(managerString(R.string.dialog_button_save))
}
},
dismissButton = {
androidx.compose.material.TextButton(onClick = onDismissRequest) {
TextButton(onClick = onDismissRequest) {
ManagerText(managerString(R.string.dialog_button_cancel))
}
},
onDismissRequest = onDismissRequest,
isDialogVisible = isDialogVisible,
onPreferenceClick = onPreferenceClick
showDialog = showDialog,
onClick = onClick
) {
LazyColumn(
modifier = Modifier.heightIn(max = 400.dp)
) {
items(buttons) { button ->
val (title, key) = button
items(entries) { entryText, entryValue ->
ListDialogCheckboxItem(
text = title,
checked = currentSelectedKeys.contains(key),
text = entryText.text,
checked = selected.contains(entryValue),
onCheckedChange = { isChecked ->
onItemCheckChange(isChecked, key)
val mutableSelected = selected.toMutableList()
when (isChecked) {
true -> mutableSelected.add(entryValue)
false -> mutableSelected.remove(entryValue)
}
onEntriesSelect(mutableSelected)
}
)
}

View File

@ -2,29 +2,32 @@ package com.vanced.manager.ui.screen
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBackIosNew
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.vanced.manager.R
import com.vanced.manager.core.util.notificationApps
import com.vanced.manager.core.preferences.holder.managerThemePref
import com.vanced.manager.core.preferences.holder.managerVariantPref
import com.vanced.manager.core.util.isMagiskInstalled
import com.vanced.manager.ui.component.*
import com.vanced.manager.ui.resource.managerString
import com.vanced.manager.ui.theme.ManagerTheme
import com.vanced.manager.ui.util.Screen
import com.vanced.manager.ui.widget.SettingsCustomTabsItem
import com.vanced.manager.ui.widget.SettingsManagerVariantItem
import com.vanced.manager.ui.widget.SettingsNotificationsItem
import com.vanced.manager.ui.widget.ThemeSettingsItem
import com.vanced.manager.ui.viewmodel.SettingsViewModel
import org.koin.androidx.compose.viewModel
@ExperimentalMaterial3Api
@Composable
fun SettingsScreen(
onToolbarBackButtonClick: () -> Unit,
onThemeChange: (ManagerTheme) -> Unit,
) {
val viewModel: SettingsViewModel by viewModel()
ManagerScaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
@ -52,20 +55,86 @@ fun SettingsScreen(
managerString(R.string.settings_category_behaviour)
}) {
item {
SettingsCustomTabsItem()
}
items(notificationApps) { notificationApp ->
SettingsNotificationsItem(notificationApp)
ManagerSwitchPreference(
preferenceTitle = stringResource(id = R.string.settings_preference_use_custom_tabs_title),
preferenceDescription = stringResource(id = R.string.settings_preference_use_custom_tabs_summary),
isChecked = viewModel.managerUseCustomTabs,
onCheckedChange = {
viewModel.managerUseCustomTabs = it
}
)
}
item {
SettingsManagerVariantItem()
var showDialog by remember { mutableStateOf(false) }
var selectedMode by remember { mutableStateOf(EntryValue(viewModel.managerMode)) }
ManagerSingleSelectDialogPreference(
preferenceTitle = managerString(
stringId = R.string.settings_preference_variant_title
),
preferenceDescription = selectedMode.value,
showDialog = showDialog,
selected = selectedMode,
entries = mapOf(
EntryText("nonroot") to EntryValue("nonroot"),
EntryText("root") to EntryValue("root"),
),
onClick = {
showDialog = true
},
onDismissRequest = {
showDialog = false
selectedMode = EntryValue(managerVariantPref)
},
onEntrySelect = {
if (it.value == "root" && !isMagiskInstalled)
return@ManagerSingleSelectDialogPreference
selectedMode = it
},
onSave = {
viewModel.managerMode = selectedMode.value
showDialog = false
}
)
}
}
managerCategory(categoryName = {
managerString(R.string.settings_category_appearance)
}) {
item {
ThemeSettingsItem()
var showDialog by remember { mutableStateOf(false) }
var selectedTheme by remember { mutableStateOf(EntryValue(managerThemePref)) }
ManagerSingleSelectDialogPreference(
preferenceTitle = managerString(stringId = R.string.settings_preference_theme_title),
preferenceDescription = managerString(
stringId = viewModel.getThemeStringIdByValue(selectedTheme.value)
),
showDialog = showDialog,
selected = selectedTheme,
entries = mapOf(
EntryText(managerString(viewModel.getThemeStringIdByValue(SettingsViewModel.THEME_LIGHT_VALUE))) to
EntryValue(SettingsViewModel.THEME_LIGHT_VALUE),
EntryText(managerString(viewModel.getThemeStringIdByValue(SettingsViewModel.THEME_DARK_VALUE))) to
EntryValue(SettingsViewModel.THEME_DARK_VALUE),
EntryText(managerString(viewModel.getThemeStringIdByValue(SettingsViewModel.THEME_SYSTEM_DEFAULT_VALUE))) to
EntryValue(SettingsViewModel.THEME_SYSTEM_DEFAULT_VALUE),
),
onClick = {
showDialog = true
},
onDismissRequest = {
showDialog = false
selectedTheme = EntryValue(viewModel.managerTheme)
},
onEntrySelect = {
selectedTheme = it
},
onSave = {
viewModel.managerTheme = selectedTheme.value
showDialog = false
onThemeChange(ManagerTheme.fromKey(selectedTheme.value))
}
)
}
}
}

View File

@ -8,7 +8,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import com.vanced.manager.core.preferences.holder.managerThemePref
import com.vanced.manager.ui.viewmodel.SettingsViewModel
const val defAccentColor = 0xFF0477E1
@ -67,12 +67,27 @@ private val DarkThemeColors = darkColorScheme(
inverseSurface = md_theme_dark_inverseSurface,
)
@Composable
fun isDark(): Boolean = when (managerThemePref) {
"Dark" -> true
"Light" -> false
"System Default" -> isSystemInDarkTheme()
else -> throw IllegalArgumentException("Unknown theme")
enum class ManagerTheme {
LIGHT,
DARK,
SYSTEM_DEFAULT;
@Composable
fun isDark() = when (this) {
LIGHT -> false
DARK -> true
SYSTEM_DEFAULT -> isSystemInDarkTheme()
}
companion object {
fun fromKey(key: String?): ManagerTheme {
return when (key) {
SettingsViewModel.THEME_DARK_VALUE -> DARK
SettingsViewModel.THEME_LIGHT_VALUE -> LIGHT
else -> SYSTEM_DEFAULT
}
}
}
}
@Composable
@ -89,11 +104,12 @@ inline fun apiDependantColorScheme(
@Composable
fun ManagerTheme(
darkMode: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val context = LocalContext.current
val colorScheme =
if (isDark()) {
if (darkMode) {
apiDependantColorScheme(
dynamic = { dynamicDarkColorScheme(context) },
static = { DarkThemeColors }

View File

@ -4,6 +4,7 @@ import android.app.Application
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Intent
import android.content.SharedPreferences
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -18,12 +19,14 @@ import com.vanced.manager.network.util.MUSIC_NAME
import com.vanced.manager.network.util.VANCED_NAME
import com.vanced.manager.repository.MainRepository
import com.vanced.manager.repository.MirrorRepository
import com.vanced.manager.ui.theme.ManagerTheme
import kotlinx.coroutines.launch
class MainViewModel(
private val mainRepository: MainRepository,
private val mirrorRepository: MirrorRepository,
private val app: Application
private val preferences: SharedPreferences,
private val app: Application,
) : AndroidViewModel(app) {
private val isRoot
@ -45,6 +48,15 @@ class MainViewModel(
var appState by mutableStateOf<AppState>(AppState.Fetching(appCount))
private set
var appTheme by mutableStateOf(
ManagerTheme.fromKey(
preferences.getString(
SettingsViewModel.MANAGER_THEME_KEY,
SettingsViewModel.THEME_SYSTEM_DEFAULT_VALUE
)
)
)
fun fetch() {
viewModelScope.launch {
appState = AppState.Fetching(appCount)

View File

@ -0,0 +1,30 @@
package com.vanced.manager.ui.viewmodel
import androidx.lifecycle.ViewModel
import com.vanced.manager.R
import com.vanced.manager.core.preferences.managerBooleanPreference
import com.vanced.manager.core.preferences.managerStringPreference
class SettingsViewModel : ViewModel() {
companion object {
const val MANAGER_THEME_KEY = "manager_theme"
const val MANAGER_MODE_KEY = "manager_mode"
const val THEME_DARK_VALUE = "dark"
const val THEME_LIGHT_VALUE = "light"
const val THEME_SYSTEM_DEFAULT_VALUE = "system_default"
}
var managerUseCustomTabs by managerBooleanPreference(key = "manager_use_custom_tabs")
var managerMode by managerStringPreference(key = MANAGER_MODE_KEY, defaultValue = "nonroot")
var managerTheme by managerStringPreference(MANAGER_THEME_KEY, THEME_SYSTEM_DEFAULT_VALUE)
fun getThemeStringIdByValue(value: String): Int {
return when (value) {
THEME_DARK_VALUE -> R.string.settings_preference_theme_dark
THEME_LIGHT_VALUE -> R.string.settings_preference_theme_light
else -> R.string.settings_option_system_default
}
}
}

View File

@ -1,138 +0,0 @@
package com.vanced.manager.ui.widget
import androidx.compose.runtime.*
import androidx.compose.ui.res.stringResource
import com.vanced.manager.R
import com.vanced.manager.core.preferences.RadioButtonPreference
import com.vanced.manager.core.preferences.holder.managerThemePref
import com.vanced.manager.core.preferences.holder.managerVariantPref
import com.vanced.manager.core.preferences.holder.useCustomTabsPref
import com.vanced.manager.core.preferences.managerBooleanPreference
import com.vanced.manager.core.util.isMagiskInstalled
import com.vanced.manager.domain.model.NotificationPrefModel
import com.vanced.manager.ui.component.ManagerPreference
import com.vanced.manager.ui.component.ManagerSingleSelectDialogPreference
import com.vanced.manager.ui.component.ManagerSwitchPreference
import com.vanced.manager.ui.resource.managerString
@Composable
fun SettingsClearFilesItem() {
ManagerPreference(
preferenceTitle = managerString(
stringId = R.string.settings_preference_clear_files_title
),
preferenceDescription = null,
onClick = {}
)
}
@Composable
fun SettingsCustomTabsItem() {
ManagerSwitchPreference(
preferenceTitle = stringResource(id = R.string.settings_preference_use_custom_tabs_title),
preferenceDescription = stringResource(id = R.string.settings_preference_use_custom_tabs_summary),
isChecked = useCustomTabsPref,
onCheckedChange = {
useCustomTabsPref = it
}
)
}
@Composable
fun SettingsManagerVariantItem() {
var showDialog by remember { mutableStateOf(false) }
var selectedKey by remember { mutableStateOf(managerVariantPref) }
ManagerSingleSelectDialogPreference(
preferenceTitle = managerString(
stringId = R.string.settings_preference_variant_title
),
preferenceDescription = managerVariantPref,
isDialogVisible = showDialog,
currentSelectedKey = selectedKey,
buttons = listOf(
RadioButtonPreference(
title = "nonroot",
key = "nonroot"
),
RadioButtonPreference(
title = "root",
key = "root"
),
),
onPreferenceClick = {
showDialog = true
},
onDismissRequest = {
showDialog = false
selectedKey = managerVariantPref
},
onItemClick = {
if (it == "root" && !isMagiskInstalled)
return@ManagerSingleSelectDialogPreference
selectedKey = it
},
onSave = {
managerVariantPref = selectedKey
showDialog = false
}
)
}
@Composable
fun SettingsNotificationsItem(notificationApp: NotificationPrefModel) {
with(notificationApp) {
var appNotificationsPref by managerBooleanPreference(
key = "${prefKey}_notifications",
defaultValue = true
)
ManagerSwitchPreference(
preferenceTitle = "$app Push Notifications",
preferenceDescription = "Receive push notifications when an update for $app is released",
isChecked = appNotificationsPref,
onCheckedChange = {
appNotificationsPref = it
}
)
}
}
@Composable
fun ThemeSettingsItem() {
var showDialog by remember { mutableStateOf(false) }
var selectedKey by remember { mutableStateOf(managerThemePref) }
ManagerSingleSelectDialogPreference(
preferenceTitle = managerString(stringId = R.string.settings_preference_theme_title),
preferenceDescription = managerThemePref,
isDialogVisible = showDialog,
currentSelectedKey = selectedKey,
buttons = listOf(
RadioButtonPreference(
title = managerString(R.string.settings_preference_theme_light),
key = "Light"
),
RadioButtonPreference(
title = managerString(R.string.settings_preference_theme_dark),
key = "Dark"
),
RadioButtonPreference(
title = managerString(R.string.settings_option_system_default),
key = "System Default"
)
),
onPreferenceClick = {
showDialog = true
},
onDismissRequest = {
showDialog = false
selectedKey = managerThemePref
},
onItemClick = {
selectedKey = it
},
onSave = {
managerThemePref = selectedKey
showDialog = false
}
)
}