From fb1b7fab2b90692afa945db2f110fa7fac8831d1 Mon Sep 17 00:00:00 2001 From: Sad Ellie Date: Wed, 11 Oct 2023 23:16:22 +0300 Subject: [PATCH] Auto brackets closes: #41 --- core/base/src/main/res/values/strings.xml | 6 ++ .../unitto/core/ui/common/KeyboardButton.kt | 21 ++++++ .../core/ui/common/textfield/CursorFixer.kt | 26 ++++---- .../textfield/TextFieldValueExtensions.kt | 66 ++++++++++++++++--- .../core/ui/TextFieldValueExtensionsTest.kt | 58 ++++++++++++++++ .../unitto/data/userprefs/PreferenceModels.kt | 3 + .../unitto/data/userprefs/PrefsKeys.kt | 1 + .../unitto/data/userprefs/UserPreferences.kt | 15 ++++- .../feature/calculator/CalculatorScreen.kt | 13 +++- .../feature/calculator/CalculatorUIState.kt | 1 + .../feature/calculator/CalculatorViewModel.kt | 6 +- .../components/CalculatorKeyboard.kt | 44 ++++++++++--- .../feature/converter/ConverterScreen.kt | 11 +++- .../feature/converter/ConverterUIState.kt | 1 + .../feature/converter/ConverterViewModel.kt | 10 ++- .../converter/components/ConverterKeyboard.kt | 18 ++++- .../calculator/CalculatorSettingsScreen.kt | 3 +- .../converter/ConverterSettingsScreen.kt | 1 + .../feature/settings/display/DisplayScreen.kt | 17 +++++ .../settings/display/DisplayViewModel.kt | 24 ++----- 20 files changed, 285 insertions(+), 60 deletions(-) diff --git a/core/base/src/main/res/values/strings.xml b/core/base/src/main/res/values/strings.xml index 1144d3c6..de34f1ce 100644 --- a/core/base/src/main/res/values/strings.xml +++ b/core/base/src/main/res/values/strings.xml @@ -81,6 +81,12 @@ Used in this dialog window. Should be short --> Select time About Unitto Learn about the app + + + AC button + + + Show separate clear button Additional AMOLED Dark Use black background for dark themes diff --git a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/KeyboardButton.kt b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/KeyboardButton.kt index e76a4e6b..a77994a7 100644 --- a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/KeyboardButton.kt +++ b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/KeyboardButton.kt @@ -140,3 +140,24 @@ fun KeyboardButtonAdditional( allowVibration = allowVibration, ) } + +@Composable +fun KeyboardButtonTertiary( + modifier: Modifier, + icon: ImageVector, + allowVibration: Boolean, + contentHeight: Float = if (isPortrait()) 0.578f else 0.793f, + onLongClick: (() -> Unit)? = null, + onClick: () -> Unit, +) { + BasicKeyboardButton( + modifier = modifier, + contentHeight = contentHeight, + onClick = onClick, + onLongClick = onLongClick, + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + icon = icon, + iconColor = MaterialTheme.colorScheme.onTertiaryContainer, + allowVibration = allowVibration, + ) +} diff --git a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/CursorFixer.kt b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/CursorFixer.kt index acbd5134..010f8876 100644 --- a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/CursorFixer.kt +++ b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/CursorFixer.kt @@ -85,14 +85,14 @@ private fun Int.isAfterToken(str: String, token: String): Boolean { } -// This can make [TextFieldValue.addTokens] better by checking tokens both ways. Needs more tests -//fun String.tokenAfter(pos: Int): String { -// Token.Func.allWithOpeningBracket.forEach { -// if (pos.isBeforeToken(this, it)) return it -// } -// -// return substring(pos, (pos + 1).coerceAtMost(this.length)) -//} +// This can also make [TextFieldValue.addTokens] better by checking tokens both ways. Needs more tests +fun String.tokenAfter(pos: Int): String { + Token.Func.allWithOpeningBracket.forEach { + if (pos.isBeforeToken(this, it)) return it + } + + return substring(pos, (pos + 1).coerceAtMost(this.length)) +} //private fun String.numberNearby(cursor: Int): String { // val text = this @@ -111,8 +111,8 @@ private fun Int.isAfterToken(str: String, token: String): Boolean { // return text.substring(aheadCursor, afterCursor) //} -//private fun Int.isBeforeToken(str: String, token: String): Boolean { -// return str -// .substring(this, (this + token.length).coerceAtMost(str.length)) -// .contains(token) -//} +private fun Int.isBeforeToken(str: String, token: String): Boolean { + return str + .substring(this, (this + token.length).coerceAtMost(str.length)) + .contains(token) +} diff --git a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/TextFieldValueExtensions.kt b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/TextFieldValueExtensions.kt index d97b1e8b..3df15e5b 100644 --- a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/TextFieldValueExtensions.kt +++ b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/TextFieldValueExtensions.kt @@ -53,15 +53,52 @@ fun TextFieldValue.addTokens(tokens: String): TextFieldValue { ) } -/** - * !!! Recursive !!! (one wrong step and you are dead 💀) - */ -private fun TextFieldValue.deleteAheadAndAdd(tokens: String): TextFieldValue { - var newValue = this - if (!selection.collapsed) newValue = this.deleteTokens() - return newValue - .deleteTokens() - .addTokens(tokens) +fun TextFieldValue.addBracket(): TextFieldValue { + val subStringBeforeCursor = text.substring(0..!!! Recursive !!! (one wrong step and you are dead 💀) + */ +private fun TextFieldValue.deleteAheadAndAdd(tokens: String): TextFieldValue { + var newValue = this + if (!selection.collapsed) newValue = this.deleteTokens() + return newValue + .deleteTokens() + .addTokens(tokens) +} diff --git a/core/ui/src/test/java/com/sadellie/unitto/core/ui/TextFieldValueExtensionsTest.kt b/core/ui/src/test/java/com/sadellie/unitto/core/ui/TextFieldValueExtensionsTest.kt index 4c419be3..8e350164 100644 --- a/core/ui/src/test/java/com/sadellie/unitto/core/ui/TextFieldValueExtensionsTest.kt +++ b/core/ui/src/test/java/com/sadellie/unitto/core/ui/TextFieldValueExtensionsTest.kt @@ -21,6 +21,7 @@ package com.sadellie.unitto.core.ui import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import com.sadellie.unitto.core.base.Token +import com.sadellie.unitto.core.ui.common.textfield.addBracket import com.sadellie.unitto.core.ui.common.textfield.addTokens import org.junit.Assert.assertEquals import org.junit.Test @@ -260,6 +261,60 @@ class TextFieldValueExtensionsTest { assertEquals(tf("123+45.[]78"), tf("123+45[6.]78").addTokens(Token.Digit.dot)) } + @Test + fun auto() { + // Open on empty in front + assertEquals(tf("([]"), tf("[]").addBracket()) + assertEquals(tf("([]123("), tf("[]123(").addBracket()) + assertEquals(tf("([]("), tf("[123](").addBracket()) + assertEquals(tf("([]"), tf("[123(]").addBracket()) + + // Close before multiply + assertEquals(tf("123)[]*456"), tf("123[]*456").addBracket()) + assertEquals(tf("(123)[]*456"), tf("(123[]*456").addBracket()) + assertEquals(tf(")123)[]*456"), tf(")123[]*456").addBracket()) + // Close before divide + assertEquals(tf("123)[]/456"), tf("123[]/456").addBracket()) + assertEquals(tf("(123)[]/456"), tf("(123[]/456").addBracket()) + assertEquals(tf(")123)[]/456"), tf(")123[]/456").addBracket()) + // Close before plus + assertEquals(tf("123)[]+456"), tf("123[]+456").addBracket()) + assertEquals(tf("(123)[]+456"), tf("(123[]+456").addBracket()) + assertEquals(tf(")123)[]+456"), tf(")123[]+456").addBracket()) + // Close before minus + assertEquals(tf("123)[]-456"), tf("123[]-456").addBracket()) + assertEquals(tf("(123)[]-456"), tf("(123[]-456").addBracket()) + assertEquals(tf(")123)[]-456"), tf(")123[]-456").addBracket()) + // Close before power + assertEquals(tf("123)[]^456"), tf("123[]^456").addBracket()) + assertEquals(tf("(123)[]^456"), tf("(123[]^456").addBracket()) + assertEquals(tf(")123)[]^456"), tf(")123[]^456").addBracket()) + + // Open on balanced in front + assertEquals(tf("123([]"), tf("123[]").addBracket()) + assertEquals(tf("123([](((("), tf("123[]((((").addBracket()) + + // Open after multiply + assertEquals(tf("123*([]456"), tf("123*[]456").addBracket()) + assertEquals(tf("123*([]"), tf("123*[456]").addBracket()) + // Open after divide + assertEquals(tf("123/([]456"), tf("123/[]456").addBracket()) + assertEquals(tf("123/([]"), tf("123/[456]").addBracket()) + // Open after plus + assertEquals(tf("123+([]456"), tf("123+[]456").addBracket()) + assertEquals(tf("123+([]"), tf("123+[456]").addBracket()) + // Open after minus + assertEquals(tf("123-([]456"), tf("123-[]456").addBracket()) + assertEquals(tf("123-([]"), tf("123-[456]").addBracket()) + // Open after power + assertEquals(tf("123^([]456"), tf("123^[]456").addBracket()) + assertEquals(tf("123^([]"), tf("123^[456]").addBracket()) + + // Default + assertEquals(tf("123([]"), tf("123[]").addBracket()) + assertEquals(tf("123(456+789)[]"), tf("123(456+789[]").addBracket()) + } + // Use [] for selection private fun tf( text: String = "", @@ -267,6 +322,9 @@ class TextFieldValueExtensionsTest { val selectionStart = text.indexOf("[") val selectionEnd = text.indexOf("]") - 1 + if (selectionStart < 0) throw Exception("forgot selectionStart") + if (selectionEnd < 0) throw Exception("forgot selectionEnd") + return TextFieldValue( text = text .replace("[", "") diff --git a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PreferenceModels.kt b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PreferenceModels.kt index 9a0ecb47..cf45c596 100644 --- a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PreferenceModels.kt +++ b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PreferenceModels.kt @@ -45,6 +45,7 @@ data class CalculatorPreferences( val enableVibrations: Boolean, val separator: Int, val middleZero: Boolean, + val acButton: Boolean, val partialHistoryView: Boolean, val precision: Int, val outputFormat: Int, @@ -54,6 +55,7 @@ data class ConverterPreferences( val enableVibrations: Boolean, val separator: Int, val middleZero: Boolean, + val acButton: Boolean, val precision: Int, val outputFormat: Int, val unitConverterFormatTime: Boolean, @@ -68,6 +70,7 @@ data class ConverterPreferences( data class DisplayPreferences( val systemFont: Boolean, val middleZero: Boolean, + val acButton: Boolean, ) data class FormattingPreferences( diff --git a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PrefsKeys.kt b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PrefsKeys.kt index 36a4a18f..156a6c44 100644 --- a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PrefsKeys.kt +++ b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PrefsKeys.kt @@ -46,4 +46,5 @@ internal object PrefsKeys { val MIDDLE_ZERO = booleanPreferencesKey("MIDDLE_ZERO_PREF_KEY") val SYSTEM_FONT = booleanPreferencesKey("SYSTEM_FONT_PREF_KEY") val PARTIAL_HISTORY_VIEW = booleanPreferencesKey("PARTIAL_HISTORY_VIEW_PREF_KEY") + val AC_BUTTON = booleanPreferencesKey("AC_BUTTON_PREF_KEY") } diff --git a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt index a988391d..45912ddb 100644 --- a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt +++ b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt @@ -75,7 +75,8 @@ class UserPreferencesRepository @Inject constructor( middleZero = preferences.getMiddleZero(), partialHistoryView = preferences.getPartialHistoryView(), precision = preferences.getDigitsPrecision(), - outputFormat = preferences.getOutputFormat() + outputFormat = preferences.getOutputFormat(), + acButton = preferences.getAcButton(), ) } @@ -94,6 +95,7 @@ class UserPreferencesRepository @Inject constructor( enableToolsExperiment = preferences.getEnableToolsExperiment(), latestLeftSideUnit = preferences.getLatestLeftSide(), latestRightSideUnit = preferences.getLatestRightSide(), + acButton = preferences.getAcButton(), ) } @@ -102,6 +104,7 @@ class UserPreferencesRepository @Inject constructor( DisplayPreferences( systemFont = preferences.getSystemFont(), middleZero = preferences.getMiddleZero(), + acButton = preferences.getAcButton(), ) } @@ -263,6 +266,12 @@ class UserPreferencesRepository @Inject constructor( preferences[PrefsKeys.PARTIAL_HISTORY_VIEW] = enabled } } + + suspend fun updateAcButton(enabled: Boolean) { + dataStore.edit { preferences -> + preferences[PrefsKeys.AC_BUTTON] = enabled + } + } } private fun Preferences.getEnableDynamicTheme(): Boolean { @@ -359,6 +368,10 @@ private fun Preferences.getLatestRightSide(): String { return this[PrefsKeys.LATEST_RIGHT_SIDE] ?: MyUnitIDS.mile } +private fun Preferences.getAcButton(): Boolean { + return this[PrefsKeys.AC_BUTTON] ?: false +} + private inline fun T.letTryOrNull(block: (T) -> R): R? = try { this?.let(block) } catch (e: Exception) { diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt index 20484ca3..2fa0d461 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt @@ -89,7 +89,8 @@ internal fun CalculatorRoute( onCursorChange = viewModel::onCursorChange, toggleCalculatorMode = viewModel::toggleCalculatorMode, evaluate = viewModel::evaluate, - clearHistory = viewModel::clearHistory + clearHistory = viewModel::clearHistory, + addBracket = viewModel::addBracket ) } @@ -99,6 +100,7 @@ internal fun CalculatorScreen( navigateToMenu: () -> Unit, navigateToSettings: () -> Unit, addTokens: (String) -> Unit, + addBracket: () -> Unit, clearInput: () -> Unit, deleteTokens: () -> Unit, onCursorChange: (TextRange) -> Unit, @@ -118,7 +120,8 @@ internal fun CalculatorScreen( onCursorChange = onCursorChange, toggleAngleMode = toggleCalculatorMode, evaluate = evaluate, - clearHistory = clearHistory + clearHistory = clearHistory, + addBracket = addBracket ) } } @@ -129,6 +132,7 @@ private fun Ready( navigateToMenu: () -> Unit, navigateToSettings: () -> Unit, addSymbol: (String) -> Unit, + addBracket: () -> Unit, clearSymbols: () -> Unit, deleteSymbol: () -> Unit, onCursorChange: (TextRange) -> Unit, @@ -264,6 +268,8 @@ private fun Ready( toggleAngleMode = toggleAngleMode, evaluate = evaluate, middleZero = uiState.middleZero, + acButton = uiState.acButton, + addBracket = addBracket ) } } @@ -343,6 +349,7 @@ private fun PreviewCalculatorScreen() { onCursorChange = {}, toggleCalculatorMode = {}, evaluate = {}, - clearHistory = {} + clearHistory = {}, + addBracket = {} ) } \ No newline at end of file diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorUIState.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorUIState.kt index c1fc01eb..862d4b7a 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorUIState.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorUIState.kt @@ -35,6 +35,7 @@ internal sealed class CalculatorUIState { val allowVibration: Boolean = false, val formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces, val middleZero: Boolean = false, + val acButton: Boolean = false, val partialHistoryView: Boolean = true, ) : CalculatorUIState() } diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt index e4816934..6cc81be5 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt @@ -26,6 +26,7 @@ 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.ui.common.textfield.AllFormatterSymbols +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.deleteTokens import com.sadellie.unitto.data.calculator.CalculatorHistoryRepository @@ -67,7 +68,8 @@ internal class CalculatorViewModel @Inject constructor( middleZero = false, partialHistoryView = true, precision = 3, - outputFormat = OutputFormat.PLAIN + outputFormat = OutputFormat.PLAIN, + acButton = false, ) ) @@ -87,6 +89,7 @@ internal class CalculatorViewModel @Inject constructor( allowVibration = userPrefs.enableVibrations, formatterSymbols = AllFormatterSymbols.getById(userPrefs.separator), middleZero = userPrefs.middleZero, + acButton = userPrefs.acButton, partialHistoryView = userPrefs.partialHistoryView, ) } @@ -95,6 +98,7 @@ internal class CalculatorViewModel @Inject constructor( ) fun addTokens(tokens: String) = _input.update { it.addTokens(tokens) } + fun addBracket() = _input.update { it.addBracket() } fun deleteTokens() = _input.update { it.deleteTokens() } fun clearInput() = _input.update { TextFieldValue() } fun onCursorChange(selection: TextRange) = _input.update { it.copy(selection = selection) } diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/CalculatorKeyboard.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/CalculatorKeyboard.kt index 69f11377..b3475eb5 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/CalculatorKeyboard.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/CalculatorKeyboard.kt @@ -56,12 +56,15 @@ import com.sadellie.unitto.core.ui.common.ColumnWithConstraints import com.sadellie.unitto.core.ui.common.KeyboardButtonAdditional import com.sadellie.unitto.core.ui.common.KeyboardButtonFilled import com.sadellie.unitto.core.ui.common.KeyboardButtonLight +import com.sadellie.unitto.core.ui.common.KeyboardButtonTertiary import com.sadellie.unitto.core.ui.common.RowWithConstraints import com.sadellie.unitto.core.ui.common.key.UnittoIcons import com.sadellie.unitto.core.ui.common.key.unittoicons.AcTan import com.sadellie.unitto.core.ui.common.key.unittoicons.ArCos import com.sadellie.unitto.core.ui.common.key.unittoicons.ArSin import com.sadellie.unitto.core.ui.common.key.unittoicons.Backspace +import com.sadellie.unitto.core.ui.common.key.unittoicons.Brackets +import com.sadellie.unitto.core.ui.common.key.unittoicons.Clear import com.sadellie.unitto.core.ui.common.key.unittoicons.Comma import com.sadellie.unitto.core.ui.common.key.unittoicons.Cos import com.sadellie.unitto.core.ui.common.key.unittoicons.Deg @@ -106,7 +109,9 @@ internal fun CalculatorKeyboard( fractional: String, allowVibration: Boolean, middleZero: Boolean, + acButton: Boolean, addSymbol: (String) -> Unit, + addBracket: () -> Unit, clearSymbols: () -> Unit, deleteSymbol: () -> Unit, toggleAngleMode: () -> Unit, @@ -123,7 +128,9 @@ internal fun CalculatorKeyboard( toggleAngleMode = toggleAngleMode, deleteSymbol = deleteSymbol, clearSymbols = clearSymbols, - evaluate = evaluate + evaluate = evaluate, + acButton = acButton, + addBracket = addBracket, ) } else { LandscapeKeyboard( @@ -136,7 +143,9 @@ internal fun CalculatorKeyboard( toggleAngleMode = toggleAngleMode, deleteSymbol = deleteSymbol, clearSymbols = clearSymbols, - evaluate = evaluate + evaluate = evaluate, + acButton = acButton, + addBracket = addBracket, ) } } @@ -152,7 +161,9 @@ private fun PortraitKeyboard( toggleAngleMode: () -> Unit, deleteSymbol: () -> Unit, clearSymbols: () -> Unit, - evaluate: () -> Unit + evaluate: () -> Unit, + acButton: Boolean, + addBracket: () -> Unit, ) { val fractionalIcon = remember { if (fractional == Token.Digit.dot) UnittoIcons.Dot else UnittoIcons.Comma } var showAdditional: Boolean by remember { mutableStateOf(false) } @@ -239,8 +250,13 @@ private fun PortraitKeyboard( Spacer(modifier = Modifier.height(spacerHeight)) Row(weightModifier) { - KeyboardButtonFilled(mainButtonModifier, UnittoIcons.LeftBracket, allowVibration) { addSymbol(Token.Operator.leftBracket) } - KeyboardButtonFilled(mainButtonModifier, UnittoIcons.RightBracket, allowVibration) { addSymbol(Token.Operator.rightBracket) } + if (acButton) { + KeyboardButtonTertiary(mainButtonModifier, UnittoIcons.Clear, allowVibration) { clearSymbols() } + KeyboardButtonFilled(mainButtonModifier, UnittoIcons.Brackets, allowVibration) { addBracket() } + } else { + KeyboardButtonFilled(mainButtonModifier, UnittoIcons.LeftBracket, allowVibration) { addSymbol(Token.Operator.leftBracket) } + KeyboardButtonFilled(mainButtonModifier, UnittoIcons.RightBracket, allowVibration) { addSymbol(Token.Operator.rightBracket) } + } KeyboardButtonFilled(mainButtonModifier, UnittoIcons.Percent, allowVibration) { addSymbol(Token.Operator.percent) } KeyboardButtonFilled(mainButtonModifier, UnittoIcons.Divide, allowVibration) { addSymbol(Token.Operator.divide) } } @@ -361,7 +377,9 @@ private fun LandscapeKeyboard( toggleAngleMode: () -> Unit, deleteSymbol: () -> Unit, clearSymbols: () -> Unit, - evaluate: () -> Unit + evaluate: () -> Unit, + acButton: Boolean, + addBracket: () -> Unit, ) { val fractionalIcon = remember { if (fractional == Token.Digit.dot) UnittoIcons.Dot else UnittoIcons.Comma } var invMode: Boolean by remember { mutableStateOf(false) } @@ -426,13 +444,21 @@ private fun LandscapeKeyboard( } Column(Modifier.weight(1f)) { - KeyboardButtonFilled(buttonModifier, UnittoIcons.LeftBracket, allowVibration) { addSymbol(Token.Operator.leftBracket) } + if (acButton) { + KeyboardButtonTertiary(buttonModifier, UnittoIcons.Clear, allowVibration) { clearSymbols() } + } else { + KeyboardButtonFilled(buttonModifier, UnittoIcons.LeftBracket, allowVibration) { addSymbol(Token.Operator.leftBracket) } + } KeyboardButtonFilled(buttonModifier, UnittoIcons.Multiply, allowVibration) { addSymbol(Token.Operator.multiply) } KeyboardButtonFilled(buttonModifier, UnittoIcons.Minus, allowVibration) { addSymbol(Token.Operator.minus) } KeyboardButtonFilled(buttonModifier, UnittoIcons.Plus, allowVibration) { addSymbol(Token.Operator.plus) } } Column(Modifier.weight(1f)) { - KeyboardButtonFilled(buttonModifier, UnittoIcons.RightBracket, allowVibration) { addSymbol(Token.Operator.rightBracket) } + if (acButton) { + KeyboardButtonTertiary(buttonModifier, UnittoIcons.Brackets, allowVibration) { addBracket() } + } else { + KeyboardButtonFilled(buttonModifier, UnittoIcons.RightBracket, allowVibration) { addSymbol(Token.Operator.rightBracket) } + } KeyboardButtonFilled(buttonModifier, UnittoIcons.Divide, allowVibration) { addSymbol(Token.Operator.divide) } KeyboardButtonFilled(buttonModifier, UnittoIcons.Percent, allowVibration) { addSymbol(Token.Operator.percent) } KeyboardButtonFilled(buttonModifier, UnittoIcons.Equal, allowVibration) { evaluate() } @@ -518,5 +544,7 @@ private fun PreviewCalculatorKeyboard() { evaluate = {}, allowVibration = false, middleZero = false, + acButton = true, + addBracket = {} ) } diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt index 8a675cd7..de4709fd 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt @@ -110,6 +110,7 @@ internal fun ConverterRoute( clearInput = viewModel::clearInput, onCursorChange = viewModel::onCursorChange, onErrorClick = viewModel::updateCurrencyRates, + addBracket = viewModel::addBracket ) } @@ -126,6 +127,7 @@ private fun ConverterScreen( clearInput: () -> Unit, onCursorChange: (TextRange) -> Unit, onErrorClick: (AbstractUnit) -> Unit, + addBracket: () -> Unit, ) { when (uiState) { UnitConverterUIState.Loading -> UnittoEmptyScreen() @@ -164,7 +166,8 @@ private fun ConverterScreen( swapUnits = swapUnits, navigateToRightScreen = navigateToRightScreen, clearInput = clearInput, - refreshCurrencyRates = onErrorClick + refreshCurrencyRates = onErrorClick, + addBracket = addBracket, ) } } @@ -258,6 +261,7 @@ private fun Default( navigateToRightScreen: () -> Unit, clearInput: () -> Unit, refreshCurrencyRates: (AbstractUnit) -> Unit, + addBracket: () -> Unit, ) { val locale: Locale = LocalLocale.current var calculation by remember(uiState.calculation) { @@ -365,7 +369,9 @@ private fun Default( clearInput = clearInput, allowVibration = uiState.enableHaptic, fractional = uiState.formatterSymbols.fractional, - middleZero = uiState.middleZero + middleZero = uiState.middleZero, + acButton = uiState.acButton, + addBracket = addBracket ) } ) @@ -522,5 +528,6 @@ private fun PreviewConverterScreen() { clearInput = {}, onCursorChange = {}, onErrorClick = {}, + addBracket = {} ) } diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterUIState.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterUIState.kt index e264d76e..cd91c026 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterUIState.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterUIState.kt @@ -46,6 +46,7 @@ internal sealed class UnitConverterUIState { val outputFormat: Int, val formatTime: Boolean, val currencyRateUpdateState: CurrencyRateUpdateState, + val acButton: Boolean, ) : UnitConverterUIState() data class NumberBase( diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt index edc214ab..f07cc8f1 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope 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.addTokens import com.sadellie.unitto.core.ui.common.textfield.deleteTokens import com.sadellie.unitto.data.common.combine @@ -105,7 +106,8 @@ internal class ConverterViewModel @Inject constructor( scale = prefs.precision, outputFormat = prefs.outputFormat, formatTime = prefs.unitConverterFormatTime, - currencyRateUpdateState = currenciesState + currencyRateUpdateState = currenciesState, + acButton = prefs.acButton, ) } (unitFrom is NumberBaseUnit) and (unitTo is NumberBaseUnit) -> { @@ -256,6 +258,12 @@ internal class ConverterViewModel @Inject constructor( newValue } + fun addBracket() = _input.update { + val newValue = it.addBracket() + savedStateHandle[converterInputKey] = newValue.text + newValue + } + fun deleteTokens() = _input.update { val newValue = it.deleteTokens() savedStateHandle[converterInputKey] = newValue.text diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/ConverterKeyboard.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/ConverterKeyboard.kt index b859ae97..b64df4f1 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/ConverterKeyboard.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/components/ConverterKeyboard.kt @@ -31,8 +31,11 @@ import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.ui.common.ColumnWithConstraints import com.sadellie.unitto.core.ui.common.KeyboardButtonFilled import com.sadellie.unitto.core.ui.common.KeyboardButtonLight +import com.sadellie.unitto.core.ui.common.KeyboardButtonTertiary import com.sadellie.unitto.core.ui.common.key.UnittoIcons import com.sadellie.unitto.core.ui.common.key.unittoicons.Backspace +import com.sadellie.unitto.core.ui.common.key.unittoicons.Brackets +import com.sadellie.unitto.core.ui.common.key.unittoicons.Clear import com.sadellie.unitto.core.ui.common.key.unittoicons.Comma import com.sadellie.unitto.core.ui.common.key.unittoicons.Divide import com.sadellie.unitto.core.ui.common.key.unittoicons.Dot @@ -70,6 +73,8 @@ internal fun DefaultKeyboard( allowVibration: Boolean, fractional: String, middleZero: Boolean, + acButton: Boolean, + addBracket: () -> Unit, ) { ColumnWithConstraints(modifier) { val fractionalIcon = remember { if (fractional == Token.Digit.dot) UnittoIcons.Dot else UnittoIcons.Comma } @@ -85,8 +90,13 @@ internal fun DefaultKeyboard( // Column modifier val cModifier = Modifier.weight(1f) Row(cModifier) { - KeyboardButtonFilled(bModifier, UnittoIcons.LeftBracket, allowVibration) { addDigit(Token.Operator.leftBracket) } - KeyboardButtonFilled(bModifier, UnittoIcons.RightBracket, allowVibration) { addDigit(Token.Operator.rightBracket) } + if (acButton) { + KeyboardButtonTertiary(bModifier, UnittoIcons.Clear, allowVibration) { clearInput() } + KeyboardButtonFilled(bModifier, UnittoIcons.Brackets, allowVibration) { addBracket() } + } else { + KeyboardButtonFilled(bModifier, UnittoIcons.LeftBracket, allowVibration) { addDigit(Token.Operator.leftBracket) } + KeyboardButtonFilled(bModifier, UnittoIcons.RightBracket, allowVibration) { addDigit(Token.Operator.rightBracket) } + } KeyboardButtonFilled(bModifier, UnittoIcons.Power, allowVibration) { addDigit(Token.Operator.power) } KeyboardButtonFilled(bModifier, UnittoIcons.Root, allowVibration) { addDigit(Token.Operator.sqrt) } } @@ -188,7 +198,9 @@ private fun PreviewConverterKeyboard() { deleteDigit = {}, allowVibration = false, fractional = FormatterSymbols.Spaces.fractional, - middleZero = false + middleZero = false, + acButton = true, + addBracket = {} ) } diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt index 5fcaf898..1674c6c7 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt @@ -88,7 +88,8 @@ private fun PreviewCalculatorSettingsScreen() { middleZero = false, partialHistoryView = true, precision = 3, - outputFormat = OutputFormat.PLAIN + outputFormat = OutputFormat.PLAIN, + acButton = true, ), navigateUpAction = {}, updatePartialHistoryView = {} diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt index 61b5b4bb..0fa6c17c 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt @@ -147,6 +147,7 @@ private fun PreviewConverterSettingsScreen() { enableToolsExperiment = false, latestLeftSideUnit = "kilometer", latestRightSideUnit = "mile", + acButton = true, ), navigateUpAction = {}, navigateToUnitsGroup = {}, diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayScreen.kt index b6da7f26..77209105 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayScreen.kt @@ -62,6 +62,8 @@ import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar +import com.sadellie.unitto.core.ui.common.key.UnittoIcons +import com.sadellie.unitto.core.ui.common.key.unittoicons.Clear import com.sadellie.unitto.feature.settings.components.ColorSelector import com.sadellie.unitto.feature.settings.components.MonetModeSelector import io.github.sadellie.themmo.MonetMode @@ -113,6 +115,8 @@ internal fun DisplayRoute( }, systemFont = prefs.systemFont, updateSystemFont = viewModel::updateSystemFont, + acButton = prefs.acButton, + updateAcButton = viewModel::updateAcButton, middleZero = prefs.middleZero, updateMiddleZero = viewModel::updateMiddleZero, navigateToLanguages = navigateToLanguages @@ -136,6 +140,8 @@ private fun DisplayScreen( onMonetModeChange: (MonetMode) -> Unit, systemFont: Boolean, updateSystemFont: (Boolean) -> Unit, + acButton: Boolean, + updateAcButton: (Boolean) -> Unit, middleZero: Boolean, updateMiddleZero: (Boolean) -> Unit, navigateToLanguages: () -> Unit, @@ -266,6 +272,15 @@ private fun DisplayScreen( onSwitchChange = updateSystemFont ) + UnittoListItem( + icon = UnittoIcons.Clear, + iconDescription = stringResource(R.string.settings_middle_zero), + headlineText = stringResource(R.string.settings_ac_button), + supportingText = stringResource(R.string.settings_ac_button_support), + switchState = acButton, + onSwitchChange = updateAcButton + ) + UnittoListItem( icon = Icons.Default.ExposureZero, iconDescription = stringResource(R.string.settings_middle_zero), @@ -304,6 +319,8 @@ private fun Preview() { onMonetModeChange = themmoController::setMonetMode, systemFont = false, updateSystemFont = {}, + acButton = false, + updateAcButton = {}, middleZero = false, updateMiddleZero = {}, navigateToLanguages = {} diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayViewModel.kt index 21e9a5d7..7a4ce77c 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayViewModel.kt @@ -37,60 +37,48 @@ class DisplayViewModel @Inject constructor( val prefs = userPrefsRepository.displayPrefs .stateIn(viewModelScope, null) - /** - * @see UserPreferencesRepository.updateThemingMode - */ fun updateThemingMode(themingMode: ThemingMode) { viewModelScope.launch { userPrefsRepository.updateThemingMode(themingMode) } } - /** - * @see UserPreferencesRepository.updateDynamicTheme - */ fun updateDynamicTheme(enabled: Boolean) { viewModelScope.launch { userPrefsRepository.updateDynamicTheme(enabled) } } - /** - * @see UserPreferencesRepository.updateAmoledTheme - */ fun updateAmoledTheme(enabled: Boolean) { viewModelScope.launch { userPrefsRepository.updateAmoledTheme(enabled) } } - /** - * @see UserPreferencesRepository.updateCustomColor - */ fun updateCustomColor(color: Color) { viewModelScope.launch { userPrefsRepository.updateCustomColor(color) } } - /** - * @see UserPreferencesRepository.updateMonetMode - */ fun updateMonetMode(monetMode: MonetMode) { viewModelScope.launch { userPrefsRepository.updateMonetMode(monetMode) } } - /** - * @see UserPreferencesRepository.updateSystemFont - */ fun updateSystemFont(enabled: Boolean) { viewModelScope.launch { userPrefsRepository.updateSystemFont(enabled) } } + fun updateAcButton(enabled: Boolean) { + viewModelScope.launch { + userPrefsRepository.updateAcButton(enabled) + } + } + fun updateMiddleZero(enabled: Boolean) = viewModelScope.launch { userPrefsRepository.updateMiddleZero(enabled) }