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.core.ui.common.textfield.UnformattedTextField
import com.sadellie.unitto.data.model.HistoryItem import com.sadellie.unitto.data.model.HistoryItem
import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboard 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 com.sadellie.unitto.feature.calculator.components.HistoryList
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -80,10 +81,15 @@ internal fun CalculatorRoute(
navigateToSettings: () -> Unit, navigateToSettings: () -> Unit,
viewModel: CalculatorViewModel = hiltViewModel() viewModel: CalculatorViewModel = hiltViewModel()
) { ) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle() val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
CalculatorScreen( when (uiState) {
uiState = uiState.value, is CalculatorUIState.Loading -> Loading(
navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings,
)
is CalculatorUIState.Ready -> CalculatorScreen(
uiState = uiState,
navigateToMenu = navigateToMenu, navigateToMenu = navigateToMenu,
navigateToSettings = navigateToSettings, navigateToSettings = navigateToSettings,
addSymbol = viewModel::addTokens, addSymbol = viewModel::addTokens,
@ -94,11 +100,12 @@ internal fun CalculatorRoute(
evaluate = viewModel::evaluate, evaluate = viewModel::evaluate,
clearHistory = viewModel::clearHistory clearHistory = viewModel::clearHistory
) )
}
} }
@Composable @Composable
private fun CalculatorScreen( private fun CalculatorScreen(
uiState: CalculatorUIState, uiState: CalculatorUIState.Ready,
navigateToMenu: () -> Unit, navigateToMenu: () -> Unit,
navigateToSettings: () -> Unit, navigateToSettings: () -> Unit,
addSymbol: (String) -> 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 = 1008, device = "spec:parent=pixel_5,orientation=portrait")
@Preview(widthDp = 432, heightDp = 864, 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") @Preview(widthDp = 597, heightDp = 1393, device = "spec:parent=pixel_5,orientation=portrait")
@ -346,7 +427,7 @@ private fun PreviewCalculatorScreen() {
} }
CalculatorScreen( CalculatorScreen(
uiState = CalculatorUIState( uiState = CalculatorUIState.Ready(
input = TextFieldValue("1.2345"), input = TextFieldValue("1.2345"),
output = CalculationResult.Default("1234"), output = CalculationResult.Default("1234"),
history = historyItems history = historyItems

View File

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

View File

@ -69,7 +69,7 @@ internal class CalculatorViewModel @Inject constructor(
val uiState = combine( val uiState = combine(
_input, _output, _history, _userPrefs _input, _output, _history, _userPrefs
) { input, output, history, userPrefs -> ) { input, output, history, userPrefs ->
return@combine CalculatorUIState( return@combine CalculatorUIState.Ready(
input = input, input = input,
output = output, output = output,
radianMode = userPrefs.radianMode, radianMode = userPrefs.radianMode,
@ -79,7 +79,7 @@ internal class CalculatorViewModel @Inject constructor(
middleZero = userPrefs.middleZero, middleZero = userPrefs.middleZero,
) )
}.stateIn( }.stateIn(
viewModelScope, SharingStarted.WhileSubscribed(5000L), CalculatorUIState() viewModelScope, SharingStarted.WhileSubscribed(5000L), CalculatorUIState.Loading
) )
fun addTokens(tokens: String) = _input.update { it.addTokens(tokens) } 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.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column 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.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -48,9 +50,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment 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.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.tooling.preview.Preview 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.base.Token
import com.sadellie.unitto.core.ui.common.ColumnWithConstraints import com.sadellie.unitto.core.ui.common.ColumnWithConstraints
import com.sadellie.unitto.core.ui.common.KeyboardButtonAdditional 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 @Composable
private fun PortraitKeyboard( private fun PortraitKeyboard(
modifier: Modifier, modifier: Modifier,

View File

@ -28,12 +28,15 @@ import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith 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.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SwapHoriz import androidx.compose.material.icons.outlined.SwapHoriz
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -48,6 +51,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment 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.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext 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.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
@ -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.common.format
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
import com.sadellie.unitto.feature.converter.components.LoadingKeyboard
import com.sadellie.unitto.feature.converter.components.NumberBaseKeyboard import com.sadellie.unitto.feature.converter.components.NumberBaseKeyboard
import com.sadellie.unitto.feature.converter.components.UnitSelectionButton import com.sadellie.unitto.feature.converter.components.UnitSelectionButton
@ -191,7 +195,12 @@ private fun ConverterLoading(modifier: Modifier) {
} }
}, },
content2 = { 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 package com.sadellie.unitto.feature.converter.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding 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.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier 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.base.Token
import com.sadellie.unitto.core.ui.common.ColumnWithConstraints import com.sadellie.unitto.core.ui.common.ColumnWithConstraints
import com.sadellie.unitto.core.ui.common.KeyboardButtonFilled 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()
)
}