Navigation drawer instead of tools screen

This commit is contained in:
Sad Ellie 2023-02-25 00:25:27 +04:00
parent afbd918265
commit 827a42bf49
18 changed files with 198 additions and 269 deletions

View File

@ -105,6 +105,7 @@ dependencies {
coreLibraryDesugaring(libs.android.desugarJdkLibs)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.com.github.sadellie.themmo)
implementation(libs.com.google.accompanist.systemuicontroller)
@ -113,10 +114,10 @@ dependencies {
implementation(project(mapOf("path" to ":feature:calculator")))
implementation(project(mapOf("path" to ":feature:settings")))
implementation(project(mapOf("path" to ":feature:unitslist")))
implementation(project(mapOf("path" to ":feature:tools")))
implementation(project(mapOf("path" to ":feature:epoch")))
implementation(project(mapOf("path" to ":data:units")))
implementation(project(mapOf("path" to ":data:model")))
implementation(project(mapOf("path" to ":data:userprefs")))
implementation(project(mapOf("path" to ":core:ui")))
implementation(project(mapOf("path" to ":core:base")))
}

View File

@ -19,13 +19,28 @@
package com.sadellie.unitto
import androidx.compose.animation.core.tween
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Calculate
import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.core.ui.common.UnittoDrawerSheet
import com.sadellie.unitto.feature.converter.ConverterViewModel
import com.sadellie.unitto.feature.unitslist.SecondViewModel
import com.sadellie.unitto.core.ui.theme.AppTypography
@ -34,6 +49,7 @@ import com.sadellie.unitto.core.ui.theme.LightThemeColors
import com.sadellie.unitto.feature.settings.SettingsViewModel
import io.github.sadellie.themmo.Themmo
import io.github.sadellie.themmo.rememberThemmoController
import kotlinx.coroutines.launch
@Composable
internal fun UnittoApp() {
@ -53,6 +69,18 @@ internal fun UnittoApp() {
val navController = rememberNavController()
val sysUiController = rememberSystemUiController()
// Navigation drawer stuff
val drawerState = rememberDrawerState(DrawerValue.Closed)
val drawerScope = rememberCoroutineScope()
val mainTabs = listOf(
TopLevelDestinations.Calculator to Icons.Default.Calculate,
TopLevelDestinations.Converter to Icons.Default.SwapHoriz
)
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute by remember(navBackStackEntry?.destination) {
derivedStateOf { navBackStackEntry?.destination?.route }
}
Themmo(
themmoController = themmoController,
typography = AppTypography,
@ -60,14 +88,36 @@ internal fun UnittoApp() {
) {
val backgroundColor = MaterialTheme.colorScheme.background
ModalNavigationDrawer(
drawerState = drawerState,
gesturesEnabled = true,
drawerContent = {
UnittoDrawerSheet(
modifier = Modifier,
mainTabs = mainTabs,
currentDestination = currentRoute
) {
drawerScope.launch { drawerState.close() }
navController.navigate(it) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
}
) {
UnittoNavigation(
navController = navController,
converterViewModel = converterViewModel,
secondViewModel = secondViewModel,
settingsViewModel = settingsViewModel,
themmoController = it,
startDestination = userPrefs.value.startingScreen
startDestination = userPrefs.value.startingScreen,
openDrawer = { drawerScope.launch { drawerState.open() } }
)
}
SideEffect { sysUiController.setSystemBarsColor(backgroundColor) }
}

View File

@ -19,23 +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.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
import com.sadellie.unitto.feature.settings.navigation.navigateToSettings
import com.sadellie.unitto.feature.settings.navigation.navigateToUnitGroups
import com.sadellie.unitto.feature.settings.navigation.settingGraph
import com.sadellie.unitto.feature.tools.navigation.navigateToTools
import com.sadellie.unitto.feature.tools.navigation.toolsScreen
import com.sadellie.unitto.feature.unitslist.SecondViewModel
import com.sadellie.unitto.feature.unitslist.navigation.leftScreen
import com.sadellie.unitto.feature.unitslist.navigation.navigateToLeftSide
@ -50,7 +43,8 @@ internal fun UnittoNavigation(
secondViewModel: SecondViewModel,
settingsViewModel: SettingsViewModel,
themmoController: ThemmoController,
startDestination: String
startDestination: String,
openDrawer: () -> Unit
) {
NavHost(
navController = navController,
@ -60,7 +54,7 @@ internal fun UnittoNavigation(
navigateToLeftScreen = navController::navigateToLeftSide,
navigateToRightScreen = navController::navigateToRightSide,
navigateToSettings = navController::navigateToSettings,
navigateToMenu = navController::navigateToTools,
navigateToMenu = openDrawer,
viewModel = converterViewModel
)
@ -84,21 +78,11 @@ internal fun UnittoNavigation(
navController = navController
)
toolsScreen(
navigateUpAction = navController::navigateUp,
navigateToConverter = { navController.navigateToConverter(navController.clearStack) },
navigateToCalculator = { navController.navigateToCalculator(navController.clearStack) },
navigateToEpoch = { navController.navigateToEpoch(navController.clearStack) }
)
calculatorScreen(
navigateToMenu = navController::navigateToTools,
navigateToMenu = openDrawer,
navigateToSettings = navController::navigateToSettings
)
epochScreen(navigateToMenu = navController::navigateToTools)
epochScreen(navigateToMenu = openDrawer)
}
}
private val NavController.clearStack: NavOptions
get() = NavOptions.Builder().setPopUpTo(this.graph.id, false).build()

View File

@ -18,15 +18,31 @@
package com.sadellie.unitto.core.base
object TopLevelDestinations {
const val CONVERTER = "converter_route"
const val CALCULATOR = "calculator_route"
const val EPOCH = "epoch_route"
}
import androidx.annotation.StringRes
val TOP_LEVEL_DESTINATIONS: Map<String, Int> by lazy {
mapOf(
TopLevelDestinations.CONVERTER to R.string.unit_converter,
TopLevelDestinations.CALCULATOR to R.string.calculator,
sealed class TopLevelDestinations(
val route: String,
@StringRes val name: Int
) {
object Converter : TopLevelDestinations(
route = "converter_route",
name = R.string.unit_converter
)
object Calculator : TopLevelDestinations(
route = "calculator_route",
name = R.string.calculator
)
object Epoch : TopLevelDestinations(
route = "epoch_route",
name = R.string.epoch_converter
)
}
val TOP_LEVEL_DESTINATIONS: Map<TopLevelDestinations, Int> by lazy {
mapOf(
TopLevelDestinations.Converter to R.string.unit_converter,
TopLevelDestinations.Calculator to R.string.calculator,
)
}

View File

@ -0,0 +1,45 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.core.ui.common
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import com.sadellie.unitto.core.base.TopLevelDestinations
@Composable
internal fun UnittoDrawerItem(
modifier: Modifier = Modifier,
destination: TopLevelDestinations,
icon: ImageVector,
selected: Boolean,
onClick: (String) -> Unit
) {
NavigationDrawerItem(
modifier = modifier,
label = { Text(stringResource(destination.name)) },
icon = { Icon(icon, stringResource(destination.name)) },
selected = selected,
onClick = { onClick(destination.route) }
)
}

View File

@ -0,0 +1,57 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.core.ui.common
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.TopLevelDestinations
@Composable
fun UnittoDrawerSheet(
modifier: Modifier,
mainTabs: List<Pair<TopLevelDestinations, ImageVector>>,
currentDestination: String?,
onItemClick: (String) -> Unit
) {
ModalDrawerSheet(
modifier = modifier
) {
Spacer(Modifier.height(24.dp))
Text("Unitto", Modifier.padding(NavigationDrawerItemDefaults.ItemPadding))
Spacer(Modifier.height(12.dp))
mainTabs.forEach { (destination, icon) ->
UnittoDrawerItem(
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
destination = destination,
icon = icon,
selected = destination.route == currentDestination,
onClick = onItemClick
)
}
}
}

View File

@ -67,7 +67,7 @@ data class UserPreferences(
val shownUnitGroups: List<UnitGroup> = ALL_UNIT_GROUPS,
val enableVibrations: Boolean = true,
val enableToolsExperiment: Boolean = false,
val startingScreen: String = TopLevelDestinations.CONVERTER
val startingScreen: String = TopLevelDestinations.Converter.route
)
/**
@ -133,7 +133,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
} ?: ALL_UNIT_GROUPS
val enableVibrations: Boolean = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true
val enableToolsExperiment: Boolean = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false
val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.CONVERTER
val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Converter.route
UserPreferences(
themingMode = themingMode,

View File

@ -18,19 +18,13 @@
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.core.base.TopLevelDestinations
import com.sadellie.unitto.feature.calculator.CalculatorRoute
private const val calculatorRoute = TopLevelDestinations.CALCULATOR
fun NavController.navigateToCalculator(navOptions: NavOptions) {
navigate(calculatorRoute, navOptions)
}
private val calculatorRoute: String by lazy { TopLevelDestinations.Calculator.route }
fun NavGraphBuilder.calculatorScreen(
navigateToMenu: () -> Unit,

View File

@ -18,19 +18,13 @@
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.core.base.TopLevelDestinations
import com.sadellie.unitto.feature.converter.ConverterRoute
import com.sadellie.unitto.feature.converter.ConverterViewModel
const val converterRoute = TopLevelDestinations.CONVERTER
fun NavController.navigateToConverter(navOptions: NavOptions) {
navigate(converterRoute, navOptions)
}
private val converterRoute: String by lazy { TopLevelDestinations.Converter.route }
fun NavGraphBuilder.converterScreen(
navigateToLeftScreen: (String) -> Unit,

View File

@ -18,19 +18,13 @@
package com.sadellie.unitto.feature.epoch.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.core.base.TopLevelDestinations
import com.sadellie.unitto.feature.epoch.EpochRoute
private const val epochRoute = TopLevelDestinations.EPOCH
fun NavController.navigateToEpoch(navOptions: NavOptions) {
navigate(epochRoute, navOptions)
}
private val epochRoute: String by lazy { TopLevelDestinations.Epoch.route }
fun NavGraphBuilder.epochScreen(
navigateToMenu: () -> Unit

View File

@ -254,7 +254,7 @@ internal fun SettingsScreen(
AlertDialogWithList(
title = stringResource(R.string.starting_screen_setting),
selectedItemIndex = userPrefs.value.startingScreen,
listItems = TOP_LEVEL_DESTINATIONS,
listItems = TOP_LEVEL_DESTINATIONS.mapKeys { it.key.route },
selectAction = viewModel::updateStartingScreen,
dismissAction = { resetDialog() }
)

View File

@ -1 +0,0 @@
/build

View File

@ -1,28 +0,0 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
plugins {
id("unitto.library")
id("unitto.library.compose")
id("unitto.library.feature")
id("unitto.android.hilt")
}
android {
namespace = "com.sadellie.unitto.feature.tools"
}

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@ -1,126 +0,0 @@
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
import androidx.compose.material3.MaterialTheme
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
) {
UnittoLargeTopAppBar(
title = stringResource(R.string.tools_screen),
navigateUpAction = navigateUpAction
) { padding ->
LazyColumn(contentPadding = padding) {
item {
Card(
Modifier
.clickable(onClick = {})
.padding(16.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
) {
Text(
text = stringResource(R.string.tools_notice_title),
style = MaterialTheme.typography.titleLarge
)
Text(
text = stringResource(R.string.tools_notice_description),
style = MaterialTheme.typography.bodyMedium
)
Text(
modifier = Modifier.align(Alignment.End),
text = stringResource(R.string.click_to_read_more),
style = MaterialTheme.typography.labelLarge
)
}
}
}
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 = {
Icon(
Icons.Default.Calculate,
stringResource(R.string.calculator)
)
},
headlineText = { Text(stringResource(R.string.calculator)) },
supportingText = { Text(stringResource(R.string.calculator_support)) },
modifier = Modifier.clickable { navigateToCalculator() }
)
}
item {
ListItem(
leadingContent = {
Icon(
Icons.Default.Schedule,
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.alpha(0.5f)
)
}
}
}
}
@Preview
@Composable
internal fun PreviewToolsScreen() {
ToolsScreen(
navigateUpAction = {},
navigateToConverter = {},
navigateToEpoch = {},
navigateToCalculator = {}
)
}

View File

@ -1,46 +0,0 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 Elshan Agaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sadellie.unitto.feature.tools.navigation
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import com.sadellie.unitto.feature.tools.ToolsScreen
private const val toolsRoute = "tools_route"
fun NavController.navigateToTools() {
navigate(toolsRoute)
}
fun NavGraphBuilder.toolsScreen(
navigateUpAction: () -> Unit,
navigateToConverter: () -> Unit,
navigateToCalculator: () -> Unit,
navigateToEpoch: () -> Unit
) {
composable(toolsRoute) {
ToolsScreen(
navigateUpAction = navigateUpAction,
navigateToConverter = navigateToConverter,
navigateToCalculator = navigateToCalculator,
navigateToEpoch = navigateToEpoch
)
}
}

View File

@ -24,7 +24,6 @@ include(":feature:converter")
include(":feature:unitslist")
include(":feature:calculator")
include(":feature:settings")
include(":feature:tools")
include(":feature:epoch")
include(":data:userprefs")
include(":data:unitgroups")