Tried using floating app bar layout for screen navigation

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:47 +00:00
1 parent 82f41940c7
commit 42aa4deb0d
11 files changed
+135 -144

No files matched your search

@@ -1,75 +1,53 @@
package com.rtbishop.look4sat.presentation
import androidx.compose.material3.Icon
import androidx.compose.foundation.background
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
import com.rtbishop.look4sat.presentation.map.mapDestination
import com.rtbishop.look4sat.presentation.passes.passesDestination
import com.rtbishop.look4sat.presentation.radar.radarDestination
import com.rtbishop.look4sat.presentation.satellites.satellitesDestination
import com.rtbishop.look4sat.presentation.settings.settingsDestination
sealed class Screen(val title: String, val icon: Int, val route: String) {
data object Satellites : Screen("Satellites", R.drawable.ic_sputnik, "satellites")
data object Passes : Screen("Passes", R.drawable.ic_passes, "passes")
data object Radar : Screen("Radar", R.drawable.ic_satellite, "radar")
data object Map : Screen("Map", R.drawable.ic_map, "map")
data object Settings : Screen("Settings", R.drawable.ic_settings, "settings")
sealed class Screen(val route: String) {
data object Satellites : Screen("satellites")
data object Passes : Screen("passes")
data object Radar : Screen("radar")
data object Map : Screen("map")
data object Settings : Screen("settings")
}
private val startDestination = Screen.Passes.route
data class NavActions(
val openSatellites: () -> Unit,
val openRadar: (Int, Long) -> Unit,
val openMap: () -> Unit,
val openSettings: () -> Unit
)
@Composable
fun MainScreen(navController: NavHostController = rememberNavController()) {
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Map, Screen.Settings)
val destinationRoute = navController.currentBackStackEntryAsState().value?.destination?.route
NavigationSuiteScaffold(
navigationSuiteItems = {
items.forEach {
item(
icon = { Icon(painterResource(it.icon), it.title) },
label = { Text(it.title) },
selected = destinationRoute?.contains(it.route) ?: false,
onClick = {
if (destinationRoute?.contains(it.route) ?: false) return@item
navController.navigate(it.route) {
popUpTo(startDestination) { saveState = false }
launchSingleTop = true
restoreState = false
}
})
}
}, navigationSuiteColors = NavigationSuiteDefaults.colors(
navigationRailContainerColor = MaterialTheme.colorScheme.surfaceContainer
), layoutType = when {
isVerticalLayout() -> NavigationSuiteType.NavigationBar
else -> NavigationSuiteType.NavigationRail
}
) { MainNavHost(navController) }
}
@Composable
private fun MainNavHost(navController: NavHostController) {
val navigateToRadar = { catNum: Int, aosTime: Long ->
val navActions = NavActions(
openSatellites = { navController.navigate(Screen.Satellites.route) },
openRadar = { catNum: Int, aosTime: Long ->
val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
navController.navigate(routeWithParams)
}
NavHost(navController = navController, startDestination = startDestination) {
},
openMap = { navController.navigate(Screen.Map.route) },
openSettings = { navController.navigate(Screen.Settings.route) }
)
NavHost(
navController = navController,
startDestination = Screen.Passes.route,
modifier = Modifier.background(color = MaterialTheme.colorScheme.background)
) {
satellitesDestination { navController.navigateUp() }
passesDestination(navigateToRadar)
passesDestination(navActions)
radarDestination { navController.navigateUp() }
mapDestination()
settingsDestination()
mapDestination { navController.navigateUp() }
settingsDestination { navController.navigateUp() }
}
}
@@ -22,23 +22,14 @@ import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat
val LocalSpacing = compositionLocalOf { Spacing }
//val MaterialTheme.spacing: Spacing
// @Composable @ReadOnlyComposable get() = Spacing
data object SpacingDefaults {
internal const val EXTRA_SMALL = 4
internal const val SMALL = 6
internal const val MEDIUM = 8
internal const val LARGE = 12
internal const val EXTRA_LARGE = 16
}
data object Spacing {
val extraSmall = SpacingDefaults.EXTRA_SMALL.dp
val small = SpacingDefaults.SMALL.dp
val medium = SpacingDefaults.MEDIUM.dp
val large = SpacingDefaults.LARGE.dp
val extraLarge = SpacingDefaults.EXTRA_LARGE.dp
val extraExtraSmall = 4.dp
val extraSmall = 6.dp
val small = 8.dp
val medium = 12.dp
val large = 16.dp
val extraLarge = 24.dp
}
@Composable
@@ -17,21 +17,22 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@@ -56,9 +57,9 @@ private val sdfTime = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
@Preview(showBackground = true)
private fun TopBarPreview() = MainTheme {
TopBar {
IconCard(onClick = {}, iconId = R.drawable.ic_filter)
IconCard(action = {}, resId = R.drawable.ic_filter)
TimerRow(timeString = "88:88:88", isTimeAos = true)
IconCard(onClick = {}, iconId = R.drawable.ic_satellite)
IconCard(action = {}, resId = R.drawable.ic_satellite)
}
}
@@ -200,12 +201,13 @@ fun CardButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier)
}
@Composable
fun IconCard(onClick: () -> Unit, iconId: Int, modifier: Modifier = Modifier) {
val clickableModifier = Modifier.clickable { onClick() }
val iconRes = ImageVector.vectorResource(iconId)
ElevatedCard(modifier = Modifier.size(48.dp)) {
Box(modifier = clickableModifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon(imageVector = iconRes, contentDescription = null, modifier = modifier)
fun IconCard(action: () -> Unit, resId: Int, modifier: Modifier = Modifier, main: Boolean = false) {
val clickableMod = Modifier.clickable { action() }
val defaultC = CardDefaults.elevatedCardColors()
val mainC = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.primary)
ElevatedCard(modifier = Modifier.size(48.dp), colors = if (main) mainC else defaultC, shape = CircleShape) {
Box(modifier = clickableMod.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon(painter = painterResource(resId), contentDescription = null, modifier = modifier)
}
}
}
@@ -241,7 +243,7 @@ fun getDefaultPass(): OrbitalPass {
fun SharedDialog(
title: String, onCancel: () -> Unit, onAccept: () -> Unit, content: @Composable () -> Unit
) {
val padding = LocalSpacing.current.extraLarge
val padding = LocalSpacing.current.large
Dialog(onDismissRequest = { onCancel() }) {
ElevatedCard {
Column(
@@ -268,7 +270,7 @@ fun SharedDialog(
@Composable
fun InfoDialog(title: String, text: String, onDismiss: () -> Unit) {
val padding = LocalSpacing.current.extraLarge
val padding = LocalSpacing.current.large
Dialog(onDismissRequest = onDismiss) {
ElevatedCard {
Column(
@@ -290,7 +292,10 @@ fun InfoDialog(title: String, text: String, onDismiss: () -> Unit) {
)
Row(modifier = Modifier.padding(start = padding, bottom = padding, end = padding)) {
Spacer(modifier = Modifier.weight(1f))
CardButton(onClick = onDismiss, text = stringResource(id = R.string.btn_understand))
CardButton(
onClick = onDismiss,
text = stringResource(id = R.string.btn_understand)
)
}
}
}
@@ -311,9 +316,41 @@ fun Modifier.infiniteMarquee(): Modifier {
@Composable
fun Modifier.layoutPadding(): Modifier {
val statusBarMod = this.statusBarsPadding()
val spacing = LocalSpacing.current.small
return when {
isVerticalLayout() -> statusBarMod.padding(horizontal = spacing)
else -> statusBarMod.padding(start = 0.dp, top = 0.dp, end = spacing, bottom = spacing)
val spacing = LocalSpacing.current.extraSmall
return statusBarMod.padding(start = spacing, top = 0.dp, end = spacing, bottom = spacing)
}
@Composable
fun ScreenColumn(floatingBar: @Composable () -> Unit = {}, content: @Composable (Boolean) -> Unit) {
Box(contentAlignment = Alignment.BottomCenter) {
Column(
modifier = Modifier.layoutPadding(),
verticalArrangement = Arrangement.spacedBy(LocalSpacing.current.extraSmall)
) { content(isVerticalLayout()) }
floatingBar()
}
}
@Composable
fun FloatingBar(
startAction: () -> Unit, startIconResId: Int,
centerAction: () -> Unit, centerIconResId: Int,
endAction: () -> Unit, endIconResId: Int
) {
val spacing = LocalSpacing.current
Surface(
color = MaterialTheme.colorScheme.background,
shape = CircleShape,
modifier = Modifier.padding(bottom = spacing.large)
) {
Row(
horizontalArrangement = Arrangement.spacedBy(spacing.medium),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(spacing.medium)
) {
IconCard(action = startAction, resId = startIconResId)
IconCard(action = centerAction, resId = centerIconResId, main = true)
IconCard(action = endAction, resId = endIconResId)
}
}
}
@@ -85,7 +85,7 @@ private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
}
private val labelRect = Rect()
fun NavGraphBuilder.mapDestination() {
fun NavGraphBuilder.mapDestination(function: () -> Boolean) {
composable(Screen.Map.route) {
val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory)
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
@@ -110,17 +110,17 @@ private fun MapScreen(uiState: State<MapState>, mapView: MapView) {
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
if (isVerticalLayout()) {
TopBar {
IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
IconCard(action = selectPrev, resId = R.drawable.ic_arrow, modifier = rotateMod)
TimerRow(timeString = timeString, isTimeAos = isTimeAos)
IconCard(onClick = selectNext, iconId = R.drawable.ic_arrow)
IconCard(action = selectNext, resId = R.drawable.ic_arrow)
}
NextPassRow(pass = uiState.value.orbitalPass)
} else {
TopBar {
IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
IconCard(action = selectPrev, resId = R.drawable.ic_arrow, modifier = rotateMod)
TimerRow(timeString = timeString, isTimeAos = isTimeAos)
NextPassRow(pass = uiState.value.orbitalPass, modifier = Modifier.weight(1f))
IconCard(onClick = selectNext, iconId = R.drawable.ic_arrow)
IconCard(action = selectNext, resId = R.drawable.ic_arrow)
}
}
ElevatedCard(modifier = Modifier.weight(1f)) {
@@ -102,11 +102,11 @@ private fun SliderRow(
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(LocalSpacing.current.medium),
modifier = Modifier.padding(horizontal = LocalSpacing.current.extraLarge)
verticalArrangement = Arrangement.spacedBy(LocalSpacing.current.small),
modifier = Modifier.padding(horizontal = LocalSpacing.current.large)
) {
Row(
horizontalArrangement = Arrangement.spacedBy(LocalSpacing.current.medium),
horizontalArrangement = Arrangement.spacedBy(LocalSpacing.current.small),
verticalAlignment = Alignment.CenterVertically
) {
Text(
@@ -44,16 +44,18 @@ import com.rtbishop.look4sat.domain.predict.NearEarthObject
import com.rtbishop.look4sat.domain.predict.OrbitalData
import com.rtbishop.look4sat.domain.predict.OrbitalPass
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.NavActions
import com.rtbishop.look4sat.presentation.Screen
import com.rtbishop.look4sat.presentation.common.EmptyListCard
import com.rtbishop.look4sat.presentation.common.FloatingBar
import com.rtbishop.look4sat.presentation.common.IconCard
import com.rtbishop.look4sat.presentation.common.InfoDialog
import com.rtbishop.look4sat.presentation.common.NextPassRow
import com.rtbishop.look4sat.presentation.common.ScreenColumn
import com.rtbishop.look4sat.presentation.common.TimerRow
import com.rtbishop.look4sat.presentation.common.TopBar
import com.rtbishop.look4sat.presentation.common.infiniteMarquee
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
import com.rtbishop.look4sat.presentation.common.layoutPadding
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@@ -61,20 +63,20 @@ import java.util.Locale
private val sdfDate = SimpleDateFormat("EEE dd MMM", Locale.ENGLISH)
private val sdfTime = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
fun NavGraphBuilder.passesDestination(navigateToRadar: (Int, Long) -> Unit) {
fun NavGraphBuilder.passesDestination(navActions: NavActions) {
composable(Screen.Passes.route) {
val viewModel = viewModel(
modelClass = PassesViewModel::class.java,
factory = PassesViewModel.Factory
)
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
PassesScreen(uiState, navigateToRadar)
PassesScreen(uiState, navActions)
}
}
@Composable
private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> Unit) {
val refreshPasses = { uiState.takeAction(PassesAction.RefreshPasses) }
private fun PassesScreen(uiState: PassesState, navActions: NavActions) {
val refreshList = { uiState.takeAction(PassesAction.RefreshPasses) }
val showPassesDialog = { uiState.takeAction(PassesAction.TogglePassesDialog) }
val showRadiosDialog = { uiState.takeAction(PassesAction.ToggleRadiosDialog) }
if (uiState.isPassesDialogShown) {
@@ -95,26 +97,35 @@ private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> U
uiState.takeAction(PassesAction.DismissWelcome)
}
}
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
if (isVerticalLayout()) {
ScreenColumn(floatingBar = { PassesFloatingBar(navActions) }) { isVerticalLayout ->
if (isVerticalLayout) {
TopBar {
IconCard(onClick = { showPassesDialog() }, iconId = R.drawable.ic_filter)
IconCard(action = { showPassesDialog() }, resId = R.drawable.ic_filter)
TimerRow(timeString = uiState.nextTime, isTimeAos = uiState.isNextTimeAos)
IconCard(onClick = { showRadiosDialog() }, iconId = R.drawable.ic_satellite)
IconCard(action = { showRadiosDialog() }, resId = R.drawable.ic_satellite)
}
NextPassRow(pass = uiState.nextPass)
} else {
TopBar {
IconCard(onClick = { showPassesDialog() }, iconId = R.drawable.ic_filter)
IconCard(action = { showPassesDialog() }, resId = R.drawable.ic_filter)
TimerRow(timeString = uiState.nextTime, isTimeAos = uiState.isNextTimeAos)
NextPassRow(pass = uiState.nextPass, modifier = Modifier.weight(1f))
IconCard(onClick = { showRadiosDialog() }, iconId = R.drawable.ic_satellite)
IconCard(action = { showRadiosDialog() }, resId = R.drawable.ic_satellite)
}
}
PassesList(uiState.isRefreshing, uiState.itemsList, navigateToRadar, refreshPasses)
PassesList(uiState.isRefreshing, uiState.itemsList, navActions.openRadar, refreshList)
}
}
@Composable
private fun PassesFloatingBar(navActions: NavActions) {
FloatingBar(
startAction = navActions.openSettings, startIconResId = R.drawable.ic_settings,
centerAction = navActions.openSatellites, centerIconResId = R.drawable.ic_sputnik,
endAction = navActions.openMap, endIconResId = R.drawable.ic_map
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun PassesList(
@@ -84,17 +84,17 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
if (isVerticalLayout()) {
TopBar {
IconCard(onClick = navigateUp, iconId = R.drawable.ic_back)
IconCard(action = navigateUp, resId = R.drawable.ic_back)
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
IconCard(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
}
NextPassRow(pass = upcomingPass)
} else {
TopBar {
IconCard(onClick = navigateUp, iconId = R.drawable.ic_back)
IconCard(action = navigateUp, resId = R.drawable.ic_back)
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
NextPassRow(pass = upcomingPass, modifier = Modifier.weight(1f))
IconCard(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
}
}
if(isVerticalLayout()) {
@@ -92,16 +92,16 @@ private fun SatellitesScreen(uiState: SatellitesState, navigateUp: () -> Unit) {
}
TopBar {
SearchBar(setQuery = { setQuery(it) }, modifier = Modifier.weight(1f))
IconCard(onClick = unselectAll, iconId = R.drawable.ic_check_off)
IconCard(onClick = selectAll, iconId = R.drawable.ic_check_on)
IconCard(action = unselectAll, resId = R.drawable.ic_check_off)
IconCard(action = selectAll, resId = R.drawable.ic_check_on)
}
} else {
TopBar {
SaveButton(saveSelection = saveSelection, modifier = Modifier.height(48.dp))
TypeCard(types = uiState.currentTypes, toggleDialog, modifier = Modifier.weight(1f))
SearchBar(setQuery = { setQuery(it) }, modifier = Modifier.weight(1f))
IconCard(onClick = unselectAll, iconId = R.drawable.ic_check_off)
IconCard(onClick = selectAll, iconId = R.drawable.ic_check_on)
IconCard(action = unselectAll, resId = R.drawable.ic_check_off)
IconCard(action = selectAll, resId = R.drawable.ic_check_on)
}
}
ElevatedCard(modifier = Modifier.fillMaxSize()) {
@@ -50,7 +50,7 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
fun NavGraphBuilder.settingsDestination() {
fun NavGraphBuilder.settingsDestination(function: () -> Boolean) {
composable(Screen.Settings.route) {
val viewModel = viewModel(
modelClass = SettingsViewModel::class.java,
@@ -348,7 +348,7 @@ private fun DataCard(
Spacer(modifier = Modifier.width(6.dp))
CardButton(
onClick = { updateFromFile() },
text = stringResource(id = R.string.btn_file),
text = stringResource(id = R.string.btn_import),
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(6.dp))
-26
View File
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.Look4Sat.SplashScreen" parent="Theme.SplashScreen">
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
<item name="postSplashScreenTheme">@style/Theme.Look4Sat.Main</item>
<item name="windowSplashScreenBackground">@color/background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
<style name="Theme.Look4Sat.Main" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorAccent">@color/accent</item>
<item name="colorControlNormal">@color/textMain</item>
<item name="colorPrimary">@color/accent</item>
<item name="colorSecondary">@color/accent</item>
<item name="colorOnPrimaryContainer">@color/background</item>
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
<item name="android:listDivider">@drawable/item_divider</item>
<item name="android:navigationBarColor">@color/background</item>
<item name="android:textColorPrimary">@color/textMain</item>
<item name="android:statusBarColor">@color/background</item>
<item name="android:windowBackground">@color/background</item>
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
</resources>
+5 -5
View File
@@ -1,5 +1,5 @@
[versions]
android-gradle-plugin = "8.12.1"
android-gradle-plugin = "8.12.2"
google-ksp = "2.2.10-2.0.2"
kotlin = "2.2.10"
@@ -7,9 +7,9 @@ androidx-core-ktx = "1.17.0"
androidx-core-splashscreen = "1.0.1"
androidx-room = "2.7.2"
compose-bom = "2025.08.00"
compose-bom = "2025.08.01"
compose-activity = "1.10.1"
compose-lifecycle = "2.9.2"
compose-lifecycle = "2.9.3"
compose-navigation = "2.9.3"
other-coroutines = "1.10.2"
@@ -33,7 +33,7 @@ androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
compose-animation = { group = "androidx.compose.animation", name = "animation" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
compose-material3-navigation = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite" }
compose-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive" }
compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
compose-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
@@ -68,7 +68,7 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
[bundles]
composeAll = [
"compose-animation", "compose-runtime", "compose-tooling", "compose-activity",
"compose-lifecycle", "compose-material3", "compose-material3-navigation",
"compose-lifecycle", "compose-material3", "compose-material3-adaptive",
"compose-navigation", "compose-viewmodel"
]
composeDebug = ["compose-debug-manifest", "compose-debug-tooling"]