mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 16:55:26 +02:00
parent
839954ce85
commit
5b7169d396
@ -31,6 +31,8 @@ import androidx.compose.animation.fadeOut
|
|||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
@ -46,6 +48,7 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.VerticalDivider
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
@ -57,6 +60,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.focus.onFocusEvent
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -64,6 +68,7 @@ import androidx.compose.ui.text.TextRange
|
|||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.sadellie.unitto.core.base.OutputFormat
|
import com.sadellie.unitto.core.base.OutputFormat
|
||||||
@ -81,6 +86,7 @@ import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
|||||||
import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField
|
import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField
|
||||||
import com.sadellie.unitto.core.ui.datetime.formatDateWeekDayMonthYear
|
import com.sadellie.unitto.core.ui.datetime.formatDateWeekDayMonthYear
|
||||||
import com.sadellie.unitto.data.common.format
|
import com.sadellie.unitto.data.common.format
|
||||||
|
import com.sadellie.unitto.data.converter.MyUnitIDS
|
||||||
import com.sadellie.unitto.data.model.UnitGroup
|
import com.sadellie.unitto.data.model.UnitGroup
|
||||||
import com.sadellie.unitto.data.model.unit.AbstractUnit
|
import com.sadellie.unitto.data.model.unit.AbstractUnit
|
||||||
import com.sadellie.unitto.feature.converter.components.DefaultKeyboard
|
import com.sadellie.unitto.feature.converter.components.DefaultKeyboard
|
||||||
@ -109,6 +115,7 @@ internal fun ConverterRoute(
|
|||||||
deleteDigit = viewModel::deleteTokens,
|
deleteDigit = viewModel::deleteTokens,
|
||||||
clearInput = viewModel::clearInput,
|
clearInput = viewModel::clearInput,
|
||||||
onCursorChange = viewModel::onCursorChange,
|
onCursorChange = viewModel::onCursorChange,
|
||||||
|
onFocusOnInput2 = viewModel::updateFocused,
|
||||||
onErrorClick = viewModel::updateCurrencyRates,
|
onErrorClick = viewModel::updateCurrencyRates,
|
||||||
addBracket = viewModel::addBracket
|
addBracket = viewModel::addBracket
|
||||||
)
|
)
|
||||||
@ -126,6 +133,7 @@ private fun ConverterScreen(
|
|||||||
deleteDigit: () -> Unit,
|
deleteDigit: () -> Unit,
|
||||||
clearInput: () -> Unit,
|
clearInput: () -> Unit,
|
||||||
onCursorChange: (TextRange) -> Unit,
|
onCursorChange: (TextRange) -> Unit,
|
||||||
|
onFocusOnInput2: (Boolean) -> Unit,
|
||||||
onErrorClick: (AbstractUnit) -> Unit,
|
onErrorClick: (AbstractUnit) -> Unit,
|
||||||
addBracket: () -> Unit,
|
addBracket: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@ -160,6 +168,7 @@ private fun ConverterScreen(
|
|||||||
modifier = Modifier.padding(it),
|
modifier = Modifier.padding(it),
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
onCursorChange = onCursorChange,
|
onCursorChange = onCursorChange,
|
||||||
|
onFocusOnInput2 = onFocusOnInput2,
|
||||||
processInput = processInput,
|
processInput = processInput,
|
||||||
deleteDigit = deleteDigit,
|
deleteDigit = deleteDigit,
|
||||||
navigateToLeftScreen = navigateToLeftScreen,
|
navigateToLeftScreen = navigateToLeftScreen,
|
||||||
@ -254,6 +263,7 @@ private fun Default(
|
|||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
uiState: UnitConverterUIState.Default,
|
uiState: UnitConverterUIState.Default,
|
||||||
onCursorChange: (TextRange) -> Unit,
|
onCursorChange: (TextRange) -> Unit,
|
||||||
|
onFocusOnInput2: (Boolean) -> Unit,
|
||||||
processInput: (String) -> Unit,
|
processInput: (String) -> Unit,
|
||||||
deleteDigit: () -> Unit,
|
deleteDigit: () -> Unit,
|
||||||
navigateToLeftScreen: () -> Unit,
|
navigateToLeftScreen: () -> Unit,
|
||||||
@ -288,7 +298,9 @@ private fun Default(
|
|||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
content1 = { contentModifier ->
|
content1 = { contentModifier ->
|
||||||
ColumnWithConstraints(modifier = contentModifier) {
|
ColumnWithConstraints(modifier = contentModifier) {
|
||||||
val textFieldModifier = Modifier.fillMaxWidth().weight(2f)
|
val textFieldModifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(2f)
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = lastUpdate != null,
|
visible = lastUpdate != null,
|
||||||
@ -307,11 +319,56 @@ private fun Default(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uiState.unitFrom.id == MyUnitIDS.foot) {
|
||||||
|
Row(
|
||||||
|
modifier = textFieldModifier,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
ExpressionTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth().weight(1f),
|
||||||
|
minRatio = 0.7f,
|
||||||
|
placeholder = Token.Digit._0,
|
||||||
|
value = uiState.input1,
|
||||||
|
onCursorChange = onCursorChange,
|
||||||
|
pasteCallback = processInput,
|
||||||
|
cutCallback = deleteDigit,
|
||||||
|
formatterSymbols = uiState.formatterSymbols,
|
||||||
|
)
|
||||||
|
AnimatedUnitShortName(stringResource(uiState.unitFrom.shortName))
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalDivider()
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
ExpressionTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth().weight(1f)
|
||||||
|
.onFocusEvent { state -> onFocusOnInput2(state.hasFocus) },
|
||||||
|
minRatio = 0.7f,
|
||||||
|
placeholder = Token.Digit._0,
|
||||||
|
value = uiState.input2,
|
||||||
|
onCursorChange = onCursorChange,
|
||||||
|
pasteCallback = processInput,
|
||||||
|
cutCallback = deleteDigit,
|
||||||
|
formatterSymbols = uiState.formatterSymbols,
|
||||||
|
)
|
||||||
|
AnimatedUnitShortName(stringResource(R.string.unit_inch_short))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
ExpressionTextField(
|
ExpressionTextField(
|
||||||
modifier = textFieldModifier,
|
modifier = textFieldModifier,
|
||||||
minRatio = 0.7f,
|
minRatio = 0.7f,
|
||||||
placeholder = Token.Digit._0,
|
placeholder = Token.Digit._0,
|
||||||
value = uiState.input,
|
value = uiState.input1,
|
||||||
onCursorChange = onCursorChange,
|
onCursorChange = onCursorChange,
|
||||||
pasteCallback = processInput,
|
pasteCallback = processInput,
|
||||||
cutCallback = deleteDigit,
|
cutCallback = deleteDigit,
|
||||||
@ -334,6 +391,7 @@ private fun Default(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
AnimatedUnitShortName(stringResource(uiState.unitFrom.shortName))
|
AnimatedUnitShortName(stringResource(uiState.unitFrom.shortName))
|
||||||
|
}
|
||||||
|
|
||||||
ConverterResultTextField(
|
ConverterResultTextField(
|
||||||
modifier = textFieldModifier,
|
modifier = textFieldModifier,
|
||||||
@ -392,6 +450,7 @@ private fun ConverterResultTextField(
|
|||||||
is ConverterResult.Default -> result.value.format(scale, outputFormat)
|
is ConverterResult.Default -> result.value.format(scale, outputFormat)
|
||||||
is ConverterResult.NumberBase -> result.value.uppercase()
|
is ConverterResult.NumberBase -> result.value.uppercase()
|
||||||
is ConverterResult.Time -> result.format(mContext, formatterSymbols)
|
is ConverterResult.Time -> result.format(mContext, formatterSymbols)
|
||||||
|
is ConverterResult.FootInch -> result.format(mContext, scale, outputFormat, formatterSymbols)
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
mutableStateOf(TextFieldValue(value))
|
mutableStateOf(TextFieldValue(value))
|
||||||
@ -430,7 +489,9 @@ private fun ConverterResultTextField(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ConverterResult.NumberBase, is ConverterResult.Time -> {
|
is ConverterResult.NumberBase,
|
||||||
|
is ConverterResult.Time,
|
||||||
|
is ConverterResult.FootInch -> {
|
||||||
UnformattedTextField(
|
UnformattedTextField(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
value = resultTextField,
|
value = resultTextField,
|
||||||
@ -527,6 +588,7 @@ private fun PreviewConverterScreen() {
|
|||||||
deleteDigit = {},
|
deleteDigit = {},
|
||||||
clearInput = {},
|
clearInput = {},
|
||||||
onCursorChange = {},
|
onCursorChange = {},
|
||||||
|
onFocusOnInput2 = {},
|
||||||
onErrorClick = {},
|
onErrorClick = {},
|
||||||
addBracket = {}
|
addBracket = {}
|
||||||
)
|
)
|
||||||
|
@ -24,6 +24,7 @@ import com.sadellie.unitto.core.base.R
|
|||||||
import com.sadellie.unitto.core.base.Token
|
import com.sadellie.unitto.core.base.Token
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
||||||
import com.sadellie.unitto.core.ui.common.textfield.formatExpression
|
import com.sadellie.unitto.core.ui.common.textfield.formatExpression
|
||||||
|
import com.sadellie.unitto.data.common.format
|
||||||
import com.sadellie.unitto.data.common.isEqualTo
|
import com.sadellie.unitto.data.common.isEqualTo
|
||||||
import com.sadellie.unitto.data.common.isGreaterThan
|
import com.sadellie.unitto.data.common.isGreaterThan
|
||||||
import com.sadellie.unitto.data.common.isLessThan
|
import com.sadellie.unitto.data.common.isLessThan
|
||||||
@ -37,7 +38,8 @@ internal sealed class UnitConverterUIState {
|
|||||||
data object Loading : UnitConverterUIState()
|
data object Loading : UnitConverterUIState()
|
||||||
|
|
||||||
data class Default(
|
data class Default(
|
||||||
val input: TextFieldValue = TextFieldValue(),
|
val input1: TextFieldValue,
|
||||||
|
val input2: TextFieldValue,
|
||||||
val calculation: BigDecimal?,
|
val calculation: BigDecimal?,
|
||||||
val result: ConverterResult,
|
val result: ConverterResult,
|
||||||
val unitFrom: DefaultUnit,
|
val unitFrom: DefaultUnit,
|
||||||
@ -53,7 +55,7 @@ internal sealed class UnitConverterUIState {
|
|||||||
) : UnitConverterUIState()
|
) : UnitConverterUIState()
|
||||||
|
|
||||||
data class NumberBase(
|
data class NumberBase(
|
||||||
val input: TextFieldValue = TextFieldValue(),
|
val input: TextFieldValue,
|
||||||
val result: ConverterResult,
|
val result: ConverterResult,
|
||||||
val unitFrom: NumberBaseUnit,
|
val unitFrom: NumberBaseUnit,
|
||||||
val unitTo: NumberBaseUnit,
|
val unitTo: NumberBaseUnit,
|
||||||
@ -85,6 +87,11 @@ internal sealed class ConverterResult {
|
|||||||
val attosecond: BigDecimal,
|
val attosecond: BigDecimal,
|
||||||
) : ConverterResult()
|
) : ConverterResult()
|
||||||
|
|
||||||
|
data class FootInch(
|
||||||
|
val foot: BigDecimal,
|
||||||
|
val inch: BigDecimal,
|
||||||
|
) : ConverterResult()
|
||||||
|
|
||||||
data object Loading : ConverterResult()
|
data object Loading : ConverterResult()
|
||||||
|
|
||||||
data object Error : ConverterResult()
|
data object Error : ConverterResult()
|
||||||
@ -128,6 +135,25 @@ internal fun ConverterResult.Time.format(mContext: Context, formatterSymbols: Fo
|
|||||||
return (if (negative) Token.Operator.minus else "") + result.joinToString(" ").ifEmpty { Token.Digit._0 }
|
return (if (negative) Token.Operator.minus else "") + result.joinToString(" ").ifEmpty { Token.Digit._0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun ConverterResult.FootInch.format(
|
||||||
|
mContext: Context,
|
||||||
|
scale: Int,
|
||||||
|
outputFormat: Int,
|
||||||
|
formatterSymbols: FormatterSymbols
|
||||||
|
): String {
|
||||||
|
var result = ""
|
||||||
|
result += foot.format(scale, outputFormat).formatExpression(formatterSymbols)
|
||||||
|
|
||||||
|
if (inch.isGreaterThan(BigDecimal.ZERO)) {
|
||||||
|
result += mContext.getString(R.string.unit_foot_short)
|
||||||
|
result += " "
|
||||||
|
result += inch.format(scale, outputFormat).formatExpression(formatterSymbols)
|
||||||
|
result += mContext.getString(R.string.unit_inch_short)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
internal fun formatTime(
|
internal fun formatTime(
|
||||||
input: BigDecimal,
|
input: BigDecimal,
|
||||||
): ConverterResult.Time {
|
): ConverterResult.Time {
|
||||||
@ -202,6 +228,26 @@ internal fun formatTime(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object for displaying formatted foot and inch output. Units are passed as objects so
|
||||||
|
* that changes in basic units don't require modifying the method. Also this method can't access
|
||||||
|
* units repository directly.
|
||||||
|
*
|
||||||
|
* @param input Input in feet.
|
||||||
|
* @param footUnit Foot unit [DefaultUnit].
|
||||||
|
* @param inchUnit Inch unit [DefaultUnit].
|
||||||
|
* @return Result where decimal places are converter into inches.
|
||||||
|
*/
|
||||||
|
internal fun formatFootInch(
|
||||||
|
input: BigDecimal,
|
||||||
|
footUnit: DefaultUnit,
|
||||||
|
inchUnit: DefaultUnit
|
||||||
|
): ConverterResult.FootInch {
|
||||||
|
val (integral, fractional) = input.divideAndRemainder(BigDecimal.ONE)
|
||||||
|
|
||||||
|
return ConverterResult.FootInch(integral, footUnit.convert(inchUnit, fractional))
|
||||||
|
}
|
||||||
|
|
||||||
private val dayBasicUnit by lazy { BigDecimal("86400000000000000000000") }
|
private val dayBasicUnit by lazy { BigDecimal("86400000000000000000000") }
|
||||||
private val hourBasicUnit by lazy { BigDecimal("3600000000000000000000") }
|
private val hourBasicUnit by lazy { BigDecimal("3600000000000000000000") }
|
||||||
private val minuteBasicUnit by lazy { BigDecimal("60000000000000000000") }
|
private val minuteBasicUnit by lazy { BigDecimal("60000000000000000000") }
|
||||||
|
@ -65,8 +65,11 @@ internal class ConverterViewModel @Inject constructor(
|
|||||||
private val savedStateHandle: SavedStateHandle
|
private val savedStateHandle: SavedStateHandle
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val converterInputKey = "CONVERTER_INPUT"
|
private val converterInputKey1 = "CONVERTER_INPUT_1"
|
||||||
private val _input = MutableStateFlow(savedStateHandle.getTextField(converterInputKey))
|
private val converterInputKey2 = "CONVERTER_INPUT_2"
|
||||||
|
private val _input1 = MutableStateFlow(savedStateHandle.getTextField(converterInputKey1))
|
||||||
|
private val _input2 = MutableStateFlow(savedStateHandle.getTextField(converterInputKey2))
|
||||||
|
private val _focusedOnInput2 = MutableStateFlow(false)
|
||||||
private val _calculation = MutableStateFlow<BigDecimal?>(null)
|
private val _calculation = MutableStateFlow<BigDecimal?>(null)
|
||||||
private val _result = MutableStateFlow<ConverterResult>(ConverterResult.Loading)
|
private val _result = MutableStateFlow<ConverterResult>(ConverterResult.Loading)
|
||||||
private val _unitFrom = MutableStateFlow<AbstractUnit?>(null)
|
private val _unitFrom = MutableStateFlow<AbstractUnit?>(null)
|
||||||
@ -83,18 +86,20 @@ internal class ConverterViewModel @Inject constructor(
|
|||||||
private var _loadCurrenciesJob: Job? = null
|
private var _loadCurrenciesJob: Job? = null
|
||||||
|
|
||||||
val converterUiState: StateFlow<UnitConverterUIState> = combine(
|
val converterUiState: StateFlow<UnitConverterUIState> = combine(
|
||||||
_input,
|
_input1,
|
||||||
|
_input2,
|
||||||
_calculation,
|
_calculation,
|
||||||
_result,
|
_result,
|
||||||
_unitFrom,
|
_unitFrom,
|
||||||
_unitTo,
|
_unitTo,
|
||||||
userPrefsRepository.converterPrefs,
|
userPrefsRepository.converterPrefs,
|
||||||
_currenciesState
|
_currenciesState
|
||||||
) { input, calculation, result, unitFrom, unitTo, prefs, currenciesState ->
|
) { input1, input2, calculation, result, unitFrom, unitTo, prefs, currenciesState ->
|
||||||
return@combine when {
|
return@combine when {
|
||||||
(unitFrom is DefaultUnit) and (unitTo is DefaultUnit) -> {
|
(unitFrom is DefaultUnit) and (unitTo is DefaultUnit) -> {
|
||||||
UnitConverterUIState.Default(
|
UnitConverterUIState.Default(
|
||||||
input = input,
|
input1 = input1,
|
||||||
|
input2 = input2,
|
||||||
calculation = calculation,
|
calculation = calculation,
|
||||||
result = result,
|
result = result,
|
||||||
unitFrom = unitFrom as DefaultUnit,
|
unitFrom = unitFrom as DefaultUnit,
|
||||||
@ -111,7 +116,7 @@ internal class ConverterViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
(unitFrom is NumberBaseUnit) and (unitTo is NumberBaseUnit) -> {
|
(unitFrom is NumberBaseUnit) and (unitTo is NumberBaseUnit) -> {
|
||||||
UnitConverterUIState.NumberBase(
|
UnitConverterUIState.NumberBase(
|
||||||
input = input,
|
input = input1,
|
||||||
result = result,
|
result = result,
|
||||||
unitFrom = unitFrom as NumberBaseUnit,
|
unitFrom = unitFrom as NumberBaseUnit,
|
||||||
unitTo = unitTo as NumberBaseUnit,
|
unitTo = unitTo as NumberBaseUnit,
|
||||||
@ -134,12 +139,14 @@ internal class ConverterViewModel @Inject constructor(
|
|||||||
is CurrencyRateUpdateState.Ready, is CurrencyRateUpdateState.Nothing -> {}
|
is CurrencyRateUpdateState.Ready, is CurrencyRateUpdateState.Nothing -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
when (ui) {
|
when (ui) {
|
||||||
is UnitConverterUIState.Default -> {
|
is UnitConverterUIState.Default -> {
|
||||||
convertDefault(
|
convertDefault(
|
||||||
unitFrom = ui.unitFrom,
|
unitFrom = ui.unitFrom,
|
||||||
unitTo = ui.unitTo,
|
unitTo = ui.unitTo,
|
||||||
input = ui.input,
|
input1 = ui.input1,
|
||||||
|
input2 = ui.input2,
|
||||||
formatTime = ui.formatTime
|
formatTime = ui.formatTime
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -152,6 +159,11 @@ internal class ConverterViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
is UnitConverterUIState.Loading -> {}
|
is UnitConverterUIState.Loading -> {}
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_result.update { ConverterResult.Default(BigDecimal.ZERO) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ui
|
ui
|
||||||
}
|
}
|
||||||
.stateIn(viewModelScope, UnitConverterUIState.Loading)
|
.stateIn(viewModelScope, UnitConverterUIState.Loading)
|
||||||
@ -194,7 +206,7 @@ internal class ConverterViewModel @Inject constructor(
|
|||||||
val rightSideUIState = combine(
|
val rightSideUIState = combine(
|
||||||
_unitFrom,
|
_unitFrom,
|
||||||
_unitTo,
|
_unitTo,
|
||||||
_input,
|
_input1,
|
||||||
_calculation,
|
_calculation,
|
||||||
_rightQuery,
|
_rightQuery,
|
||||||
_rightUnits,
|
_rightUnits,
|
||||||
@ -251,30 +263,73 @@ internal class ConverterViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addTokens(tokens: String) = _input.update {
|
/**
|
||||||
|
* Change currently focused text field. For feet and inches input
|
||||||
|
*
|
||||||
|
* @param focusOnInput2 `true` if focus is on inches input. `false`if focus on feet input.
|
||||||
|
*/
|
||||||
|
fun updateFocused(focusOnInput2: Boolean) = _focusedOnInput2.update { focusOnInput2 }
|
||||||
|
|
||||||
|
fun addTokens(tokens: String) {
|
||||||
|
if (_focusedOnInput2.value) {
|
||||||
|
_input2.update {
|
||||||
val newValue = it.addTokens(tokens)
|
val newValue = it.addTokens(tokens)
|
||||||
savedStateHandle[converterInputKey] = newValue.text
|
savedStateHandle[converterInputKey2] = newValue.text
|
||||||
newValue
|
newValue
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_input1.update {
|
||||||
|
val newValue = it.addTokens(tokens)
|
||||||
|
savedStateHandle[converterInputKey1] = newValue.text
|
||||||
|
newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun addBracket() = _input.update {
|
fun addBracket() {
|
||||||
|
if (_focusedOnInput2.value) {
|
||||||
|
_input2.update {
|
||||||
val newValue = it.addBracket()
|
val newValue = it.addBracket()
|
||||||
savedStateHandle[converterInputKey] = newValue.text
|
savedStateHandle[converterInputKey2] = newValue.text
|
||||||
newValue
|
newValue
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_input1.update {
|
||||||
|
val newValue = it.addBracket()
|
||||||
|
savedStateHandle[converterInputKey1] = newValue.text
|
||||||
|
newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun deleteTokens() = _input.update {
|
fun deleteTokens() {
|
||||||
|
if (_focusedOnInput2.value) {
|
||||||
|
_input2.update {
|
||||||
val newValue = it.deleteTokens()
|
val newValue = it.deleteTokens()
|
||||||
savedStateHandle[converterInputKey] = newValue.text
|
savedStateHandle[converterInputKey2] = newValue.text
|
||||||
newValue
|
newValue
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_input1.update {
|
||||||
|
val newValue = it.deleteTokens()
|
||||||
|
savedStateHandle[converterInputKey1] = newValue.text
|
||||||
|
newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun clearInput() = _input.update {
|
fun clearInput() {
|
||||||
savedStateHandle[converterInputKey] = ""
|
_input1.update {
|
||||||
|
savedStateHandle[converterInputKey1] = ""
|
||||||
TextFieldValue()
|
TextFieldValue()
|
||||||
}
|
}
|
||||||
|
_input2.update {
|
||||||
|
savedStateHandle[converterInputKey2] = ""
|
||||||
|
TextFieldValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onCursorChange(selection: TextRange) = _input.update { it.copy(selection = selection) }
|
fun onCursorChange(selection: TextRange) = _input1.update { it.copy(selection = selection) }
|
||||||
|
|
||||||
fun updateCurrencyRates(unit: AbstractUnit) {
|
fun updateCurrencyRates(unit: AbstractUnit) {
|
||||||
_loadCurrenciesJob = viewModelScope.launch(Dispatchers.IO) {
|
_loadCurrenciesJob = viewModelScope.launch(Dispatchers.IO) {
|
||||||
@ -387,31 +442,50 @@ internal class ConverterViewModel @Inject constructor(
|
|||||||
private fun convertDefault(
|
private fun convertDefault(
|
||||||
unitFrom: DefaultUnit,
|
unitFrom: DefaultUnit,
|
||||||
unitTo: DefaultUnit,
|
unitTo: DefaultUnit,
|
||||||
input: TextFieldValue,
|
input1: TextFieldValue,
|
||||||
|
input2: TextFieldValue,
|
||||||
formatTime: Boolean,
|
formatTime: Boolean,
|
||||||
) = viewModelScope.launch(Dispatchers.Default) {
|
) = viewModelScope.launch(Dispatchers.Default) {
|
||||||
val calculated = try {
|
val footInchInput = unitFrom.id == MyUnitIDS.foot
|
||||||
Expression(input.text.ifEmpty { Token.Digit._0 }).calculate()
|
|
||||||
|
if (footInchInput) { _calculation.update { null } }
|
||||||
|
|
||||||
|
// Calculate
|
||||||
|
val calculated1 = try {
|
||||||
|
Expression(input1.text.ifEmpty { Token.Digit._0 }).calculate()
|
||||||
} catch (e: ExpressionException.DivideByZero) {
|
} catch (e: ExpressionException.DivideByZero) {
|
||||||
_calculation.update { null }
|
_calculation.update { null }
|
||||||
return@launch
|
return@launch
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
_calculation.update { if (input.text.isExpression()) calculated else null }
|
val calculated2 = try {
|
||||||
|
Expression(input2.text.ifEmpty { Token.Digit._0 }).calculate()
|
||||||
try {
|
} catch (e: ExpressionException.DivideByZero) {
|
||||||
if ((unitFrom.group == UnitGroup.TIME) and (formatTime)) {
|
_calculation.update { null }
|
||||||
_result.update { formatTime(calculated.multiply(unitFrom.basicUnit)) }
|
return@launch
|
||||||
|
} catch (e: Exception) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
val conversion = unitFrom.convert(unitTo, calculated)
|
// Update calculation
|
||||||
|
_calculation.update { if (input1.text.isExpression()) calculated1 else null }
|
||||||
|
|
||||||
_result.update { ConverterResult.Default(conversion) }
|
// Convert
|
||||||
} catch (e: Exception) {
|
var conversion = unitFrom.convert(unitTo, calculated1)
|
||||||
_result.update { ConverterResult.Default(BigDecimal.ZERO) }
|
if (footInchInput) {
|
||||||
|
// Converted from second text field too
|
||||||
|
val inches = unitsRepo.getById(MyUnitIDS.inch) as DefaultUnit
|
||||||
|
conversion += inches.convert(unitTo, calculated2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update result
|
||||||
|
_result.update {
|
||||||
|
when {
|
||||||
|
(unitFrom.group == UnitGroup.TIME) and (formatTime) -> formatTime(calculated1.multiply(unitFrom.basicUnit))
|
||||||
|
unitTo.id == MyUnitIDS.foot -> formatFootInch(conversion, unitTo, unitsRepo.getById(MyUnitIDS.inch) as DefaultUnit)
|
||||||
|
else -> ConverterResult.Default(conversion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user