Added initial support for portrait/landscape TopBar

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:47 +00:00
1 parent 94735bc453
commit da2c3853b0
14 files changed
+80 -153

No files matched your search

@@ -17,7 +17,6 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.info.infoDestination
import com.rtbishop.look4sat.presentation.map.mapDestination
import com.rtbishop.look4sat.presentation.passes.passesDestination
import com.rtbishop.look4sat.presentation.radar.radarDestination
@@ -31,7 +30,6 @@ sealed class Screen(var title: String, var icon: Int, var route: String) {
data object Passes : Screen("Passes", R.drawable.ic_passes, "passes")
data object Map : Screen("Map", R.drawable.ic_map, "map")
data object Settings : Screen("Settings", R.drawable.ic_settings, "settings")
data object Info : Screen("Info", R.drawable.ic_info, "info")
}
@Composable
@@ -65,14 +63,13 @@ private fun NavBarScreen(navigateToRadar: (Int, Long) -> Unit) {
passesDestination(navigateToRadar)
mapDestination()
settingsDestination()
infoDestination()
}
}
}
@Composable
private fun MainNavBar(navController: NavController) {
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Map, Screen.Settings, Screen.Info)
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Map, Screen.Settings)
val destinationRoute = navController.currentBackStackEntryAsState().value?.destination?.route
NavigationBar {
items.forEach { item ->
@@ -1,4 +1,4 @@
package com.rtbishop.look4sat.presentation.components
package com.rtbishop.look4sat.presentation.common
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@@ -21,6 +21,7 @@ import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
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
@@ -34,6 +35,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.window.core.layout.WindowWidthSizeClass
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.predict.NearEarthObject
import com.rtbishop.look4sat.domain.predict.OrbitalData
@@ -48,16 +50,16 @@ private val sdfTime = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH)
@Composable
@Preview(showBackground = true)
private fun TimerRowPreview() = MainTheme {
TimerRow {
CardIcon(onClick = {}, iconId = R.drawable.ic_filter)
TimerBar(timeString = "88:88:88", isTimeAos = true)
CardIcon(onClick = {}, iconId = R.drawable.ic_satellite)
private fun TopBarPreview() = MainTheme {
TopBar {
IconCard(onClick = {}, iconId = R.drawable.ic_filter)
TimerRow(timeString = "88:88:88", isTimeAos = true)
IconCard(onClick = {}, iconId = R.drawable.ic_satellite)
}
}
@Composable
fun TimerRow(content: @Composable (RowScope.() -> Unit)) {
fun TopBar(content: @Composable (RowScope.() -> Unit)) {
Row(
modifier = Modifier.height(48.dp),
horizontalArrangement = Arrangement.spacedBy(6.dp),
@@ -66,9 +68,10 @@ fun TimerRow(content: @Composable (RowScope.() -> Unit)) {
}
@Composable
fun RowScope.TimerBar(timeString: String, isTimeAos: Boolean) {
val aosColor = if (isTimeAos) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
val losColor = if (!isTimeAos) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
fun RowScope.TimerRow(timeString: String, isTimeAos: Boolean) {
val colorScheme = MaterialTheme.colorScheme
val aosColor = if (isTimeAos) colorScheme.primary else colorScheme.onSurface
val losColor = if (!isTimeAos) colorScheme.primary else colorScheme.onSurface
ElevatedCard(modifier = Modifier.weight(1f)) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -94,8 +97,8 @@ private fun NextPassRowPreview() = MainTheme {
}
@Composable
fun NextPassRow(pass: OrbitalPass) {
ElevatedCard(modifier = Modifier.height(48.dp)) {
fun NextPassRow(pass: OrbitalPass, modifier: Modifier = Modifier) {
ElevatedCard(modifier = modifier.height(48.dp)) {
Column(
verticalArrangement = Arrangement.spacedBy((-2).dp),
modifier = Modifier
@@ -192,7 +195,7 @@ fun CardButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier)
}
@Composable
fun CardIcon(onClick: () -> Unit, iconId: Int, modifier: Modifier = Modifier) {
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)) {
@@ -274,3 +277,9 @@ fun InfoDialog(title: String, text: String, onDismiss: () -> Unit) {
}
}
}
@Composable
fun isVerticalLayout(): Boolean {
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT
}
@@ -1,91 +0,0 @@
package com.rtbishop.look4sat.presentation.info
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ElevatedCard
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.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.Screen
import com.rtbishop.look4sat.presentation.components.CardButton
private const val POLICY_URL = "https://sites.google.com/view/look4sat-privacy-policy/home"
private const val LICENSE_URL = "https://www.gnu.org/licenses/gpl-3.0.html"
fun NavGraphBuilder.infoDestination() {
composable(Screen.Info.route) { InfoScreen() }
}
@Composable
private fun InfoScreen() {
LazyColumn(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
item { CardCredits() }
}
}
@Preview(showBackground = true)
@Composable
private fun CardCreditsPreview() = MainTheme { CardCredits() }
@Composable
private fun CardCredits(modifier: Modifier = Modifier) {
val uriHandler = LocalUriHandler.current
ElevatedCard(modifier = modifier.fillMaxWidth()) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Text(
text = stringResource(id = R.string.outro_title),
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.primary,
modifier = modifier.padding(6.dp)
)
Text(
text = stringResource(
id = R.string.outro_thanks
), fontSize = 16.sp, textAlign = TextAlign.Center
)
Text(
text = stringResource(id = R.string.outro_license),
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.primary,
modifier = modifier.padding(6.dp)
)
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
CardButton(
onClick = { uriHandler.openUri(LICENSE_URL) },
text = stringResource(id = R.string.btn_license),
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(6.dp))
CardButton(
onClick = { uriHandler.openUri(POLICY_URL) },
text = stringResource(id = R.string.btn_privacy),
modifier = Modifier.weight(1f)
)
}
}
}
}
@@ -43,10 +43,10 @@ import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.OrbitalObject
import com.rtbishop.look4sat.domain.predict.OrbitalPos
import com.rtbishop.look4sat.presentation.Screen
import com.rtbishop.look4sat.presentation.components.CardIcon
import com.rtbishop.look4sat.presentation.components.NextPassRow
import com.rtbishop.look4sat.presentation.components.TimerBar
import com.rtbishop.look4sat.presentation.components.TimerRow
import com.rtbishop.look4sat.presentation.common.IconCard
import com.rtbishop.look4sat.presentation.common.NextPassRow
import com.rtbishop.look4sat.presentation.common.TimerRow
import com.rtbishop.look4sat.presentation.common.TopBar
import org.osmdroid.tileprovider.tilesource.XYTileSource
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.CustomZoomButtonsController
@@ -99,10 +99,10 @@ private fun MapScreen(uiState: State<MapState>, mapView: MapView) {
val isTimeAos = uiState.value.mapData?.isTimeAos ?: true
val osmInfo = "© OpenStreetMap contributors"
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
TimerRow {
CardIcon(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
TimerBar(timeString = timeString, isTimeAos = isTimeAos)
CardIcon(onClick = selectNext, iconId = R.drawable.ic_arrow)
TopBar {
IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
TimerRow(timeString = timeString, isTimeAos = isTimeAos)
IconCard(onClick = selectNext, iconId = R.drawable.ic_arrow)
}
NextPassRow(pass = uiState.value.orbitalPass)
ElevatedCard(modifier = Modifier.weight(1f)) {
@@ -32,7 +32,7 @@ import com.rtbishop.look4sat.domain.utility.clipLon
import com.rtbishop.look4sat.domain.utility.positionToQth
import com.rtbishop.look4sat.domain.utility.toDegrees
import com.rtbishop.look4sat.domain.utility.toTimerString
import com.rtbishop.look4sat.presentation.components.getDefaultPass
import com.rtbishop.look4sat.presentation.common.getDefaultPass
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
@@ -51,7 +51,7 @@ import androidx.compose.ui.unit.sp
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.LocalSpacing
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.components.SharedDialog
import com.rtbishop.look4sat.presentation.common.SharedDialog
private val allModes = listOf(
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",
@@ -44,11 +44,12 @@ 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.Screen
import com.rtbishop.look4sat.presentation.components.CardIcon
import com.rtbishop.look4sat.presentation.components.InfoDialog
import com.rtbishop.look4sat.presentation.components.NextPassRow
import com.rtbishop.look4sat.presentation.components.TimerBar
import com.rtbishop.look4sat.presentation.components.TimerRow
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.TimerRow
import com.rtbishop.look4sat.presentation.common.TopBar
import com.rtbishop.look4sat.presentation.common.isVerticalLayout
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@@ -84,19 +85,28 @@ private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> U
}
if (uiState.shouldSeeWelcome) {
InfoDialog(
stringResource(R.string.passes_welcome_title),
stringResource(R.string.passes_welcome_message)
title = stringResource(R.string.passes_welcome_title),
text = stringResource(R.string.passes_welcome_message)
) {
uiState.takeAction(PassesAction.DismissWelcome)
}
}
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
TimerRow {
CardIcon(onClick = { showPassesDialog() }, iconId = R.drawable.ic_filter)
TimerBar(timeString = uiState.nextTime, isTimeAos = uiState.isNextTimeAos)
CardIcon(onClick = { showRadiosDialog() }, iconId = R.drawable.ic_satellite)
if (isVerticalLayout()) {
TopBar {
IconCard(onClick = { showPassesDialog() }, iconId = R.drawable.ic_filter)
TimerRow(timeString = uiState.nextTime, isTimeAos = uiState.isNextTimeAos)
IconCard(onClick = { showRadiosDialog() }, iconId = R.drawable.ic_satellite)
}
NextPassRow(pass = uiState.nextPass)
} else {
TopBar {
IconCard(onClick = { showPassesDialog() }, iconId = 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)
}
}
NextPassRow(pass = uiState.nextPass)
PassesList(uiState.isRefreshing, uiState.itemsList, navigateToRadar, refreshPasses)
}
}
@@ -28,7 +28,7 @@ import com.rtbishop.look4sat.domain.predict.OrbitalPass
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
import com.rtbishop.look4sat.domain.utility.toTimerString
import com.rtbishop.look4sat.presentation.components.getDefaultPass
import com.rtbishop.look4sat.presentation.common.getDefaultPass
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
@@ -53,11 +53,11 @@ import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.utility.toDegrees
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.Screen
import com.rtbishop.look4sat.presentation.components.CardIcon
import com.rtbishop.look4sat.presentation.components.NextPassRow
import com.rtbishop.look4sat.presentation.components.TimerBar
import com.rtbishop.look4sat.presentation.components.TimerRow
import com.rtbishop.look4sat.presentation.components.getDefaultPass
import com.rtbishop.look4sat.presentation.common.IconCard
import com.rtbishop.look4sat.presentation.common.NextPassRow
import com.rtbishop.look4sat.presentation.common.TimerRow
import com.rtbishop.look4sat.presentation.common.TopBar
import com.rtbishop.look4sat.presentation.common.getDefaultPass
fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) {
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
@@ -87,10 +87,10 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
modifier = paddingMod.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
TimerRow {
CardIcon(onClick = navigateBack, iconId = R.drawable.ic_back)
TimerBar(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
TopBar {
IconCard(onClick = navigateBack, iconId = R.drawable.ic_back)
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
IconCard(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
}
NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.aspectRatio(1f)) {
@@ -23,7 +23,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.components.SharedDialog
import com.rtbishop.look4sat.presentation.common.SharedDialog
@Preview(showBackground = true)
@Composable
@@ -46,9 +46,9 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.Screen
import com.rtbishop.look4sat.presentation.components.CardIcon
import com.rtbishop.look4sat.presentation.components.CardLoadingIndicator
import com.rtbishop.look4sat.presentation.components.InfoDialog
import com.rtbishop.look4sat.presentation.common.IconCard
import com.rtbishop.look4sat.presentation.common.CardLoadingIndicator
import com.rtbishop.look4sat.presentation.common.InfoDialog
fun NavGraphBuilder.satellitesDestination(navigateToPasses: () -> Unit) {
composable(Screen.Satellites.route) {
@@ -178,8 +178,8 @@ private fun MiddleBar(
) {
Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(48.dp)) {
TypeCard(types = types, { navigate() }, modifier = Modifier.weight(1f))
CardIcon(onClick = { uncheck() }, iconId = R.drawable.ic_check_off)
CardIcon(onClick = { check() }, iconId = R.drawable.ic_check_on)
IconCard(onClick = { uncheck() }, iconId = R.drawable.ic_check_off)
IconCard(onClick = { check() }, iconId = R.drawable.ic_check_on)
}
}
@@ -9,7 +9,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.components.SharedDialog
import com.rtbishop.look4sat.presentation.common.SharedDialog
@Preview(showBackground = true)
@Composable
@@ -43,7 +43,7 @@ import com.rtbishop.look4sat.domain.model.OtherSettings
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.Screen
import com.rtbishop.look4sat.presentation.components.CardButton
import com.rtbishop.look4sat.presentation.common.CardButton
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
+7 -5
View File
@@ -7,10 +7,10 @@ androidx-core-ktx = "1.16.0"
androidx-core-splashscreen = "1.0.1"
androidx-room = "2.7.2"
compose-bom = "2025.06.01"
compose-bom = "2025.07.00"
compose-activity = "1.10.1"
compose-lifecycle = "2.9.1"
compose-navigation = "2.9.1"
compose-lifecycle = "2.9.2"
compose-navigation = "2.9.2"
other-coroutines = "1.10.2"
other-json = "20250517"
@@ -18,7 +18,7 @@ other-okhttp = "5.1.0"
other-osmdroid = "6.1.20"
test-junit4 = "4.13.2"
test-mockk = "1.14.4"
test-mockk = "1.14.5"
androidTest-junit = "1.2.1"
androidTest-espresso = "3.6.1"
@@ -33,6 +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-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" }
@@ -67,7 +68,8 @@ 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-navigation", "compose-viewmodel"
"compose-lifecycle", "compose-material3", "compose-material3-adaptive",
"compose-navigation", "compose-viewmodel"
]
composeDebug = ["compose-debug-manifest", "compose-debug-tooling"]
unitTest = ["test-coroutines", "test-junit4", "test-mockk"]