Improved navigation

- Clear back stack when navigating between tools
- Added unit converter to tools screen
- Disabled epoch converter until ready
This commit is contained in:
Sad Ellie 2023-02-22 02:00:02 +04:00
parent 36befca5da
commit 951523a235
13 changed files with 81 additions and 102 deletions

View File

@ -19,13 +19,16 @@
package com.sadellie.unitto
import androidx.compose.runtime.Composable
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.NavOptions
import androidx.navigation.compose.NavHost
import com.sadellie.unitto.feature.calculator.navigation.calculatorScreen
import com.sadellie.unitto.feature.calculator.navigation.navigateToCalculator
import com.sadellie.unitto.feature.converter.ConverterViewModel
import com.sadellie.unitto.feature.converter.navigation.converterRoute
import com.sadellie.unitto.feature.converter.navigation.converterScreen
import com.sadellie.unitto.feature.converter.navigation.navigateToConverter
import com.sadellie.unitto.feature.epoch.navigation.epochScreen
import com.sadellie.unitto.feature.epoch.navigation.navigateToEpoch
import com.sadellie.unitto.feature.settings.SettingsViewModel
@ -57,7 +60,7 @@ fun UnittoNavigation(
navigateToLeftScreen = navController::navigateToLeftSide,
navigateToRightScreen = navController::navigateToRightSide,
navigateToSettings = navController::navigateToSettings,
navigateToTools = navController::navigateToTools,
navigateToMenu = navController::navigateToTools,
viewModel = converterViewModel
)
@ -83,14 +86,16 @@ fun UnittoNavigation(
toolsScreen(
navigateUpAction = navController::navigateUp,
navigateToCalculator = navController::navigateToCalculator,
navigateToEpoch = navController::navigateToEpoch
navigateToConverter = { navController.navigateToConverter(navController.clearStack) },
navigateToCalculator = { navController.navigateToCalculator(navController.clearStack) },
navigateToEpoch = { navController.navigateToEpoch(navController.clearStack) }
)
calculatorScreen(navigateUpAction = navController::navigateUp)
calculatorScreen(navigateToMenu = navController::navigateToTools)
epochScreen(
navigateUpAction = navController::navigateUp
)
epochScreen(navigateToMenu = navController::navigateToTools)
}
}
val NavController.clearStack: NavOptions
get() = NavOptions.Builder().setPopUpTo(this.graph.id, false).build()

View File

@ -1016,9 +1016,12 @@
<string name="additional_settings_group">Additional</string>
<!--Tools-->
<string name="unit_converter">Unit converter</string>
<string name="unit_converter_support">Convert from one unit to another</string>
<string name="epoch_converter">Epoch converter</string>
<string name="epoch_converter_support">Convert Unix timestamp</string>
<string name="tools_notice_title">Experimental features!</string>
<string name="soon_label">Soon</string>
<string name="tools_notice_description">This screen is part of an experiment. It may change in the future.</string>
<string name="click_to_read_more">Click to read more!</string>

View File

@ -75,14 +75,14 @@ import kotlin.math.roundToInt
@Composable
internal fun CalculatorRoute(
navigateUpAction: () -> Unit,
navigateToMenu: () -> Unit,
viewModel: CalculatorViewModel = hiltViewModel()
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
CalculatorScreen(
uiState = uiState.value,
navigateUpAction = navigateUpAction,
navigateToMenu = navigateToMenu,
addSymbol = viewModel::addSymbol,
clearSymbols = viewModel::clearSymbols,
deleteSymbol = viewModel::deleteSymbol,
@ -96,7 +96,7 @@ internal fun CalculatorRoute(
@Composable
private fun CalculatorScreen(
uiState: CalculatorUIState,
navigateUpAction: () -> Unit,
navigateToMenu: () -> Unit,
addSymbol: (String) -> Unit,
clearSymbols: () -> Unit,
deleteSymbol: () -> Unit,
@ -115,7 +115,7 @@ private fun CalculatorScreen(
UnittoScreenWithTopBar(
title = { Text(stringResource(R.string.calculator)) },
navigationIcon = { MenuButton { navigateUpAction() } },
navigationIcon = { MenuButton { navigateToMenu() } },
colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceVariant),
actions = {
AnimatedVisibility(
@ -287,7 +287,7 @@ private fun PreviewCalculatorScreen() {
output = "12345",
history = historyItems
),
navigateUpAction = {},
navigateToMenu = {},
addSymbol = {},
clearSymbols = {},
deleteSymbol = {},

View File

@ -20,24 +20,25 @@ package com.sadellie.unitto.feature.calculator.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import com.sadellie.unitto.feature.calculator.CalculatorRoute
private const val calculatorRoute = "calculator_route"
fun NavController.navigateToCalculator() {
navigate(calculatorRoute)
fun NavController.navigateToCalculator(navOptions: NavOptions) {
navigate(calculatorRoute, navOptions)
}
fun NavGraphBuilder.calculatorScreen(
navigateUpAction: () -> Unit
navigateToMenu: () -> Unit
) {
composable(
route = calculatorRoute,
deepLinks = listOf(
navDeepLink { uriPattern = "app://com.sadellie.unitto/$calculatorRoute" }
)) {
CalculatorRoute(navigateUpAction = navigateUpAction)
CalculatorRoute(navigateToMenu = navigateToMenu)
}
}

View File

@ -18,39 +18,30 @@
package com.sadellie.unitto.feature.converter
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.Science
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
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.ui.R
import com.sadellie.unitto.core.ui.common.AnimatedTopBarText
import com.sadellie.unitto.feature.converter.components.Keyboard
import com.sadellie.unitto.core.ui.common.MenuButton
import com.sadellie.unitto.core.ui.common.PortraitLandscape
import com.sadellie.unitto.core.ui.common.UnittoScreenWithTopBar
import com.sadellie.unitto.feature.converter.components.Keyboard
import com.sadellie.unitto.feature.converter.components.TopScreenPart
import kotlinx.coroutines.delay
@ -59,7 +50,7 @@ internal fun ConverterRoute(
viewModel: ConverterViewModel = hiltViewModel(),
navigateToLeftScreen: (String) -> Unit,
navigateToRightScreen: (unitFrom: String, unitTo: String, input: String) -> Unit,
navigateToTools: () -> Unit,
navigateToMenu: () -> Unit,
navigateToSettings: () -> Unit
) {
val uiState = viewModel.uiStateFlow.collectAsStateWithLifecycle()
@ -69,7 +60,7 @@ internal fun ConverterRoute(
navigateToLeftScreen = navigateToLeftScreen,
navigateToRightScreen = navigateToRightScreen,
navigateToSettings = navigateToSettings,
navigateToTools = navigateToTools,
navigateToMenu = navigateToMenu,
swapMeasurements = viewModel::swapUnits,
processInput = viewModel::processInput,
deleteDigit = viewModel::deleteDigit,
@ -84,7 +75,7 @@ private fun ConverterScreen(
navigateToLeftScreen: (String) -> Unit,
navigateToRightScreen: (unitFrom: String, unitTo: String, input: String) -> Unit,
navigateToSettings: () -> Unit,
navigateToTools: () -> Unit,
navigateToMenu: () -> Unit,
swapMeasurements: () -> Unit,
processInput: (String) -> Unit,
deleteDigit: () -> Unit,
@ -95,27 +86,7 @@ private fun ConverterScreen(
UnittoScreenWithTopBar(
title = { AnimatedTopBarText(launched) },
navigationIcon = {
BadgedBox(
modifier = Modifier
.padding(horizontal = 16.dp)
.clickable(
onClick = navigateToTools,
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(false),
role = Role.Button
),
badge = {
Badge { Text("1") }
},
content = {
Icon(
Icons.Outlined.Science,
contentDescription = stringResource(R.string.tools_screen)
)
}
)
},
navigationIcon = { MenuButton { navigateToMenu() } },
actions = {
IconButton(onClick = navigateToSettings) {
Icon(
@ -180,7 +151,7 @@ private fun PreviewConverterScreen() {
navigateToLeftScreen = {},
navigateToRightScreen = {_, _, _ -> },
navigateToSettings = {},
navigateToTools = {},
navigateToMenu = {},
swapMeasurements = {},
processInput = {},
deleteDigit = {},

View File

@ -34,7 +34,6 @@ import com.sadellie.unitto.data.model.AbstractUnit
* @property unitTo Unit on the right.
* @property mode
* @property formatTime If true will format output when converting time.
* @property showTools If true will show tools button in TopBar.
* @property allowVibration When true will vibrate on button clicks.
*/
data class ConverterUIState(
@ -47,7 +46,6 @@ data class ConverterUIState(
val unitTo: AbstractUnit? = null,
val mode: ConverterMode = ConverterMode.DEFAULT,
val formatTime: Boolean = true,
val showTools: Boolean = false,
val allowVibration: Boolean = false
)

View File

@ -56,6 +56,7 @@ import com.sadellie.unitto.data.units.MyUnitIDS
import com.sadellie.unitto.data.units.combine
import com.sadellie.unitto.data.units.remote.CurrencyApi
import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse
import com.sadellie.unitto.data.userprefs.UserPreferences
import com.sadellie.unitto.data.userprefs.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -86,7 +87,7 @@ class ConverterViewModel @Inject constructor(
private val _userPrefs = userPrefsRepository.userPreferencesFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5000),
com.sadellie.unitto.data.userprefs.UserPreferences()
UserPreferences()
)
/**
@ -159,7 +160,6 @@ class ConverterViewModel @Inject constructor(
*/
mode = if (_unitFrom.value is NumberBaseUnit) ConverterMode.BASE else ConverterMode.DEFAULT,
formatTime = formatTime,
showTools = prefs.enableToolsExperiment,
allowVibration = prefs.enableVibrations
)
}

View File

@ -18,18 +18,24 @@
package com.sadellie.unitto.feature.converter.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.sadellie.unitto.feature.converter.ConverterRoute
import com.sadellie.unitto.feature.converter.ConverterViewModel
const val converterRoute = "converter_route"
fun NavController.navigateToConverter(navOptions: NavOptions) {
navigate(converterRoute, navOptions)
}
fun NavGraphBuilder.converterScreen(
navigateToLeftScreen: (String) -> Unit,
navigateToRightScreen: (unitFrom: String, unitTo: String, input: String) -> Unit,
navigateToSettings: () -> Unit,
navigateToTools: () -> Unit,
navigateToMenu: () -> Unit,
viewModel: ConverterViewModel
) {
composable(converterRoute) {
@ -38,7 +44,7 @@ fun NavGraphBuilder.converterScreen(
navigateToLeftScreen = navigateToLeftScreen,
navigateToRightScreen = navigateToRightScreen,
navigateToSettings = navigateToSettings,
navigateToTools = navigateToTools
navigateToMenu = navigateToMenu
)
}
}

View File

@ -35,12 +35,12 @@ import com.sadellie.unitto.feature.epoch.component.TopPart
@Composable
internal fun EpochRoute(
navigateUpAction: () -> Unit,
navigateToMenu: () -> Unit,
viewModel: EpochViewModel = hiltViewModel()
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
EpochScreen(
navigateUpAction = navigateUpAction,
navigateToMenu = navigateToMenu,
uiState = uiState.value,
addSymbol = viewModel::addSymbol,
deleteSymbol = viewModel::deleteSymbol,
@ -52,7 +52,7 @@ internal fun EpochRoute(
@Composable
private fun EpochScreen(
navigateUpAction: () -> Unit,
navigateToMenu: () -> Unit,
uiState: EpochUIState,
addSymbol: (String) -> Unit,
deleteSymbol: () -> Unit,
@ -62,7 +62,7 @@ private fun EpochScreen(
) {
UnittoScreenWithTopBar(
title = { Text(stringResource(R.string.epoch_converter)) },
navigationIcon = { MenuButton { navigateUpAction() } }
navigationIcon = { MenuButton { navigateToMenu() } }
) { padding ->
PortraitLandscape(
modifier = Modifier.padding(padding),

View File

@ -18,42 +18,21 @@
package com.sadellie.unitto.feature.epoch.navigation
import android.content.Intent
import android.net.Uri
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import com.sadellie.unitto.feature.epoch.EpochRoute
import com.sadellie.unitto.feature.epoch.R
private const val epochRoute = "epoch_route"
fun NavController.navigateToEpoch() {
val shortcut = ShortcutInfoCompat
.Builder(context, epochRoute)
.setShortLabel(context.getString(R.string.epoch_converter))
.setLongLabel(context.getString(R.string.epoch_converter))
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_epoch))
.setIntent(
Intent(
Intent.ACTION_VIEW,
Uri.parse("app://com.sadellie.unitto/$epochRoute"),
context,
context.javaClass
)
)
.build()
ShortcutManagerCompat.pushDynamicShortcut(context, shortcut)
navigate(epochRoute)
fun NavController.navigateToEpoch(navOptions: NavOptions) {
navigate(epochRoute, navOptions)
}
fun NavGraphBuilder.epochScreen(
navigateUpAction: () -> Unit
navigateToMenu: () -> Unit
) {
composable(
route = epochRoute,
@ -61,8 +40,6 @@ fun NavGraphBuilder.epochScreen(
navDeepLink { uriPattern = "app://com.sadellie.unitto/$epochRoute" }
)
) {
EpochRoute(
navigateUpAction = navigateUpAction
)
EpochRoute(navigateToMenu = navigateToMenu)
}
}

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#186C31"
android:pathData="m31.35,33.65 l2.25,-2.25 -7.95,-8L25.65,13.35h-3L22.65,24.6ZM24,44q-4.1,0 -7.75,-1.575 -3.65,-1.575 -6.375,-4.3 -2.725,-2.725 -4.3,-6.375Q4,28.1 4,24t1.575,-7.75q1.575,-3.65 4.3,-6.375 2.725,-2.725 6.375,-4.3Q19.9,4 24,4t7.75,1.575q3.65,1.575 6.375,4.3 2.725,2.725 4.3,6.375Q44,19.9 44,24t-1.575,7.75q-1.575,3.65 -4.3,6.375 -2.725,2.725 -6.375,4.3Q28.1,44 24,44ZM24,24ZM24,41q7,0 12,-5t5,-12q0,-7 -5,-12T24,7q-7,0 -12,5T7,24q0,7 5,12t12,5Z"/>
</vector>

View File

@ -1,13 +1,17 @@
package com.sadellie.unitto.feature.tools
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Calculate
import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.material3.Badge
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
@ -16,14 +20,17 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.ui.common.UnittoLargeTopAppBar
@Composable
@Suppress("UNUSED_PARAMETER")
internal fun ToolsScreen(
navigateUpAction: () -> Unit,
navigateToConverter: () -> Unit,
navigateToCalculator: () -> Unit,
navigateToEpoch: () -> Unit
) {
@ -59,6 +66,19 @@ internal fun ToolsScreen(
}
}
}
item {
ListItem(
leadingContent = {
Icon(
Icons.Default.SwapHoriz,
stringResource(R.string.unit_converter)
)
},
headlineText = { Text(stringResource(R.string.unit_converter)) },
supportingText = { Text(stringResource(R.string.unit_converter_support)) },
modifier = Modifier.clickable { navigateToConverter() }
)
}
item {
ListItem(
leadingContent = {
@ -80,9 +100,14 @@ internal fun ToolsScreen(
stringResource(R.string.epoch_converter)
)
},
headlineText = { Text(stringResource(R.string.epoch_converter)) },
headlineText = {
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
Text(stringResource(R.string.epoch_converter))
Badge { Text(stringResource(R.string.soon_label)) }
}
},
supportingText = { Text(stringResource(R.string.epoch_converter_support)) },
modifier = Modifier.clickable { navigateToEpoch() }
modifier = Modifier.alpha(0.5f)
)
}
}
@ -94,6 +119,7 @@ internal fun ToolsScreen(
internal fun PreviewToolsScreen() {
ToolsScreen(
navigateUpAction = {},
navigateToConverter = {},
navigateToEpoch = {},
navigateToCalculator = {}
)

View File

@ -31,12 +31,14 @@ fun NavController.navigateToTools() {
fun NavGraphBuilder.toolsScreen(
navigateUpAction: () -> Unit,
navigateToConverter: () -> Unit,
navigateToCalculator: () -> Unit,
navigateToEpoch: () -> Unit
) {
composable(toolsRoute) {
ToolsScreen(
navigateUpAction = navigateUpAction,
navigateToConverter = navigateToConverter,
navigateToCalculator = navigateToCalculator,
navigateToEpoch = navigateToEpoch
)