add currency settings
This commit is contained in:
@@ -3,9 +3,6 @@ package de.mm20.launcher2.ui.component.preferences
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
@@ -22,11 +19,8 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
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.graphics.toArgb
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
@@ -37,13 +31,16 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.dragndrop.LazyDragAndDropColumn
|
||||
import de.mm20.launcher2.ui.component.dragndrop.LazyDragAndDropListState
|
||||
import de.mm20.launcher2.ui.component.dragndrop.rememberLazyDragAndDropListState
|
||||
import de.mm20.launcher2.ui.locals.LocalBackStack
|
||||
|
||||
|
||||
@Composable
|
||||
@Deprecated("Use the overload with title as a composable function instead")
|
||||
fun PreferenceScreen(
|
||||
title: String,
|
||||
floatingActionButton: @Composable () -> Unit = {},
|
||||
topBarActions: @Composable RowScope.() -> Unit = {},
|
||||
helpUrl: String? = null,
|
||||
lazyColumnState: LazyListState = rememberLazyListState(),
|
||||
@@ -59,7 +56,6 @@ fun PreferenceScreen(
|
||||
maxLines = 1
|
||||
)
|
||||
},
|
||||
floatingActionButton = floatingActionButton,
|
||||
topBarActions = topBarActions,
|
||||
helpUrl = helpUrl,
|
||||
lazyColumnState = lazyColumnState,
|
||||
@@ -71,75 +67,21 @@ fun PreferenceScreen(
|
||||
@Composable
|
||||
fun PreferenceScreen(
|
||||
title: @Composable () -> Unit = {},
|
||||
floatingActionButton: @Composable () -> Unit = {},
|
||||
topBarActions: @Composable RowScope.() -> Unit = {},
|
||||
helpUrl: String? = null,
|
||||
lazyColumnState: LazyListState = rememberLazyListState(),
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(12.dp),
|
||||
content: LazyListScope.() -> Unit,
|
||||
) {
|
||||
val backStack = LocalBackStack.current
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
|
||||
var fabVisible by remember { mutableStateOf(true) }
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
|
||||
val activity = LocalActivity.current
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
AnimatedVisibility(
|
||||
fabVisible,
|
||||
enter = scaleIn(),
|
||||
exit = scaleOut(),
|
||||
) {
|
||||
floatingActionButton()
|
||||
}
|
||||
},
|
||||
topBar = {
|
||||
CenterAlignedTopAppBar(
|
||||
PreferenceScreenTopBar(
|
||||
title = title,
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
if (backStack.size <= 1) {
|
||||
activity?.onBackPressed()
|
||||
} else {
|
||||
backStack.removeLastOrNull()
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.arrow_back_24px),
|
||||
contentDescription = "Back"
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (helpUrl != null) {
|
||||
IconButton(onClick = {
|
||||
CustomTabsIntent.Builder()
|
||||
.setDefaultColorSchemeParams(
|
||||
CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(colorScheme.primaryContainer.toArgb())
|
||||
.setSecondaryToolbarColor(colorScheme.secondaryContainer.toArgb())
|
||||
.build()
|
||||
)
|
||||
.build().launchUrl(context, helpUrl.toUri())
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.help_24px),
|
||||
contentDescription = stringResource(R.string.help)
|
||||
)
|
||||
}
|
||||
}
|
||||
topBarActions()
|
||||
},
|
||||
actions = topBarActions,
|
||||
helpUrl = helpUrl,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
@@ -160,5 +102,101 @@ fun PreferenceScreen(
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun DragAndDropPreferenceScreen(
|
||||
title: @Composable () -> Unit = {},
|
||||
topBarActions: @Composable RowScope.() -> Unit = {},
|
||||
helpUrl: String? = null,
|
||||
lazyColumnState: LazyDragAndDropListState,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(1.dp),
|
||||
content: LazyListScope.() -> Unit,
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
PreferenceScreenTopBar(
|
||||
title = title,
|
||||
actions = topBarActions,
|
||||
helpUrl = helpUrl,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||
) {
|
||||
LazyDragAndDropColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
state = lazyColumnState,
|
||||
content = content,
|
||||
verticalArrangement = verticalArrangement,
|
||||
contentPadding = PaddingValues(
|
||||
top = it.calculateTopPadding() + 12.dp,
|
||||
bottom = it.calculateBottomPadding() + 12.dp,
|
||||
start = 12.dp,
|
||||
end = 12.dp
|
||||
),
|
||||
bidirectionalDrag = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreferenceScreenTopBar(
|
||||
title: @Composable () -> Unit = {},
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
helpUrl: String? = null,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
) {
|
||||
val backStack = LocalBackStack.current
|
||||
val context = LocalContext.current
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
val activity = LocalActivity.current
|
||||
|
||||
CenterAlignedTopAppBar(
|
||||
title = title,
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
if (backStack.size <= 1) {
|
||||
activity?.onBackPressed()
|
||||
} else {
|
||||
backStack.removeLastOrNull()
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.arrow_back_24px),
|
||||
contentDescription = "Back"
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (helpUrl != null) {
|
||||
IconButton(onClick = {
|
||||
CustomTabsIntent.Builder()
|
||||
.setDefaultColorSchemeParams(
|
||||
CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(colorScheme.primaryContainer.toArgb())
|
||||
.setSecondaryToolbarColor(colorScheme.secondaryContainer.toArgb())
|
||||
.build()
|
||||
)
|
||||
.build().launchUrl(context, helpUrl.toUri())
|
||||
}) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.help_24px),
|
||||
contentDescription = stringResource(R.string.help)
|
||||
)
|
||||
}
|
||||
}
|
||||
actions()
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
}
|
||||
@@ -100,6 +100,8 @@ import de.mm20.launcher2.ui.settings.license.LicenseRoute
|
||||
import de.mm20.launcher2.ui.settings.license.LicenseScreen
|
||||
import de.mm20.launcher2.ui.settings.locale.CalendarSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.locale.CalendarSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.locale.CurrencySettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.locale.CurrencySettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.locale.LocaleSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.locale.LocaleSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.locations.LocationsSettingsRoute
|
||||
@@ -304,6 +306,9 @@ class SettingsActivity : BaseActivity() {
|
||||
entry<CalendarSettingsRoute> {
|
||||
CalendarSettingsScreen()
|
||||
}
|
||||
entry<CurrencySettingsRoute> {
|
||||
CurrencySettingsScreen()
|
||||
}
|
||||
entry<BackupSettingsRoute> {
|
||||
BackupSettingsScreen()
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ fun CorePaletteColorPreference(
|
||||
) {
|
||||
SwitchPreference(
|
||||
icon = R.drawable.rule_settings_24px,
|
||||
title = stringResource(R.string.theme_color_scheme_system_default),
|
||||
title = stringResource(R.string.preference_use_system_default),
|
||||
value = currentValue == null,
|
||||
onValueChanged = {
|
||||
currentValue = if (it) null else defaultValue
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
package de.mm20.launcher2.ui.settings.locale
|
||||
|
||||
import android.icu.util.Currency
|
||||
import android.icu.util.ULocale
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.DropdownMenuGroup
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.DropdownMenuPopup
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MenuDefaults
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
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.draw.clip
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalLocale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import de.mm20.launcher2.preferences.ui.LocaleSettings
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.DismissableBottomSheet
|
||||
import de.mm20.launcher2.ui.component.dragndrop.DraggableItem
|
||||
import de.mm20.launcher2.ui.component.dragndrop.rememberLazyDragAndDropListState
|
||||
import de.mm20.launcher2.ui.component.preferences.DragAndDropPreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.ktx.animateShapeAsState
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.koin.compose.koinInject
|
||||
import java.util.Date
|
||||
|
||||
@Serializable
|
||||
data object CurrencySettingsRoute : NavKey
|
||||
|
||||
@Composable
|
||||
fun CurrencySettingsScreen() {
|
||||
val localeSettings = koinInject<LocaleSettings>()
|
||||
val currencies by localeSettings.currencies.collectAsState(null)
|
||||
|
||||
val dragAndDropListState = rememberLazyDragAndDropListState(
|
||||
onDragStart = {
|
||||
it.key.toString().startsWith("currency_")
|
||||
},
|
||||
onItemMove = { from, to ->
|
||||
val currencies = currencies?.toMutableList() ?: return@rememberLazyDragAndDropListState
|
||||
|
||||
val fromIndex = from.index - 2
|
||||
val toIndex = to.index - 2
|
||||
|
||||
if (fromIndex > currencies.lastIndex || fromIndex < 0) return@rememberLazyDragAndDropListState
|
||||
if (toIndex > currencies.lastIndex || toIndex < 0) return@rememberLazyDragAndDropListState
|
||||
|
||||
val item = currencies.removeAt(fromIndex)
|
||||
currencies.add(toIndex, item)
|
||||
localeSettings.setCurrencies(currencies)
|
||||
}
|
||||
)
|
||||
|
||||
val locale = LocalLocale.current
|
||||
val defaultCurrency = remember(locale) {
|
||||
Currency.getInstance(locale.platformLocale)
|
||||
}
|
||||
|
||||
DragAndDropPreferenceScreen(
|
||||
title = {
|
||||
Text(stringResource(R.string.preference_currencies))
|
||||
},
|
||||
lazyColumnState = dragAndDropListState,
|
||||
) {
|
||||
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 10.dp)
|
||||
) {
|
||||
PreferenceCategory {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_use_system_default),
|
||||
value = currencies?.isEmpty() == true,
|
||||
onValueChanged = {
|
||||
if (it) {
|
||||
localeSettings.setCurrencies(emptyList())
|
||||
} else {
|
||||
localeSettings.setCurrencies(listOf(defaultCurrency.currencyCode))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currencies != null) {
|
||||
if (currencies!!.isEmpty()) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
Preference(
|
||||
title = defaultCurrency.displayName,
|
||||
icon = R.drawable.euro_24px,
|
||||
enabled = false
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.info_20px), null,
|
||||
modifier = Modifier
|
||||
.padding(end = 12.dp)
|
||||
.size(16.dp),
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
text = stringResource(R.string.hint_drag_and_drop_reorder),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
itemsIndexed(
|
||||
items = currencies!!,
|
||||
key = { _, it -> "currency_$it" }
|
||||
) { index, currency ->
|
||||
DraggableItem(
|
||||
state = dragAndDropListState,
|
||||
key = "currency_$currency"
|
||||
) {
|
||||
val shape = getShape(index, currencies!!.size)
|
||||
val draggedShape = MaterialTheme.shapes.medium
|
||||
|
||||
val elevation by animateDpAsState(if (it) 4.dp else 0.dp)
|
||||
|
||||
val currencyName = try {
|
||||
Currency.getInstance(currency).displayName
|
||||
} catch (e: IllegalArgumentException) {
|
||||
currency
|
||||
}
|
||||
|
||||
Surface(
|
||||
shadowElevation = elevation,
|
||||
tonalElevation = elevation,
|
||||
modifier = Modifier
|
||||
.zIndex(if (it) 1f else 0f),
|
||||
shape = animateShapeAsState(
|
||||
if (it) draggedShape else shape
|
||||
).value
|
||||
) {
|
||||
Preference(
|
||||
title = currencyName,
|
||||
icon = getCurrencySymbol(currency),
|
||||
iconPadding = true,
|
||||
enabled = true,
|
||||
controls = {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
IconButton(
|
||||
onClick = { showMenu = true }
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.more_vert_24px),
|
||||
stringResource(R.string.action_more_actions),
|
||||
)
|
||||
}
|
||||
DropdownMenuPopup(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false },
|
||||
) {
|
||||
DropdownMenuGroup(
|
||||
shapes = MenuDefaults.groupShapes()
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.menu_remove)) },
|
||||
leadingIcon = {
|
||||
Icon(painterResource(R.drawable.delete_24px), null)
|
||||
},
|
||||
onClick = {
|
||||
val newCurrencies = currencies!!.toMutableList()
|
||||
newCurrencies.removeAt(index)
|
||||
localeSettings.setCurrencies(newCurrencies)
|
||||
showMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
var showModal by remember { mutableStateOf(false) }
|
||||
FilledTonalButton(
|
||||
modifier = Modifier
|
||||
.padding(top = 10.dp)
|
||||
.navigationBarsPadding(),
|
||||
contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
|
||||
onClick = {
|
||||
showModal = true
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(R.drawable.add_20px),
|
||||
null,
|
||||
modifier = Modifier
|
||||
.padding(end = ButtonDefaults.IconSpacing)
|
||||
.size(ButtonDefaults.IconSize)
|
||||
)
|
||||
Text(stringResource(R.string.preference_add_currency))
|
||||
}
|
||||
|
||||
CurrencyPickerSheet(
|
||||
selectedCurrencies = currencies!!,
|
||||
onCurrencySelected = { currency ->
|
||||
val newCurrencies = currencies!!.toMutableList()
|
||||
newCurrencies.add(currency)
|
||||
localeSettings.setCurrencies(newCurrencies)
|
||||
showModal = false
|
||||
},
|
||||
expanded = showModal,
|
||||
onDismissRequest = { showModal = false }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrencySymbol(currencyCode: String): Int? {
|
||||
return when (currencyCode) {
|
||||
"AUD", "BBD", "BMD", "BND", "BSD", "BZD", "CAD", "FJD", "GYD", "HKD", "JMD", "KID", "KYD",
|
||||
"LRD", "NAD", "NZD", "SBD", "SGD", "SRD", "TTD", "TVD", "TWD", "USD", "XCD", "ARS", "CLP",
|
||||
"COP", "CUP", "DOP", "MXN", "UYU" -> R.drawable.attach_money_24px
|
||||
|
||||
"EUR" -> R.drawable.euro_24px
|
||||
"EGP", "FKP", "GBP", "GIP", "SHP", "SDG", "SYP" -> R.drawable.currency_pound_24px
|
||||
"JPY" -> R.drawable.currency_yen_24px
|
||||
"CNY" -> R.drawable.currency_yuan_24px
|
||||
"INR" -> R.drawable.currency_rupee_24px
|
||||
"RUB" -> R.drawable.currency_ruble_24px
|
||||
else -> R.drawable.toll_24px
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getShape(index: Int, total: Int): Shape {
|
||||
if (total == 1) {
|
||||
return MaterialTheme.shapes.medium
|
||||
}
|
||||
|
||||
if (total > 1 && index > 0 && index < total - 1) {
|
||||
return MaterialTheme.shapes.extraSmall
|
||||
}
|
||||
|
||||
val xs = MaterialTheme.shapes.extraSmall
|
||||
val md = MaterialTheme.shapes.medium
|
||||
|
||||
if (index == 0) {
|
||||
return xs.copy(
|
||||
topStart = md.topStart,
|
||||
topEnd = md.topEnd
|
||||
)
|
||||
} else {
|
||||
return xs.copy(
|
||||
bottomStart = md.bottomStart,
|
||||
bottomEnd = md.bottomEnd
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CurrencyPickerSheet(
|
||||
selectedCurrencies: List<String>,
|
||||
onCurrencySelected: (String) -> Unit,
|
||||
expanded: Boolean,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
val availableCurrencies = remember {
|
||||
val locales = ULocale.getAvailableLocales()
|
||||
val currencyCodes = mutableSetOf<String>()
|
||||
|
||||
for (locale in locales) {
|
||||
val currencies = Currency.getAvailableCurrencyCodes(locale, Date()) ?: continue
|
||||
currencyCodes.addAll(currencies)
|
||||
}
|
||||
|
||||
val currencies = mutableListOf<Currency>()
|
||||
|
||||
for (currencyCode in currencyCodes) {
|
||||
try {
|
||||
currencies += Currency.getInstance(currencyCode)
|
||||
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Ignore invalid currency codes
|
||||
}
|
||||
}
|
||||
|
||||
currencies.sortBy { it.displayName }
|
||||
|
||||
currencies
|
||||
}
|
||||
|
||||
DismissableBottomSheet(
|
||||
expanded = expanded,
|
||||
onDismissRequest = onDismissRequest
|
||||
) {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = 12.dp,
|
||||
bottom = WindowInsets.navigationBars.asPaddingValues()
|
||||
.calculateBottomPadding() + 12.dp,
|
||||
start = 12.dp,
|
||||
end = 12.dp
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||
) {
|
||||
itemsIndexed(availableCurrencies) { i, currency ->
|
||||
Box(
|
||||
modifier = Modifier.clip(
|
||||
shape = getShape(i, availableCurrencies.size)
|
||||
)
|
||||
) {
|
||||
Preference(
|
||||
title = currency.displayName,
|
||||
onClick = {
|
||||
onCurrencySelected(currency.currencyCode)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.settings.locale
|
||||
import android.content.Intent
|
||||
import android.icu.text.ListFormatter
|
||||
import android.icu.text.Transliterator
|
||||
import android.icu.util.Currency
|
||||
import android.icu.util.ULocale
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -43,6 +44,7 @@ fun LocaleSettingsScreen() {
|
||||
val measurementSystem by viewModel.measurementSystem.collectAsStateWithLifecycle(null)
|
||||
val transliterator by viewModel.transliterator.collectAsStateWithLifecycle(null)
|
||||
val calendars by viewModel.calendars.collectAsStateWithLifecycle(emptyList())
|
||||
val currencies by viewModel.currencies.collectAsStateWithLifecycle(null)
|
||||
|
||||
// The language that has been selected by the user, or null to use the system language
|
||||
val selectedLocale = remember {
|
||||
@@ -234,6 +236,28 @@ fun LocaleSettingsScreen() {
|
||||
)
|
||||
}
|
||||
)
|
||||
Preference(
|
||||
title = stringResource(R.string.preference_currencies),
|
||||
summary = currencies?.let {
|
||||
if (it.isEmpty()) {
|
||||
return@let stringResource(R.string.preference_value_system_default)
|
||||
}
|
||||
|
||||
val names = it.map {
|
||||
try {
|
||||
Currency.getInstance(it).displayName
|
||||
} catch (e: IllegalArgumentException) {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
ListFormatter.getInstance().format(names)
|
||||
},
|
||||
icon = R.drawable.toll_24px,
|
||||
onClick = {
|
||||
backstack += CurrencySettingsRoute
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,6 @@ class LocaleSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
val calendars = combine(localeSettings.primaryCalendar, localeSettings.secondaryCalendar) {
|
||||
it.toImmutableList()
|
||||
}
|
||||
|
||||
val currencies = localeSettings.currencies
|
||||
}
|
||||
@@ -121,27 +121,6 @@ fun LogScreen() {
|
||||
}
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
floatingActionButton = {
|
||||
AnimatedVisibility(
|
||||
listState.canScrollForward,
|
||||
enter = scaleIn(),
|
||||
exit = scaleOut(),
|
||||
) {
|
||||
SmallFloatingActionButton(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
listState.animateScrollToItem(lines.lastIndex)
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(R.drawable.arrow_downward_24px),
|
||||
null,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
lazyColumnState = listState,
|
||||
) {
|
||||
itemsIndexed(lines) { i, it ->
|
||||
|
||||
@@ -163,13 +163,15 @@ fun SearchActionsSettingsScreen() {
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = 28.dp, top = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.info_20px), null,
|
||||
modifier = Modifier
|
||||
.padding(end = 24.dp)
|
||||
.padding(end = 12.dp)
|
||||
.size(16.dp),
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.locals.LocalBackStack
|
||||
import de.mm20.launcher2.ui.settings.locale.CurrencySettingsRoute
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@@ -58,6 +59,13 @@ fun UnitConverterSettingsScreen() {
|
||||
backStack.add(UnitConverterHelpSettingsRoute)
|
||||
}
|
||||
)
|
||||
Preference(
|
||||
title = stringResource(R.string.preference_currency_settings),
|
||||
icon = R.drawable.open_in_new_24px,
|
||||
onClick = {
|
||||
backStack.add(CurrencySettingsRoute)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
package de.mm20.launcher2.ui.settings.unitconverter
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.search.UnitConverterSettings
|
||||
import de.mm20.launcher2.unitconverter.UnitConverterRepository
|
||||
import de.mm20.launcher2.unitconverter.converters.Converter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
@@ -29,13 +22,13 @@ class UnitConverterSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
settings.setEnabled(unitConverter)
|
||||
}
|
||||
|
||||
val currencyConverter = settings.currencies
|
||||
val currencyConverter = settings.currenciesEnabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setCurrencyConverter(currencyConverter: Boolean) {
|
||||
settings.setCurrencies(currencyConverter)
|
||||
settings.setCurrenciesEnabled(currencyConverter)
|
||||
}
|
||||
|
||||
val availableConverters = settings.currencies.map {
|
||||
val availableConverters = settings.currenciesEnabled.map {
|
||||
repository.getAvailableConverters(includeCurrencies = it)
|
||||
}.shareIn(viewModelScope, SharingStarted.WhileSubscribed(100), 1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user