Added Number base converter #14

This commit is contained in:
Sad Ellie 2023-01-02 18:17:26 +04:00
parent 9308398d77
commit b1c8780fc1
12 changed files with 325 additions and 68 deletions

View File

@ -29,6 +29,13 @@ const val KEY_8 = "8"
const val KEY_9 = "9" const val KEY_9 = "9"
const val KEY_0 = "0" 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_DOT = "."
const val KEY_COMMA = "," const val KEY_COMMA = ","
const val KEY_CLEAR = "<" const val KEY_CLEAR = "<"

View File

@ -62,6 +62,7 @@ class AllUnitsRepository @Inject constructor() {
UnitGroup.ANGLE to angleCollection, UnitGroup.ANGLE to angleCollection,
UnitGroup.DATA_TRANSFER to dataTransferCollection, UnitGroup.DATA_TRANSFER to dataTransferCollection,
UnitGroup.FLUX to fluxCollection, 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), MyUnit(MyUnitIDS.gigaweber, BigDecimal.valueOf(100000000000000000), UnitGroup.FLUX, R.string.gigaweber, R.string.gigaweber_short),
) )
} }
private val numberBaseCollection: List<AbstractUnit> 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),
)
}
} }

View File

@ -495,4 +495,21 @@ object MyUnitIDS {
const val kiloweber = "kiloweber" const val kiloweber = "kiloweber"
const val megaweber = "megaweber" const val megaweber = "megaweber"
const val gigaweber = "gigaweber" 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"
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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)
}
}

View File

@ -49,4 +49,5 @@ enum class UnitGroup(
ANGLE(res = R.string.angle), ANGLE(res = R.string.angle),
DATA_TRANSFER(res = R.string.data_transfer), DATA_TRANSFER(res = R.string.data_transfer),
FLUX(res = R.string.flux), FLUX(res = R.string.flux),
NUMBER_BASE(res = R.string.number_base),
} }

View File

@ -40,6 +40,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.sadellie.unitto.R import com.sadellie.unitto.R
import com.sadellie.unitto.data.NavRoutes.SETTINGS_SCREEN import com.sadellie.unitto.data.NavRoutes.SETTINGS_SCREEN
import com.sadellie.unitto.data.units.AbstractUnit 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.common.AnimatedTopBarText
import com.sadellie.unitto.screens.main.components.Keyboard import com.sadellie.unitto.screens.main.components.Keyboard
import com.sadellie.unitto.screens.main.components.TopScreenPart import com.sadellie.unitto.screens.main.components.TopScreenPart
@ -81,8 +82,10 @@ fun MainScreen(
navControllerAction = { navControllerAction(it) }, navControllerAction = { navControllerAction(it) },
swapMeasurements = { viewModel.swapUnits() }, swapMeasurements = { viewModel.swapUnits() },
processInput = { viewModel.processInput(it) }, processInput = { viewModel.processInput(it) },
deleteDigit = { viewModel.deleteDigit() } deleteDigit = { viewModel.deleteDigit() },
) { viewModel.clearInput() } clearInput = { viewModel.clearInput() },
baseConverterMode = viewModel.unitFrom.group == UnitGroup.NUMBER_BASE
)
} }
) )
@ -108,6 +111,7 @@ private fun MainScreenContent(
processInput: (String) -> Unit = {}, processInput: (String) -> Unit = {},
deleteDigit: () -> Unit = {}, deleteDigit: () -> Unit = {},
clearInput: () -> Unit = {}, clearInput: () -> Unit = {},
baseConverterMode: Boolean,
) { ) {
PortraitLandscape( PortraitLandscape(
modifier = modifier, modifier = modifier,
@ -123,7 +127,8 @@ private fun MainScreenContent(
loadingNetwork = mainScreenUIState.isLoadingNetwork, loadingNetwork = mainScreenUIState.isLoadingNetwork,
networkError = mainScreenUIState.showError, networkError = mainScreenUIState.showError,
onUnitSelectionClick = navControllerAction, onUnitSelectionClick = navControllerAction,
swapUnits = swapMeasurements swapUnits = swapMeasurements,
baseConverterMode = baseConverterMode,
) )
}, },
content2 = { content2 = {
@ -132,6 +137,7 @@ private fun MainScreenContent(
addDigit = processInput, addDigit = processInput,
deleteDigit = deleteDigit, deleteDigit = deleteDigit,
clearInput = clearInput, clearInput = clearInput,
baseConverter = baseConverterMode,
) )
} }
) )

View File

@ -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_RIGHT_BRACKET
import com.sadellie.unitto.data.KEY_SQRT import com.sadellie.unitto.data.KEY_SQRT
import com.sadellie.unitto.data.OPERATORS 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.UserPreferences
import com.sadellie.unitto.data.preferences.UserPreferencesRepository 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.AbstractUnit
import com.sadellie.unitto.data.units.AllUnitsRepository import com.sadellie.unitto.data.units.AllUnitsRepository
import com.sadellie.unitto.data.units.MyUnitIDS 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.UnitGroup
import com.sadellie.unitto.data.units.database.MyBasedUnit import com.sadellie.unitto.data.units.database.MyBasedUnit
import com.sadellie.unitto.data.units.database.MyBasedUnitsRepository import com.sadellie.unitto.data.units.database.MyBasedUnitsRepository
import com.sadellie.unitto.data.units.remote.CurrencyApi import com.sadellie.unitto.data.units.remote.CurrencyApi
import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse 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 dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
@ -134,10 +135,35 @@ class MainViewModel @Inject constructor(
var unitTo: AbstractUnit by mutableStateOf(allUnitsRepository.getById(MyUnitIDS.mile)) var unitTo: AbstractUnit by mutableStateOf(allUnitsRepository.getById(MyUnitIDS.mile))
private set private set
/** private suspend fun convertAsNumberBase() {
* This function takes local variables, converts values and then causes the UI to update withContext(Dispatchers.Default) {
*/ while (isActive) {
private suspend fun convertInput() { 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) { withContext(Dispatchers.Default) {
while (isActive) { while (isActive) {
// First we clean the input from garbage at the end // 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 * Change left side unit. Unit to convert from
* *
* @param clickedUnit Unit we need to change to * @param clickedUnit Unit we need to change to
*/ */
fun changeUnitFrom(clickedUnit: AbstractUnit) { 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 // First we change unit
unitFrom = clickedUnit 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) * Returns value to be used when converting value on the right side screen (unit selection)
*/ */
fun inputValue(): BigDecimal? { fun inputValue(): String {
return try { return mainFlow.value.calculatedValue ?: mainFlow.value.inputValue
(mainFlow.value.calculatedValue ?: mainFlow.value.inputValue).toBigDecimal()
} catch (e: NumberFormatException) {
null
}
} }
/** /**

View File

@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.data.KEY_0 import com.sadellie.unitto.data.KEY_0
import com.sadellie.unitto.data.KEY_1 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_7
import com.sadellie.unitto.data.KEY_8 import com.sadellie.unitto.data.KEY_8
import com.sadellie.unitto.data.KEY_9 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_CLEAR
import com.sadellie.unitto.data.KEY_DIVIDE import com.sadellie.unitto.data.KEY_DIVIDE
import com.sadellie.unitto.data.KEY_DIVIDE_DISPLAY 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 addDigit Function that is called when clicking number and dot buttons
* @param deleteDigit Function that is called when clicking delete "<" button * @param deleteDigit Function that is called when clicking delete "<" button
* @param clearInput Function that is called when clicking clear "AC" button * @param clearInput Function that is called when clicking clear "AC" button
* @param baseConverter When True will use layout for base conversion.
*/ */
@Composable @Composable
fun Keyboard( fun Keyboard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
addDigit: (String) -> Unit = {}, addDigit: (String) -> Unit = {},
deleteDigit: () -> Unit = {}, deleteDigit: () -> Unit = {},
clearInput: () -> Unit = {} clearInput: () -> Unit = {},
baseConverter: Boolean = false,
) { ) {
Column( Column(
modifier = modifier.fillMaxSize() modifier = modifier.fillMaxSize()
@ -76,6 +83,37 @@ fun Keyboard(
.padding(4.dp) .padding(4.dp)
// Column modifier // Column modifier
val cModifier = Modifier.weight(1f) val cModifier = Modifier.weight(1f)
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) { Row(cModifier) {
KeyboardButton(bModifier, KEY_LEFT_BRACKET, isPrimary = false, onClick = addDigit) KeyboardButton(bModifier, KEY_LEFT_BRACKET, isPrimary = false, onClick = addDigit)
KeyboardButton(bModifier, KEY_RIGHT_BRACKET, isPrimary = false, onClick = addDigit) KeyboardButton(bModifier, KEY_RIGHT_BRACKET, isPrimary = false, onClick = addDigit)
@ -103,14 +141,9 @@ fun Keyboard(
Row(cModifier) { Row(cModifier) {
KeyboardButton(bModifier, KEY_0, onClick = addDigit) KeyboardButton(bModifier, KEY_0, onClick = addDigit)
KeyboardButton(bModifier, Formatter.fractional) { addDigit(KEY_DOT) } KeyboardButton(bModifier, Formatter.fractional) { addDigit(KEY_DOT) }
KeyboardButton(Modifier.fillMaxSize().weight(2f).padding(4.dp), KEY_CLEAR, onLongClick = clearInput) { deleteDigit() } KeyboardButton(bModifier, KEY_CLEAR, onLongClick = clearInput) { deleteDigit() }
KeyboardButton(bModifier, KEY_PLUS, isPrimary = false) { addDigit(KEY_PLUS) } KeyboardButton(bModifier, KEY_PLUS, isPrimary = false) { addDigit(KEY_PLUS) }
} }
} }
} }
@Preview
@Composable
fun PreviewKeyboard() {
Keyboard()
} }

View File

@ -76,7 +76,8 @@ fun TopScreenPart(
loadingNetwork: Boolean, loadingNetwork: Boolean,
networkError: Boolean, networkError: Boolean,
onUnitSelectionClick: (String) -> Unit, onUnitSelectionClick: (String) -> Unit,
swapUnits: () -> Unit swapUnits: () -> Unit,
baseConverterMode: Boolean,
) { ) {
var swapped by remember { mutableStateOf(false) } var swapped by remember { mutableStateOf(false) }
val swapButtonRotation: Float by animateFloatAsState( val swapButtonRotation: Float by animateFloatAsState(
@ -94,6 +95,7 @@ fun TopScreenPart(
when { when {
loadingDatabase || loadingNetwork -> stringResource(R.string.loading_label) loadingDatabase || loadingNetwork -> stringResource(R.string.loading_label)
networkError -> stringResource(R.string.error_label) networkError -> stringResource(R.string.error_label)
baseConverterMode -> inputValue.uppercase()
else -> Formatter.format(inputValue) else -> Formatter.format(inputValue)
} }
}, },
@ -107,6 +109,7 @@ fun TopScreenPart(
when { when {
loadingDatabase || loadingNetwork -> stringResource(R.string.loading_label) loadingDatabase || loadingNetwork -> stringResource(R.string.loading_label)
networkError -> stringResource(R.string.error_label) networkError -> stringResource(R.string.error_label)
baseConverterMode -> outputValue.uppercase()
else -> Formatter.format(outputValue) else -> Formatter.format(outputValue)
} }
}, },

View File

@ -48,6 +48,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.R import com.sadellie.unitto.R
import com.sadellie.unitto.data.units.AbstractUnit 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.data.units.UnitGroup
import com.sadellie.unitto.screens.Formatter import com.sadellie.unitto.screens.Formatter
import com.sadellie.unitto.screens.common.Header import com.sadellie.unitto.screens.common.Header
@ -174,12 +175,27 @@ fun RightSideScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
navigateToSettingsAction: () -> Unit, navigateToSettingsAction: () -> Unit,
selectAction: (AbstractUnit) -> Unit, selectAction: (AbstractUnit) -> Unit,
inputValue: BigDecimal?, inputValue: String,
unitFrom: AbstractUnit unitFrom: AbstractUnit
) { ) {
val uiState = viewModel.mainFlow.collectAsStateWithLifecycle() val uiState = viewModel.mainFlow.collectAsStateWithLifecycle()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val focusManager = LocalFocusManager.current 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( Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
@ -224,13 +240,7 @@ fun RightSideScreen(
navigateUp() navigateUp()
}, },
favoriteAction = { viewModel.favoriteUnit(it) }, favoriteAction = { viewModel.favoriteUnit(it) },
convertValue = { convertValue = convertMethod
inputValue?.let {
Formatter.format(
unitFrom.convert(unit, it, 3).toPlainString()
) + " "
} ?: ""
}
) )
} }
} }
@ -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 @Composable
private fun UnitGroupHeader(modifier: Modifier, unitGroup: UnitGroup) { private fun UnitGroupHeader(modifier: Modifier, unitGroup: UnitGroup) {
Header( Header(

View File

@ -931,6 +931,38 @@
<string name="gigaweber">Gigaweber</string> <string name="gigaweber">Gigaweber</string>
<string name="gigaweber_short">GWb</string> <string name="gigaweber_short">GWb</string>
<!--Number base-->
<string name="binary">Binary</string>
<string name="binary_short">base2</string>
<string name="ternary">Ternary</string>
<string name="ternary_short">base3</string>
<string name="quaternary">Quaternary</string>
<string name="quaternary_short">base4</string>
<string name="quinary">Quinary</string>
<string name="quinary_short">base5</string>
<string name="senary">Senary</string>
<string name="senary_short">base6</string>
<string name="septenary">Septenary</string>
<string name="septenary_short">base7</string>
<string name="octal">Octal</string>
<string name="octal_short">base8</string>
<string name="nonary">Nonary</string>
<string name="nonary_short">base9</string>
<string name="decimal">Decimal</string>
<string name="decimal_short">base10</string>
<string name="undecimal">Undecimal</string>
<string name="undecimal_short">base11</string>
<string name="duodecimal">Duodecimal</string>
<string name="duodecimal_short">base12</string>
<string name="tridecimal">Tridecimal</string>
<string name="tridecimal_short">base13</string>
<string name="tetradecimal">Tetradecimal</string>
<string name="tetradecimal_short">base14</string>
<string name="pentadecimal">Pentadecimal</string>
<string name="pentadecimal_short">base15</string>
<string name="hexadecimal">Hexadecimal</string>
<string name="hexadecimal_short">base16</string>
<!--Groups--> <!--Groups-->
<string name="length">Length</string> <string name="length">Length</string>
<string name="time">Time</string> <string name="time">Time</string>
@ -948,6 +980,7 @@
<string name="acceleration">Acceleration</string> <string name="acceleration">Acceleration</string>
<string name="currency">Currency</string> <string name="currency">Currency</string>
<string name="flux">Flux</string> <string name="flux">Flux</string>
<string name="number_base">Base</string>
<!--Screen names--> <!--Screen names-->
<string name="units_screen_from">Convert from</string> <string name="units_screen_from">Convert from</string>

View File

@ -351,15 +351,41 @@ class AllUnitsTest {
MyUnitIDS.gigaweber.checkWith(MyUnitIDS.weber, "68.2", "68200000000") 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) { private fun String.checkWith(checkingId: String, value: String, expected: String) {
val unit = allUnitsRepository.getById(this) val unitFrom = allUnitsRepository.getById(this)
val actual = unit val unitTo = allUnitsRepository.getById(checkingId)
.convert(allUnitsRepository.getById(checkingId), BigDecimal(value), 5)
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() .toPlainString()
}
assertEquals("Failed at $this to $checkingId", expected, actual) assertEquals("Failed at $this to $checkingId", expected, actual)
println("PASSED: $this -> $expected == $actual") println("PASSED: $this -> $expected == $actual")
val content: Set<String> = history.getOrDefault(unit.group, setOf()) val content: Set<String> = history.getOrDefault(unitFrom.group, setOf())
history[unit.group] = content.plus(this) history[unitFrom.group] = content.plus(this)
} }
@After @After