Refactor FormatterSymbols

closes #90
This commit is contained in:
Sad Ellie 2024-02-12 22:12:07 +03:00
parent 224ddd8bad
commit 263a1139dc
54 changed files with 323 additions and 332 deletions

View File

@ -1,6 +1,6 @@
/* /*
* Unitto is a calculator for Android * Unitto is a calculator for Android
* Copyright (c) 2022-2024 Elshan Agaev * Copyright (c) 2024 Elshan Agaev
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -19,10 +19,12 @@
package com.sadellie.unitto.core.base package com.sadellie.unitto.core.base
/** /**
* Separators mean symbols that separate fractional part * Formatter symbols. Always use [Token].
*
* @property grouping Symbol fpr thousands separator.
* @property fractional Symbol decimal separator.
*/ */
object Separator { data class FormatterSymbols(
const val SPACE = 0 val grouping: String,
const val PERIOD = 1 val fractional: String,
const val COMMA = 2 )
}

View File

@ -20,6 +20,10 @@ package com.sadellie.unitto.core.base
@Suppress("ObjectPropertyName") @Suppress("ObjectPropertyName")
object Token { object Token {
const val SPACE = " "
const val PERIOD = "."
const val COMMA = ","
object Digit { object Digit {
const val _1 = "1" const val _1 = "1"
const val _2 = "2" const val _2 = "2"

View File

@ -245,6 +245,7 @@ Alternatively you can use "Export" -->
<string name="settings_currency_rates_note_text">Currency rates are updated daily. There\'s no real-time market monitoring in the app</string> <string name="settings_currency_rates_note_text">Currency rates are updated daily. There\'s no real-time market monitoring in the app</string>
<string name="settings_currency_rates_note_title">Wrong currency rates?</string> <string name="settings_currency_rates_note_title">Wrong currency rates?</string>
<string name="settings_dark_mode">Dark</string> <string name="settings_dark_mode">Dark</string>
<string name="settings_decimal_separator">Decimal separator</string>
<string name="settings_disable_unit_group_description">Disable unit group</string> <string name="settings_disable_unit_group_description">Disable unit group</string>
<string name="settings_display">Display</string> <string name="settings_display">Display</string>
<string name="settings_display_support">App look and feel</string> <string name="settings_display_support">App look and feel</string>
@ -301,6 +302,7 @@ Maybe this can be labeled better? Let me know. It should be something that can d
<string name="settings_system_font_support">Use system font for texts in app</string> <string name="settings_system_font_support">Use system font for texts in app</string>
<string name="settings_terms_and_conditions">Terms and Conditions</string> <string name="settings_terms_and_conditions">Terms and Conditions</string>
<string name="settings_third_party_licenses">Third party licenses</string> <string name="settings_third_party_licenses">Third party licenses</string>
<string name="settings_thousands_separator">Thousands separator</string>
<string name="settings_title">Settings</string> <string name="settings_title">Settings</string>
<string name="settings_translate_app">Translate this app</string> <string name="settings_translate_app">Translate this app</string>
<string name="settings_translate_app_support">Join POEditor project to help</string> <string name="settings_translate_app_support">Join POEditor project to help</string>

View File

@ -1,45 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 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.core.base
import org.junit.Assert
import org.junit.Test
class SeparatorTest {
@Test
fun testExists() {
Assert.assertNotNull(Separator)
}
@Test
fun testSeparatorSpace() {
Assert.assertEquals(0, Separator.SPACE)
}
@Test
fun testSeparatorPeriod() {
Assert.assertEquals(1, Separator.PERIOD)
}
@Test
fun testSeparatorComma() {
Assert.assertEquals(2, Separator.COMMA)
}
}

View File

@ -22,6 +22,14 @@ import org.junit.Assert
import org.junit.Test import org.junit.Test
class TokenTest { class TokenTest {
@Test
fun testFormatterSymbols() {
Assert.assertEquals(" ", Token.SPACE)
Assert.assertEquals(".", Token.PERIOD)
Assert.assertEquals(",", Token.COMMA)
}
@Test @Test
fun testDigit() { fun testDigit() {
Assert.assertEquals("1234567890", Token.Digit.all.joinToString("")) Assert.assertEquals("1234567890", Token.Digit.all.joinToString(""))
@ -126,7 +134,7 @@ class TokenTest {
"e", "e",
).joinToString("") ).joinToString("")
Assert.assertEquals("1234567890.$operator$func$consts", Token.expressionTokens.joinToString("")) Assert.assertEquals("1234567890.$operator$func${consts}E", Token.expressionTokens.joinToString(""))
} }
@Test @Test

View File

@ -21,6 +21,7 @@ package com.sadellie.unitto.core.ui.common.textfield
import android.content.ClipData import android.content.ClipData
import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
/** /**

View File

@ -22,6 +22,7 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.OffsetMapping import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.TransformedText import androidx.compose.ui.text.input.TransformedText
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import com.sadellie.unitto.core.base.FormatterSymbols
class ExpressionTransformer(private val formatterSymbols: FormatterSymbols) : VisualTransformation { class ExpressionTransformer(private val formatterSymbols: FormatterSymbols) : VisualTransformation {

View File

@ -35,6 +35,7 @@ import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.ui.theme.LocalNumberTypography import com.sadellie.unitto.core.ui.theme.LocalNumberTypography
@Composable @Composable

View File

@ -18,6 +18,7 @@
package com.sadellie.unitto.core.ui.common.textfield package com.sadellie.unitto.core.ui.common.textfield
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
private val numbersRegex by lazy { Regex("[\\d.]+") } private val numbersRegex by lazy { Regex("[\\d.]+") }

View File

@ -1,46 +0,0 @@
/*
* Unitto is a calculator for Android
* Copyright (c) 2023-2024 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.core.ui.common.textfield
import com.sadellie.unitto.core.base.Separator
sealed class FormatterSymbols(val grouping: String, val fractional: String) {
data object Spaces : FormatterSymbols(" ", ".")
data object Period : FormatterSymbols(".", ",")
data object Comma : FormatterSymbols(",", ".")
}
object AllFormatterSymbols {
private val allFormatterSymbols by lazy {
hashMapOf(
Separator.SPACE to FormatterSymbols.Spaces,
Separator.PERIOD to FormatterSymbols.Period,
Separator.COMMA to FormatterSymbols.Comma
)
}
/**
* Defaults to [FormatterSymbols.Spaces] if not found.
*
* @see Separator
*/
fun getById(separator: Int): FormatterSymbols {
return allFormatterSymbols.getOrElse(separator) { FormatterSymbols.Spaces }
}
}

View File

@ -44,6 +44,7 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.ui.common.autosize.AutoSizeTextStyleBox import com.sadellie.unitto.core.ui.common.autosize.AutoSizeTextStyleBox
import com.sadellie.unitto.core.ui.theme.LocalNumberTypography import com.sadellie.unitto.core.ui.theme.LocalNumberTypography

View File

@ -18,40 +18,44 @@
package com.sadellie.unitto.core.ui package com.sadellie.unitto.core.ui
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.clearAndFilterExpression import com.sadellie.unitto.core.ui.common.textfield.clearAndFilterExpression
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
class CleanAndFilterExpression { class CleanAndFilterExpression {
private val formatterSymbols = FormatterSymbols(Token.COMMA, Token.PERIOD)
@Test @Test
fun noAdditionalSymbols() { fun noAdditionalSymbols() {
assertEquals("123", "123".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("123", "123".clearAndFilterExpression(formatterSymbols))
assertEquals("123.456", "123.456".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("123.456", "123.456".clearAndFilterExpression(formatterSymbols))
} }
@Test @Test
fun hasFormatterSymbol() { fun hasFormatterSymbol() {
assertEquals("123456", "123,456".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("123456", "123,456".clearAndFilterExpression(formatterSymbols))
assertEquals("123456.789", "123,456.789".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("123456.789", "123,456.789".clearAndFilterExpression(formatterSymbols))
} }
@Test @Test
fun hasWrongFormatterSymbol() { fun hasWrongFormatterSymbol() {
assertEquals("123456", "123 456".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("123456", "123 456".clearAndFilterExpression(formatterSymbols))
assertEquals("123456.789", "123 456.789".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("123456.789", "123 456.789".clearAndFilterExpression(formatterSymbols))
} }
@Test @Test
fun fractionExpression() { fun fractionExpression() {
assertEquals("1600+1234÷56789", "1,600 123456789".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("1600+1234÷56789", "1,600 123456789".clearAndFilterExpression(formatterSymbols))
assertEquals("123456.789+1234÷56789", "123,456.789 123456789".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("123456.789+1234÷56789", "123,456.789 123456789".clearAndFilterExpression(formatterSymbols))
} }
@Test @Test
fun garbage() { fun garbage() {
// 'e' is a known symbol // 'e' is a known symbol
assertEquals("eeee123", "pee pee poo poo -123".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("eeee123", "pee pee poo poo -123".clearAndFilterExpression(formatterSymbols))
assertEquals("eeee123.456", "pee pee poo poo -123.456".clearAndFilterExpression(FormatterSymbols.Comma)) assertEquals("eeee123.456", "pee pee poo poo -123.456".clearAndFilterExpression(formatterSymbols))
} }
} }

View File

@ -18,14 +18,15 @@
package com.sadellie.unitto.core.ui package com.sadellie.unitto.core.ui
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
class ExpressionTransformerTest { class ExpressionTransformerTest {
private val expr = ExpressionTransformer(FormatterSymbols.Comma) private val expr = ExpressionTransformer(FormatterSymbols(Token.COMMA, Token.PERIOD))
// Use "|" for cursor // Use "|" for cursor
private fun origToTrans(orig: String, trans: String) { private fun origToTrans(orig: String, trans: String) {

View File

@ -18,7 +18,8 @@
package com.sadellie.unitto.core.ui package com.sadellie.unitto.core.ui
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
@ -40,7 +41,7 @@ class FormatterExpressionTest {
@Test @Test
fun setSeparatorSpaces() { fun setSeparatorSpaces() {
fun String.format(): String = formatExpression(FormatterSymbols.Spaces) fun String.format(): String = formatExpression(FormatterSymbols(Token.SPACE, Token.PERIOD))
assertEquals("123E+21", ENG_VALUE.format()) assertEquals("123E+21", ENG_VALUE.format())
assertEquals("123.3E+21", ENG_VALUE_FRACTIONAL.format()) assertEquals("123.3E+21", ENG_VALUE_FRACTIONAL.format())
assertEquals("123E+21+(123 456.789)", ENG_VALUE_EXPRESSION.format()) assertEquals("123E+21+(123 456.789)", ENG_VALUE_EXPRESSION.format())
@ -57,7 +58,7 @@ class FormatterExpressionTest {
@Test @Test
fun setSeparatorComma() { fun setSeparatorComma() {
fun String.format(): String = formatExpression(FormatterSymbols.Comma) fun String.format(): String = formatExpression(FormatterSymbols(Token.COMMA, Token.PERIOD))
assertEquals("123E+21", ENG_VALUE.format()) assertEquals("123E+21", ENG_VALUE.format())
assertEquals("123.3E+21", ENG_VALUE_FRACTIONAL.format()) assertEquals("123.3E+21", ENG_VALUE_FRACTIONAL.format())
assertEquals("123E+21+(123,456.789)", ENG_VALUE_EXPRESSION.format()) assertEquals("123E+21+(123,456.789)", ENG_VALUE_EXPRESSION.format())
@ -74,7 +75,7 @@ class FormatterExpressionTest {
@Test @Test
fun setSeparatorPeriod() { fun setSeparatorPeriod() {
fun String.format(): String = formatExpression(FormatterSymbols.Period) fun String.format(): String = formatExpression(FormatterSymbols(Token.PERIOD, Token.COMMA))
assertEquals("123E+21", ENG_VALUE.format()) assertEquals("123E+21", ENG_VALUE.format())
assertEquals("123,3E+21", ENG_VALUE_FRACTIONAL.format()) assertEquals("123,3E+21", ENG_VALUE_FRACTIONAL.format())
assertEquals("123E+21+(123.456,789)", ENG_VALUE_EXPRESSION.format()) assertEquals("123E+21+(123.456,789)", ENG_VALUE_EXPRESSION.format())

View File

@ -51,7 +51,7 @@ interface UserPreferencesRepository {
suspend fun updateDigitsPrecision(precision: Int) suspend fun updateDigitsPrecision(precision: Int)
suspend fun updateSeparator(separator: Int) suspend fun updateFormatterSymbols(grouping: String, fractional: String)
suspend fun updateOutputFormat(outputFormat: Int) suspend fun updateOutputFormat(outputFormat: Int)

View File

@ -18,6 +18,8 @@
package com.sadellie.unitto.data.model.userprefs package com.sadellie.unitto.data.model.userprefs
import com.sadellie.unitto.core.base.FormatterSymbols
interface AddSubtractPreferences{ interface AddSubtractPreferences{
val separator: Int val formatterSymbols: FormatterSymbols
} }

View File

@ -18,6 +18,8 @@
package com.sadellie.unitto.data.model.userprefs package com.sadellie.unitto.data.model.userprefs
import com.sadellie.unitto.core.base.FormatterSymbols
interface BodyMassPreferences{ interface BodyMassPreferences{
val separator: Int val formatterSymbols: FormatterSymbols
} }

View File

@ -18,9 +18,11 @@
package com.sadellie.unitto.data.model.userprefs package com.sadellie.unitto.data.model.userprefs
import com.sadellie.unitto.core.base.FormatterSymbols
interface CalculatorPreferences { interface CalculatorPreferences {
val radianMode: Boolean val radianMode: Boolean
val separator: Int val formatterSymbols: FormatterSymbols
val middleZero: Boolean val middleZero: Boolean
val acButton: Boolean val acButton: Boolean
val partialHistoryView: Boolean val partialHistoryView: Boolean

View File

@ -18,11 +18,12 @@
package com.sadellie.unitto.data.model.userprefs package com.sadellie.unitto.data.model.userprefs
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.UnitsListSorting
interface ConverterPreferences { interface ConverterPreferences {
val separator: Int val formatterSymbols: FormatterSymbols
val middleZero: Boolean val middleZero: Boolean
val acButton: Boolean val acButton: Boolean
val precision: Int val precision: Int

View File

@ -18,8 +18,10 @@
package com.sadellie.unitto.data.model.userprefs package com.sadellie.unitto.data.model.userprefs
import com.sadellie.unitto.core.base.FormatterSymbols
interface FormattingPreferences{ interface FormattingPreferences{
val digitsPrecision: Int val digitsPrecision: Int
val separator: Int val formatterSymbols: FormatterSymbols
val outputFormat: Int val outputFormat: Int
} }

View File

@ -19,8 +19,9 @@
package com.sadellie.unitto.data.userprefs package com.sadellie.unitto.data.userprefs
import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.Preferences
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitGroup
@ -76,8 +77,21 @@ internal fun Preferences.getRadianMode(): Boolean {
return this[PrefsKeys.RADIAN_MODE] ?: true return this[PrefsKeys.RADIAN_MODE] ?: true
} }
internal fun Preferences.getSeparator(): Int { internal fun Preferences.getFormatterSymbols(): FormatterSymbols {
return this[PrefsKeys.SEPARATOR] ?: Separator.SPACE val grouping = this[PrefsKeys.FORMATTER_GROUPING]
val fractional = this[PrefsKeys.FORMATTER_FRACTIONAL]
// Updating from older version or fresh install
// TODO Remove in the future
if ((grouping == null) or (fractional == null)) {
return when(this[PrefsKeys.SEPARATOR] ?: 0) {
0 -> FormatterSymbols(Token.SPACE, Token.PERIOD)
1 -> FormatterSymbols(Token.PERIOD, Token.COMMA)
else -> FormatterSymbols(Token.COMMA, Token.PERIOD)
}
}
return FormatterSymbols(grouping ?: Token.SPACE, fractional ?: Token.PERIOD)
} }
internal fun Preferences.getMiddleZero(): Boolean { internal fun Preferences.getMiddleZero(): Boolean {

View File

@ -18,6 +18,7 @@
package com.sadellie.unitto.data.userprefs package com.sadellie.unitto.data.userprefs
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.UnitsListSorting
import com.sadellie.unitto.data.model.userprefs.AboutPreferences import com.sadellie.unitto.data.model.userprefs.AboutPreferences
@ -53,7 +54,7 @@ data class GeneralPreferencesImpl(
data class CalculatorPreferencesImpl( data class CalculatorPreferencesImpl(
override val radianMode: Boolean, override val radianMode: Boolean,
override val separator: Int, override val formatterSymbols: FormatterSymbols,
override val middleZero: Boolean, override val middleZero: Boolean,
override val acButton: Boolean, override val acButton: Boolean,
override val partialHistoryView: Boolean, override val partialHistoryView: Boolean,
@ -62,7 +63,7 @@ data class CalculatorPreferencesImpl(
) : CalculatorPreferences ) : CalculatorPreferences
data class ConverterPreferencesImpl( data class ConverterPreferencesImpl(
override val separator: Int, override val formatterSymbols: FormatterSymbols,
override val middleZero: Boolean, override val middleZero: Boolean,
override val acButton: Boolean, override val acButton: Boolean,
override val precision: Int, override val precision: Int,
@ -84,7 +85,7 @@ data class DisplayPreferencesImpl(
data class FormattingPreferencesImpl( data class FormattingPreferencesImpl(
override val digitsPrecision: Int, override val digitsPrecision: Int,
override val separator: Int, override val formatterSymbols: FormatterSymbols,
override val outputFormat: Int, override val outputFormat: Int,
) : FormattingPreferences ) : FormattingPreferences
@ -93,11 +94,11 @@ data class UnitGroupsPreferencesImpl(
) : UnitGroupsPreferences ) : UnitGroupsPreferences
data class AddSubtractPreferencesImpl( data class AddSubtractPreferencesImpl(
override val separator: Int, override val formatterSymbols: FormatterSymbols,
) : AddSubtractPreferences ) : AddSubtractPreferences
data class BodyMassPreferencesImpl( data class BodyMassPreferencesImpl(
override val separator: Int, override val formatterSymbols: FormatterSymbols,
) : BodyMassPreferences ) : BodyMassPreferences
data class AboutPreferencesImpl( data class AboutPreferencesImpl(

View File

@ -42,6 +42,8 @@ object PrefsKeys {
// FORMATTER // FORMATTER
val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY") val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY")
val SEPARATOR = intPreferencesKey("SEPARATOR_PREF_KEY") val SEPARATOR = intPreferencesKey("SEPARATOR_PREF_KEY")
val FORMATTER_GROUPING = stringPreferencesKey("FORMATTER_GROUPING_PREF_KEY")
val FORMATTER_FRACTIONAL = stringPreferencesKey("FORMATTER_FRACTIONAL_PREF_KEY")
val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY") val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY")
// CALCULATOR // CALCULATOR

View File

@ -78,7 +78,7 @@ class UserPreferencesRepositoryImpl @Inject constructor(
.map { preferences -> .map { preferences ->
CalculatorPreferencesImpl( CalculatorPreferencesImpl(
radianMode = preferences.getRadianMode(), radianMode = preferences.getRadianMode(),
separator = preferences.getSeparator(), formatterSymbols = preferences.getFormatterSymbols(),
middleZero = preferences.getMiddleZero(), middleZero = preferences.getMiddleZero(),
partialHistoryView = preferences.getPartialHistoryView(), partialHistoryView = preferences.getPartialHistoryView(),
precision = preferences.getDigitsPrecision(), precision = preferences.getDigitsPrecision(),
@ -90,7 +90,7 @@ class UserPreferencesRepositoryImpl @Inject constructor(
override val converterPrefs: Flow<ConverterPreferences> = data override val converterPrefs: Flow<ConverterPreferences> = data
.map { preferences -> .map { preferences ->
ConverterPreferencesImpl( ConverterPreferencesImpl(
separator = preferences.getSeparator(), formatterSymbols = preferences.getFormatterSymbols(),
middleZero = preferences.getMiddleZero(), middleZero = preferences.getMiddleZero(),
precision = preferences.getDigitsPrecision(), precision = preferences.getDigitsPrecision(),
outputFormat = preferences.getOutputFormat(), outputFormat = preferences.getOutputFormat(),
@ -118,7 +118,7 @@ class UserPreferencesRepositoryImpl @Inject constructor(
.map { preferences -> .map { preferences ->
FormattingPreferencesImpl( FormattingPreferencesImpl(
digitsPrecision = preferences.getDigitsPrecision(), digitsPrecision = preferences.getDigitsPrecision(),
separator = preferences.getSeparator(), formatterSymbols = preferences.getFormatterSymbols(),
outputFormat = preferences.getOutputFormat(), outputFormat = preferences.getOutputFormat(),
) )
} }
@ -133,14 +133,14 @@ class UserPreferencesRepositoryImpl @Inject constructor(
override val addSubtractPrefs: Flow<AddSubtractPreferences> = data override val addSubtractPrefs: Flow<AddSubtractPreferences> = data
.map { preferences -> .map { preferences ->
AddSubtractPreferencesImpl( AddSubtractPreferencesImpl(
separator = preferences.getSeparator(), formatterSymbols = preferences.getFormatterSymbols(),
) )
} }
override val bodyMassPrefs: Flow<BodyMassPreferences> = data override val bodyMassPrefs: Flow<BodyMassPreferences> = data
.map { preferences -> .map { preferences ->
BodyMassPreferencesImpl( BodyMassPreferencesImpl(
separator = preferences.getSeparator(), formatterSymbols = preferences.getFormatterSymbols(),
) )
} }
@ -164,9 +164,13 @@ class UserPreferencesRepositoryImpl @Inject constructor(
} }
} }
override suspend fun updateSeparator(separator: Int) { override suspend fun updateFormatterSymbols(grouping: String, fractional: String) {
// Grouping and fractional symbols are always different
if (grouping == fractional) return
dataStore.edit { preferences -> dataStore.edit { preferences ->
preferences[PrefsKeys.SEPARATOR] = separator preferences[PrefsKeys.FORMATTER_GROUPING] = grouping
preferences[PrefsKeys.FORMATTER_FRACTIONAL] = fractional
} }
} }

View File

@ -49,14 +49,15 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.DrawerButton import com.sadellie.unitto.core.ui.common.DrawerButton
import com.sadellie.unitto.core.ui.common.EmptyScreen import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar
import com.sadellie.unitto.core.ui.common.SegmentedButton import com.sadellie.unitto.core.ui.common.SegmentedButton
import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer import com.sadellie.unitto.core.ui.common.textfield.ExpressionTransformer
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.core.ui.openLink import com.sadellie.unitto.core.ui.openLink
import com.sadellie.unitto.data.common.isEqualTo import com.sadellie.unitto.data.common.isEqualTo
import com.sadellie.unitto.feature.bodymass.components.BodyMassResult import com.sadellie.unitto.feature.bodymass.components.BodyMassResult
@ -223,7 +224,7 @@ fun PreviewBodyMassScreen() {
weight = TextFieldValue(), weight = TextFieldValue(),
normalWeightRange = BigDecimal(30) to BigDecimal(50), normalWeightRange = BigDecimal(30) to BigDecimal(50),
result = BigDecimal(18.5), result = BigDecimal(18.5),
formatterSymbols = FormatterSymbols.Spaces formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD)
), ),
updateHeight1 = {}, updateHeight1 = {},
updateHeight2 = {}, updateHeight2 = {},

View File

@ -24,7 +24,6 @@ import android.os.Build
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
import com.sadellie.unitto.data.common.combine import com.sadellie.unitto.data.common.combine
import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
@ -64,7 +63,7 @@ internal class BodyMassViewModel @Inject constructor(
weight = weight, weight = weight,
result = result, result = result,
normalWeightRange = normalWeightRange, normalWeightRange = normalWeightRange,
formatterSymbols = AllFormatterSymbols.getById(userPrefs.separator) formatterSymbols = userPrefs.formatterSymbols
) )
} }
.mapLatest { ui -> .mapLatest { ui ->

View File

@ -19,7 +19,7 @@
package com.sadellie.unitto.feature.bodymass package com.sadellie.unitto.feature.bodymass
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import java.math.BigDecimal import java.math.BigDecimal
internal sealed class UIState { internal sealed class UIState {

View File

@ -37,9 +37,10 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.format
import java.math.BigDecimal import java.math.BigDecimal
@ -171,6 +172,6 @@ fun PreviewBodyMassResult() {
value = BigDecimal(18.5), value = BigDecimal(18.5),
range = BigDecimal(50) to BigDecimal(80), range = BigDecimal(50) to BigDecimal(80),
rangeSuffix = "kg", rangeSuffix = "kg",
formatterSymbols = FormatterSymbols.Spaces, formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
) )
} }

View File

@ -26,9 +26,10 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown import androidx.compose.ui.test.swipeDown
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.Token
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -67,7 +68,7 @@ class CalculatorScreenTest {
radianMode = false, radianMode = false,
precision = 3, precision = 3,
outputFormat = OutputFormat.PLAIN, outputFormat = OutputFormat.PLAIN,
formatterSymbols = FormatterSymbols.Spaces, formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
history = emptyList(), history = emptyList(),
middleZero = false, middleZero = false,
acButton = true, acButton = true,
@ -100,7 +101,7 @@ class CalculatorScreenTest {
radianMode = false, radianMode = false,
precision = 3, precision = 3,
outputFormat = OutputFormat.PLAIN, outputFormat = OutputFormat.PLAIN,
formatterSymbols = FormatterSymbols.Spaces, formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
history = emptyList(), history = emptyList(),
middleZero = false, middleZero = false,
acButton = true, acButton = true,

View File

@ -62,14 +62,15 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.LocalWindowSize import com.sadellie.unitto.core.ui.LocalWindowSize
import com.sadellie.unitto.core.ui.WindowHeightSizeClass import com.sadellie.unitto.core.ui.WindowHeightSizeClass
import com.sadellie.unitto.core.ui.common.DrawerButton import com.sadellie.unitto.core.ui.common.DrawerButton
import com.sadellie.unitto.core.ui.common.EmptyScreen import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.data.model.HistoryItem import com.sadellie.unitto.data.model.HistoryItem
import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboard import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboard
import com.sadellie.unitto.feature.calculator.components.HistoryItemHeight import com.sadellie.unitto.feature.calculator.components.HistoryItemHeight
@ -355,7 +356,7 @@ private fun PreviewCalculatorScreen() {
radianMode = false, radianMode = false,
precision = 3, precision = 3,
outputFormat = OutputFormat.PLAIN, outputFormat = OutputFormat.PLAIN,
formatterSymbols = FormatterSymbols.Spaces, formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
history = historyItems, history = historyItems,
middleZero = false, middleZero = false,
acButton = true, acButton = true,

View File

@ -20,8 +20,8 @@ package com.sadellie.unitto.feature.calculator
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.data.model.HistoryItem import com.sadellie.unitto.data.model.HistoryItem
internal sealed class CalculatorUIState { internal sealed class CalculatorUIState {

View File

@ -24,7 +24,6 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.addBracket import com.sadellie.unitto.core.ui.common.textfield.addBracket
import com.sadellie.unitto.core.ui.common.textfield.addTokens import com.sadellie.unitto.core.ui.common.textfield.addTokens
import com.sadellie.unitto.core.ui.common.textfield.deleteTokens import com.sadellie.unitto.core.ui.common.textfield.deleteTokens
@ -79,7 +78,7 @@ internal class CalculatorViewModel @Inject constructor(
radianMode = prefs.radianMode, radianMode = prefs.radianMode,
precision = prefs.precision, precision = prefs.precision,
outputFormat = prefs.outputFormat, outputFormat = prefs.outputFormat,
formatterSymbols = AllFormatterSymbols.getById(prefs.separator), formatterSymbols = prefs.formatterSymbols,
history = history, history = history,
middleZero = prefs.middleZero, middleZero = prefs.middleZero,
acButton = prefs.acButton, acButton = prefs.acButton,

View File

@ -110,7 +110,6 @@ import com.sadellie.unitto.core.ui.common.icons.iconpack.RightBracket
import com.sadellie.unitto.core.ui.common.icons.iconpack.Root import com.sadellie.unitto.core.ui.common.icons.iconpack.Root
import com.sadellie.unitto.core.ui.common.icons.iconpack.Sin import com.sadellie.unitto.core.ui.common.icons.iconpack.Sin
import com.sadellie.unitto.core.ui.common.icons.iconpack.Tan import com.sadellie.unitto.core.ui.common.icons.iconpack.Tan
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
@Composable @Composable
internal fun CalculatorKeyboard( internal fun CalculatorKeyboard(
@ -182,8 +181,8 @@ private fun PortraitKeyboard(
val angleIcon = remember(radianMode) { if (radianMode) IconPack.Rad else IconPack.Deg } val angleIcon = remember(radianMode) { if (radianMode) IconPack.Rad else IconPack.Deg }
val angleIconDescription = remember(radianMode) { if (radianMode) R.string.keyboard_radian else R.string.keyboard_degree } val angleIconDescription = remember(radianMode) { if (radianMode) R.string.keyboard_radian else R.string.keyboard_degree }
val fractionalIcon = remember(fractional) { if (fractional == Token.Digit.dot) IconPack.Dot else IconPack.Comma } val fractionalIcon = remember(fractional) { if (fractional == Token.PERIOD) IconPack.Dot else IconPack.Comma }
val fractionalIconDescription = remember(fractional) { if (fractional == Token.Digit.dot) R.string.keyboard_dot else R.string.comma } val fractionalIconDescription = remember(fractional) { if (fractional == Token.PERIOD) R.string.keyboard_dot else R.string.comma }
var showAdditional: Boolean by remember { mutableStateOf(false) } var showAdditional: Boolean by remember { mutableStateOf(false) }
val expandRotation: Float by animateFloatAsState( val expandRotation: Float by animateFloatAsState(
@ -414,8 +413,8 @@ private fun LandscapeKeyboard(
val angleIcon = remember(radianMode) { if (radianMode) IconPack.Rad else IconPack.Deg } val angleIcon = remember(radianMode) { if (radianMode) IconPack.Rad else IconPack.Deg }
val angleIconDescription = remember(radianMode) { if (radianMode) R.string.keyboard_radian else R.string.keyboard_degree } val angleIconDescription = remember(radianMode) { if (radianMode) R.string.keyboard_radian else R.string.keyboard_degree }
val fractionalIcon = remember(fractional) { if (fractional == Token.Digit.dot) IconPack.Dot else IconPack.Comma } val fractionalIcon = remember(fractional) { if (fractional == Token.PERIOD) IconPack.Dot else IconPack.Comma }
val fractionalIconDescription = remember(fractional) { if (fractional == Token.Digit.dot) R.string.keyboard_dot else R.string.comma } val fractionalIconDescription = remember(fractional) { if (fractional == Token.PERIOD) R.string.keyboard_dot else R.string.comma }
Crossfade( Crossfade(
targetState = invMode, targetState = invMode,
@ -544,7 +543,7 @@ private fun PreviewPortraitKeyboard() {
PortraitKeyboard( PortraitKeyboard(
modifier = Modifier.fillMaxHeight(), modifier = Modifier.fillMaxHeight(),
radianMode = true, radianMode = true,
fractional = FormatterSymbols.Comma.fractional, fractional = Token.PERIOD,
addSymbol = {}, addSymbol = {},
clearSymbols = {}, clearSymbols = {},
deleteSymbol = {}, deleteSymbol = {},
@ -564,7 +563,7 @@ private fun PreviewLandscapeKeyboard() {
LandscapeKeyboard( LandscapeKeyboard(
modifier = Modifier.fillMaxHeight(), modifier = Modifier.fillMaxHeight(),
radianMode = true, radianMode = true,
fractional = FormatterSymbols.Comma.fractional, fractional = Token.PERIOD,
addSymbol = {}, addSymbol = {},
clearSymbols = {}, clearSymbols = {},
deleteSymbol = {}, deleteSymbol = {},

View File

@ -47,9 +47,10 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.FixedExpressionInputTextField import com.sadellie.unitto.core.ui.common.textfield.FixedExpressionInputTextField
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.data.model.HistoryItem import com.sadellie.unitto.data.model.HistoryItem
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -225,7 +226,7 @@ private fun PreviewHistoryList() {
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)) .background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f))
.fillMaxSize(), .fillMaxSize(),
historyItems = historyItems, historyItems = historyItems,
formatterSymbols = FormatterSymbols.Spaces, formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
addTokens = {}, addTokens = {},
onDelete = {}, onDelete = {},
showDeleteButtons = true, showDeleteButtons = true,

View File

@ -39,10 +39,10 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.ui.LocalWindowSize import com.sadellie.unitto.core.ui.LocalWindowSize
import com.sadellie.unitto.core.ui.WindowHeightSizeClass import com.sadellie.unitto.core.ui.WindowHeightSizeClass
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.SimpleTextField import com.sadellie.unitto.core.ui.common.textfield.SimpleTextField
import com.sadellie.unitto.feature.calculator.CalculationResult import com.sadellie.unitto.feature.calculator.CalculationResult

View File

@ -70,6 +70,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
@ -80,7 +81,6 @@ import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.PortraitLandscape import com.sadellie.unitto.core.ui.common.PortraitLandscape
import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar import com.sadellie.unitto.core.ui.common.ScaffoldWithTopBar
import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.NumberBaseTextField import com.sadellie.unitto.core.ui.common.textfield.NumberBaseTextField
import com.sadellie.unitto.core.ui.common.textfield.SimpleTextField import com.sadellie.unitto.core.ui.common.textfield.SimpleTextField
import com.sadellie.unitto.core.ui.datetime.formatDateWeekDayMonthYear import com.sadellie.unitto.core.ui.datetime.formatDateWeekDayMonthYear
@ -425,7 +425,7 @@ private fun ConverterResultTextField(
result: ConverterResult, result: ConverterResult,
scale: Int = 0, scale: Int = 0,
outputFormat: Int = OutputFormat.PLAIN, outputFormat: Int = OutputFormat.PLAIN,
formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces, formatterSymbols: FormatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
onErrorClick: () -> Unit = {}, onErrorClick: () -> Unit = {},
) { ) {
val mContext = LocalContext.current val mContext = LocalContext.current

View File

@ -20,9 +20,9 @@ package com.sadellie.unitto.feature.converter
import android.content.Context import android.content.Context
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.format
import com.sadellie.unitto.data.common.isEqualTo import com.sadellie.unitto.data.common.isEqualTo

View File

@ -23,7 +23,6 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.addBracket import com.sadellie.unitto.core.ui.common.textfield.addBracket
import com.sadellie.unitto.core.ui.common.textfield.addTokens import com.sadellie.unitto.core.ui.common.textfield.addTokens
import com.sadellie.unitto.core.ui.common.textfield.deleteTokens import com.sadellie.unitto.core.ui.common.textfield.deleteTokens
@ -94,7 +93,7 @@ internal class ConverterViewModel @Inject constructor(
unitFrom = unitFrom as DefaultUnit, unitFrom = unitFrom as DefaultUnit,
unitTo = unitTo as DefaultUnit, unitTo = unitTo as DefaultUnit,
middleZero = prefs.middleZero, middleZero = prefs.middleZero,
formatterSymbols = AllFormatterSymbols.getById(prefs.separator), formatterSymbols = prefs.formatterSymbols,
scale = prefs.precision, scale = prefs.precision,
outputFormat = prefs.outputFormat, outputFormat = prefs.outputFormat,
formatTime = prefs.unitConverterFormatTime, formatTime = prefs.unitConverterFormatTime,

View File

@ -19,7 +19,7 @@
package com.sadellie.unitto.feature.converter package com.sadellie.unitto.feature.converter
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.UnitsListSorting
import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.unit.AbstractUnit

View File

@ -22,7 +22,6 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.model.repository.UnitsRepository import com.sadellie.unitto.data.model.repository.UnitsRepository
@ -112,7 +111,7 @@ internal class UnitSelectorViewModel @Inject constructor(
sorting = prefs.unitConverterSorting, sorting = prefs.unitConverterSorting,
scale = prefs.precision, scale = prefs.precision,
outputFormat = prefs.outputFormat, outputFormat = prefs.outputFormat,
formatterSymbols = AllFormatterSymbols.getById(prefs.separator), formatterSymbols = prefs.formatterSymbols,
) )
} }
.mapLatest { ui -> .mapLatest { ui ->

View File

@ -28,11 +28,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.EmptyScreen import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.SearchBar import com.sadellie.unitto.core.ui.common.SearchBar
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.format
import com.sadellie.unitto.data.converter.UnitID import com.sadellie.unitto.data.converter.UnitID
@ -189,7 +190,7 @@ private fun UnitToSelectorPreview() {
input = "100", input = "100",
scale = 3, scale = 3,
outputFormat = OutputFormat.PLAIN, outputFormat = OutputFormat.PLAIN,
formatterSymbols = FormatterSymbols.Spaces, formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
), ),
onQueryChange = {}, onQueryChange = {},
toggleFavoritesOnly = {}, toggleFavoritesOnly = {},

View File

@ -66,7 +66,6 @@ import com.sadellie.unitto.core.ui.common.icons.iconpack.Plus
import com.sadellie.unitto.core.ui.common.icons.iconpack.Power import com.sadellie.unitto.core.ui.common.icons.iconpack.Power
import com.sadellie.unitto.core.ui.common.icons.iconpack.RightBracket import com.sadellie.unitto.core.ui.common.icons.iconpack.RightBracket
import com.sadellie.unitto.core.ui.common.icons.iconpack.Root import com.sadellie.unitto.core.ui.common.icons.iconpack.Root
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
@Composable @Composable
internal fun DefaultKeyboard( internal fun DefaultKeyboard(
@ -79,8 +78,8 @@ internal fun DefaultKeyboard(
acButton: Boolean, acButton: Boolean,
addBracket: () -> Unit, addBracket: () -> Unit,
) { ) {
val fractionalIcon = remember(fractional) { if (fractional == Token.Digit.dot) IconPack.Dot else IconPack.Comma } val fractionalIcon = remember(fractional) { if (fractional == Token.PERIOD) IconPack.Dot else IconPack.Comma }
val fractionalIconDescription = remember(fractional) { if (fractional == Token.Digit.dot) R.string.keyboard_dot else R.string.comma } val fractionalIconDescription = remember(fractional) { if (fractional == Token.PERIOD) R.string.keyboard_dot else R.string.comma }
val contentHeight: Float = if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) KeyboardButtonContentHeightShort else KeyboardButtonContentHeightTall val contentHeight: Float = if (LocalWindowSize.current.heightSizeClass < WindowHeightSizeClass.Medium) KeyboardButtonContentHeightShort else KeyboardButtonContentHeightTall
KeypadFlow( KeypadFlow(
@ -178,7 +177,7 @@ private fun PreviewConverterKeyboard() {
addDigit = {}, addDigit = {},
clearInput = {}, clearInput = {},
deleteDigit = {}, deleteDigit = {},
fractional = FormatterSymbols.Spaces.fractional, fractional = Token.PERIOD,
middleZero = false, middleZero = false,
acButton = true, acButton = true,
addBracket = {} addBracket = {}

View File

@ -19,7 +19,8 @@
package com.sadellie.unitto.feature.converter package com.sadellie.unitto.feature.converter
import android.content.Context import android.content.Context
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -32,7 +33,7 @@ class ConverterUIStateKtTest {
@Test @Test
fun format() { fun format() {
val formatterSymbols = FormatterSymbols.Spaces val formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD)
var basicValue = BigDecimal("1") var basicValue = BigDecimal("1")
val mContext: Context = RuntimeEnvironment.getApplication().applicationContext val mContext: Context = RuntimeEnvironment.getApplication().applicationContext

View File

@ -19,7 +19,8 @@
package com.sadellie.unitto.feature.datecalculator.addsubtract package com.sadellie.unitto.feature.datecalculator.addsubtract
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.feature.datecalculator.ZonedDateTimeUtils import com.sadellie.unitto.feature.datecalculator.ZonedDateTimeUtils
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -32,6 +33,6 @@ internal data class AddSubtractState(
val hours: TextFieldValue = TextFieldValue(), val hours: TextFieldValue = TextFieldValue(),
val minutes: TextFieldValue = TextFieldValue(), val minutes: TextFieldValue = TextFieldValue(),
val addition: Boolean = true, val addition: Boolean = true,
val formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces, val formatterSymbols: FormatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
val allowVibration: Boolean = false, val allowVibration: Boolean = false,
) )

View File

@ -22,7 +22,6 @@ import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -46,7 +45,7 @@ internal class AddSubtractViewModel @Inject constructor(
val uiState: StateFlow<AddSubtractState> = _uiState val uiState: StateFlow<AddSubtractState> = _uiState
.combine(userPreferencesRepository.addSubtractPrefs) { uiState, userPrefs -> .combine(userPreferencesRepository.addSubtractPrefs) { uiState, userPrefs ->
return@combine uiState.copy( return@combine uiState.copy(
formatterSymbols = AllFormatterSymbols.getById(userPrefs.separator), formatterSymbols = userPrefs.formatterSymbols,
) )
} }
.onEach { updateResult() } .onEach { updateResult() }

View File

@ -40,9 +40,10 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.format
import com.sadellie.unitto.feature.datecalculator.ZonedDateTimeUtils import com.sadellie.unitto.feature.datecalculator.ZonedDateTimeUtils
@ -185,7 +186,7 @@ fun DateDifferenceViewPreview() {
), ),
precision = 3, precision = 3,
outputFormat = OutputFormat.PLAIN, outputFormat = OutputFormat.PLAIN,
formatterSymbols = FormatterSymbols.Spaces formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD)
), ),
setStartDate = {}, setStartDate = {},
setEndDate = {}, setEndDate = {},

View File

@ -20,7 +20,6 @@ package com.sadellie.unitto.feature.datecalculator.difference
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import com.sadellie.unitto.feature.datecalculator.ZonedDateTimeUtils import com.sadellie.unitto.feature.datecalculator.ZonedDateTimeUtils
@ -55,7 +54,7 @@ internal class DateDifferenceViewModel @Inject constructor(
result = result, result = result,
precision = prefs.digitsPrecision, precision = prefs.digitsPrecision,
outputFormat = prefs.outputFormat, outputFormat = prefs.outputFormat,
formatterSymbols = AllFormatterSymbols.getById(prefs.separator) formatterSymbols = prefs.formatterSymbols
) )
} }
.mapLatest { ui -> .mapLatest { ui ->

View File

@ -18,7 +18,7 @@
package com.sadellie.unitto.feature.datecalculator.difference package com.sadellie.unitto.feature.datecalculator.difference
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
import java.time.ZonedDateTime import java.time.ZonedDateTime
internal sealed class DifferenceUIState { internal sealed class DifferenceUIState {

View File

@ -54,9 +54,8 @@ import androidx.glance.text.Text
import androidx.glance.text.TextAlign import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProvider import androidx.glance.unit.ColorProvider
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import com.sadellie.unitto.data.model.userprefs.CalculatorPreferences import com.sadellie.unitto.data.model.userprefs.CalculatorPreferences
@ -139,7 +138,7 @@ private fun ReadyUI(
val input = glancePrefs[CalculatorWidget.inputPrefKey] ?: "" val input = glancePrefs[CalculatorWidget.inputPrefKey] ?: ""
val output = glancePrefs[CalculatorWidget.outputPrefKey] ?: "" val output = glancePrefs[CalculatorWidget.outputPrefKey] ?: ""
val equalClicked = glancePrefs[CalculatorWidget.equalClickedPrefKey] ?: false val equalClicked = glancePrefs[CalculatorWidget.equalClickedPrefKey] ?: false
val formatterSymbols = AllFormatterSymbols.getById(appPrefs.separator) val formatterSymbols = appPrefs.formatterSymbols
fun runCalculateAction( fun runCalculateAction(
input: String, input: String,

View File

@ -28,9 +28,10 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.EmptyScreen import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.ListItem import com.sadellie.unitto.core.ui.common.ListItem
import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.NavigateUpButton
@ -83,7 +84,7 @@ private fun PreviewCalculatorSettingsScreenStandard() {
CalculatorSettingsScreen( CalculatorSettingsScreen(
prefs = CalculatorPreferencesImpl( prefs = CalculatorPreferencesImpl(
radianMode = true, radianMode = true,
separator = Separator.SPACE, formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
middleZero = false, middleZero = false,
acButton = false, acButton = false,
partialHistoryView = false, partialHistoryView = false,

View File

@ -34,9 +34,10 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.EmptyScreen import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.ListItem import com.sadellie.unitto.core.ui.common.ListItem
import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.NavigateUpButton
@ -133,7 +134,7 @@ private fun ConverterSettingsScreen(
private fun PreviewConverterSettingsScreen() { private fun PreviewConverterSettingsScreen() {
ConverterSettingsScreen( ConverterSettingsScreen(
prefs = ConverterPreferencesImpl( prefs = ConverterPreferencesImpl(
separator = Separator.SPACE, formatterSymbols = FormatterSymbols(Token.SPACE, Token.PERIOD),
middleZero = false, middleZero = false,
precision = 3, precision = 3,
outputFormat = OutputFormat.PLAIN, outputFormat = OutputFormat.PLAIN,

View File

@ -18,13 +18,18 @@
package com.sadellie.unitto.feature.settings.formatting package com.sadellie.unitto.feature.settings.formatting
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -38,6 +43,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -48,10 +54,11 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.FormatterSymbols
import com.sadellie.unitto.core.base.MAX_PRECISION import com.sadellie.unitto.core.base.MAX_PRECISION
import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.OutputFormat
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.EmptyScreen import com.sadellie.unitto.core.ui.common.EmptyScreen
import com.sadellie.unitto.core.ui.common.ListItem import com.sadellie.unitto.core.ui.common.ListItem
import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.NavigateUpButton
@ -60,7 +67,6 @@ import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar
import com.sadellie.unitto.core.ui.common.SegmentedButton import com.sadellie.unitto.core.ui.common.SegmentedButton
import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow
import com.sadellie.unitto.core.ui.common.Slider import com.sadellie.unitto.core.ui.common.Slider
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.core.ui.common.textfield.formatExpression
import com.sadellie.unitto.core.ui.theme.LocalNumberTypography import com.sadellie.unitto.core.ui.theme.LocalNumberTypography
import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.common.format
@ -79,7 +85,7 @@ fun FormattingRoute(
navigateUpAction = navigateUpAction, navigateUpAction = navigateUpAction,
uiState = uiState, uiState = uiState,
onPrecisionChange = viewModel::updatePrecision, onPrecisionChange = viewModel::updatePrecision,
onSeparatorChange = viewModel::updateSeparator, updateFormatterSymbols = viewModel::updateFormatterSymbols,
onOutputFormatChange = viewModel::updateOutputFormat, onOutputFormatChange = viewModel::updateOutputFormat,
) )
} }
@ -91,7 +97,7 @@ fun FormattingScreen(
navigateUpAction: () -> Unit, navigateUpAction: () -> Unit,
uiState: FormattingUIState, uiState: FormattingUIState,
onPrecisionChange: (Int) -> Unit, onPrecisionChange: (Int) -> Unit,
onSeparatorChange: (Int) -> Unit, updateFormatterSymbols: (grouping: String, fractional: String) -> Unit,
onOutputFormatChange: (Int) -> Unit, onOutputFormatChange: (Int) -> Unit,
precisions: ClosedFloatingPointRange<Float> = 0f..16f, // 16th is a MAX_PRECISION (1000) precisions: ClosedFloatingPointRange<Float> = 0f..16f, // 16th is a MAX_PRECISION (1000)
) { ) {
@ -114,143 +120,162 @@ fun FormattingScreen(
title = stringResource(R.string.settings_formatting), title = stringResource(R.string.settings_formatting),
navigationIcon = { NavigateUpButton(navigateUpAction) }, navigationIcon = { NavigateUpButton(navigateUpAction) },
) { paddingValues -> ) { paddingValues ->
LazyColumn( Column(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
) { ) {
item("preview") { PagedIsland(
PagedIsland( modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
pagerState = rememberPagerState { 2 },
) { currentPage ->
val preview = when (currentPage) {
0 -> "123456.${"789123456".repeat(ceil(uiState.precision.toDouble() / 9.0).toInt())}"
1 -> "0.${"1".padStart(uiState.precision, '0')}"
else -> ""
}
.toBigDecimalOrNull()
?.format(uiState.precision, uiState.outputFormat)
?.formatExpression(uiState.formatterSymbols)
?: ""
Text(
text = preview,
style = LocalNumberTypography.current.displayMedium,
maxLines = 1,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp), .horizontalScroll(rememberScrollState()),
pagerState = rememberPagerState { 2 }, textAlign = TextAlign.End,
) { currentPage -> color = MaterialTheme.colorScheme.onSecondaryContainer
val preview = when (currentPage) { )
0 -> "123456.${"789123456".repeat(ceil(uiState.precision.toDouble() / 9.0).toInt())}" }
1 -> "0.${"1".padStart(uiState.precision, '0')}"
else -> ""
}
.toBigDecimalOrNull()
?.format(uiState.precision, uiState.outputFormat)
?.formatExpression(uiState.formatterSymbols)
?: ""
Text( ListItem(
text = preview, leadingContent = {
style = LocalNumberTypography.current.displayMedium, Icon(
maxLines = 1, Icons.Default.Architecture,
modifier = Modifier stringResource(R.string.settings_precision)
.fillMaxWidth() )
.horizontalScroll(rememberScrollState()), },
textAlign = TextAlign.End, headlineContent = {
color = MaterialTheme.colorScheme.onSecondaryContainer Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.settings_precision))
Text(precisionText)
}
},
supportingContent = {
Text(stringResource(R.string.settings_precision_support))
}
)
Slider(
modifier = Modifier.padding(start = 56.dp, end = 16.dp),
value = uiState.precision.toFloat(),
valueRange = precisions,
onValueChange = { onPrecisionChange(it.roundToInt()) },
)
ListItem(
leadingContent = {
Icon(Icons.Default._123, stringResource(R.string.settings_thousands_separator))
},
headlineContent = { Text(stringResource(R.string.settings_thousands_separator)) },
)
Row(
Modifier
.horizontalScroll(rememberScrollState())
.wrapContentWidth()
.padding(start = 56.dp)
) {
SegmentedButtonsRow {
SegmentedButton(
label = stringResource(R.string.settings_space),
onClick = { updateFormatterSymbols(Token.SPACE, uiState.formatterSymbols.fractional) },
selected = uiState.formatterSymbols.grouping == Token.SPACE
)
SegmentedButton(
label = stringResource(R.string.settings_period),
onClick = { updateFormatterSymbols(Token.PERIOD, Token.COMMA) },
selected = uiState.formatterSymbols.grouping == Token.PERIOD,
)
SegmentedButton(
label = stringResource(R.string.comma),
onClick = { updateFormatterSymbols(Token.COMMA, Token.PERIOD) },
selected = uiState.formatterSymbols.grouping == Token.COMMA,
) )
} }
} }
item("precision_label") { AnimatedVisibility(
ListItem( visible = uiState.formatterSymbols.grouping == Token.SPACE,
leadingContent = { enter = expandVertically() + fadeIn(),
Icon( exit = shrinkVertically() + fadeOut()
Icons.Default.Architecture, ) {
stringResource(R.string.settings_precision) Column(
) modifier = Modifier.padding(start = 40.dp)
},
headlineContent = {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.settings_precision))
Text(precisionText)
}
},
supportingContent = {
Text(stringResource(R.string.settings_precision_support))
}
)
}
item("precision_slider") {
Slider(
modifier = Modifier.padding(start = 56.dp, end = 16.dp),
value = uiState.precision.toFloat(),
valueRange = precisions,
onValueChange = { onPrecisionChange(it.roundToInt()) },
)
}
item("separator_label") {
ListItem(
leadingContent = {
Icon(Icons.Default._123, stringResource(R.string.settings_separator))
},
headlineContent = { Text(stringResource(R.string.settings_separator)) },
supportingContent = { Text(stringResource(R.string.settings_separator_support)) },
)
}
item("separator") {
Row(
Modifier
.horizontalScroll(rememberScrollState())
.wrapContentWidth()
.padding(start = 56.dp)
) { ) {
SegmentedButtonsRow { ListItem(
SegmentedButton( modifier = Modifier,
label = stringResource(R.string.settings_space), headlineContent = { Text(stringResource(R.string.settings_decimal_separator)) },
onClick = { onSeparatorChange(Separator.SPACE) }, )
selected = Separator.SPACE == uiState.separator Row(
) Modifier
SegmentedButton( .horizontalScroll(rememberScrollState())
label = stringResource(R.string.settings_period), .wrapContentWidth()
onClick = { onSeparatorChange(Separator.PERIOD) }, .padding(start = 16.dp)
selected = Separator.PERIOD == uiState.separator ) {
) SegmentedButtonsRow {
SegmentedButton( SegmentedButton(
label = stringResource(R.string.comma), label = stringResource(R.string.settings_period),
onClick = { onSeparatorChange(Separator.COMMA) }, onClick = { updateFormatterSymbols(Token.SPACE, Token.PERIOD) },
selected = Separator.COMMA == uiState.separator selected = uiState.formatterSymbols.fractional == Token.PERIOD,
) )
SegmentedButton(
label = stringResource(R.string.comma),
onClick = { updateFormatterSymbols(Token.SPACE, Token.COMMA) },
selected = uiState.formatterSymbols.fractional == Token.COMMA,
)
}
} }
} }
} }
item("output_format_label") { ListItem(
ListItem( leadingContent = {
leadingContent = { Icon(Icons.Default.EMobiledata, stringResource(R.string.settings_precision))
Icon(Icons.Default.EMobiledata, stringResource(R.string.settings_precision)) },
}, headlineContent = { Text(stringResource(R.string.settings_exponential_notation)) },
headlineContent = { Text(stringResource(R.string.settings_exponential_notation)) }, supportingContent = { Text(stringResource(R.string.settings_exponential_notation_support)) }
supportingContent = { Text(stringResource(R.string.settings_exponential_notation_support)) } )
)
}
item("output_format") { Row(
Row( Modifier
Modifier .horizontalScroll(rememberScrollState())
.horizontalScroll(rememberScrollState()) .wrapContentWidth()
.wrapContentWidth() .padding(start = 56.dp)
.padding(start = 56.dp) ) {
) { SegmentedButtonsRow {
SegmentedButtonsRow { SegmentedButton(
SegmentedButton( label = stringResource(R.string.settings_auto),
label = stringResource(R.string.settings_auto), onClick = { onOutputFormatChange(OutputFormat.ALLOW_ENGINEERING) },
onClick = { onOutputFormatChange(OutputFormat.ALLOW_ENGINEERING) }, selected = OutputFormat.ALLOW_ENGINEERING == uiState.outputFormat
selected = OutputFormat.ALLOW_ENGINEERING == uiState.outputFormat )
) SegmentedButton(
SegmentedButton( label = stringResource(R.string.enabled_label),
label = stringResource(R.string.enabled_label), onClick = { onOutputFormatChange(OutputFormat.FORCE_ENGINEERING) },
onClick = { onOutputFormatChange(OutputFormat.FORCE_ENGINEERING) }, selected = OutputFormat.FORCE_ENGINEERING == uiState.outputFormat
selected = OutputFormat.FORCE_ENGINEERING == uiState.outputFormat )
) SegmentedButton(
SegmentedButton( label = stringResource(R.string.disabled_label),
label = stringResource(R.string.disabled_label), onClick = { onOutputFormatChange(OutputFormat.PLAIN) },
onClick = { onOutputFormatChange(OutputFormat.PLAIN) }, selected = OutputFormat.PLAIN == uiState.outputFormat
selected = OutputFormat.PLAIN == uiState.outputFormat )
)
}
} }
} }
} }
@ -261,18 +286,19 @@ fun FormattingScreen(
@Composable @Composable
private fun PreviewFormattingScreen() { private fun PreviewFormattingScreen() {
var currentPrecision by remember { mutableIntStateOf(6) } var currentPrecision by remember { mutableIntStateOf(6) }
var currentSeparator by remember { mutableIntStateOf(Separator.COMMA) } var currentFormatterSymbols by remember { mutableStateOf(FormatterSymbols(Token.SPACE, Token.PERIOD)) }
var currentOutputFormat by remember { mutableIntStateOf(OutputFormat.PLAIN) } var currentOutputFormat by remember { mutableIntStateOf(OutputFormat.PLAIN) }
FormattingScreen( FormattingScreen(
uiState = FormattingUIState( uiState = FormattingUIState(
precision = 16, precision = 16,
separator = Separator.SPACE,
outputFormat = OutputFormat.PLAIN, outputFormat = OutputFormat.PLAIN,
formatterSymbols = FormatterSymbols.Spaces formatterSymbols = currentFormatterSymbols
), ),
onPrecisionChange = { currentPrecision = it }, onPrecisionChange = { currentPrecision = it },
onSeparatorChange = { currentSeparator = it }, updateFormatterSymbols = updateFormatterSymbols@{ grouping, fractional ->
currentFormatterSymbols = FormatterSymbols(grouping, fractional)
},
onOutputFormatChange = { currentOutputFormat = it }, onOutputFormatChange = { currentOutputFormat = it },
navigateUpAction = {}, navigateUpAction = {},
) )

View File

@ -18,11 +18,10 @@
package com.sadellie.unitto.feature.settings.formatting package com.sadellie.unitto.feature.settings.formatting
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.base.FormatterSymbols
data class FormattingUIState( data class FormattingUIState(
val precision: Int, val precision: Int,
val separator: Int,
val outputFormat: Int, val outputFormat: Int,
val formatterSymbols: FormatterSymbols, val formatterSymbols: FormatterSymbols,
) )

View File

@ -21,7 +21,6 @@ package com.sadellie.unitto.feature.settings.formatting
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.core.base.MAX_PRECISION import com.sadellie.unitto.core.base.MAX_PRECISION
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.stateIn
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -38,9 +37,8 @@ class FormattingViewModel @Inject constructor(
val uiState = _prefs.map { mainPrefs -> val uiState = _prefs.map { mainPrefs ->
FormattingUIState( FormattingUIState(
precision = mainPrefs.digitsPrecision, precision = mainPrefs.digitsPrecision,
separator = mainPrefs.separator,
outputFormat = mainPrefs.outputFormat, outputFormat = mainPrefs.outputFormat,
formatterSymbols = AllFormatterSymbols.getById(mainPrefs.separator) formatterSymbols = mainPrefs.formatterSymbols
) )
} }
.stateIn(viewModelScope, null) .stateIn(viewModelScope, null)
@ -55,10 +53,10 @@ class FormattingViewModel @Inject constructor(
} }
/** /**
* @see UserPreferencesRepository.updateSeparator * @see UserPreferencesRepository.updateFormatterSymbols
*/ */
fun updateSeparator(separator: Int) = viewModelScope.launch { fun updateFormatterSymbols(grouping: String, fractional: String) = viewModelScope.launch {
userPreferencesRepository.updateSeparator(separator) userPreferencesRepository.updateFormatterSymbols(grouping, fractional)
} }
/** /**