compose: migrate preferences screens
Settings, UI, Installation, Network and Updates preference panes are now native Compose. Replaces the old PreferenceFragment XML hierarchy with explicit ListItem / Switch / dialog rows.
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.preferences
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
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.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(onNavigateTo: (Destination) -> Unit) {
|
||||
ScreenContent(onNavigateTo = onNavigateTo)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(onNavigateTo: (Destination) -> Unit = {}) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(R.string.title_settings)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
onNavigateTo(
|
||||
Destination.PermissionRationale(PermissionType.entries.toSet())
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_list_check),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.onboarding_title_permissions))
|
||||
}
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
onNavigateTo(Destination.InstallationPreference)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_installation),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.title_installation)) }
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { onNavigateTo(Destination.UIPreference) },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_ui),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.pref_ui_title)) }
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { onNavigateTo(Destination.NetworkPreference) },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_network),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.pref_network_title)) }
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { onNavigateTo(Destination.UpdatesPreference) },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_updates),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.title_updates)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SettingsScreenPreview() {
|
||||
ScreenContent()
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.preferences
|
||||
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.ListItem
|
||||
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.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLocale
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.core.net.toUri
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.extensions.setAppTheme
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.compose.ui.preferences.network.SingleChoiceDialog
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_THEME_STYLE
|
||||
import com.aurora.store.util.save
|
||||
|
||||
@Composable
|
||||
fun UIPreferenceScreen() {
|
||||
ScreenContent()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent() {
|
||||
val context = LocalContext.current
|
||||
|
||||
val themeEntries = stringArrayResource(R.array.pref_theme_style)
|
||||
var themeStyle by remember {
|
||||
mutableIntStateOf(Preferences.getInteger(context, PREFERENCE_THEME_STYLE))
|
||||
}
|
||||
val tabEntries = stringArrayResource(R.array.pref_default_tab)
|
||||
var selectedTab by remember {
|
||||
mutableIntStateOf(Preferences.getInteger(context, PREFERENCE_DEFAULT_SELECTED_TAB))
|
||||
}
|
||||
var forYou by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_FOR_YOU, true))
|
||||
}
|
||||
var showThemeDialog by remember { mutableStateOf(false) }
|
||||
var showTabDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showThemeDialog) {
|
||||
SingleChoiceDialog(
|
||||
title = stringResource(R.string.pref_ui_theme),
|
||||
options = themeEntries.toList(),
|
||||
selected = themeStyle,
|
||||
onSelect = { index ->
|
||||
themeStyle = index
|
||||
context.save(PREFERENCE_THEME_STYLE, index)
|
||||
setAppTheme(index)
|
||||
showThemeDialog = false
|
||||
},
|
||||
onDismiss = { showThemeDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
if (showTabDialog) {
|
||||
SingleChoiceDialog(
|
||||
title = stringResource(R.string.pref_ui_layout_tab),
|
||||
options = tabEntries.toList(),
|
||||
selected = selectedTab,
|
||||
onSelect = { index ->
|
||||
selectedTab = index
|
||||
context.save(PREFERENCE_DEFAULT_SELECTED_TAB, index)
|
||||
showTabDialog = false
|
||||
},
|
||||
onDismiss = { showTabDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(R.string.pref_ui_title)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
if (isTAndAbove) {
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
context.startActivity(
|
||||
Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply {
|
||||
data = ("package:" + context.packageName).toUri()
|
||||
}
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.app_language)) },
|
||||
supportingContent = { Text(LocalLocale.current.platformLocale.displayName) }
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.pref_ui_theme)) })
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showThemeDialog = true },
|
||||
headlineContent = { Text(stringResource(R.string.pref_ui_theme)) },
|
||||
supportingContent = { Text(themeEntries.getOrElse(themeStyle) { "" }) }
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.pref_ui_layout)) })
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showTabDialog = true },
|
||||
headlineContent = { Text(stringResource(R.string.pref_ui_layout_tab)) },
|
||||
supportingContent = { Text(tabEntries.getOrElse(selectedTab) { "" }) }
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.pref_common_extra)) })
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
forYou = !forYou
|
||||
context.save(PREFERENCE_FOR_YOU, forYou)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.pref_ui_no_for_you)) },
|
||||
supportingContent = { Text(stringResource(R.string.pref_ui_no_for_you_desc)) },
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = forYou,
|
||||
onCheckedChange = { checked ->
|
||||
forYou = checked
|
||||
context.save(PREFERENCE_FOR_YOU, checked)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UIPreferenceScreenPreview() {
|
||||
ScreenContent()
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.preferences.installation
|
||||
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.core.content.getSystemService
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
|
||||
import com.aurora.store.util.save
|
||||
|
||||
@Composable
|
||||
fun InstallationPreferenceScreen(onNavigateTo: (Destination) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val devicePolicyManager = context.getSystemService<DevicePolicyManager>()
|
||||
val isDeviceOwner = devicePolicyManager?.isDeviceOwnerApp(context.packageName) ?: false
|
||||
|
||||
ScreenContent(
|
||||
onNavigateTo = onNavigateTo,
|
||||
isDeviceOwner = isDeviceOwner,
|
||||
onClearDeviceOwner = {
|
||||
@Suppress("DEPRECATION")
|
||||
devicePolicyManager?.clearDeviceOwnerApp(context.packageName)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(
|
||||
onNavigateTo: (Destination) -> Unit = {},
|
||||
isDeviceOwner: Boolean = false,
|
||||
onClearDeviceOwner: () -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var autoDelete by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_AUTO_DELETE, true))
|
||||
}
|
||||
var showClearOwnerDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showClearOwnerDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showClearOwnerDialog = false },
|
||||
title = { Text(stringResource(R.string.pref_clear_device_owner_title)) },
|
||||
text = { Text(stringResource(R.string.pref_clear_device_owner_desc)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showClearOwnerDialog = false
|
||||
onClearDeviceOwner()
|
||||
}) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showClearOwnerDialog = false }) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(R.string.title_installation)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { onNavigateTo(Destination.Installer) },
|
||||
headlineContent = { Text(stringResource(R.string.pref_install_mode_title)) },
|
||||
supportingContent = { Text(stringResource(R.string.pref_install_mode_summary)) }
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.pref_common_extra)) }
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
autoDelete = !autoDelete
|
||||
context.save(PREFERENCE_AUTO_DELETE, autoDelete)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.pref_install_delete_title)) },
|
||||
supportingContent = {
|
||||
Text(stringResource(R.string.pref_install_delete_summary))
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = autoDelete,
|
||||
onCheckedChange = { checked ->
|
||||
autoDelete = checked
|
||||
context.save(PREFERENCE_AUTO_DELETE, checked)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (isDeviceOwner) {
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showClearOwnerDialog = true },
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.pref_clear_device_owner_title))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(stringResource(R.string.pref_clear_device_owner_summary))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun InstallationPreferenceScreenPreview() {
|
||||
ScreenContent()
|
||||
}
|
||||
@@ -35,7 +35,7 @@ import com.aurora.store.data.model.InstallerInfo
|
||||
import com.aurora.store.viewmodel.preferences.InstallerViewModel
|
||||
|
||||
@Composable
|
||||
fun InstallerScreen(onNavigateUp: () -> Unit, viewModel: InstallerViewModel = hiltViewModel()) {
|
||||
fun InstallerScreen(viewModel: InstallerViewModel = hiltViewModel()) {
|
||||
val currentInstallerId by viewModel.currentInstaller.collectAsStateWithLifecycle()
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
|
||||
@@ -46,7 +46,6 @@ fun InstallerScreen(onNavigateUp: () -> Unit, viewModel: InstallerViewModel = hi
|
||||
}
|
||||
|
||||
ScreenContent(
|
||||
onNavigateUp = onNavigateUp,
|
||||
snackBarHostState = snackBarHostState,
|
||||
currentInstaller = Installer.entries[currentInstallerId],
|
||||
availableInstallers = AppInstaller.getAvailableInstallersInfo(LocalContext.current),
|
||||
@@ -56,7 +55,6 @@ fun InstallerScreen(onNavigateUp: () -> Unit, viewModel: InstallerViewModel = hi
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(
|
||||
onNavigateUp: () -> Unit = {},
|
||||
snackBarHostState: SnackbarHostState = SnackbarHostState(),
|
||||
currentInstaller: Installer = Installer.SESSION,
|
||||
availableInstallers: List<InstallerInfo> = emptyList(),
|
||||
@@ -70,8 +68,7 @@ private fun ScreenContent(
|
||||
},
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(R.string.pref_install_mode_title),
|
||||
onNavigateUp = onNavigateUp
|
||||
title = stringResource(R.string.pref_install_mode_title)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.preferences.network
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.compose.ui.commons.ForceRestartDialog
|
||||
import com.aurora.store.util.CommonUtil
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_MICROG_AUTH
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_INFO
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_URL
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
|
||||
import com.aurora.store.util.remove
|
||||
import com.aurora.store.util.save
|
||||
import com.aurora.store.viewmodel.preferences.ProxyURLViewModel
|
||||
import com.jakewharton.processphoenix.ProcessPhoenix
|
||||
|
||||
@Composable
|
||||
fun NetworkPreferenceScreen(
|
||||
onNavigateTo: (Destination) -> Unit,
|
||||
viewModel: ProxyURLViewModel = hiltViewModel()
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val hasMicroG = PackageUtil.hasSupportedMicroGVariant(context)
|
||||
|
||||
ScreenContent(
|
||||
onNavigateTo = onNavigateTo,
|
||||
hasMicroG = hasMicroG,
|
||||
onSaveProxyUrl = { url ->
|
||||
val proxyInfo = CommonUtil.parseProxyUrl(url)
|
||||
if (proxyInfo != null) {
|
||||
context.save(PREFERENCE_PROXY_URL, url)
|
||||
context.save(PREFERENCE_PROXY_INFO, viewModel.json.encodeToString(proxyInfo))
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
onDeleteProxyUrl = {
|
||||
Preferences.remove(context, PREFERENCE_PROXY_URL)
|
||||
Preferences.remove(context, PREFERENCE_PROXY_INFO)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(
|
||||
onNavigateTo: (Destination) -> Unit = {},
|
||||
hasMicroG: Boolean = false,
|
||||
onSaveProxyUrl: (String) -> Boolean = { false },
|
||||
onDeleteProxyUrl: () -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val vendingEntries = stringArrayResource(R.array.pref_vending_version)
|
||||
var vendingVersion by remember {
|
||||
mutableIntStateOf(Preferences.getInteger(context, PREFERENCE_VENDING_VERSION))
|
||||
}
|
||||
var microGAuth by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_MICROG_AUTH, true))
|
||||
}
|
||||
var showProxyDialog by remember { mutableStateOf(false) }
|
||||
var showVendingDialog by remember { mutableStateOf(false) }
|
||||
var showForceRestartDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showForceRestartDialog) {
|
||||
ForceRestartDialog(
|
||||
onConfirm = { ProcessPhoenix.triggerRebirth(context) },
|
||||
onDismiss = { showForceRestartDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
if (showProxyDialog) {
|
||||
ProxyURLDialog(
|
||||
currentUrl = Preferences.getString(context, PREFERENCE_PROXY_URL),
|
||||
onSave = { url ->
|
||||
showProxyDialog = false
|
||||
if (onSaveProxyUrl(url)) {
|
||||
showForceRestartDialog = true
|
||||
}
|
||||
},
|
||||
onDelete = {
|
||||
showProxyDialog = false
|
||||
onDeleteProxyUrl()
|
||||
showForceRestartDialog = true
|
||||
},
|
||||
onDismiss = { showProxyDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
if (showVendingDialog) {
|
||||
SingleChoiceDialog(
|
||||
title = stringResource(R.string.pref_vending_version_title),
|
||||
options = vendingEntries.toList(),
|
||||
selected = vendingVersion,
|
||||
onSelect = { index ->
|
||||
vendingVersion = index
|
||||
context.save(PREFERENCE_VENDING_VERSION, index)
|
||||
showVendingDialog = false
|
||||
},
|
||||
onDismiss = { showVendingDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(R.string.pref_network_title)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { onNavigateTo(Destination.Dispenser) },
|
||||
headlineContent = { Text(stringResource(R.string.pref_dispenser_title)) },
|
||||
supportingContent = { Text(stringResource(R.string.pref_dispenser_summary)) }
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showProxyDialog = true },
|
||||
headlineContent = { Text(stringResource(R.string.pref_network_proxy_url)) },
|
||||
supportingContent = { Text(stringResource(R.string.pref_network_proxy_desc)) }
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.pref_common_extra)) })
|
||||
}
|
||||
if (hasMicroG) {
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
microGAuth = !microGAuth
|
||||
context.save(PREFERENCE_MICROG_AUTH, microGAuth)
|
||||
},
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.pref_network_microg_login_title))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(stringResource(R.string.pref_network_microg_login_desc))
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = microGAuth,
|
||||
onCheckedChange = { checked ->
|
||||
microGAuth = checked
|
||||
context.save(PREFERENCE_MICROG_AUTH, checked)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showVendingDialog = true },
|
||||
headlineContent = { Text(stringResource(R.string.pref_vending_version_title)) },
|
||||
supportingContent = { Text(vendingEntries.getOrElse(vendingVersion) { "" }) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProxyURLDialog(
|
||||
currentUrl: String,
|
||||
onSave: (String) -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
var url by remember { mutableStateOf(currentUrl) }
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.pref_network_proxy_url)) },
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.pref_network_proxy_url_message),
|
||||
modifier = Modifier.padding(bottom = dimensionResource(R.dimen.padding_small))
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = url,
|
||||
onValueChange = { url = it },
|
||||
label = { Text(stringResource(R.string.pref_network_proxy_url_hint)) },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
enabled = url.isNotBlank(),
|
||||
onClick = { onSave(url.trim()) }
|
||||
) {
|
||||
Text(stringResource(R.string.set))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Row {
|
||||
if (currentUrl.isNotBlank()) {
|
||||
TextButton(onClick = onDelete) {
|
||||
Text(stringResource(R.string.disable))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small)))
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun SingleChoiceDialog(
|
||||
title: String,
|
||||
options: List<String>,
|
||||
selected: Int,
|
||||
onSelect: (Int) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(title) },
|
||||
text = {
|
||||
Column(modifier = Modifier.selectableGroup()) {
|
||||
options.forEachIndexed { index, option ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = index == selected,
|
||||
onClick = { onSelect(index) },
|
||||
role = Role.RadioButton
|
||||
)
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_small)),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = index == selected,
|
||||
onClick = null
|
||||
)
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small)))
|
||||
Text(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun NetworkPreferenceScreenPreview() {
|
||||
ScreenContent(hasMicroG = true)
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.preferences.updates
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.aurora.extensions.isIgnoringBatteryOptimizations
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.compose.ui.preferences.network.SingleChoiceDialog
|
||||
import com.aurora.store.data.model.UpdateMode
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_BATTERY
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_IDLE
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_METERED
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
|
||||
import com.aurora.store.util.save
|
||||
import com.aurora.store.viewmodel.all.UpdatesViewModel
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun UpdatesPreferenceScreen(viewModel: UpdatesViewModel = hiltViewModel()) {
|
||||
ScreenContent(
|
||||
onCancelAutomatedCheck = { viewModel.updateHelper.cancelAutomatedCheck() },
|
||||
onScheduleAutomatedCheck = { viewModel.updateHelper.scheduleAutomatedCheck() },
|
||||
onUpdateAutomatedCheck = { viewModel.updateHelper.updateAutomatedCheck() },
|
||||
onCheckUpdatesNow = { viewModel.updateHelper.checkUpdatesNow() }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(
|
||||
onCancelAutomatedCheck: () -> Unit = {},
|
||||
onScheduleAutomatedCheck: () -> Unit = {},
|
||||
onUpdateAutomatedCheck: () -> Unit = {},
|
||||
onCheckUpdatesNow: () -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val autoEntries = stringArrayResource(R.array.pref_updates_auto)
|
||||
|
||||
var autoMode by remember {
|
||||
mutableIntStateOf(Preferences.getInteger(context, PREFERENCE_UPDATES_AUTO, 2))
|
||||
}
|
||||
val autoEnabled = autoMode != UpdateMode.DISABLED.ordinal
|
||||
|
||||
var checkInterval by remember {
|
||||
mutableFloatStateOf(
|
||||
Preferences.getInteger(context, PREFERENCE_UPDATES_CHECK_INTERVAL, 3).toFloat()
|
||||
)
|
||||
}
|
||||
var filterFDroid by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_FILTER_FDROID, true))
|
||||
}
|
||||
var filterAuroraOnly by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_FILTER_AURORA_ONLY))
|
||||
}
|
||||
var updatesExtended by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCE_UPDATES_EXTENDED))
|
||||
}
|
||||
var showAutoDialog by remember { mutableStateOf(false) }
|
||||
var showRestrictionsDialog by remember { mutableStateOf(false) }
|
||||
|
||||
val notifPermissionLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { granted ->
|
||||
if (granted) {
|
||||
context.save(PREFERENCE_UPDATES_AUTO, UpdateMode.CHECK_AND_NOTIFY.ordinal)
|
||||
autoMode = UpdateMode.CHECK_AND_NOTIFY.ordinal
|
||||
onScheduleAutomatedCheck()
|
||||
} else {
|
||||
autoMode = Preferences.getInteger(context, PREFERENCE_UPDATES_AUTO, 2)
|
||||
}
|
||||
}
|
||||
|
||||
val batteryOptLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
if (context.isIgnoringBatteryOptimizations()) {
|
||||
context.save(PREFERENCE_UPDATES_AUTO, UpdateMode.CHECK_AND_INSTALL.ordinal)
|
||||
autoMode = UpdateMode.CHECK_AND_INSTALL.ordinal
|
||||
onScheduleAutomatedCheck()
|
||||
} else {
|
||||
autoMode = Preferences.getInteger(context, PREFERENCE_UPDATES_AUTO, 2)
|
||||
}
|
||||
}
|
||||
|
||||
if (showAutoDialog) {
|
||||
SingleChoiceDialog(
|
||||
title = stringResource(R.string.pref_updates_auto),
|
||||
options = autoEntries.toList(),
|
||||
selected = autoMode,
|
||||
onSelect = { index ->
|
||||
showAutoDialog = false
|
||||
when (UpdateMode.entries[index]) {
|
||||
UpdateMode.DISABLED -> {
|
||||
context.save(PREFERENCE_UPDATES_AUTO, 0)
|
||||
autoMode = 0
|
||||
onCancelAutomatedCheck()
|
||||
}
|
||||
UpdateMode.CHECK_AND_NOTIFY -> {
|
||||
if (!isTAndAbove ||
|
||||
context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) ==
|
||||
android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
context.save(PREFERENCE_UPDATES_AUTO, 1)
|
||||
autoMode = 1
|
||||
onScheduleAutomatedCheck()
|
||||
} else {
|
||||
notifPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
UpdateMode.CHECK_AND_INSTALL -> {
|
||||
if (context.isIgnoringBatteryOptimizations()) {
|
||||
context.save(PREFERENCE_UPDATES_AUTO, 2)
|
||||
autoMode = 2
|
||||
onScheduleAutomatedCheck()
|
||||
} else {
|
||||
batteryOptLauncher.launch(
|
||||
Intent(
|
||||
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||
"package:${context.packageName}".toUri()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
},
|
||||
onDismiss = { showAutoDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
if (showRestrictionsDialog) {
|
||||
UpdatesRestrictionsDialog(
|
||||
onUpdateAutomatedCheck = onUpdateAutomatedCheck,
|
||||
onDismiss = { showRestrictionsDialog = false }
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(R.string.title_updates)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showAutoDialog = true },
|
||||
headlineContent = { Text(stringResource(R.string.pref_updates_auto)) },
|
||||
supportingContent = { Text(autoEntries.getOrElse(autoMode) { "" }) }
|
||||
)
|
||||
}
|
||||
if (autoEnabled) {
|
||||
item {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.pref_updates_check_frequency))
|
||||
},
|
||||
supportingContent = {
|
||||
Column {
|
||||
Text(stringResource(R.string.pref_updates_check_frequency_desc))
|
||||
Spacer(Modifier.height(dimensionResource(R.dimen.margin_small)))
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Slider(
|
||||
value = checkInterval,
|
||||
onValueChange = { checkInterval = it },
|
||||
onValueChangeFinished = {
|
||||
context.save(
|
||||
PREFERENCE_UPDATES_CHECK_INTERVAL,
|
||||
checkInterval.roundToInt()
|
||||
)
|
||||
onUpdateAutomatedCheck()
|
||||
},
|
||||
valueRange = 1f..24f,
|
||||
steps = 22,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small)))
|
||||
Text(checkInterval.roundToInt().toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showRestrictionsDialog = true },
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.pref_updates_restrictions_title))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(stringResource(R.string.pref_updates_restrictions_desc))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(headlineContent = {
|
||||
Text(stringResource(R.string.pref_updates_app_source))
|
||||
})
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
filterFDroid = !filterFDroid
|
||||
context.save(PREFERENCE_FILTER_FDROID, filterFDroid)
|
||||
onCheckUpdatesNow()
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.pref_filter_fdroid_title)) },
|
||||
supportingContent = {
|
||||
Text(stringResource(R.string.pref_filter_fdroid_summary))
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = filterFDroid,
|
||||
onCheckedChange = { checked ->
|
||||
filterFDroid = checked
|
||||
context.save(PREFERENCE_FILTER_FDROID, checked)
|
||||
onCheckUpdatesNow()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
filterAuroraOnly = !filterAuroraOnly
|
||||
context.save(PREFERENCE_FILTER_AURORA_ONLY, filterAuroraOnly)
|
||||
if (filterAuroraOnly) filterFDroid = false
|
||||
onCheckUpdatesNow()
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.pref_aurora_only)) },
|
||||
supportingContent = { Text(stringResource(R.string.pref_aurora_only_desc)) },
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = filterAuroraOnly,
|
||||
onCheckedChange = { checked ->
|
||||
filterAuroraOnly = checked
|
||||
context.save(PREFERENCE_FILTER_AURORA_ONLY, checked)
|
||||
if (checked) filterFDroid = false
|
||||
onCheckUpdatesNow()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
item { HorizontalDivider() }
|
||||
item {
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.pref_common_advanced)) })
|
||||
}
|
||||
item {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
updatesExtended = !updatesExtended
|
||||
context.save(PREFERENCE_UPDATES_EXTENDED, updatesExtended)
|
||||
onCheckUpdatesNow()
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.pref_updates_incompatible)) },
|
||||
supportingContent = {
|
||||
Text(stringResource(R.string.pref_updates_incompatible_desc))
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = updatesExtended,
|
||||
onCheckedChange = { checked ->
|
||||
updatesExtended = checked
|
||||
context.save(PREFERENCE_UPDATES_EXTENDED, checked)
|
||||
onCheckUpdatesNow()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpdatesRestrictionsDialog(onUpdateAutomatedCheck: () -> Unit, onDismiss: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
var metered by remember {
|
||||
mutableStateOf(
|
||||
Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_METERED, true)
|
||||
)
|
||||
}
|
||||
var idle by remember {
|
||||
mutableStateOf(Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_IDLE, true))
|
||||
}
|
||||
var battery by remember {
|
||||
mutableStateOf(
|
||||
Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_BATTERY, true)
|
||||
)
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose { onUpdateAutomatedCheck() }
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.pref_updates_restrictions_title)) },
|
||||
text = {
|
||||
Column {
|
||||
Text(stringResource(R.string.pref_updates_restrictions_desc))
|
||||
Spacer(Modifier.height(dimensionResource(R.dimen.margin_small)))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
metered = !metered
|
||||
Preferences.putBoolean(
|
||||
context,
|
||||
PREFERENCES_UPDATES_RESTRICTIONS_METERED,
|
||||
metered
|
||||
)
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Checkbox(
|
||||
checked = metered,
|
||||
onCheckedChange = { checked ->
|
||||
metered = checked
|
||||
Preferences.putBoolean(
|
||||
context,
|
||||
PREFERENCES_UPDATES_RESTRICTIONS_METERED,
|
||||
checked
|
||||
)
|
||||
}
|
||||
)
|
||||
Text(stringResource(R.string.pref_updates_restrictions_metered))
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
idle = !idle
|
||||
Preferences.putBoolean(
|
||||
context,
|
||||
PREFERENCES_UPDATES_RESTRICTIONS_IDLE,
|
||||
idle
|
||||
)
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Checkbox(
|
||||
checked = idle,
|
||||
onCheckedChange = { checked ->
|
||||
idle = checked
|
||||
Preferences.putBoolean(
|
||||
context,
|
||||
PREFERENCES_UPDATES_RESTRICTIONS_IDLE,
|
||||
checked
|
||||
)
|
||||
}
|
||||
)
|
||||
Text(stringResource(R.string.pref_updates_restrictions_idle))
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
battery = !battery
|
||||
Preferences.putBoolean(
|
||||
context,
|
||||
PREFERENCES_UPDATES_RESTRICTIONS_BATTERY,
|
||||
battery
|
||||
)
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Checkbox(
|
||||
checked = battery,
|
||||
onCheckedChange = { checked ->
|
||||
battery = checked
|
||||
Preferences.putBoolean(
|
||||
context,
|
||||
PREFERENCES_UPDATES_RESTRICTIONS_BATTERY,
|
||||
checked
|
||||
)
|
||||
}
|
||||
)
|
||||
Text(stringResource(R.string.pref_updates_restrictions_battery))
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UpdatesPreferenceScreenPreview() {
|
||||
ScreenContent()
|
||||
}
|
||||
Reference in New Issue
Block a user