Loading state for Unit Converter and Calculator keyboards

This commit is contained in:
Sad Ellie 2023-09-11 13:13:44 +03:00
parent 2405a2656a
commit 1d5bbaf5b2
6 changed files with 238 additions and 46 deletions

View File

@ -70,6 +70,7 @@ import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField
import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField
import com.sadellie.unitto.data.model.HistoryItem
import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboard
import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboardLoading
import com.sadellie.unitto.feature.calculator.components.HistoryList
import java.text.SimpleDateFormat
import java.util.Locale
@ -80,25 +81,31 @@ internal fun CalculatorRoute(
navigateToSettings: () -> Unit,
viewModel: CalculatorViewModel = hiltViewModel()
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
CalculatorScreen(
uiState = uiState.value,
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings,
addSymbol = viewModel::addTokens,
clearSymbols = viewModel::clearInput,
deleteSymbol = viewModel::deleteTokens,
onCursorChange = viewModel::onCursorChange,
toggleAngleMode = viewModel::toggleCalculatorMode,
evaluate = viewModel::evaluate,
clearHistory = viewModel::clearHistory
)
when (uiState) {
is CalculatorUIState.Loading -> Loading(
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings,
)
is CalculatorUIState.Ready -> CalculatorScreen(
uiState = uiState,
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings,
addSymbol = viewModel::addTokens,
clearSymbols = viewModel::clearInput,
deleteSymbol = viewModel::deleteTokens,
onCursorChange = viewModel::onCursorChange,
toggleAngleMode = viewModel::toggleCalculatorMode,
evaluate = viewModel::evaluate,
clearHistory = viewModel::clearHistory
)
}
}
@Composable
private fun CalculatorScreen(
uiState: CalculatorUIState,
uiState: CalculatorUIState.Ready,
navigateToMenu: () -> Unit,
navigateToSettings: () -> Unit,
addSymbol: (String) -> Unit,
@ -317,6 +324,80 @@ private fun CalculatorScreen(
}
}
@Composable
private fun Loading(
navigateToMenu: () -> Unit,
navigateToSettings: () -> Unit,
) {
UnittoScreenWithTopBar(
title = { Text(stringResource(R.string.calculator)) },
navigationIcon = { MenuButton { navigateToMenu() } },
colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceVariant),
actions = { SettingsButton(navigateToSettings) }
) { paddingValues ->
BoxWithConstraints(
modifier = Modifier.padding(paddingValues),
) {
// Input
Column(
Modifier
.height(maxHeight * 0.25f)
.background(
MaterialTheme.colorScheme.surfaceVariant,
RoundedCornerShape(
topStartPercent = 0, topEndPercent = 0,
bottomStartPercent = 20, bottomEndPercent = 20
)
)
.padding(top = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
UnformattedTextField(
modifier = Modifier
.weight(2f)
.fillMaxWidth()
.padding(horizontal = 8.dp),
value = TextFieldValue(),
minRatio = 0.5f,
onCursorChange = {},
)
if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) {
UnformattedTextField(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.padding(horizontal = 8.dp),
value = TextFieldValue(),
minRatio = 1f,
onCursorChange = {},
readOnly = true,
)
}
// Handle
Box(
Modifier
.padding(8.dp)
.background(
MaterialTheme.colorScheme.onSurfaceVariant,
RoundedCornerShape(100)
)
.sizeIn(24.dp, 4.dp)
)
}
// Keyboard
CalculatorKeyboardLoading(
modifier = Modifier
.offset(y = maxHeight * 0.25f)
.height(maxHeight * 0.75f)
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 4.dp),
)
}
}
}
@Preview(widthDp = 432, heightDp = 1008, device = "spec:parent=pixel_5,orientation=portrait")
@Preview(widthDp = 432, heightDp = 864, device = "spec:parent=pixel_5,orientation=portrait")
@Preview(widthDp = 597, heightDp = 1393, device = "spec:parent=pixel_5,orientation=portrait")
@ -346,7 +427,7 @@ private fun PreviewCalculatorScreen() {
}
CalculatorScreen(
uiState = CalculatorUIState(
uiState = CalculatorUIState.Ready(
input = TextFieldValue("1.2345"),
output = CalculationResult.Default("1234"),
history = historyItems

View File

@ -24,15 +24,19 @@ import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
import com.sadellie.unitto.data.model.HistoryItem
data class CalculatorUIState(
val input: TextFieldValue = TextFieldValue(),
val output: CalculationResult = CalculationResult.Default(),
val radianMode: Boolean = true,
val history: List<HistoryItem> = emptyList(),
val allowVibration: Boolean = false,
val formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces,
val middleZero: Boolean = false,
)
internal sealed class CalculatorUIState {
data object Loading : CalculatorUIState()
data class Ready(
val input: TextFieldValue = TextFieldValue(),
val output: CalculationResult = CalculationResult.Default(),
val radianMode: Boolean = true,
val history: List<HistoryItem> = emptyList(),
val allowVibration: Boolean = false,
val formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces,
val middleZero: Boolean = false,
) : CalculatorUIState()
}
sealed class CalculationResult(@StringRes val label: Int? = null) {
data class Default(val text: String = "") : CalculationResult()

View File

@ -69,7 +69,7 @@ internal class CalculatorViewModel @Inject constructor(
val uiState = combine(
_input, _output, _history, _userPrefs
) { input, output, history, userPrefs ->
return@combine CalculatorUIState(
return@combine CalculatorUIState.Ready(
input = input,
output = output,
radianMode = userPrefs.radianMode,
@ -79,7 +79,7 @@ internal class CalculatorViewModel @Inject constructor(
middleZero = userPrefs.middleZero,
)
}.stateIn(
viewModelScope, SharingStarted.WhileSubscribed(5000L), CalculatorUIState()
viewModelScope, SharingStarted.WhileSubscribed(5000L), CalculatorUIState.Loading
)
fun addTokens(tokens: String) = _input.update { it.addTokens(tokens) }

View File

@ -24,6 +24,7 @@ import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -34,6 +35,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material3.Icon
@ -48,9 +50,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.ColumnWithConstraints
import com.sadellie.unitto.core.ui.common.KeyboardButtonAdditional
@ -140,6 +144,118 @@ internal fun CalculatorKeyboard(
}
}
@Composable
internal fun CalculatorKeyboardLoading(
modifier: Modifier
) {
if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) {
PortraitKeyboardLoading(modifier)
} else {
LandscapeKeyboardLoading(modifier)
}
}
@Composable
private fun PortraitKeyboardLoading(
modifier: Modifier
) {
ColumnWithConstraints(
modifier = modifier
) { constraints ->
val additionalButtonHeight by remember {
mutableStateOf(constraints.maxHeight * 0.09f)
}
val spacerHeight by remember {
mutableStateOf(constraints.maxHeight * 0.025f)
}
val additionalRowSpacedBy by remember {
mutableStateOf(constraints.maxWidth * 0.03f)
}
val weightModifier = Modifier.weight(1f)
val additionalButtonModifier = Modifier
.weight(1f)
.height(additionalButtonHeight)
Spacer(modifier = Modifier.height(spacerHeight))
Row(
modifier = Modifier,
horizontalArrangement = Arrangement.spacedBy(additionalRowSpacedBy)
) {
// Additional buttons
Box(weightModifier) {
AdditionalButtonsPortrait(
modifier = additionalButtonModifier,
allowVibration = false,
addSymbol = {},
showAdditional = false,
radianMode = false,
toggleAngleMode = {},
toggleInvMode = {}
)
}
Box(
modifier = Modifier.size(additionalButtonHeight),
contentAlignment = Alignment.Center
) {
// Expand/Collapse
IconButton(
onClick = { },
colors = IconButtonDefaults.iconButtonColors(containerColor = MaterialTheme.colorScheme.inverseOnSurface)
) {
Icon(Icons.Default.ExpandLess, null)
}
}
}
Spacer(modifier = Modifier.height(spacerHeight))
Box(
modifier = weightModifier
.clip(RoundedCornerShape(32.dp))
.background(MaterialTheme.colorScheme.inverseOnSurface)
.fillMaxSize()
)
Spacer(modifier = Modifier.height(spacerHeight))
}
}
@Composable
private fun LandscapeKeyboardLoading(
modifier: Modifier
) {
RowWithConstraints(modifier) { constraints ->
val buttonModifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(constraints.maxWidth * 0.005f, constraints.maxHeight * 0.02f)
AdditionalButtonsLandscape(
modifier = Modifier.weight(1f),
buttonModifier = buttonModifier,
allowVibration = false,
radianMode = false,
addSymbol = {},
toggleAngleMode = {},
toggleInvMode = {}
)
Box(
modifier = Modifier
.clip(RoundedCornerShape(32.dp))
.background(MaterialTheme.colorScheme.inverseOnSurface)
.weight(5f)
.fillMaxSize()
)
}
}
@Composable
private fun PortraitKeyboard(
modifier: Modifier,

View File

@ -28,12 +28,15 @@ import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SwapHoriz
import androidx.compose.material3.Icon
@ -48,6 +51,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
@ -56,6 +60,7 @@ import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.OutputFormat
@ -72,7 +77,6 @@ import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField
import com.sadellie.unitto.data.common.format
import com.sadellie.unitto.data.model.unit.AbstractUnit
import com.sadellie.unitto.feature.converter.components.DefaultKeyboard
import com.sadellie.unitto.feature.converter.components.LoadingKeyboard
import com.sadellie.unitto.feature.converter.components.NumberBaseKeyboard
import com.sadellie.unitto.feature.converter.components.UnitSelectionButton
@ -191,7 +195,12 @@ private fun ConverterLoading(modifier: Modifier) {
}
},
content2 = {
LoadingKeyboard(modifier = it)
Box(
modifier = it
.clip(RoundedCornerShape(32.dp))
.background(MaterialTheme.colorScheme.inverseOnSurface)
.fillMaxSize()
)
}
)
}

View File

@ -18,19 +18,13 @@
package com.sadellie.unitto.feature.converter.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.Token
import com.sadellie.unitto.core.ui.common.ColumnWithConstraints
import com.sadellie.unitto.core.ui.common.KeyboardButtonFilled
@ -169,15 +163,3 @@ internal fun NumberBaseKeyboard(
}
}
}
@Composable
internal fun LoadingKeyboard(
modifier: Modifier,
) {
Box(
modifier = modifier
.clip(RoundedCornerShape(32.dp))
.background(MaterialTheme.colorScheme.inverseOnSurface)
.fillMaxSize()
)
}