mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Migrated to Compose Navigation3, updated dependencies
This commit is contained in:
1 parent
fb2cd85b11
commit
7d5bca300d
14 files changed
+252
-220
No files matched your search
@@ -25,19 +25,17 @@ import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -58,128 +56,152 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
import androidx.navigation3.runtime.rememberNavBackStack
|
||||
import androidx.navigation3.ui.NavDisplay
|
||||
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
||||
import com.rtbishop.look4sat.core.presentation.Screen
|
||||
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
||||
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
||||
import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
||||
import com.rtbishop.look4sat.feature.map.mapDestination
|
||||
import com.rtbishop.look4sat.feature.passes.passesDestination
|
||||
import com.rtbishop.look4sat.feature.radar.radarDestination
|
||||
import com.rtbishop.look4sat.feature.radiocontrol.radioControlDestination
|
||||
import com.rtbishop.look4sat.feature.satellites.satellitesDestination
|
||||
import com.rtbishop.look4sat.feature.settings.settingsDestination
|
||||
import com.rtbishop.look4sat.feature.map.MapDestination
|
||||
import com.rtbishop.look4sat.feature.passes.PassesDestination
|
||||
import com.rtbishop.look4sat.feature.radar.RadarDestination
|
||||
import com.rtbishop.look4sat.feature.radiocontrol.RadioControlDestination
|
||||
import com.rtbishop.look4sat.feature.satellites.SatellitesDestination
|
||||
import com.rtbishop.look4sat.feature.settings.SettingsDestination
|
||||
|
||||
@Composable
|
||||
fun MainScreen(navController: NavHostController = rememberNavController()) {
|
||||
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Map, Screen.Settings)
|
||||
val currentDestination = navController.currentBackStackEntryAsState().value?.destination?.route
|
||||
val startDestination = Screen.Passes.route
|
||||
fun MainScreen() {
|
||||
val backStack = rememberNavBackStack(Screen.Passes)
|
||||
val currentKey = backStack.lastOrNull()
|
||||
val navigateBack: () -> Unit = { backStack.removeAt(backStack.size - 1) }
|
||||
val fadeTransition = fadeIn(animationSpec = tween(350)) togetherWith fadeOut(animationSpec = tween(350))
|
||||
val navItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar(), Screen.Map, Screen.Settings)
|
||||
|
||||
// Observe radio tracking state for the status bar
|
||||
val context = LocalContext.current
|
||||
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
||||
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
||||
|
||||
NavigationSuiteScaffold(
|
||||
navigationSuiteItems = {
|
||||
items.forEach {
|
||||
navItems.forEach { screen ->
|
||||
val isSelected = when (currentKey) {
|
||||
is Screen.Satellites -> screen is Screen.Satellites
|
||||
is Screen.Passes -> screen is Screen.Passes
|
||||
is Screen.Radar -> screen is Screen.Radar
|
||||
is Screen.Map -> screen is Screen.Map
|
||||
is Screen.Settings -> screen is Screen.Settings
|
||||
else -> false
|
||||
}
|
||||
item(
|
||||
icon = { Icon(painterResource(it.iconResId), stringResource(it.titleResId)) },
|
||||
label = { Text(stringResource(it.titleResId)) },
|
||||
selected = currentDestination?.contains(it.route) ?: false,
|
||||
icon = { Icon(painterResource(screen.iconResId), stringResource(screen.titleResId)) },
|
||||
label = { Text(stringResource(screen.titleResId)) },
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
if (currentDestination?.contains(it.route) ?: false) return@item
|
||||
navController.navigate(it.route) {
|
||||
popUpTo(startDestination) { saveState = false }
|
||||
launchSingleTop = true
|
||||
restoreState = false
|
||||
}
|
||||
})
|
||||
if (isSelected) return@item
|
||||
while (backStack.size > 1) backStack.removeAt(backStack.size - 1)
|
||||
if (screen !is Screen.Passes) backStack.add(screen)
|
||||
}
|
||||
)
|
||||
}
|
||||
}, navigationSuiteColors = NavigationSuiteDefaults.colors(
|
||||
},
|
||||
navigationSuiteColors = NavigationSuiteDefaults.colors(
|
||||
navigationRailContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||
), layoutType = when {
|
||||
),
|
||||
layoutType = when {
|
||||
!hasEnoughHeight() && hasEnoughWidth() -> NavigationSuiteType.NavigationRail
|
||||
!hasEnoughWidth() -> NavigationSuiteType.ShortNavigationBarCompact
|
||||
else -> NavigationSuiteType.ShortNavigationBarMedium
|
||||
}
|
||||
) {
|
||||
Column {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = startDestination,
|
||||
enterTransition = { fadeIn(animationSpec = tween(350)) },
|
||||
exitTransition = { fadeOut(animationSpec = tween(350)) },
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
satellitesDestination { navController.navigateUp() }
|
||||
passesDestination { catNum: Int, aosTime: Long ->
|
||||
val radarRoute = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
||||
navController.navigate(radarRoute)
|
||||
}
|
||||
radarDestination(
|
||||
navigateUp = { navController.navigateUp() },
|
||||
navigateToRadioControl = { catNum, aosTime ->
|
||||
val route = "${Screen.RadioControl.route}?catNum=$catNum&aosTime=$aosTime"
|
||||
navController.navigate(route)
|
||||
}
|
||||
)
|
||||
radioControlDestination { navController.navigateUp() }
|
||||
mapDestination()
|
||||
settingsDestination()
|
||||
}
|
||||
|
||||
// Radio tracking status banner (above bottom navigation)
|
||||
if (trackingState.isActive) {
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "trackingPulse")
|
||||
val alpha by infiniteTransition.animateFloat(
|
||||
initialValue = 1f, targetValue = 0.4f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(1000, easing = LinearEasing),
|
||||
repeatMode = RepeatMode.Reverse
|
||||
), label = "pulseAlpha"
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.primaryContainer)
|
||||
.clickable {
|
||||
val pass = trackingState.currentPass
|
||||
if (pass != null) {
|
||||
val route = "${Screen.RadioControl.route}?catNum=${pass.catNum}&aosTime=${pass.aosTime}"
|
||||
navController.navigate(route)
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
modifier = Modifier.weight(1f),
|
||||
onBack = navigateBack,
|
||||
transitionSpec = { fadeTransition },
|
||||
popTransitionSpec = { fadeTransition },
|
||||
predictivePopTransitionSpec = { fadeTransition },
|
||||
entryProvider = entryProvider {
|
||||
entry<Screen.Satellites> {
|
||||
SatellitesDestination(navigateUp = navigateBack)
|
||||
}
|
||||
entry<Screen.Passes> {
|
||||
PassesDestination { catNum, aosTime ->
|
||||
backStack.add(Screen.Radar(catNum, aosTime))
|
||||
}
|
||||
}
|
||||
.padding(horizontal = 12.dp, vertical = 6.dp)
|
||||
) {
|
||||
Box(
|
||||
entry<Screen.Radar> { route ->
|
||||
RadarDestination(
|
||||
catNum = route.catNum,
|
||||
aosTime = route.aosTime,
|
||||
navigateUp = navigateBack,
|
||||
navigateToRadioControl = { catNum, aosTime ->
|
||||
backStack.add(Screen.RadioControl(catNum, aosTime))
|
||||
}
|
||||
)
|
||||
}
|
||||
entry<Screen.RadioControl> { route ->
|
||||
RadioControlDestination(
|
||||
catNum = route.catNum,
|
||||
aosTime = route.aosTime,
|
||||
navigateUp = navigateBack
|
||||
)
|
||||
}
|
||||
entry<Screen.Map> {
|
||||
MapDestination()
|
||||
}
|
||||
entry<Screen.Settings> {
|
||||
SettingsDestination()
|
||||
}
|
||||
}
|
||||
)
|
||||
// Radio tracking status banner
|
||||
if (trackingState.isActive) {
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "trackingPulse")
|
||||
val alpha by infiniteTransition.animateFloat(
|
||||
initialValue = 1f, targetValue = 0.4f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(1000, easing = LinearEasing),
|
||||
repeatMode = RepeatMode.Reverse
|
||||
), label = "pulseAlpha"
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.size(8.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Color(0xFF4CAF50).copy(alpha = alpha))
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Tracking: ${trackingState.currentPass?.name ?: ""}",
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
val txOk = if (trackingState.txConnected) "TX" else ""
|
||||
val rxOk = if (trackingState.rxConnected) "RX" else ""
|
||||
Text(
|
||||
text = listOf(txOk, rxOk).filter { it.isNotBlank() }.joinToString("/"),
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.primaryContainer)
|
||||
.clickable {
|
||||
val pass = trackingState.currentPass
|
||||
if (pass != null) {
|
||||
backStack.add(Screen.RadioControl(pass.catNum, pass.aosTime))
|
||||
}
|
||||
}
|
||||
.padding(horizontal = 12.dp, vertical = 6.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(8.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Color(0xFF4CAF50).copy(alpha = alpha))
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Tracking: ${trackingState.currentPass?.name ?: ""}",
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
val txOk = if (trackingState.txConnected) "TX" else ""
|
||||
val rxOk = if (trackingState.rxConnected) "RX" else ""
|
||||
Text(
|
||||
text = listOf(txOk, rxOk).filter { it.isNotBlank() }.joinToString("/"),
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end Column
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user