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 android.os.Build
import androidx.compose.animation.rememberSplineBasedDecay 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.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults 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.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.sadellie.unitto.BuildConfig import com.sadellie.unitto.BuildConfig
import com.sadellie.unitto.R import com.sadellie.unitto.R
import com.sadellie.unitto.data.ABOUT_SCREEN 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.data.preferences.SEPARATORS
import com.sadellie.unitto.screens.MainViewModel import com.sadellie.unitto.screens.MainViewModel
import com.sadellie.unitto.screens.openLink 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 @Composable
fun SettingsScreen( fun SettingsScreen(
@ -64,9 +60,13 @@ fun SettingsScreen(
) { ) {
val mContext = LocalContext.current val mContext = LocalContext.current
// Scrollable // Scrollable
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberSplineBasedDecay(), rememberTopAppBarScrollState()) val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
rememberSplineBasedDecay(),
var currentDialogState: Int by rememberSaveable { mutableStateOf(0) } rememberTopAppBarScrollState()
)
var dialogState: DialogState by rememberSaveable {
mutableStateOf(DialogState.NONE)
}
Scaffold( Scaffold(
modifier = Modifier modifier = Modifier
@ -89,144 +89,148 @@ fun SettingsScreen(
}, },
content = { padding -> content = { padding ->
LazyColumn(contentPadding = padding) { LazyColumn(contentPadding = padding) {
// GENERAL GROUP
item { SettingsHeader(stringResource(R.string.general_settings_group)) }
// PRECISION
item { item {
Column { SettingsListItem(
// Group header stringResource(R.string.precision_setting),
Text( stringResource(R.string.precision_setting_support)
modifier = Modifier ) { dialogState = DialogState.PRECISION }
.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 }
)
// Group header // SEPARATOR
Text( item {
modifier = Modifier SettingsListItem(
.fillMaxWidth() stringResource(R.string.separator_setting),
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 12.dp), stringResource(R.string.separator_setting_support)
text = stringResource(id = R.string.additional_settings_group), ) { dialogState = DialogState.SEPARATOR }
style = MaterialTheme.typography.titleSmall, }
color = MaterialTheme.colorScheme.primary
)
SettingsListItem(
label = stringResource(R.string.terms_and_conditions),
onClick = {
openLink(
mContext,
"http://sadellie.github.io/unitto/terms-app.html"
)
}
)
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
SettingsListItem( item {
label = stringResource(R.string.rate_this_app), SettingsListItem(
onClick = { stringResource(R.string.output_format_setting),
openLink( stringResource(R.string.output_format_setting_support)
mContext, ) { dialogState = DialogState.OUTPUT_FORMAT }
it }
)
} // 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 // Showing dialog
when (currentDialogState) { when (dialogState) {
1 -> { DialogState.PRECISION -> {
AlertDialogWithList( AlertDialogWithList(
title = stringResource(id = R.string.precision_setting), title = stringResource(id = R.string.precision_setting),
listItems = PRECISIONS, listItems = PRECISIONS,
selectedItemIndex = mainViewModel.precision, selectedItemIndex = mainViewModel.precision,
selectAction = { mainViewModel.updatePrecision(it) }, selectAction = { mainViewModel.updatePrecision(it) },
dismissAction = { currentDialogState = 0 }, dismissAction = { resetDialog() },
supportText = stringResource(id = R.string.precision_setting_info) supportText = stringResource(id = R.string.precision_setting_info)
) )
} }
2 -> { DialogState.SEPARATOR -> {
AlertDialogWithList( AlertDialogWithList(
title = stringResource(id = R.string.separator_setting), title = stringResource(id = R.string.separator_setting),
listItems = SEPARATORS, listItems = SEPARATORS,
selectedItemIndex = mainViewModel.separator, selectedItemIndex = mainViewModel.separator,
selectAction = { mainViewModel.updateSeparator(it) }, selectAction = { mainViewModel.updateSeparator(it) },
dismissAction = { currentDialogState = 0 } dismissAction = { resetDialog() }
) )
} }
3 -> { DialogState.OUTPUT_FORMAT -> {
AlertDialogWithList( AlertDialogWithList(
title = stringResource(id = R.string.output_format_setting), title = stringResource(id = R.string.output_format_setting),
listItems = OUTPUT_FORMAT, listItems = OUTPUT_FORMAT,
selectedItemIndex = mainViewModel.outputFormat, selectedItemIndex = mainViewModel.outputFormat,
selectAction = { mainViewModel.updateOutputFormat(it) }, selectAction = { mainViewModel.updateOutputFormat(it) },
dismissAction = { currentDialogState = 0 }, dismissAction = { resetDialog() },
supportText = stringResource(id = R.string.output_format_setting_info) supportText = stringResource(id = R.string.output_format_setting_info)
) )
} }
4 -> { DialogState.THEME -> {
AlertDialogWithList( AlertDialogWithList(
title = stringResource(id = R.string.theme_setting), title = stringResource(id = R.string.theme_setting),
listItems = APP_THEMES, listItems = APP_THEMES,
selectedItemIndex = mainViewModel.currentTheme, selectedItemIndex = mainViewModel.currentTheme,
selectAction = { mainViewModel.updateCurrentAppTheme(it) }, selectAction = { mainViewModel.updateCurrentAppTheme(it) },
dismissAction = { currentDialogState = 0 }, dismissAction = { resetDialog() },
// Show note for users with devices that support custom Dynamic theming // 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( supportText = if (Build.VERSION.SDK_INT in (Build.VERSION_CODES.O_MR1..Build.VERSION_CODES.R)) stringResource(
id = R.string.theme_setting_info id = R.string.theme_setting_info
) else null ) else null
) )
} }
5 -> { DialogState.CURRENCY_RATE -> {
AlertDialogWithList( AlertDialogWithList(
title = stringResource(id = R.string.currency_rates_note_title), title = stringResource(id = R.string.currency_rates_note_title),
dismissAction = { currentDialogState = 0 }, dismissAction = { resetDialog() },
supportText = stringResource(id = R.string.currency_rates_note_text), supportText = stringResource(id = R.string.currency_rates_note_text),
dismissButtonLabel = stringResource(id = R.string.ok_label) dismissButtonLabel = stringResource(id = R.string.ok_label)
) )
@ -235,3 +239,10 @@ fun SettingsScreen(
else -> {} 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/>. * 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.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource 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/>. * 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.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource 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. * 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, * 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 label Main text.
* @param supportText Text that is located below label * @param supportText Text that is located below label.
* @param onClick Action to perform when user clicks on this component (whole component is clickable) * @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 content Additional composable: buttons, switches, checkboxes or something else.
*/ */
@Composable @Composable
private fun BasicSettingsListItem( 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 label Main text.
* @param supportText Text that is located below label * @param supportText Text that is located below label.
* @param onClick Action to perform when user clicks on this component (whole component is clickable) * @param onClick Action to perform when user clicks on this component (whole component is clickable).
*/ */
@Composable @Composable
fun SettingsListItem( fun SettingsListItem(
@ -105,12 +105,12 @@ fun SettingsListItem(
) = BasicSettingsListItem(label, supportText, onClick) ) = 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 label Main text.
* @param supportText Text that is located below label * @param supportText Text that is located below label.
* @param switchState Current switch state * @param switchState Current switch state.
* @param onSwitchChange Action to perform when user clicks on this component or just switch * @param onSwitchChange Action to perform when user clicks on this component or just switch.
*/ */
@Composable @Composable
fun SettingsListItem( fun SettingsListItem(

View File

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

View File

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

View File

@ -932,6 +932,7 @@
<string name="rate_this_app">Rate this app</string> <string name="rate_this_app">Rate this app</string>
<string name="send_usage_statistics">Send usage statistics</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="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="general_settings_group">General</string>
<string name="additional_settings_group">Additional</string> <string name="additional_settings_group">Additional</string>