Fix (?) for input conversion (#12)

- Now doing conversion in coroutine.
- Little refactor for MainViewModel
- Text fields are limited to 1000 symbols now

- Little bug added. Conversion result can get really slow when converting extremely large numbers 99^999999 (like really big). Very rare case and user would probably never enter such values. Can't find a proper fix yet.
This commit is contained in:
Sad Ellie 2022-12-24 13:45:00 +04:00
parent b18b38bc85
commit 16b8bd35a3
6 changed files with 167 additions and 107 deletions

View File

@ -24,6 +24,7 @@ import com.sadellie.unitto.data.preferences.MAX_PRECISION
import com.sadellie.unitto.data.units.database.MyBasedUnit import com.sadellie.unitto.data.units.database.MyBasedUnit
import com.sadellie.unitto.screens.setMinimumRequiredScale import com.sadellie.unitto.screens.setMinimumRequiredScale
import com.sadellie.unitto.screens.sortByLev import com.sadellie.unitto.screens.sortByLev
import com.sadellie.unitto.screens.trimZeros
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
import javax.inject.Inject import javax.inject.Inject
@ -499,7 +500,7 @@ class AllUnitsRepository @Inject constructor() {
else -> value else -> value
} }
.setMinimumRequiredScale(scale) .setMinimumRequiredScale(scale)
.stripTrailingZeros() .trimZeros()
} }
}, },
object : AbstractUnit( object : AbstractUnit(
@ -529,7 +530,7 @@ class AllUnitsRepository @Inject constructor() {
else -> value else -> value
} }
.setMinimumRequiredScale(scale) .setMinimumRequiredScale(scale)
.stripTrailingZeros() .trimZeros()
} }
}, },
object : AbstractUnit( object : AbstractUnit(
@ -556,7 +557,7 @@ class AllUnitsRepository @Inject constructor() {
else -> value else -> value
} }
.setMinimumRequiredScale(scale) .setMinimumRequiredScale(scale)
.stripTrailingZeros() .trimZeros()
} }
}, },
) )

View File

@ -21,6 +21,7 @@ package com.sadellie.unitto.data.units
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.sadellie.unitto.data.preferences.MAX_PRECISION import com.sadellie.unitto.data.preferences.MAX_PRECISION
import com.sadellie.unitto.screens.setMinimumRequiredScale import com.sadellie.unitto.screens.setMinimumRequiredScale
import com.sadellie.unitto.screens.trimZeros
import java.math.BigDecimal import java.math.BigDecimal
/** /**
@ -54,6 +55,6 @@ class MyUnit(
.multiply(value) .multiply(value)
.div(unitTo.basicUnit) .div(unitTo.basicUnit)
.setMinimumRequiredScale(scale) .setMinimumRequiredScale(scale)
.stripTrailingZeros() .trimZeros()
} }
} }

View File

@ -30,6 +30,8 @@ import com.sadellie.unitto.data.OPERATORS
import com.sadellie.unitto.data.preferences.OutputFormat import com.sadellie.unitto.data.preferences.OutputFormat
import com.sadellie.unitto.data.preferences.Separator import com.sadellie.unitto.data.preferences.Separator
import com.sadellie.unitto.data.units.AbstractUnit import com.sadellie.unitto.data.units.AbstractUnit
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
import kotlin.math.floor import kotlin.math.floor
@ -273,3 +275,34 @@ fun Sequence<AbstractUnit>.sortByLev(stringA: String): Sequence<AbstractUnit> {
.map { it.first } .map { it.first }
.asSequence() .asSequence()
} }
@Suppress("UNCHECKED_CAST")
fun <T1, T2, T3, T4, T5, T6, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
flow3: Flow<T3>,
flow4: Flow<T4>,
flow5: Flow<T5>,
flow6: Flow<T6>,
transform: suspend (T1, T2, T3, T4, T5, T6,) -> R
): Flow<R> = combine(flow, flow2, flow3, flow4, flow5, flow6,) { args: Array<*> ->
transform(
args[0] as T1,
args[1] as T2,
args[2] as T3,
args[3] as T4,
args[4] as T5,
args[5] as T6,
)
}
/**
* Removes all trailing zeroes.
*
* @throws NumberFormatException if value is bigger than [Double.MAX_VALUE] to avoid memory overflow.
*/
fun BigDecimal.trimZeros(): BigDecimal {
if (this.abs() > BigDecimal.valueOf(Double.MAX_VALUE)) throw NumberFormatException()
return if (this.compareTo(BigDecimal.ZERO) == 0) BigDecimal.ZERO else this.stripTrailingZeros()
}

View File

@ -59,15 +59,22 @@ 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.screens.combine
import com.sadellie.unitto.screens.toStringWith import com.sadellie.unitto.screens.toStringWith
import com.sadellie.unitto.screens.trimZeros
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
import javax.inject.Inject import javax.inject.Inject
@ -80,29 +87,38 @@ class MainViewModel @Inject constructor(
private val allUnitsRepository: AllUnitsRepository private val allUnitsRepository: AllUnitsRepository
) : ViewModel() { ) : ViewModel() {
val inputValue: MutableStateFlow<String> = MutableStateFlow(KEY_0) val input: MutableStateFlow<String> = MutableStateFlow(KEY_0)
private val latestInputStack: MutableList<String> = mutableListOf(KEY_0) private val _calculated: MutableStateFlow<String?> = MutableStateFlow(null)
private val _inputDisplayValue: MutableStateFlow<String> = MutableStateFlow(KEY_0) private val _result: MutableStateFlow<String> = MutableStateFlow(KEY_0)
private val _latestInputStack: MutableList<String> = mutableListOf(KEY_0)
private val _inputDisplay: MutableStateFlow<String> = MutableStateFlow(KEY_0)
private val _isLoadingDatabase: MutableStateFlow<Boolean> = MutableStateFlow(true) private val _isLoadingDatabase: MutableStateFlow<Boolean> = MutableStateFlow(true)
private val _isLoadingNetwork: MutableStateFlow<Boolean> = MutableStateFlow(false) private val _isLoadingNetwork: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val _showError: MutableStateFlow<Boolean> = MutableStateFlow(false) private val _showError: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val _calculatedValue: MutableStateFlow<String?> = MutableStateFlow(null)
private val _userPrefs = userPrefsRepository.userPreferencesFlow.stateIn( private val _userPrefs = userPrefsRepository.userPreferencesFlow.stateIn(
viewModelScope, viewModelScope, SharingStarted.WhileSubscribed(5000), UserPreferences()
SharingStarted.WhileSubscribed(5000),
UserPreferences()
) )
val mainFlow = combine( val mainFlow: StateFlow<MainScreenUIState> = combine(
_inputDisplayValue, _isLoadingDatabase, _isLoadingNetwork, _showError, _userPrefs _inputDisplay,
) { inputDisplayValue, isLoadingDatabase, isLoadingNetwork, showError, _ -> _calculated,
_result,
_isLoadingNetwork,
_isLoadingDatabase,
_showError,
) { inputValue: String,
calculatedValue: String?,
resultValue: String,
showLoadingNetwork: Boolean,
showLoadingDatabase: Boolean,
showError: Boolean ->
return@combine MainScreenUIState( return@combine MainScreenUIState(
inputValue = inputDisplayValue, inputValue = inputValue,
resultValue = convertValue(), calculatedValue = calculatedValue,
isLoadingDatabase = isLoadingDatabase, resultValue = resultValue,
isLoadingNetwork = isLoadingNetwork, isLoadingNetwork = showLoadingNetwork,
isLoadingDatabase = showLoadingDatabase,
showError = showError, showError = showError,
calculatedValue = _calculatedValue.value
) )
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), MainScreenUIState()) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), MainScreenUIState())
@ -121,66 +137,62 @@ class MainViewModel @Inject constructor(
/** /**
* This function takes local variables, converts values and then causes the UI to update * This function takes local variables, converts values and then causes the UI to update
*/ */
private fun convertValue(): String { private suspend fun convertInput() {
withContext(Dispatchers.Default) {
while (isActive) {
// First we clean the input from garbage at the end
var cleanInput = input.value.dropLastWhile { !it.isDigit() }
var cleanInput = inputValue.value.dropLastWhile { !it.isDigit() } // Now we close open brackets that user didn't close
// AUTOCLOSE ALL BRACKETS
val leftBrackets = input.value.count { it.toString() == KEY_LEFT_BRACKET }
val rightBrackets = input.value.count { it.toString() == KEY_RIGHT_BRACKET }
val neededBrackets = leftBrackets - rightBrackets
if (neededBrackets > 0) cleanInput += KEY_RIGHT_BRACKET.repeat(neededBrackets)
// AUTOCLOSE ALL BRACKETS // Now we evaluate expression in input
val leftBrackets = inputValue.value.count { it.toString() == KEY_LEFT_BRACKET } val evaluationResult: BigDecimal = try {
val rightBrackets = inputValue.value.count { it.toString() == KEY_RIGHT_BRACKET } Expressions().eval(cleanInput)
.setScale(_userPrefs.value.digitsPrecision, RoundingMode.HALF_EVEN)
.trimZeros()
} catch (e: Exception) {
when (e) {
is ExpressionException,
is ArrayIndexOutOfBoundsException,
is IndexOutOfBoundsException,
is NumberFormatException,
is ArithmeticException -> {
// Invalid expression, can't do anything further
cancel()
return@withContext
}
else -> throw e
}
}
val neededBrackets = leftBrackets - rightBrackets // Evaluated. Hide calculated result if no expression entered.
if (neededBrackets > 0) { // 123.456 will be true
cleanInput += KEY_RIGHT_BRACKET.repeat(neededBrackets) // -123.456 will be true
} // -123.456-123 will be false (first minus gets removed, ending with 123.456)
if (input.value.removePrefix(KEY_MINUS).all { it.toString() !in OPERATORS }) {
// No operators
_calculated.update { null }
} else {
_calculated.update { evaluationResult.toStringWith(_userPrefs.value.outputFormat) }
}
// Kotlin doesn't have a multi catch // Now we just convert.
val calculatedInput = try { // We can use evaluation result here, input is valid
val evaluated = Expressions() val conversionResult: BigDecimal = unitFrom.convert(
.eval(cleanInput) unitTo, evaluationResult, _userPrefs.value.digitsPrecision
.setScale(_userPrefs.value.digitsPrecision, RoundingMode.HALF_EVEN) )
.stripTrailingZeros()
/** // Converted
* Too long values crash the UI. If the number is too big, we don't do conversion. _result.update { conversionResult.toStringWith(_userPrefs.value.outputFormat) }
* User probably wouldn't need numbers beyond infinity.
*/ cancel()
if (evaluated.abs() > BigDecimal.valueOf(Double.MAX_VALUE)) {
_calculatedValue.value = null
return ""
}
if (evaluated.compareTo(BigDecimal.ZERO) == 0) BigDecimal.ZERO else evaluated
} catch (e: Exception) {
// Kotlin doesn't have a multi catch
when (e) {
is ExpressionException, is ArrayIndexOutOfBoundsException, is IndexOutOfBoundsException, is NumberFormatException, is ArithmeticException -> return mainFlow.value.resultValue
else -> throw e
} }
} }
// Ugly way of determining when to hide calculated value
_calculatedValue.value = if (!latestInputStack.none { it in OPERATORS }) {
calculatedInput.toStringWith(_userPrefs.value.outputFormat)
} else {
null
}
// Converting value using a specified precision
val convertedValue: BigDecimal = unitFrom.convert(
unitTo, calculatedInput, _userPrefs.value.digitsPrecision
)
/**
* There is a very interesting bug when trailing zeros are not stripped when input
* consists of ZEROS only (0.00000 as an example). This check is a workaround. If the result
* is zero, than we make sure there are no trailing zeros.
*/
val resultValue = if (convertedValue.compareTo(BigDecimal.ZERO) == 0) {
KEY_0
} else {
convertedValue.toStringWith(_userPrefs.value.outputFormat)
}
return resultValue
} }
/** /**
@ -194,7 +206,7 @@ class MainViewModel @Inject constructor(
// Now we change to positive if the group we switched to supports negate // Now we change to positive if the group we switched to supports negate
if (!clickedUnit.group.canNegate) { if (!clickedUnit.group.canNegate) {
inputValue.update { inputValue.value.removePrefix(KEY_MINUS) } input.update { input.value.removePrefix(KEY_MINUS) }
} }
// Now setting up right unit (pair for the left one) // Now setting up right unit (pair for the left one)
@ -305,7 +317,7 @@ class MainViewModel @Inject constructor(
* @param[symbolToAdd] Digit/Symbol we want to add, can be any digit 0..9 or a dot symbol * @param[symbolToAdd] Digit/Symbol we want to add, can be any digit 0..9 or a dot symbol
*/ */
fun processInput(symbolToAdd: String) { fun processInput(symbolToAdd: String) {
val lastTwoSymbols = latestInputStack.takeLast(2) val lastTwoSymbols = _latestInputStack.takeLast(2)
val lastSymbol: String = lastTwoSymbols.getOrNull(1) ?: lastTwoSymbols[0] val lastSymbol: String = lastTwoSymbols.getOrNull(1) ?: lastTwoSymbols[0]
val lastSecondSymbol: String? = lastTwoSymbols.getOrNull(0) val lastSecondSymbol: String? = lastTwoSymbols.getOrNull(0)
@ -313,8 +325,8 @@ class MainViewModel @Inject constructor(
KEY_PLUS, KEY_DIVIDE, KEY_MULTIPLY, KEY_EXPONENT -> { KEY_PLUS, KEY_DIVIDE, KEY_MULTIPLY, KEY_EXPONENT -> {
when { when {
// Don't need expressions that start with zero // Don't need expressions that start with zero
(inputValue.value == KEY_0) -> {} (input.value == KEY_0) -> {}
(inputValue.value == KEY_MINUS) -> {} (input.value == KEY_MINUS) -> {}
(lastSymbol == KEY_LEFT_BRACKET) -> {} (lastSymbol == KEY_LEFT_BRACKET) -> {}
(lastSymbol == KEY_SQRT) -> {} (lastSymbol == KEY_SQRT) -> {}
/** /**
@ -338,7 +350,7 @@ class MainViewModel @Inject constructor(
KEY_0 -> { KEY_0 -> {
when { when {
// Don't add zero if the input is already a zero // Don't add zero if the input is already a zero
(inputValue.value == KEY_0) -> {} (input.value == KEY_0) -> {}
(lastSymbol == KEY_RIGHT_BRACKET) -> { (lastSymbol == KEY_RIGHT_BRACKET) -> {
processInput(KEY_MULTIPLY) processInput(KEY_MULTIPLY)
setInputSymbols(symbolToAdd) setInputSymbols(symbolToAdd)
@ -353,7 +365,7 @@ class MainViewModel @Inject constructor(
KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9 -> { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9 -> {
// Replace single zero (default input) if it's here // Replace single zero (default input) if it's here
when { when {
(inputValue.value == KEY_0) -> { (input.value == KEY_0) -> {
setInputSymbols(symbolToAdd, false) setInputSymbols(symbolToAdd, false)
} }
(lastSymbol == KEY_RIGHT_BRACKET) -> { (lastSymbol == KEY_RIGHT_BRACKET) -> {
@ -368,7 +380,7 @@ class MainViewModel @Inject constructor(
KEY_MINUS -> { KEY_MINUS -> {
when { when {
// Replace single zero with minus (to support negative numbers) // Replace single zero with minus (to support negative numbers)
(inputValue.value == KEY_0) -> { (input.value == KEY_0) -> {
setInputSymbols(symbolToAdd, false) setInputSymbols(symbolToAdd, false)
} }
// Don't allow multiple minuses near each other // Don't allow multiple minuses near each other
@ -391,7 +403,7 @@ class MainViewModel @Inject constructor(
KEY_LEFT_BRACKET -> { KEY_LEFT_BRACKET -> {
when { when {
// Replace single zero with minus (to support negative numbers) // Replace single zero with minus (to support negative numbers)
(inputValue.value == KEY_0) -> { (input.value == KEY_0) -> {
setInputSymbols(symbolToAdd, false) setInputSymbols(symbolToAdd, false)
} }
(lastSymbol == KEY_RIGHT_BRACKET) || (lastSymbol in DIGITS) || (lastSymbol == KEY_DOT) -> { (lastSymbol == KEY_RIGHT_BRACKET) || (lastSymbol in DIGITS) || (lastSymbol == KEY_DOT) -> {
@ -406,10 +418,11 @@ class MainViewModel @Inject constructor(
KEY_RIGHT_BRACKET -> { KEY_RIGHT_BRACKET -> {
when { when {
// Replace single zero with minus (to support negative numbers) // Replace single zero with minus (to support negative numbers)
(inputValue.value == KEY_0) -> {} (input.value == KEY_0) -> {}
(lastSymbol == KEY_LEFT_BRACKET) -> {} (lastSymbol == KEY_LEFT_BRACKET) -> {}
(latestInputStack.filter { it == KEY_LEFT_BRACKET }.size == (_latestInputStack.filter { it == KEY_LEFT_BRACKET }.size ==
latestInputStack.filter { it == KEY_RIGHT_BRACKET }.size) -> {} _latestInputStack.filter { it == KEY_RIGHT_BRACKET }.size) -> {
}
else -> { else -> {
setInputSymbols(symbolToAdd) setInputSymbols(symbolToAdd)
} }
@ -418,7 +431,7 @@ class MainViewModel @Inject constructor(
KEY_SQRT -> { KEY_SQRT -> {
when { when {
// Replace single zero with minus (to support negative numbers) // Replace single zero with minus (to support negative numbers)
(inputValue.value == KEY_0) -> { (input.value == KEY_0) -> {
setInputSymbols(symbolToAdd, false) setInputSymbols(symbolToAdd, false)
} }
(lastSymbol == KEY_RIGHT_BRACKET) || (lastSymbol in DIGITS) || (lastSymbol == KEY_DOT) -> { (lastSymbol == KEY_RIGHT_BRACKET) || (lastSymbol in DIGITS) || (lastSymbol == KEY_DOT) -> {
@ -433,7 +446,7 @@ class MainViewModel @Inject constructor(
else -> { else -> {
when { when {
// Replace single zero with minus (to support negative numbers) // Replace single zero with minus (to support negative numbers)
(inputValue.value == KEY_0) -> { (input.value == KEY_0) -> {
setInputSymbols(symbolToAdd, false) setInputSymbols(symbolToAdd, false)
} }
else -> { else -> {
@ -442,7 +455,6 @@ class MainViewModel @Inject constructor(
} }
} }
} }
} }
/** /**
@ -450,24 +462,24 @@ class MainViewModel @Inject constructor(
*/ */
fun deleteDigit() { fun deleteDigit() {
// Default input, don't delete // Default input, don't delete
if (inputValue.value == KEY_0) return if (input.value == KEY_0) return
val lastSymbol = latestInputStack.removeLast() val lastSymbol = _latestInputStack.removeLast()
// We will need to delete last symbol from both values // We will need to delete last symbol from both values
val displayRepresentation: String = INTERNAL_DISPLAY[lastSymbol] ?: lastSymbol val displayRepresentation: String = INTERNAL_DISPLAY[lastSymbol] ?: lastSymbol
// If this value are same, it means that after deleting there will be no symbols left, set to default // If this value are same, it means that after deleting there will be no symbols left, set to default
if (lastSymbol == inputValue.value) { if (lastSymbol == input.value) {
setInputSymbols(KEY_0, false) setInputSymbols(KEY_0, false)
} else { } else {
inputValue.update { it.removeSuffix(lastSymbol) } input.update { it.removeSuffix(lastSymbol) }
_inputDisplayValue.update { it.removeSuffix(displayRepresentation) } _inputDisplay.update { it.removeSuffix(displayRepresentation) }
} }
} }
/** /**
* Adds given [symbol] to [inputValue] and [_inputDisplayValue] and updates [latestInputStack]. * Adds given [symbol] to [input] and [_inputDisplay] and updates [_latestInputStack].
* *
* By default add symbol, but if [add] is False, will replace current input (when replacing * By default add symbol, but if [add] is False, will replace current input (when replacing
* default [KEY_0] input). * default [KEY_0] input).
@ -477,15 +489,15 @@ class MainViewModel @Inject constructor(
when { when {
add -> { add -> {
inputValue.update { it + symbol } input.update { it + symbol }
_inputDisplayValue.update { it + displaySymbol } _inputDisplay.update { it + displaySymbol }
latestInputStack.add(symbol) _latestInputStack.add(symbol)
} }
else -> { else -> {
latestInputStack.clear() _latestInputStack.clear()
inputValue.update { symbol } input.update { symbol }
_inputDisplayValue.update { displaySymbol } _inputDisplay.update { displaySymbol }
latestInputStack.add(symbol) _latestInputStack.add(symbol)
} }
} }
} }
@ -511,7 +523,7 @@ class MainViewModel @Inject constructor(
/** /**
* Returns True if can be placed. * Returns True if can be placed.
*/ */
private fun canEnterDot(): Boolean = !inputValue.value.takeLastWhile { private fun canEnterDot(): Boolean = !input.value.takeLastWhile {
it.toString() !in OPERATORS.minus(KEY_DOT) it.toString() !in OPERATORS.minus(KEY_DOT)
}.contains(KEY_DOT) }.contains(KEY_DOT)
@ -522,6 +534,15 @@ class MainViewModel @Inject constructor(
userPrefsRepository.updateLatestPairOfUnits(unitFrom, unitTo) userPrefsRepository.updateLatestPairOfUnits(unitFrom, unitTo)
} }
private fun startObserving() {
viewModelScope.launch(Dispatchers.Default) {
_userPrefs.collectLatest { convertInput() }
}
viewModelScope.launch(Dispatchers.Default) {
input.collectLatest { convertInput() }
}
}
init { init {
viewModelScope.launch { viewModelScope.launch {
val initialUserPrefs = userPrefsRepository.userPreferencesFlow.first() val initialUserPrefs = userPrefsRepository.userPreferencesFlow.first()
@ -546,6 +567,8 @@ class MainViewModel @Inject constructor(
// User is free to convert values and units on units screen can be sorted properly // User is free to convert values and units on units screen can be sorted properly
_isLoadingDatabase.update { false } _isLoadingDatabase.update { false }
updateCurrenciesBasicUnits() updateCurrenciesBasicUnits()
startObserving()
} }
} }
} }

View File

@ -117,7 +117,8 @@ fun MyTextField(
) { ) {
Text( Text(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = it, // Quick fix to prevent the UI from crashing
text = it.take(1000),
textAlign = TextAlign.End, textAlign = TextAlign.End,
softWrap = false, softWrap = false,
style = NumbersTextStyleDisplayLarge style = NumbersTextStyleDisplayLarge
@ -151,7 +152,8 @@ fun MyTextField(
) { ) {
Text( Text(
modifier = Modifier, modifier = Modifier,
text = Formatter.format(it ?: ""), // Quick fix to prevent the UI from crashing
text = Formatter.format(it?.take(1000) ?: ""),
textAlign = TextAlign.End, textAlign = TextAlign.End,
softWrap = false, softWrap = false,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f), color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),

View File

@ -177,7 +177,7 @@ class MainViewModelTest {
viewModel.processInput(it) viewModel.processInput(it)
viewModel.deleteDigit() viewModel.deleteDigit()
assertEquals("0", viewModel.mainFlow.value.inputValue) assertEquals("0", viewModel.mainFlow.value.inputValue)
assertEquals("0", viewModel.inputValue.value) assertEquals("0", viewModel.input.value)
} }
viewModel.clearInput() viewModel.clearInput()
@ -189,7 +189,7 @@ class MainViewModelTest {
viewModel.processInput(KEY_SQRT) viewModel.processInput(KEY_SQRT)
viewModel.processInput(KEY_9) viewModel.processInput(KEY_9)
viewModel.deleteDigit() viewModel.deleteDigit()
assertEquals("3*√", viewModel.inputValue.value) assertEquals("3*√", viewModel.input.value)
assertEquals("3×", viewModel.mainFlow.value.inputValue) assertEquals("3×", viewModel.mainFlow.value.inputValue)
} }
@ -217,7 +217,7 @@ class MainViewModelTest {
input.forEach { input.forEach {
viewModel.processInput(it.toString()) viewModel.processInput(it.toString())
} }
assertEquals(output, viewModel.inputValue.value) assertEquals(output, viewModel.input.value)
assertEquals(outputDisplay, viewModel.mainFlow.value.inputValue) assertEquals(outputDisplay, viewModel.mainFlow.value.inputValue)
viewModel.clearInput() viewModel.clearInput()
} }