Better approach for SettingsScreen. LazyColumn is now properly used and settings items are now easier to maintain.

Also added version name for SettingsScreen.
This commit is contained in:
Sad Ellie 2022-06-19 18:10:27 +03:00
parent c9ef384d14
commit dc02386d25
7 changed files with 171 additions and 119 deletions

View File

@ -20,17 +20,12 @@ package com.sadellie.unitto.screens.setttings
import android.os.Build
import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
@ -44,7 +39,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.sadellie.unitto.BuildConfig
import com.sadellie.unitto.R
import com.sadellie.unitto.data.ABOUT_SCREEN
@ -54,7 +48,9 @@ import com.sadellie.unitto.data.preferences.PRECISIONS
import com.sadellie.unitto.data.preferences.SEPARATORS
import com.sadellie.unitto.screens.MainViewModel
import com.sadellie.unitto.screens.openLink
import com.sadellie.unitto.screens.setttings.components.AlertDialogWithList
import com.sadellie.unitto.screens.setttings.components.SettingsHeader
import com.sadellie.unitto.screens.setttings.components.SettingsListItem
@Composable
fun SettingsScreen(
@ -64,9 +60,13 @@ fun SettingsScreen(
) {
val mContext = LocalContext.current
// Scrollable
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberSplineBasedDecay(), rememberTopAppBarScrollState())
var currentDialogState: Int by rememberSaveable { mutableStateOf(0) }
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
rememberSplineBasedDecay(),
rememberTopAppBarScrollState()
)
var dialogState: DialogState by rememberSaveable {
mutableStateOf(DialogState.NONE)
}
Scaffold(
modifier = Modifier
@ -89,144 +89,148 @@ fun SettingsScreen(
},
content = { padding ->
LazyColumn(contentPadding = padding) {
// GENERAL GROUP
item { SettingsHeader(stringResource(R.string.general_settings_group)) }
// PRECISION
item {
Column {
// Group header
Text(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 12.dp),
text = stringResource(id = R.string.general_settings_group),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary
)
SettingsListItem(
label = stringResource(R.string.precision_setting),
supportText = stringResource(R.string.precision_setting_support),
onClick = { currentDialogState = 1 }
)
SettingsListItem(
label = stringResource(R.string.separator_setting),
supportText = stringResource(R.string.separator_setting_support),
onClick = { currentDialogState = 2 }
)
SettingsListItem(
label = stringResource(R.string.output_format_setting),
supportText = stringResource(id = R.string.output_format_setting_support),
onClick = { currentDialogState = 3 }
)
SettingsListItem(
label = stringResource(R.string.theme_setting),
supportText = stringResource(R.string.theme_setting_support),
onClick = { currentDialogState = 4 }
)
SettingsListItem(
label = stringResource(id = R.string.currency_rates_note_setting),
onClick = { currentDialogState = 5 }
)
stringResource(R.string.precision_setting),
stringResource(R.string.precision_setting_support)
) { dialogState = DialogState.PRECISION }
}
// Group header
Text(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 12.dp),
text = stringResource(id = R.string.additional_settings_group),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary
)
// SEPARATOR
item {
SettingsListItem(
label = stringResource(R.string.terms_and_conditions),
onClick = {
openLink(
mContext,
"http://sadellie.github.io/unitto/terms-app.html"
)
stringResource(R.string.separator_setting),
stringResource(R.string.separator_setting_support)
) { dialogState = DialogState.SEPARATOR }
}
)
SettingsListItem(
label = stringResource(R.string.privacy_policy),
onClick = {
openLink(
mContext,
"http://sadellie.github.io/unitto/privacy-app.html"
)
}
)
SettingsListItem(
label = stringResource(id = R.string.send_usage_statistics),
supportText = stringResource(id = R.string.send_usage_statistics_support),
switchState = mainViewModel.enableAnalytics,
onSwitchChange = { mainViewModel.updateEnableAnalytics(!it) })
SettingsListItem(
label = stringResource(R.string.third_party_licenses),
onClick = { navControllerAction(ABOUT_SCREEN) }
)
BuildConfig.StoreLink.let {
// OUTPUT FORMAT
item {
SettingsListItem(
label = stringResource(R.string.rate_this_app),
onClick = {
openLink(
mContext,
it
)
stringResource(R.string.output_format_setting),
stringResource(R.string.output_format_setting_support)
) { dialogState = DialogState.OUTPUT_FORMAT }
}
)
// THEME
item {
SettingsListItem(
stringResource(R.string.theme_setting),
stringResource(R.string.theme_setting)
) { dialogState = DialogState.THEME }
}
// CURRENCY RATE NOTE
item {
SettingsListItem(
stringResource(R.string.currency_rates_note_setting)
) { dialogState = DialogState.CURRENCY_RATE }
}
// ADDITIONAL GROUP
item { SettingsHeader(stringResource(R.string.additional_settings_group)) }
// TERMS AND CONDITIONS
item {
SettingsListItem(
stringResource(R.string.terms_and_conditions)
) { openLink(mContext, "http://sadellie.github.io/unitto/terms-app.html") }
}
// PRIVACY POLICY
item {
SettingsListItem(
stringResource(R.string.privacy_policy)
) { openLink(mContext, "http://sadellie.github.io/unitto/privacy-app.html") }
}
// ANALYTICS
item {
SettingsListItem(
stringResource(R.string.send_usage_statistics),
stringResource(R.string.send_usage_statistics_support),
mainViewModel.enableAnalytics
) { mainViewModel.updateEnableAnalytics(!it) }
}
// THIRD PARTY
item {
SettingsListItem(
stringResource(R.string.third_party_licenses)
) { navControllerAction(ABOUT_SCREEN) }
}
// APP VERSION
item {
SettingsListItem(
label = stringResource(id = R.string.app_version_name_setting),
supportText = BuildConfig.VERSION_NAME
) {}
}
}
},
)
/**
* Function to reset currently displayed dialog.
*/
fun resetDialog() {
dialogState = DialogState.NONE
}
// Showing dialog
when (currentDialogState) {
1 -> {
when (dialogState) {
DialogState.PRECISION -> {
AlertDialogWithList(
title = stringResource(id = R.string.precision_setting),
listItems = PRECISIONS,
selectedItemIndex = mainViewModel.precision,
selectAction = { mainViewModel.updatePrecision(it) },
dismissAction = { currentDialogState = 0 },
dismissAction = { resetDialog() },
supportText = stringResource(id = R.string.precision_setting_info)
)
}
2 -> {
DialogState.SEPARATOR -> {
AlertDialogWithList(
title = stringResource(id = R.string.separator_setting),
listItems = SEPARATORS,
selectedItemIndex = mainViewModel.separator,
selectAction = { mainViewModel.updateSeparator(it) },
dismissAction = { currentDialogState = 0 }
dismissAction = { resetDialog() }
)
}
3 -> {
DialogState.OUTPUT_FORMAT -> {
AlertDialogWithList(
title = stringResource(id = R.string.output_format_setting),
listItems = OUTPUT_FORMAT,
selectedItemIndex = mainViewModel.outputFormat,
selectAction = { mainViewModel.updateOutputFormat(it) },
dismissAction = { currentDialogState = 0 },
dismissAction = { resetDialog() },
supportText = stringResource(id = R.string.output_format_setting_info)
)
}
4 -> {
DialogState.THEME -> {
AlertDialogWithList(
title = stringResource(id = R.string.theme_setting),
listItems = APP_THEMES,
selectedItemIndex = mainViewModel.currentTheme,
selectAction = { mainViewModel.updateCurrentAppTheme(it) },
dismissAction = { currentDialogState = 0 },
dismissAction = { resetDialog() },
// Show note for users with devices that support custom Dynamic theming
supportText = if (Build.VERSION.SDK_INT in (Build.VERSION_CODES.O_MR1..Build.VERSION_CODES.R)) stringResource(
id = R.string.theme_setting_info
) else null
)
}
5 -> {
DialogState.CURRENCY_RATE -> {
AlertDialogWithList(
title = stringResource(id = R.string.currency_rates_note_title),
dismissAction = { currentDialogState = 0 },
dismissAction = { resetDialog() },
supportText = stringResource(id = R.string.currency_rates_note_text),
dismissButtonLabel = stringResource(id = R.string.ok_label)
)
@ -235,3 +239,10 @@ fun SettingsScreen(
else -> {}
}
}
/**
* All possible states for alert dialog that opens when user clicks on settings.
*/
private enum class DialogState {
NONE, PRECISION, SEPARATOR, OUTPUT_FORMAT, THEME, CURRENCY_RATE,
}

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.screens.setttings
package com.sadellie.unitto.screens.setttings.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource

View File

@ -0,0 +1,38 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2022 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.screens.setttings.components
import androidx.compose.foundation.layout.fillMaxWidth
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 SettingsHeader(text: String) =
Text(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 12.dp),
text = text,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary
)

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.screens.setttings
package com.sadellie.unitto.screens.setttings.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -39,12 +39,12 @@ import androidx.compose.ui.unit.dp
/**
* Basic list item for settings screen. By default only has label and support text, clickable.
* This component can be easily modified if you provide additional component to it,
* for example a switch or a checkbox
* for example a switch or a checkbox.
*
* @param label Main text
* @param supportText Text that is located below label
* @param onClick Action to perform when user clicks on this component (whole component is clickable)
* @param content Additional composable: buttons, switches, checkboxes or something else
* @param label Main text.
* @param supportText Text that is located below label.
* @param onClick Action to perform when user clicks on this component (whole component is clickable).
* @param content Additional composable: buttons, switches, checkboxes or something else.
*/
@Composable
private fun BasicSettingsListItem(
@ -91,11 +91,11 @@ private fun BasicSettingsListItem(
}
/**
* Represents one item in list on Settings screen
* Represents one item in list on Settings screen.
*
* @param label Main text
* @param supportText Text that is located below label
* @param onClick Action to perform when user clicks on this component (whole component is clickable)
* @param label Main text.
* @param supportText Text that is located below label.
* @param onClick Action to perform when user clicks on this component (whole component is clickable).
*/
@Composable
fun SettingsListItem(
@ -105,12 +105,12 @@ fun SettingsListItem(
) = BasicSettingsListItem(label, supportText, onClick)
/**
* Represents one item in list on Settings screen
* Represents one item in list on Settings screen.
*
* @param label Main text
* @param supportText Text that is located below label
* @param switchState Current switch state
* @param onSwitchChange Action to perform when user clicks on this component or just switch
* @param label Main text.
* @param supportText Text that is located below label.
* @param switchState Current switch state.
* @param onSwitchChange Action to perform when user clicks on this component or just switch.
*/
@Composable
fun SettingsListItem(

View File

@ -720,5 +720,6 @@
<string name="empty_search_result_description">Empty search result</string>
<string name="send_usage_statistics">Send usage statistics</string>
<string name="send_usage_statistics_support">All data is anonymous and encrypted</string>
<string name="app_version_name_setting">Version name</string>
</resources>

View File

@ -672,5 +672,6 @@
<string name="acceleration">Ускорение</string>
<string name="send_usage_statistics">Отправлять статистику использования</string>
<string name="send_usage_statistics_support">Все данные анонимны и зашифрованы</string>
<string name="app_version_name_setting">Название версии</string>
</resources>

View File

@ -932,6 +932,7 @@
<string name="rate_this_app">Rate this app</string>
<string name="send_usage_statistics">Send usage statistics</string>
<string name="send_usage_statistics_support">All data is anonymous and encrypted</string>
<string name="app_version_name_setting">Version name</string>
<string name="general_settings_group">General</string>
<string name="additional_settings_group">Additional</string>