diff --git a/app/src/main/java/com/sadellie/unitto/data/KeypadSymbols.kt b/app/src/main/java/com/sadellie/unitto/data/KeypadSymbols.kt index d5c31a51..2f7fb044 100644 --- a/app/src/main/java/com/sadellie/unitto/data/KeypadSymbols.kt +++ b/app/src/main/java/com/sadellie/unitto/data/KeypadSymbols.kt @@ -29,6 +29,13 @@ const val KEY_8 = "8" const val KEY_9 = "9" const val KEY_0 = "0" +const val KEY_BASE_A = "A" +const val KEY_BASE_B = "B" +const val KEY_BASE_C = "C" +const val KEY_BASE_D = "D" +const val KEY_BASE_E = "E" +const val KEY_BASE_F = "F" + const val KEY_DOT = "." const val KEY_COMMA = "," const val KEY_CLEAR = "<" diff --git a/app/src/main/java/com/sadellie/unitto/data/units/AllUnitsRepository.kt b/app/src/main/java/com/sadellie/unitto/data/units/AllUnitsRepository.kt index af9842ef..7e9d7920 100644 --- a/app/src/main/java/com/sadellie/unitto/data/units/AllUnitsRepository.kt +++ b/app/src/main/java/com/sadellie/unitto/data/units/AllUnitsRepository.kt @@ -62,6 +62,7 @@ class AllUnitsRepository @Inject constructor() { UnitGroup.ANGLE to angleCollection, UnitGroup.DATA_TRANSFER to dataTransferCollection, UnitGroup.FLUX to fluxCollection, + UnitGroup.NUMBER_BASE to numberBaseCollection, ) } @@ -762,4 +763,23 @@ class AllUnitsRepository @Inject constructor() { MyUnit(MyUnitIDS.gigaweber, BigDecimal.valueOf(100000000000000000), UnitGroup.FLUX, R.string.gigaweber, R.string.gigaweber_short), ) } + private val numberBaseCollection: List by lazy { + listOf( + NumberBaseUnit(MyUnitIDS.binary, 2, UnitGroup.NUMBER_BASE, R.string.binary, R.string.binary_short), + NumberBaseUnit(MyUnitIDS.ternary, 3, UnitGroup.NUMBER_BASE, R.string.ternary, R.string.ternary_short), + NumberBaseUnit(MyUnitIDS.quaternary, 4, UnitGroup.NUMBER_BASE, R.string.quaternary, R.string.quaternary_short), + NumberBaseUnit(MyUnitIDS.quinary, 5, UnitGroup.NUMBER_BASE, R.string.quinary, R.string.quinary_short), + NumberBaseUnit(MyUnitIDS.senary, 6, UnitGroup.NUMBER_BASE, R.string.senary, R.string.senary_short), + NumberBaseUnit(MyUnitIDS.septenary, 7, UnitGroup.NUMBER_BASE, R.string.septenary, R.string.septenary_short), + NumberBaseUnit(MyUnitIDS.octal, 8, UnitGroup.NUMBER_BASE, R.string.octal, R.string.octal_short), + NumberBaseUnit(MyUnitIDS.nonary, 9, UnitGroup.NUMBER_BASE, R.string.nonary, R.string.nonary_short), + NumberBaseUnit(MyUnitIDS.decimal, 10, UnitGroup.NUMBER_BASE, R.string.decimal, R.string.decimal_short), + NumberBaseUnit(MyUnitIDS.undecimal, 11, UnitGroup.NUMBER_BASE, R.string.undecimal, R.string.undecimal_short), + NumberBaseUnit(MyUnitIDS.duodecimal, 12, UnitGroup.NUMBER_BASE, R.string.duodecimal, R.string.duodecimal_short), + NumberBaseUnit(MyUnitIDS.tridecimal, 13, UnitGroup.NUMBER_BASE, R.string.tridecimal, R.string.tridecimal_short), + NumberBaseUnit(MyUnitIDS.tetradecimal, 14, UnitGroup.NUMBER_BASE, R.string.tetradecimal, R.string.tetradecimal_short), + NumberBaseUnit(MyUnitIDS.pentadecimal, 15, UnitGroup.NUMBER_BASE, R.string.pentadecimal, R.string.pentadecimal_short), + NumberBaseUnit(MyUnitIDS.hexadecimal, 16, UnitGroup.NUMBER_BASE, R.string.hexadecimal, R.string.hexadecimal_short), + ) + } } diff --git a/app/src/main/java/com/sadellie/unitto/data/units/MyUnitIDS.kt b/app/src/main/java/com/sadellie/unitto/data/units/MyUnitIDS.kt index 6a67c0bc..8e1a122b 100644 --- a/app/src/main/java/com/sadellie/unitto/data/units/MyUnitIDS.kt +++ b/app/src/main/java/com/sadellie/unitto/data/units/MyUnitIDS.kt @@ -495,4 +495,21 @@ object MyUnitIDS { const val kiloweber = "kiloweber" const val megaweber = "megaweber" const val gigaweber = "gigaweber" + + // NUMBER BASE + const val binary = "binary" + const val ternary = "ternary" + const val quaternary = "quaternary" + const val quinary = "quinary" + const val senary = "senary" + const val septenary = "septenary" + const val octal = "octal" + const val nonary = "nonary" + const val decimal = "decimal" + const val undecimal = "undecimal" + const val duodecimal = "duodecimal" + const val tridecimal = "tridecimal" + const val tetradecimal = "tetradecimal" + const val pentadecimal = "pentadecimal" + const val hexadecimal = "hexadecimal" } diff --git a/app/src/main/java/com/sadellie/unitto/data/units/NumberBaseUnit.kt b/app/src/main/java/com/sadellie/unitto/data/units/NumberBaseUnit.kt new file mode 100644 index 00000000..58e718c7 --- /dev/null +++ b/app/src/main/java/com/sadellie/unitto/data/units/NumberBaseUnit.kt @@ -0,0 +1,43 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2023 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 . + */ + +package com.sadellie.unitto.data.units + +import androidx.annotation.StringRes +import java.math.BigDecimal + +class NumberBaseUnit( + unitId: String, + val base: Int, + group: UnitGroup, + @StringRes displayName: Int, + @StringRes shortName: Int, +) : AbstractUnit( + unitId = unitId, + displayName = displayName, + shortName = shortName, + basicUnit = BigDecimal.ONE, + group = group, +) { + override fun convert(unitTo: AbstractUnit, value: BigDecimal, scale: Int): BigDecimal = this.basicUnit + + fun convertToBase(input: String, toBase: Int): String { + return input.toBigInteger(base).toString(toBase) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/sadellie/unitto/data/units/UnitGroup.kt b/app/src/main/java/com/sadellie/unitto/data/units/UnitGroup.kt index 69677836..5a03a21c 100644 --- a/app/src/main/java/com/sadellie/unitto/data/units/UnitGroup.kt +++ b/app/src/main/java/com/sadellie/unitto/data/units/UnitGroup.kt @@ -49,4 +49,5 @@ enum class UnitGroup( ANGLE(res = R.string.angle), DATA_TRANSFER(res = R.string.data_transfer), FLUX(res = R.string.flux), + NUMBER_BASE(res = R.string.number_base), } diff --git a/app/src/main/java/com/sadellie/unitto/screens/main/MainScreen.kt b/app/src/main/java/com/sadellie/unitto/screens/main/MainScreen.kt index 5137a6be..85830544 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/main/MainScreen.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/main/MainScreen.kt @@ -40,6 +40,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.sadellie.unitto.R import com.sadellie.unitto.data.NavRoutes.SETTINGS_SCREEN import com.sadellie.unitto.data.units.AbstractUnit +import com.sadellie.unitto.data.units.UnitGroup import com.sadellie.unitto.screens.common.AnimatedTopBarText import com.sadellie.unitto.screens.main.components.Keyboard import com.sadellie.unitto.screens.main.components.TopScreenPart @@ -81,8 +82,10 @@ fun MainScreen( navControllerAction = { navControllerAction(it) }, swapMeasurements = { viewModel.swapUnits() }, processInput = { viewModel.processInput(it) }, - deleteDigit = { viewModel.deleteDigit() } - ) { viewModel.clearInput() } + deleteDigit = { viewModel.deleteDigit() }, + clearInput = { viewModel.clearInput() }, + baseConverterMode = viewModel.unitFrom.group == UnitGroup.NUMBER_BASE + ) } ) @@ -108,6 +111,7 @@ private fun MainScreenContent( processInput: (String) -> Unit = {}, deleteDigit: () -> Unit = {}, clearInput: () -> Unit = {}, + baseConverterMode: Boolean, ) { PortraitLandscape( modifier = modifier, @@ -123,7 +127,8 @@ private fun MainScreenContent( loadingNetwork = mainScreenUIState.isLoadingNetwork, networkError = mainScreenUIState.showError, onUnitSelectionClick = navControllerAction, - swapUnits = swapMeasurements + swapUnits = swapMeasurements, + baseConverterMode = baseConverterMode, ) }, content2 = { @@ -132,6 +137,7 @@ private fun MainScreenContent( addDigit = processInput, deleteDigit = deleteDigit, clearInput = clearInput, + baseConverter = baseConverterMode, ) } ) diff --git a/app/src/main/java/com/sadellie/unitto/screens/main/MainViewModel.kt b/app/src/main/java/com/sadellie/unitto/screens/main/MainViewModel.kt index e77ea5b7..59dbaf9d 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/main/MainViewModel.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/main/MainViewModel.kt @@ -49,19 +49,20 @@ import com.sadellie.unitto.data.KEY_PLUS import com.sadellie.unitto.data.KEY_RIGHT_BRACKET import com.sadellie.unitto.data.KEY_SQRT import com.sadellie.unitto.data.OPERATORS +import com.sadellie.unitto.data.combine import com.sadellie.unitto.data.preferences.UserPreferences import com.sadellie.unitto.data.preferences.UserPreferencesRepository +import com.sadellie.unitto.data.toStringWith +import com.sadellie.unitto.data.trimZeros import com.sadellie.unitto.data.units.AbstractUnit import com.sadellie.unitto.data.units.AllUnitsRepository import com.sadellie.unitto.data.units.MyUnitIDS +import com.sadellie.unitto.data.units.NumberBaseUnit import com.sadellie.unitto.data.units.UnitGroup import com.sadellie.unitto.data.units.database.MyBasedUnit import com.sadellie.unitto.data.units.database.MyBasedUnitsRepository import com.sadellie.unitto.data.units.remote.CurrencyApi import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse -import com.sadellie.unitto.data.combine -import com.sadellie.unitto.data.toStringWith -import com.sadellie.unitto.data.trimZeros import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel @@ -134,10 +135,35 @@ class MainViewModel @Inject constructor( var unitTo: AbstractUnit by mutableStateOf(allUnitsRepository.getById(MyUnitIDS.mile)) private set - /** - * This function takes local variables, converts values and then causes the UI to update - */ - private suspend fun convertInput() { + private suspend fun convertAsNumberBase() { + withContext(Dispatchers.Default) { + while (isActive) { + val conversionResult = try { + (unitFrom as NumberBaseUnit).convertToBase( + input = input.value, + toBase = (unitTo as NumberBaseUnit).base, + ) + } catch (e: Exception) { + when (e) { + is NumberFormatException, is IllegalArgumentException -> { + "" + } + is ClassCastException -> { + cancel() + return@withContext + } + else -> { + throw e + } + } + } + _result.update { conversionResult } + cancel() + } + } + } + + private suspend fun convertAsExpression() { withContext(Dispatchers.Default) { while (isActive) { // First we clean the input from garbage at the end @@ -195,12 +221,34 @@ class MainViewModel @Inject constructor( } } + /** + * This function takes local variables, converts values and then causes the UI to update + */ + private suspend fun convertInput() { + if (unitFrom.group == UnitGroup.NUMBER_BASE) { + convertAsNumberBase() + } else { + convertAsExpression() + } + } + /** * Change left side unit. Unit to convert from * * @param clickedUnit Unit we need to change to */ fun changeUnitFrom(clickedUnit: AbstractUnit) { + // Do we change to NumberBase? + if ((unitFrom.group != UnitGroup.NUMBER_BASE) and (clickedUnit.group == UnitGroup.NUMBER_BASE)) { + // It was not NUMBER_BASE, but now we change to it. Clear input. + clearInput() + } + + if ((unitFrom.group == UnitGroup.NUMBER_BASE) and (clickedUnit.group != UnitGroup.NUMBER_BASE)) { + // It was NUMBER_BASE, but now we change to something else. Clear input. + clearInput() + } + // First we change unit unitFrom = clickedUnit @@ -512,12 +560,8 @@ class MainViewModel @Inject constructor( /** * Returns value to be used when converting value on the right side screen (unit selection) */ - fun inputValue(): BigDecimal? { - return try { - (mainFlow.value.calculatedValue ?: mainFlow.value.inputValue).toBigDecimal() - } catch (e: NumberFormatException) { - null - } + fun inputValue(): String { + return mainFlow.value.calculatedValue ?: mainFlow.value.inputValue } /** diff --git a/app/src/main/java/com/sadellie/unitto/screens/main/components/Keyboard.kt b/app/src/main/java/com/sadellie/unitto/screens/main/components/Keyboard.kt index 025e3d5a..93e2dfc6 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/main/components/Keyboard.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/main/components/Keyboard.kt @@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.sadellie.unitto.data.KEY_0 import com.sadellie.unitto.data.KEY_1 @@ -36,6 +35,12 @@ import com.sadellie.unitto.data.KEY_6 import com.sadellie.unitto.data.KEY_7 import com.sadellie.unitto.data.KEY_8 import com.sadellie.unitto.data.KEY_9 +import com.sadellie.unitto.data.KEY_BASE_A +import com.sadellie.unitto.data.KEY_BASE_B +import com.sadellie.unitto.data.KEY_BASE_C +import com.sadellie.unitto.data.KEY_BASE_D +import com.sadellie.unitto.data.KEY_BASE_E +import com.sadellie.unitto.data.KEY_BASE_F import com.sadellie.unitto.data.KEY_CLEAR import com.sadellie.unitto.data.KEY_DIVIDE import com.sadellie.unitto.data.KEY_DIVIDE_DISPLAY @@ -58,13 +63,15 @@ import com.sadellie.unitto.screens.Formatter * @param addDigit Function that is called when clicking number and dot buttons * @param deleteDigit Function that is called when clicking delete "<" button * @param clearInput Function that is called when clicking clear "AC" button + * @param baseConverter When True will use layout for base conversion. */ @Composable fun Keyboard( modifier: Modifier = Modifier, addDigit: (String) -> Unit = {}, deleteDigit: () -> Unit = {}, - clearInput: () -> Unit = {} + clearInput: () -> Unit = {}, + baseConverter: Boolean = false, ) { Column( modifier = modifier.fillMaxSize() @@ -76,41 +83,67 @@ fun Keyboard( .padding(4.dp) // Column modifier val cModifier = Modifier.weight(1f) - Row(cModifier) { - KeyboardButton(bModifier, KEY_LEFT_BRACKET, isPrimary = false, onClick = addDigit) - KeyboardButton(bModifier, KEY_RIGHT_BRACKET, isPrimary = false, onClick = addDigit) - KeyboardButton(bModifier, KEY_EXPONENT, isPrimary = false, onClick = { addDigit(KEY_EXPONENT) }) - KeyboardButton(bModifier, KEY_SQRT, isPrimary = false, onClick = { addDigit(KEY_SQRT) }) - } - Row(cModifier) { - KeyboardButton(bModifier, KEY_7, onClick = addDigit) - KeyboardButton(bModifier, KEY_8, onClick = addDigit) - KeyboardButton(bModifier, KEY_9, onClick = addDigit) - KeyboardButton(bModifier, KEY_DIVIDE_DISPLAY, isPrimary = false) { addDigit(KEY_DIVIDE) } - } - Row(cModifier) { - KeyboardButton(bModifier, KEY_4, onClick = addDigit) - KeyboardButton(bModifier, KEY_5, onClick = addDigit) - KeyboardButton(bModifier, KEY_6, onClick = addDigit) - KeyboardButton(bModifier, KEY_MULTIPLY_DISPLAY, isPrimary = false) { addDigit(KEY_MULTIPLY) } - } - Row(cModifier) { - KeyboardButton(bModifier, KEY_1, onClick = addDigit) - KeyboardButton(bModifier, KEY_2, onClick = addDigit) - KeyboardButton(bModifier, KEY_3, onClick = addDigit) - KeyboardButton(bModifier, KEY_MINUS_DISPLAY, isPrimary = false) { addDigit(KEY_MINUS) } - } - Row(cModifier) { - KeyboardButton(bModifier, KEY_0, onClick = addDigit) - KeyboardButton(bModifier, Formatter.fractional) { addDigit(KEY_DOT) } - KeyboardButton(Modifier.fillMaxSize().weight(2f).padding(4.dp), KEY_CLEAR, onLongClick = clearInput) { deleteDigit() } - KeyboardButton(bModifier, KEY_PLUS, isPrimary = false) { addDigit(KEY_PLUS) } + if (baseConverter) { + Row(cModifier) { + KeyboardButton(bModifier, KEY_BASE_A, isPrimary = false, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_B, isPrimary = false, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_C, isPrimary = false, onClick = addDigit) + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_BASE_D, isPrimary = false, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_E, isPrimary = false, onClick = addDigit) + KeyboardButton(bModifier, KEY_BASE_F, isPrimary = false, onClick = addDigit) + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_7, onClick = addDigit) + KeyboardButton(bModifier, KEY_8, onClick = addDigit) + KeyboardButton(bModifier, KEY_9, onClick = addDigit) + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_4, onClick = addDigit) + KeyboardButton(bModifier, KEY_5, onClick = addDigit) + KeyboardButton(bModifier, KEY_6, onClick = addDigit) + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_1, onClick = addDigit) + KeyboardButton(bModifier, KEY_2, onClick = addDigit) + KeyboardButton(bModifier, KEY_3, onClick = addDigit) + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_0, onClick = addDigit) + KeyboardButton(Modifier.fillMaxSize().weight(2f).padding(4.dp), KEY_CLEAR, onLongClick = clearInput) { deleteDigit() } + } + } else { + Row(cModifier) { + KeyboardButton(bModifier, KEY_LEFT_BRACKET, isPrimary = false, onClick = addDigit) + KeyboardButton(bModifier, KEY_RIGHT_BRACKET, isPrimary = false, onClick = addDigit) + KeyboardButton(bModifier, KEY_EXPONENT, isPrimary = false, onClick = { addDigit(KEY_EXPONENT) }) + KeyboardButton(bModifier, KEY_SQRT, isPrimary = false, onClick = { addDigit(KEY_SQRT) }) + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_7, onClick = addDigit) + KeyboardButton(bModifier, KEY_8, onClick = addDigit) + KeyboardButton(bModifier, KEY_9, onClick = addDigit) + KeyboardButton(bModifier, KEY_DIVIDE_DISPLAY, isPrimary = false) { addDigit(KEY_DIVIDE) } + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_4, onClick = addDigit) + KeyboardButton(bModifier, KEY_5, onClick = addDigit) + KeyboardButton(bModifier, KEY_6, onClick = addDigit) + KeyboardButton(bModifier, KEY_MULTIPLY_DISPLAY, isPrimary = false) { addDigit(KEY_MULTIPLY) } + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_1, onClick = addDigit) + KeyboardButton(bModifier, KEY_2, onClick = addDigit) + KeyboardButton(bModifier, KEY_3, onClick = addDigit) + KeyboardButton(bModifier, KEY_MINUS_DISPLAY, isPrimary = false) { addDigit(KEY_MINUS) } + } + Row(cModifier) { + KeyboardButton(bModifier, KEY_0, onClick = addDigit) + KeyboardButton(bModifier, Formatter.fractional) { addDigit(KEY_DOT) } + KeyboardButton(bModifier, KEY_CLEAR, onLongClick = clearInput) { deleteDigit() } + KeyboardButton(bModifier, KEY_PLUS, isPrimary = false) { addDigit(KEY_PLUS) } + } } } } - -@Preview -@Composable -fun PreviewKeyboard() { - Keyboard() -} diff --git a/app/src/main/java/com/sadellie/unitto/screens/main/components/TopScreen.kt b/app/src/main/java/com/sadellie/unitto/screens/main/components/TopScreen.kt index bee0e89e..83449ac6 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/main/components/TopScreen.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/main/components/TopScreen.kt @@ -76,7 +76,8 @@ fun TopScreenPart( loadingNetwork: Boolean, networkError: Boolean, onUnitSelectionClick: (String) -> Unit, - swapUnits: () -> Unit + swapUnits: () -> Unit, + baseConverterMode: Boolean, ) { var swapped by remember { mutableStateOf(false) } val swapButtonRotation: Float by animateFloatAsState( @@ -94,6 +95,7 @@ fun TopScreenPart( when { loadingDatabase || loadingNetwork -> stringResource(R.string.loading_label) networkError -> stringResource(R.string.error_label) + baseConverterMode -> inputValue.uppercase() else -> Formatter.format(inputValue) } }, @@ -107,6 +109,7 @@ fun TopScreenPart( when { loadingDatabase || loadingNetwork -> stringResource(R.string.loading_label) networkError -> stringResource(R.string.error_label) + baseConverterMode -> outputValue.uppercase() else -> Formatter.format(outputValue) } }, diff --git a/app/src/main/java/com/sadellie/unitto/screens/second/SecondScreen.kt b/app/src/main/java/com/sadellie/unitto/screens/second/SecondScreen.kt index 4fc92fba..cebe3462 100644 --- a/app/src/main/java/com/sadellie/unitto/screens/second/SecondScreen.kt +++ b/app/src/main/java/com/sadellie/unitto/screens/second/SecondScreen.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.R import com.sadellie.unitto.data.units.AbstractUnit +import com.sadellie.unitto.data.units.NumberBaseUnit import com.sadellie.unitto.data.units.UnitGroup import com.sadellie.unitto.screens.Formatter import com.sadellie.unitto.screens.common.Header @@ -174,12 +175,27 @@ fun RightSideScreen( navigateUp: () -> Unit, navigateToSettingsAction: () -> Unit, selectAction: (AbstractUnit) -> Unit, - inputValue: BigDecimal?, + inputValue: String, unitFrom: AbstractUnit ) { val uiState = viewModel.mainFlow.collectAsStateWithLifecycle() val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val focusManager = LocalFocusManager.current + val inputAsBigDecimal: BigDecimal? = try { + inputValue.toBigDecimal() + } catch (e: NumberFormatException) { + null + } + + val convertMethod: (AbstractUnit) -> String = when { + unitFrom.group == UnitGroup.NUMBER_BASE -> {{ + convertForSecondaryNumberBase(inputValue, unitFrom as NumberBaseUnit, it as NumberBaseUnit) + }} + inputAsBigDecimal != null -> {{ + convertForSecondary(inputAsBigDecimal, unitFrom, it) + }} + else -> {{""}} + } Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), @@ -224,13 +240,7 @@ fun RightSideScreen( navigateUp() }, favoriteAction = { viewModel.favoriteUnit(it) }, - convertValue = { - inputValue?.let { - Formatter.format( - unitFrom.convert(unit, it, 3).toPlainString() - ) + " " - } ?: "" - } + convertValue = convertMethod ) } } @@ -240,6 +250,20 @@ fun RightSideScreen( } } +internal fun convertForSecondary(inputValue: BigDecimal, unitFrom: AbstractUnit, unitTo: AbstractUnit): String { + return Formatter.format( + unitFrom.convert(unitTo, inputValue, 3).toPlainString() + " " + ) +} + +internal fun convertForSecondaryNumberBase(inputValue: String, unitFrom: NumberBaseUnit, unitTo: NumberBaseUnit): String { + return try { + unitFrom.convertToBase(inputValue, unitTo.base) + " " + } catch (e: NumberFormatException) { + "" + } +} + @Composable private fun UnitGroupHeader(modifier: Modifier, unitGroup: UnitGroup) { Header( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 96d4501d..f44dd83a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -931,6 +931,38 @@ Gigaweber GWb + + Binary + base2 + Ternary + base3 + Quaternary + base4 + Quinary + base5 + Senary + base6 + Septenary + base7 + Octal + base8 + Nonary + base9 + Decimal + base10 + Undecimal + base11 + Duodecimal + base12 + Tridecimal + base13 + Tetradecimal + base14 + Pentadecimal + base15 + Hexadecimal + base16 + Length Time @@ -948,6 +980,7 @@ Acceleration Currency Flux + Base Convert from diff --git a/app/src/test/java/com/sadellie/unitto/data/units/AllUnitsTest.kt b/app/src/test/java/com/sadellie/unitto/data/units/AllUnitsTest.kt index b0f89d49..3f3f58bb 100644 --- a/app/src/test/java/com/sadellie/unitto/data/units/AllUnitsTest.kt +++ b/app/src/test/java/com/sadellie/unitto/data/units/AllUnitsTest.kt @@ -351,15 +351,41 @@ class AllUnitsTest { MyUnitIDS.gigaweber.checkWith(MyUnitIDS.weber, "68.2", "68200000000") } + @Test + fun testNumberBase() { + MyUnitIDS.binary.checkWith(MyUnitIDS.octal, "1000001001", "1011") + MyUnitIDS.ternary.checkWith(MyUnitIDS.decimal, "10112020111", "69430") + MyUnitIDS.quaternary.checkWith(MyUnitIDS.quinary, "20321", "4234") + MyUnitIDS.quinary.checkWith(MyUnitIDS.nonary, "4234", "702") + MyUnitIDS.senary.checkWith(MyUnitIDS.nonary, "4234", "1274") + MyUnitIDS.septenary.checkWith(MyUnitIDS.nonary, "4234", "2041") + MyUnitIDS.octal.checkWith(MyUnitIDS.undecimal, "42343277", "5107945") + MyUnitIDS.nonary.checkWith(MyUnitIDS.duodecimal, "42343287", "69b9a81") + MyUnitIDS.decimal.checkWith(MyUnitIDS.duodecimal, "42343287", "12220273") + MyUnitIDS.undecimal.checkWith(MyUnitIDS.hexadecimal, "4234a287", "4e3f0c2") + MyUnitIDS.duodecimal.checkWith(MyUnitIDS.hexadecimal, "4234a287", "8f30d07") + MyUnitIDS.tridecimal.checkWith(MyUnitIDS.hexadecimal, "4234a287", "f9c3ff4") + MyUnitIDS.tetradecimal.checkWith(MyUnitIDS.hexadecimal, "bb", "a5") + MyUnitIDS.pentadecimal.checkWith(MyUnitIDS.hexadecimal, "BABE", "9a82") + MyUnitIDS.hexadecimal.checkWith(MyUnitIDS.quinary, "FADE", "4023342") + } + private fun String.checkWith(checkingId: String, value: String, expected: String) { - val unit = allUnitsRepository.getById(this) - val actual = unit - .convert(allUnitsRepository.getById(checkingId), BigDecimal(value), 5) - .toPlainString() + val unitFrom = allUnitsRepository.getById(this) + val unitTo = allUnitsRepository.getById(checkingId) + + val actual = if (unitFrom.group == UnitGroup.NUMBER_BASE) { + (unitFrom as NumberBaseUnit) + .convertToBase(value, (unitTo as NumberBaseUnit).base) + } else { + unitFrom + .convert(unitTo, BigDecimal(value), 5) + .toPlainString() + } assertEquals("Failed at $this to $checkingId", expected, actual) println("PASSED: $this -> $expected == $actual") - val content: Set = history.getOrDefault(unit.group, setOf()) - history[unit.group] = content.plus(this) + val content: Set = history.getOrDefault(unitFrom.group, setOf()) + history[unitFrom.group] = content.plus(this) } @After