Refactor TopLevelDestinations

closes #160
closes #161
This commit is contained in:
Sad Ellie 2024-01-15 18:23:15 +03:00
parent 3c6abd137a
commit 9f24b9b3cc
16 changed files with 204 additions and 266 deletions

View File

@ -35,11 +35,9 @@ import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.sadellie.unitto.core.base.Shortcut
import com.sadellie.unitto.core.base.TOP_LEVEL_START_ROUTES
import com.sadellie.unitto.core.ui.common.UnittoNavigationDrawer import com.sadellie.unitto.core.ui.common.UnittoNavigationDrawer
import com.sadellie.unitto.core.ui.common.rememberDrawerState import com.sadellie.unitto.core.ui.common.rememberDrawerState
import com.sadellie.unitto.core.ui.model.DrawerItems import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.pushDynamicShortcut import com.sadellie.unitto.core.ui.pushDynamicShortcut
import com.sadellie.unitto.core.ui.theme.DarkThemeColors import com.sadellie.unitto.core.ui.theme.DarkThemeColors
import com.sadellie.unitto.core.ui.theme.LightThemeColors import com.sadellie.unitto.core.ui.theme.LightThemeColors
@ -67,7 +65,7 @@ internal fun UnittoApp(prefs: AppPreferences?) {
val navBackStackEntry by navController.currentBackStackEntryAsState() val navBackStackEntry by navController.currentBackStackEntryAsState()
val gesturesEnabled: Boolean by remember(navBackStackEntry?.destination) { val gesturesEnabled: Boolean by remember(navBackStackEntry?.destination) {
derivedStateOf { derivedStateOf {
TOP_LEVEL_START_ROUTES.contains(navBackStackEntry?.destination?.route) DrawerItem.startRoutes.contains(navBackStackEntry?.destination?.route)
} }
} }
@ -96,7 +94,7 @@ internal fun UnittoApp(prefs: AppPreferences?) {
modifier = Modifier, modifier = Modifier,
state = drawerState, state = drawerState,
gesturesEnabled = gesturesEnabled, gesturesEnabled = gesturesEnabled,
tabs = DrawerItems.MAIN, tabs = DrawerItem.main,
currentDestination = navBackStackEntry?.destination?.route, currentDestination = navBackStackEntry?.destination?.route,
onItemClick = { destination -> onItemClick = { destination ->
drawerScope.launch { drawerState.close() } drawerScope.launch { drawerState.close() }
@ -109,16 +107,7 @@ internal fun UnittoApp(prefs: AppPreferences?) {
restoreState = true restoreState = true
} }
shortcutsScope.launch { shortcutsScope.launch { mContext.pushDynamicShortcut(destination) }
destination.shortcut?.let { shortcut: Shortcut ->
mContext.pushDynamicShortcut(
destination.graph,
shortcut.shortcutShortLabel,
shortcut.shortcutLongLabel,
shortcut.shortcutDrawable
)
}
}
}, },
content = { content = {
UnittoNavigation( UnittoNavigation(

View File

@ -18,126 +18,18 @@
package com.sadellie.unitto.core.base package com.sadellie.unitto.core.base
import android.os.Build
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
// Don't touch, users have "..._route" in their settings // Don't touch, users have "..._route" in their settings
private const val CONVERTER_GRAPH = "converter_route" object TopLevelDestinations {
private const val CONVERTER_START = "converter_start" const val CONVERTER_GRAPH = "converter_route"
const val CONVERTER_START = "converter_start"
private const val CALCULATOR_GRAPH = "calculator_route" const val CALCULATOR_GRAPH = "calculator_route"
private const val CALCULATOR_START = "calculator_start" const val CALCULATOR_START = "calculator_start"
const val DATE_CALCULATOR_GRAPH = "date_calculator_route"
private const val DATE_CALCULATOR_GRAPH = "date_calculator_route" const val DATE_CALCULATOR_START = "date_calculator_start"
private const val DATE_CALCULATOR_START = "date_calculator_start" const val TIME_ZONE_GRAPH = "time_zone_route"
const val TIME_ZONE_START = "time_zone_start"
private const val TIME_ZONE_GRAPH = "time_zone_route" const val BODY_MASS_GRAPH = "body_mass_route"
private const val TIME_ZONE_START = "time_zone_start" const val BODY_MASS_START = "body_mass_start"
const val SETTINGS_GRAPH = "settings_route"
private const val BODY_MASS_GRAPH = "body_mass_route" const val SETTINGS_START = "settings_start"
private const val BODY_MASS_START = "body_mass_start"
private const val SETTINGS_GRAPH = "settings_route"
private const val SETTINGS_START = "settings_start"
data class Shortcut(
@StringRes val shortcutShortLabel: Int,
@StringRes val shortcutLongLabel: Int,
@DrawableRes val shortcutDrawable: Int,
)
sealed class TopLevelDestinations(
val graph: String,
val start: String = graph,
@StringRes val name: Int,
val shortcut: Shortcut? = null
) {
data object Converter : TopLevelDestinations(
graph = CONVERTER_GRAPH,
start = CONVERTER_START,
name = R.string.unit_converter_title,
shortcut = Shortcut(
R.string.unit_converter_title,
R.string.unit_converter_title,
R.drawable.ic_shortcut_unit_converter
)
)
data object Calculator : TopLevelDestinations(
graph = CALCULATOR_GRAPH,
start = CALCULATOR_START,
name = R.string.calculator_title,
shortcut = Shortcut(
R.string.calculator_title,
R.string.calculator_title,
R.drawable.ic_shortcut_calculator
)
)
data object DateCalculator : TopLevelDestinations(
graph = DATE_CALCULATOR_GRAPH,
start = DATE_CALCULATOR_START,
name = R.string.date_calculator_title,
shortcut = Shortcut(
R.string.date_calculator_title,
R.string.date_calculator_title,
R.drawable.ic_shortcut_date_calculator
)
)
data object TimeZone : TopLevelDestinations(
graph = TIME_ZONE_GRAPH,
start = TIME_ZONE_START,
name = R.string.time_zone_title,
shortcut = Shortcut(
R.string.time_zone_title,
R.string.time_zone_title,
R.drawable.ic_shortcut_time_zone
)
)
data object BodyMass : TopLevelDestinations(
graph = BODY_MASS_GRAPH,
start = BODY_MASS_START,
name = R.string.body_mass_title,
shortcut = Shortcut(
R.string.body_mass_title,
R.string.body_mass_title,
R.drawable.ic_shortcut_body_mass
)
)
data object Settings : TopLevelDestinations(
graph = SETTINGS_GRAPH,
start = SETTINGS_START,
name = R.string.settings_title
)
}
// Shown in settings
val TOP_LEVEL_DESTINATIONS by lazy {
var all = listOf(
TopLevelDestinations.Calculator,
TopLevelDestinations.Converter,
TopLevelDestinations.DateCalculator,
TopLevelDestinations.TimeZone,
TopLevelDestinations.BodyMass,
)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
all = all - TopLevelDestinations.TimeZone
}
all
}
// Only routes, not graphs!
val TOP_LEVEL_START_ROUTES by lazy {
listOf(
CALCULATOR_START,
CONVERTER_START,
DATE_CALCULATOR_START,
TIME_ZONE_START,
)
} }

View File

@ -23,61 +23,63 @@ import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.annotation.DrawableRes import android.os.Build
import androidx.annotation.StringRes import androidx.annotation.RequiresApi
import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.model.Shortcut
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
// I think it makes sense to run in coroutine /**
* Tries to push a dynamic shortcut. Does nothing if [DrawerItem.shortcut] is `null`
*
* @param drawerItem [DrawerItem]
*/
suspend fun Context.pushDynamicShortcut( suspend fun Context.pushDynamicShortcut(
route: String, drawerItem: DrawerItem,
@StringRes shortLabel: Int,
@StringRes longLabel: Int,
@DrawableRes drawable: Int,
) = withContext(Dispatchers.IO) { ) = withContext(Dispatchers.IO) {
// Low chance that we WILL push the shortcut val shortcut = drawerItem.shortcut ?: return@withContext
if ((0..10).random() != 0) return@withContext // Resource intensive method!
val context = this@pushDynamicShortcut val context = this@pushDynamicShortcut
val shortcut = shortcutInfoCompat( val shortcutCompat = shortcutInfoCompat(
context = context, context = context,
route = route, route = drawerItem.graph,
shortLabel = shortLabel, shortcut = shortcut
longLabel = longLabel,
drawable = drawable
) )
kotlin.runCatching { kotlin.runCatching {
ShortcutManagerCompat.pushDynamicShortcut(context, shortcut) ShortcutManagerCompat.pushDynamicShortcut(context, shortcutCompat)
} }
} }
/**
* Tries to pin shortcut. Does nothing if [DrawerItem.shortcut] is `null`
*
* @param drawerItem [DrawerItem]
*/
@RequiresApi(Build.VERSION_CODES.N_MR1)
fun Context.addShortcut( fun Context.addShortcut(
route: String, drawerItem: DrawerItem,
@StringRes shortLabel: Int,
@StringRes longLabel: Int,
@DrawableRes drawable: Int,
) { ) {
val shortcut = drawerItem.shortcut ?: return
val context = this@addShortcut val context = this@addShortcut
val shortcut = shortcutInfoCompat( val shortcutCompat = shortcutInfoCompat(
context = context, context = context,
route = route, route = drawerItem.graph,
shortLabel = shortLabel, shortcut = shortcut
longLabel = longLabel,
drawable = drawable
) )
val shortCutIntent = ShortcutManagerCompat.createShortcutResultIntent(context, shortcut) val shortCutIntent = ShortcutManagerCompat.createShortcutResultIntent(context, shortcutCompat)
try { try {
ShortcutManagerCompat.requestPinShortcut( ShortcutManagerCompat.requestPinShortcut(
context, context,
shortcut, shortcutCompat,
PendingIntent.getBroadcast(context, 0, shortCutIntent, FLAG_IMMUTABLE).intentSender PendingIntent.getBroadcast(context, 0, shortCutIntent, FLAG_IMMUTABLE).intentSender
) )
} catch (e: Exception) { } catch (e: Exception) {
@ -88,14 +90,12 @@ fun Context.addShortcut(
private fun Context.shortcutInfoCompat( private fun Context.shortcutInfoCompat(
context: Context, context: Context,
route: String, route: String,
shortLabel: Int, shortcut: Shortcut,
longLabel: Int,
drawable: Int,
): ShortcutInfoCompat { ): ShortcutInfoCompat {
return ShortcutInfoCompat.Builder(context, route) return ShortcutInfoCompat.Builder(context, route)
.setShortLabel(getString(shortLabel)) .setShortLabel(getString(shortcut.shortcutShortLabel))
.setLongLabel(getString(longLabel)) .setLongLabel(getString(shortcut.shortcutLongLabel))
.setIcon(IconCompat.createWithResource(context, drawable)) .setIcon(IconCompat.createWithResource(context, shortcut.shortcutDrawable))
.setIntent( .setIntent(
Intent( Intent(
Intent.ACTION_VIEW, Intent.ACTION_VIEW,

View File

@ -25,15 +25,15 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.ui.model.DrawerItem
@Composable @Composable
internal fun UnittoDrawerItem( internal fun UnittoDrawerItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
destination: TopLevelDestinations, destination: DrawerItem,
icon: ImageVector, icon: ImageVector,
selected: Boolean, selected: Boolean,
onClick: (TopLevelDestinations) -> Unit onClick: (DrawerItem) -> Unit
) { ) {
NavigationDrawerItem( NavigationDrawerItem(
modifier = modifier, modifier = modifier,

View File

@ -58,10 +58,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.core.ui.LocalWindowSize import com.sadellie.unitto.core.ui.LocalWindowSize
import com.sadellie.unitto.core.ui.WindowWidthSizeClass import com.sadellie.unitto.core.ui.WindowWidthSizeClass
import com.sadellie.unitto.core.ui.model.DrawerItems import com.sadellie.unitto.core.ui.model.DrawerItem
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -112,9 +111,9 @@ fun UnittoNavigationDrawer(
modifier: Modifier, modifier: Modifier,
gesturesEnabled: Boolean, gesturesEnabled: Boolean,
state: DrawerState = rememberDrawerState(), state: DrawerState = rememberDrawerState(),
tabs: List<DrawerItems>, tabs: List<DrawerItem>,
currentDestination: String?, currentDestination: String?,
onItemClick: (TopLevelDestinations) -> Unit, onItemClick: (DrawerItem) -> Unit,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
if (LocalWindowSize.current.widthSizeClass == WindowWidthSizeClass.Expanded) { if (LocalWindowSize.current.widthSizeClass == WindowWidthSizeClass.Expanded) {
@ -280,8 +279,8 @@ private fun PreviewUnittoModalNavigationDrawerClose() {
modifier = Modifier, modifier = Modifier,
state = drawerState, state = drawerState,
gesturesEnabled = true, gesturesEnabled = true,
tabs = DrawerItems.MAIN, tabs = DrawerItem.main,
currentDestination = DrawerItems.Calculator.destination.start, currentDestination = DrawerItem.Calculator.start,
onItemClick = {}, onItemClick = {},
content = { content = {
Column { Column {

View File

@ -39,18 +39,17 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.core.ui.LocalWindowSize import com.sadellie.unitto.core.ui.LocalWindowSize
import com.sadellie.unitto.core.ui.WindowHeightSizeClass import com.sadellie.unitto.core.ui.WindowHeightSizeClass
import com.sadellie.unitto.core.ui.model.DrawerItems import com.sadellie.unitto.core.ui.model.DrawerItem
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@Suppress("UnusedReceiverParameter") @Suppress("UnusedReceiverParameter")
@Composable @Composable
internal fun ColumnScope.SheetContent( internal fun ColumnScope.SheetContent(
tabs: List<DrawerItems>, tabs: List<DrawerItem>,
currentDestination: String?, currentDestination: String?,
onItemClick: (TopLevelDestinations) -> Unit, onItemClick: (DrawerItem) -> Unit,
) { ) {
var showHello by remember { mutableStateOf(false) } var showHello by remember { mutableStateOf(false) }
val interactionSource = remember { val interactionSource = remember {
@ -81,10 +80,10 @@ internal fun ColumnScope.SheetContent(
} }
tabs.forEach { drawerItem -> tabs.forEach { drawerItem ->
val selected = drawerItem.destination.start == currentDestination val selected = drawerItem.start == currentDestination
UnittoDrawerItem( UnittoDrawerItem(
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding), modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
destination = drawerItem.destination, destination = drawerItem,
icon = if (selected) drawerItem.selectedIcon else drawerItem.defaultIcon, icon = if (selected) drawerItem.selectedIcon else drawerItem.defaultIcon,
selected = selected, selected = selected,
onClick = onItemClick onClick = onItemClick
@ -97,8 +96,8 @@ internal fun ColumnScope.SheetContent(
UnittoDrawerItem( UnittoDrawerItem(
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding), modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding),
destination = DrawerItems.Settings.destination, destination = DrawerItem.Settings,
icon = DrawerItems.Settings.defaultIcon, icon = DrawerItem.Settings.defaultIcon,
selected = false, selected = false,
onClick = onItemClick onClick = onItemClick
) )
@ -111,11 +110,11 @@ private fun PreviewUnittoDrawerSheet() {
Column { Column {
SheetContent( SheetContent(
tabs = listOf( tabs = listOf(
DrawerItems.Calculator, DrawerItem.Calculator,
DrawerItems.Calculator, DrawerItem.Calculator,
DrawerItems.Calculator, DrawerItem.Calculator,
), ),
currentDestination = DrawerItems.Calculator.destination.start, currentDestination = DrawerItem.Calculator.start,
onItemClick = {} onItemClick = {}
) )
} }

View File

@ -19,15 +19,14 @@
package com.sadellie.unitto.core.ui.model package com.sadellie.unitto.core.ui.model
import android.os.Build import android.os.Build
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Accessibility
import androidx.compose.material.icons.filled.AccessibilityNew import androidx.compose.material.icons.filled.AccessibilityNew
import androidx.compose.material.icons.filled.Calculate import androidx.compose.material.icons.filled.Calculate
import androidx.compose.material.icons.filled.Event import androidx.compose.material.icons.filled.Event
import androidx.compose.material.icons.filled.Schedule import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.SwapHoriz import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.material.icons.outlined.Accessibility
import androidx.compose.material.icons.outlined.AccessibilityNew import androidx.compose.material.icons.outlined.AccessibilityNew
import androidx.compose.material.icons.outlined.Calculate import androidx.compose.material.icons.outlined.Calculate
import androidx.compose.material.icons.outlined.Event import androidx.compose.material.icons.outlined.Event
@ -35,60 +34,102 @@ import androidx.compose.material.icons.outlined.Schedule
import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.SwapHoriz import androidx.compose.material.icons.outlined.SwapHoriz
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.base.TopLevelDestinations
sealed class DrawerItems( sealed class DrawerItem(
val destination: TopLevelDestinations, val graph: String,
val start: String,
@StringRes val name: Int,
val shortcut: Shortcut?,
val selectedIcon: ImageVector, val selectedIcon: ImageVector,
val defaultIcon: ImageVector val defaultIcon: ImageVector
) { ) {
data object Calculator : DrawerItems( data object Calculator : DrawerItem(
destination = TopLevelDestinations.Calculator, graph = TopLevelDestinations.CALCULATOR_GRAPH,
start = TopLevelDestinations.CALCULATOR_START,
name = R.string.calculator_title,
shortcut = Shortcut(
R.string.calculator_title,
R.string.calculator_title,
R.drawable.ic_shortcut_calculator
),
selectedIcon = Icons.Filled.Calculate, selectedIcon = Icons.Filled.Calculate,
defaultIcon = Icons.Outlined.Calculate defaultIcon = Icons.Outlined.Calculate
) )
data object Converter : DrawerItems( data object Converter : DrawerItem(
destination = TopLevelDestinations.Converter, graph = TopLevelDestinations.CONVERTER_GRAPH,
start = TopLevelDestinations.CONVERTER_START,
name = R.string.unit_converter_title,
shortcut = Shortcut(
R.string.unit_converter_title,
R.string.unit_converter_title,
R.drawable.ic_shortcut_unit_converter
),
selectedIcon = Icons.Filled.SwapHoriz, selectedIcon = Icons.Filled.SwapHoriz,
defaultIcon = Icons.Outlined.SwapHoriz defaultIcon = Icons.Outlined.SwapHoriz
) )
data object DateDifference : DrawerItems( data object DateCalculator : DrawerItem(
destination = TopLevelDestinations.DateCalculator, graph = TopLevelDestinations.DATE_CALCULATOR_GRAPH,
start = TopLevelDestinations.DATE_CALCULATOR_START,
name = R.string.date_calculator_title,
shortcut = Shortcut(
R.string.date_calculator_title,
R.string.date_calculator_title,
R.drawable.ic_shortcut_date_calculator
),
selectedIcon = Icons.Filled.Event, selectedIcon = Icons.Filled.Event,
defaultIcon = Icons.Outlined.Event defaultIcon = Icons.Outlined.Event
) )
data object TimeZones : DrawerItems( data object TimeZones : DrawerItem(
destination = TopLevelDestinations.TimeZone, graph = TopLevelDestinations.TIME_ZONE_GRAPH,
start = TopLevelDestinations.TIME_ZONE_START,
name = R.string.time_zone_title,
shortcut = Shortcut(
R.string.time_zone_title,
R.string.time_zone_title,
R.drawable.ic_shortcut_time_zone
),
selectedIcon = Icons.Filled.Schedule, selectedIcon = Icons.Filled.Schedule,
defaultIcon = Icons.Outlined.Schedule defaultIcon = Icons.Outlined.Schedule
) )
data object BodyMass : DrawerItems( data object BodyMass : DrawerItem(
destination = TopLevelDestinations.BodyMass, graph = TopLevelDestinations.BODY_MASS_GRAPH,
start = TopLevelDestinations.BODY_MASS_START,
name = R.string.body_mass_title,
shortcut = Shortcut(
R.string.body_mass_title,
R.string.body_mass_title,
R.drawable.ic_shortcut_body_mass
),
selectedIcon = Icons.Filled.AccessibilityNew, selectedIcon = Icons.Filled.AccessibilityNew,
defaultIcon = Icons.Outlined.AccessibilityNew defaultIcon = Icons.Outlined.AccessibilityNew
) )
data object Settings : DrawerItems( data object Settings : DrawerItem(
destination = TopLevelDestinations.Settings, graph = TopLevelDestinations.SETTINGS_GRAPH,
start = TopLevelDestinations.SETTINGS_START,
name = R.string.settings_title,
shortcut = null,
selectedIcon = Icons.Filled.Settings, selectedIcon = Icons.Filled.Settings,
defaultIcon = Icons.Outlined.Settings defaultIcon = Icons.Outlined.Settings
) )
companion object { companion object {
/** /**
* Excluding Settings tab since it appears only for expanded layout * Except for [Settings]
*/ */
val MAIN by lazy { val main by lazy {
var all = listOf( var all = listOf(
Calculator, Calculator,
Converter, Converter,
DateDifference, DateCalculator,
TimeZones, TimeZones,
BodyMass BodyMass,
) )
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
@ -97,5 +138,8 @@ sealed class DrawerItems(
all all
} }
// Only routes, not graphs!
val startRoutes by lazy { main.map { it.start } }
} }
} }

View File

@ -0,0 +1,28 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2024 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.model
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
data class Shortcut(
@StringRes val shortcutShortLabel: Int,
@StringRes val shortcutLongLabel: Int,
@DrawableRes val shortcutDrawable: Int,
)

View File

@ -48,7 +48,7 @@ fun Preferences.getMonetMode(): String {
} }
fun Preferences.getStartingScreen(): String { fun Preferences.getStartingScreen(): String {
return this[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Calculator.graph return this[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.CALCULATOR_GRAPH
} }
fun Preferences.getEnableToolsExperiment(): Boolean { fun Preferences.getEnableToolsExperiment(): Boolean {

View File

@ -20,13 +20,13 @@ package com.sadellie.unitto.feature.bodymass.navigation
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.navDeepLink import androidx.navigation.navDeepLink
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.unittoComposable import com.sadellie.unitto.core.ui.unittoComposable
import com.sadellie.unitto.core.ui.unittoNavigation import com.sadellie.unitto.core.ui.unittoNavigation
import com.sadellie.unitto.feature.bodymass.BodyMassRoute import com.sadellie.unitto.feature.bodymass.BodyMassRoute
private val graph = TopLevelDestinations.BodyMass.graph private val graph = DrawerItem.BodyMass.graph
private val start = TopLevelDestinations.BodyMass.start private val start = DrawerItem.BodyMass.start
fun NavGraphBuilder.bodyMassGraph( fun NavGraphBuilder.bodyMassGraph(
openDrawer: () -> Unit, openDrawer: () -> Unit,

View File

@ -20,14 +20,14 @@ package com.sadellie.unitto.feature.calculator.navigation
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.navDeepLink import androidx.navigation.navDeepLink
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.unittoComposable import com.sadellie.unitto.core.ui.unittoComposable
import com.sadellie.unitto.core.ui.unittoNavigation import com.sadellie.unitto.core.ui.unittoNavigation
import com.sadellie.unitto.feature.calculator.CalculatorRoute import com.sadellie.unitto.feature.calculator.CalculatorRoute
import com.sadellie.unitto.feature.calculator.RPNCalculatorRoute import com.sadellie.unitto.feature.calculator.RPNCalculatorRoute
private val graph = TopLevelDestinations.Calculator.graph private val graph = DrawerItem.Calculator.graph
private val start = TopLevelDestinations.Calculator.start private val start = DrawerItem.Calculator.start
fun NavGraphBuilder.calculatorGraph( fun NavGraphBuilder.calculatorGraph(
rpnMode: Boolean, rpnMode: Boolean,

View File

@ -23,7 +23,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.navDeepLink import androidx.navigation.navDeepLink
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.unittoComposable import com.sadellie.unitto.core.ui.unittoComposable
import com.sadellie.unitto.core.ui.unittoNavigation import com.sadellie.unitto.core.ui.unittoNavigation
import com.sadellie.unitto.feature.converter.ConverterRoute import com.sadellie.unitto.feature.converter.ConverterRoute
@ -31,8 +31,8 @@ import com.sadellie.unitto.feature.converter.ConverterViewModel
import com.sadellie.unitto.feature.converter.LeftSideRoute import com.sadellie.unitto.feature.converter.LeftSideRoute
import com.sadellie.unitto.feature.converter.RightSideRoute import com.sadellie.unitto.feature.converter.RightSideRoute
private val graph = TopLevelDestinations.Converter.graph private val graph = DrawerItem.Converter.graph
private val start = TopLevelDestinations.Converter.start private val start = DrawerItem.Converter.start
private const val LEFT = "left" private const val LEFT = "left"
private const val RIGHT = "right" private const val RIGHT = "right"

View File

@ -20,13 +20,13 @@ package com.sadellie.unitto.feature.datecalculator.navigation
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.navDeepLink import androidx.navigation.navDeepLink
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.unittoComposable import com.sadellie.unitto.core.ui.unittoComposable
import com.sadellie.unitto.core.ui.unittoNavigation import com.sadellie.unitto.core.ui.unittoNavigation
import com.sadellie.unitto.feature.datecalculator.DateCalculatorRoute import com.sadellie.unitto.feature.datecalculator.DateCalculatorRoute
private val graph = TopLevelDestinations.DateCalculator.graph private val graph = DrawerItem.DateCalculator.graph
private val start = TopLevelDestinations.DateCalculator.start private val start = DrawerItem.DateCalculator.start
fun NavGraphBuilder.dateCalculatorGraph( fun NavGraphBuilder.dateCalculatorGraph(
navigateToMenu: () -> Unit, navigateToMenu: () -> Unit,

View File

@ -22,7 +22,7 @@ import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.navDeepLink import androidx.navigation.navDeepLink
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.unittoNavigation import com.sadellie.unitto.core.ui.unittoNavigation
import com.sadellie.unitto.core.ui.unittoStackedComposable import com.sadellie.unitto.core.ui.unittoStackedComposable
import com.sadellie.unitto.feature.settings.SettingsRoute import com.sadellie.unitto.feature.settings.SettingsRoute
@ -37,8 +37,8 @@ import com.sadellie.unitto.feature.settings.thirdparty.ThirdPartyLicensesScreen
import com.sadellie.unitto.feature.settings.unitgroups.UnitGroupsScreen import com.sadellie.unitto.feature.settings.unitgroups.UnitGroupsScreen
import io.github.sadellie.themmo.ThemmoController import io.github.sadellie.themmo.ThemmoController
private val graph = TopLevelDestinations.Settings.graph private val graph = DrawerItem.Settings.graph
private val start = TopLevelDestinations.Settings.start private val start = DrawerItem.Settings.start
internal const val displayRoute = "display_route" internal const val displayRoute = "display_route"
internal const val languageRoute = "language_route" internal const val languageRoute = "language_route"
internal const val startingScreenRoute = "starting_screen_route" internal const val startingScreenRoute = "starting_screen_route"
@ -50,7 +50,7 @@ internal const val calculatorSettingsRoute = "calculator_settings_route"
internal const val converterSettingsRoute = "converter_settings_route" internal const val converterSettingsRoute = "converter_settings_route"
fun NavController.navigateToSettings() { fun NavController.navigateToSettings() {
navigate(TopLevelDestinations.Settings.start) navigate(DrawerItem.Settings.start)
} }
fun NavController.navigateToUnitGroups() { fun NavController.navigateToUnitGroups() {

View File

@ -21,6 +21,7 @@ package com.sadellie.unitto.feature.settings.startingscreen
import android.os.Build import android.os.Build
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AppShortcut import androidx.compose.material.icons.filled.AppShortcut
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -35,14 +36,12 @@ import androidx.compose.ui.tooling.preview.Preview
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.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.base.Shortcut
import com.sadellie.unitto.core.base.TOP_LEVEL_DESTINATIONS
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.core.ui.addShortcut import com.sadellie.unitto.core.ui.addShortcut
import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.NavigateUpButton
import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen
import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoListItem
import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar
import com.sadellie.unitto.core.ui.model.DrawerItem
@Composable @Composable
internal fun StartingScreenRoute( internal fun StartingScreenRoute(
@ -74,40 +73,28 @@ private fun StartingScreenScreen(
navigationIcon = { NavigateUpButton(navigateUp) } navigationIcon = { NavigateUpButton(navigateUp) }
) { padding -> ) { padding ->
LazyColumn(contentPadding = padding) { LazyColumn(contentPadding = padding) {
items(DrawerItem.main, { it.graph }) { destination ->
UnittoListItem(
modifier = Modifier.clickable { updateStartingScreen(destination.graph) },
headlineContent = {
Text(stringResource(destination.name))
},
leadingContent = {
RadioButton(
selected = destination.graph == startingScreen,
onClick = { updateStartingScreen(destination.graph) }
)
},
trailingContent = trail@{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return@trail
TOP_LEVEL_DESTINATIONS.forEach { destination -> IconButton(
item { onClick = { mContext.addShortcut(destination) }
UnittoListItem( ) {
modifier = Modifier.clickable { updateStartingScreen(destination.graph) }, Icon(Icons.Default.AppShortcut, null)
headlineContent = {
Text(stringResource(destination.name))
},
leadingContent = {
RadioButton(
selected = destination.graph == startingScreen,
onClick = { updateStartingScreen(destination.graph) }
)
},
trailingContent = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
IconButton(
onClick = {
destination.shortcut?.let { shortcut: Shortcut ->
mContext.addShortcut(
destination.graph,
shortcut.shortcutShortLabel,
shortcut.shortcutLongLabel,
shortcut.shortcutDrawable
)
}
}
) {
Icon(Icons.Default.AppShortcut, null)
}
}
} }
) }
} )
} }
} }
} }
@ -117,7 +104,7 @@ private fun StartingScreenScreen(
@Composable @Composable
private fun StartingScreenPreview() { private fun StartingScreenPreview() {
StartingScreenScreen( StartingScreenScreen(
startingScreen = TopLevelDestinations.Converter.graph, startingScreen = DrawerItem.Converter.graph,
updateStartingScreen = {}, updateStartingScreen = {},
navigateUp = {} navigateUp = {}
) )

View File

@ -25,8 +25,8 @@ import androidx.navigation.NavHostController
import androidx.navigation.NavType import androidx.navigation.NavType
import androidx.navigation.navArgument import androidx.navigation.navArgument
import androidx.navigation.navDeepLink import androidx.navigation.navDeepLink
import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen
import com.sadellie.unitto.core.ui.model.DrawerItem
import com.sadellie.unitto.core.ui.unittoComposable import com.sadellie.unitto.core.ui.unittoComposable
import com.sadellie.unitto.core.ui.unittoNavigation import com.sadellie.unitto.core.ui.unittoNavigation
import com.sadellie.unitto.feature.timezone.AddTimeZoneRoute import com.sadellie.unitto.feature.timezone.AddTimeZoneRoute
@ -34,8 +34,8 @@ import com.sadellie.unitto.feature.timezone.TimeZoneRoute
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
private val graph = TopLevelDestinations.TimeZone.graph private val graph = DrawerItem.TimeZones.graph
private val start = TopLevelDestinations.TimeZone.start private val start = DrawerItem.TimeZones.start
private const val ADD_TIME_ZONE_ROUTE = "ADD_TIME_ZONE_ROUTE" private const val ADD_TIME_ZONE_ROUTE = "ADD_TIME_ZONE_ROUTE"
private const val USER_TIME_ARG = "USER_TIME_ARG" private const val USER_TIME_ARG = "USER_TIME_ARG"