From ad89830c5165febad60865c446a8f65a6f436946 Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sat, 26 Jul 2025 14:10:01 +0100 Subject: [PATCH] Set up more screens with portrait/landscape TopBar --- .../look4sat/presentation/MainScreen.kt | 105 +++++------- .../look4sat/presentation/MainTheme.kt | 2 +- .../presentation/common/Components.kt | 11 ++ .../look4sat/presentation/map/MapScreen.kt | 23 ++- .../presentation/passes/PassesScreen.kt | 149 +++--------------- .../presentation/radar/RadarScreen.kt | 114 ++++++++++++-- .../satellites/SatellitesScreen.kt | 82 +++++----- .../presentation/settings/SettingsScreen.kt | 31 ++-- app/src/main/res/values-ru/strings.xml | 22 +-- app/src/main/res/values-si/strings.xml | 22 +-- app/src/main/res/values-zh-rCN/strings.xml | 24 +-- app/src/main/res/values/strings.xml | 22 +-- gradle/libs.versions.toml | 4 +- 13 files changed, 302 insertions(+), 309 deletions(-) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt index 6d95dc10..aa85b1f0 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt @@ -1,94 +1,75 @@ package com.rtbishop.look4sat.presentation -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.Scaffold +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.Modifier import androidx.compose.ui.res.painterResource -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost -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.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(var title: String, var icon: Int, var route: String) { - data object Main : Screen("Main", R.drawable.ic_sputnik, "main") - data object Radar : Screen("Radar", R.drawable.ic_sputnik, "radar") +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") } -@Composable -fun MainScreen() { - val outerNavController: NavHostController = rememberNavController() - val navigateToRadar = { catNum: Int, aosTime: Long -> - val routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}" - outerNavController.navigate(routeWithParams) - } - NavHost(navController = outerNavController, startDestination = Screen.Main.route) { - mainDestination(navigateToRadar) - radarDestination { outerNavController.navigateUp() } - } -} - -private fun NavGraphBuilder.mainDestination(navigateToRadar: (Int, Long) -> Unit) { - composable(Screen.Main.route) { NavBarScreen(navigateToRadar) } -} +private val startDestination = Screen.Passes.route @Composable -private fun NavBarScreen(navigateToRadar: (Int, Long) -> Unit) { - val innerNavController: NavHostController = rememberNavController() - val navigateToPasses = { innerNavController.navigate(Screen.Passes.route) } - Scaffold(bottomBar = { MainNavBar(innerNavController) }) { innerPadding -> - NavHost( - navController = innerNavController, - startDestination = Screen.Passes.route, - modifier = Modifier.padding(innerPadding) - ) { - satellitesDestination(navigateToPasses) - passesDestination(navigateToRadar) - mapDestination() - settingsDestination() - } - } -} - -@Composable -private fun MainNavBar(navController: NavController) { - val items = listOf(Screen.Satellites, Screen.Passes, Screen.Map, Screen.Settings) +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 - NavigationBar { - items.forEach { item -> - NavigationBarItem( - icon = { Icon(painterResource(item.icon), item.title) }, - label = { Text(item.title) }, - selected = item.route == destinationRoute, - onClick = { - if (item.route == destinationRoute) { - // reselecting the same tab - } else { - navController.navigate(item.route) { - popUpTo(Screen.Passes.route) { saveState = false } + 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 routeWithParams = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}" + navController.navigate(routeWithParams) + } + NavHost(navController = navController, startDestination = startDestination) { + satellitesDestination { navController.navigateUp() } + passesDestination(navigateToRadar) + radarDestination { navController.navigateUp() } + mapDestination() + settingsDestination() } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainTheme.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainTheme.kt index 0caa88d5..b49b71f9 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainTheme.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainTheme.kt @@ -109,7 +109,7 @@ private val darkScheme = darkColorScheme( // outlineVariant = Color(0xFF121212), scrim = Color(0xFF000000), // surfaceBright = Color(0xFF121212), - surfaceContainer = Color(0xFF242424), // navBar background + surfaceContainer = Color(0xFF121212), // navBar background // surfaceContainerHigh = Color(0xFF121212), surfaceContainerHighest = Color(0xFF242424), // filled card background surfaceContainerLow = Color(0xFF242424), // elevated card background diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt index 8f449d9b..9215fb96 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height 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.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator @@ -283,3 +284,13 @@ fun isVerticalLayout(): Boolean { val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT } + +@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) + } +} diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt index fc3c7356..a1750e70 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt @@ -57,6 +57,8 @@ import org.osmdroid.views.overlay.Polyline import androidx.core.graphics.toColorInt import androidx.core.graphics.createBitmap import androidx.core.graphics.drawable.toDrawable +import com.rtbishop.look4sat.presentation.common.isVerticalLayout +import com.rtbishop.look4sat.presentation.common.layoutPadding private val minLat = MapView.getTileSystem().minLatitude private val maxLat = MapView.getTileSystem().maxLatitude @@ -98,13 +100,22 @@ private fun MapScreen(uiState: State, mapView: MapView) { val timeString = uiState.value.mapData?.aosTime ?: "00:00:00" val isTimeAos = uiState.value.mapData?.isTimeAos ?: true val osmInfo = "© OpenStreetMap contributors" - Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { - TopBar { - IconCard(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod) - TimerRow(timeString = timeString, isTimeAos = isTimeAos) - IconCard(onClick = selectNext, iconId = R.drawable.ic_arrow) + Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) { + if (isVerticalLayout()) { + 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) + } else { + TopBar { + IconCard(onClick = selectPrev, iconId = 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) + } } - NextPassRow(pass = uiState.value.orbitalPass) ElevatedCard(modifier = Modifier.weight(1f)) { Box(contentAlignment = Alignment.BottomCenter) { LaunchedEffect(uiState.value.track) { diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt index 3fa96c1e..1c552a02 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt @@ -11,8 +11,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -50,6 +51,7 @@ 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 com.rtbishop.look4sat.presentation.common.layoutPadding import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -91,7 +93,7 @@ private fun PassesScreen(uiState: PassesState, navigateToRadar: (Int, Long) -> U uiState.takeAction(PassesAction.DismissWelcome) } } - Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { + Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) { if (isVerticalLayout()) { TopBar { IconCard(onClick = { showPassesDialog() }, iconId = R.drawable.ic_filter) @@ -135,21 +137,16 @@ private fun PassesList( ) } ) { - LazyColumn(modifier = Modifier.fillMaxSize()) { + LazyVerticalGrid( + columns = GridCells.Adaptive(320.dp), + modifier = Modifier.fillMaxSize() + ) { items(items = passes, key = { item -> item.catNum + item.aosTime }) { pass -> - if (pass.isDeepSpace) { - DeepSpacePass( - pass = pass, - navigateToRadar = navigateToRadar, - modifier = Modifier.animateItem() - ) - } else { - NearEarthPass( - pass = pass, - navigateToRadar = navigateToRadar, - modifier = Modifier.animateItem() - ) - } + NearEarthPass( + pass = pass, + navigateToRadar = navigateToRadar, + modifier = Modifier.animateItem() + ) } } } @@ -159,114 +156,16 @@ private fun PassesList( @Preview(showBackground = true) @Composable private fun DeepSpacePassPreview() { - val data = OrbitalData( - "Satellite", 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0 - ) + val data = OrbitalData("Satellite", 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0) val satellite = DeepSpaceObject(data) val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f) - MainTheme { DeepSpacePass(pass = pass, { _, _ -> }) } -} - -@Composable -private fun DeepSpacePass( - pass: OrbitalPass, - navigateToRadar: (Int, Long) -> Unit, - modifier: Modifier = Modifier -) { - val passSatId = stringResource(id = R.string.pass_satId, pass.catNum) - Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) { - Surface(modifier = Modifier - .padding(bottom = 2.dp) - .clickable { navigateToRadar(pass.catNum, pass.aosTime) }) { - Column( - verticalArrangement = Arrangement.spacedBy(1.dp), - modifier = Modifier - .background(color = MaterialTheme.colorScheme.surface) - .padding(horizontal = 6.dp, vertical = 4.dp) - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = "$passSatId - ", - color = MaterialTheme.colorScheme.primary - ) - Text( - text = pass.name, - modifier = Modifier - .weight(1f) - .padding(end = 6.dp), - fontWeight = FontWeight.Medium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Icon( - painter = painterResource(id = R.drawable.ic_elevation), - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - modifier = Modifier.size(16.dp) - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = "${pass.maxElevation}°", - color = MaterialTheme.colorScheme.primary - ) - } - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth() - ) { - Row( - modifier = Modifier.weight(1f), - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = "DeepSpace", - fontSize = 15.sp - ) - } - Row( - modifier = Modifier.weight(1f), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - painter = painterResource(id = R.drawable.ic_altitude), - contentDescription = null, - modifier = Modifier.size(16.dp) - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = "${pass.altitude} km", - fontSize = 15.sp - ) - } - Row( - modifier = Modifier.weight(1f), - horizontalArrangement = Arrangement.End, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = stringResource( - id = R.string.pass_aosLos, - pass.aosAzimuth.toInt(), - pass.losAzimuth.toInt() - ), - fontSize = 15.sp - ) - } - } - } - } - } + MainTheme { NearEarthPass(pass = pass, { _, _ -> }) } } @Preview(showBackground = true) @Composable private fun NearEarthPassPreview() { - val data = OrbitalData( - "Satellite", 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0 - ) + val data = OrbitalData("Satellite", 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0) val satellite = NearEarthObject(data) val pass = OrbitalPass(1L, 180.0, 10L, 360.0, 36650, 45.0, satellite, 0.5f) MainTheme { NearEarthPass(pass = pass, { _, _ -> }) } @@ -281,7 +180,7 @@ private fun NearEarthPass( val passSatId = stringResource(id = R.string.pass_satId, pass.catNum) Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) { Surface(modifier = Modifier - .padding(bottom = 2.dp) + .padding(bottom = 2.dp, start = 1.dp, end = 1.dp) .clickable { navigateToRadar(pass.catNum, pass.aosTime) }) { Column( verticalArrangement = Arrangement.spacedBy(1.dp), @@ -325,10 +224,14 @@ private fun NearEarthPass( horizontalArrangement = Arrangement.Start, verticalAlignment = Alignment.CenterVertically ) { - Text( - text = sdfDate.format(Date(pass.aosTime)), - fontSize = 15.sp - ) + if (pass.isDeepSpace) { + Text(text = "DeepSpace", fontSize = 15.sp) + } else { + Text( + text = sdfDate.format(Date(pass.aosTime)), + fontSize = 15.sp + ) + } } Row( modifier = Modifier.weight(1f), diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt index 85373a35..1f57e29f 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material3.ElevatedCard import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -58,8 +57,10 @@ 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 +import com.rtbishop.look4sat.presentation.common.isVerticalLayout +import com.rtbishop.look4sat.presentation.common.layoutPadding -fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) { +fun NavGraphBuilder.radarDestination(navigateUp: () -> Unit) { val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}" val radarArgs = listOf( navArgument("catNum") { defaultValue = 0 }, @@ -68,31 +69,36 @@ fun NavGraphBuilder.radarDestination(navigateBack: () -> Unit) { composable(radarRoute, radarArgs) { val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory) val uiState = viewModel.uiState.collectAsStateWithLifecycle().value - RadarScreen(uiState, navigateBack) + RadarScreen(uiState, navigateUp) } } @Composable -private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) { +private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) { // BluetoothCIV.init(LocalContext.current) - val addToCalendar: () -> Unit = { uiState.currentPass?.let { pass -> uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime)) } } - Scaffold { innerPadding -> - val paddingMod = Modifier.padding(innerPadding) - Column( - modifier = paddingMod.padding(6.dp), - verticalArrangement = Arrangement.spacedBy(6.dp) - ) { + val upcomingPass = uiState.currentPass ?: getDefaultPass() + Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) { + if (isVerticalLayout()) { TopBar { - IconCard(onClick = navigateBack, iconId = R.drawable.ic_back) + IconCard(onClick = navigateUp, 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()) + NextPassRow(pass = upcomingPass) + } else { + TopBar { + IconCard(onClick = navigateUp, iconId = 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) + } + } + if(isVerticalLayout()) { Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.aspectRatio(1f)) { uiState.orbitalPos?.let { position -> ElevatedCard { @@ -172,6 +178,88 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) { } } } + } else { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.weight(1f)) { + uiState.orbitalPos?.let { position -> + ElevatedCard { + RadarViewCompose( + item = position, + items = uiState.satTrack, + azimElev = uiState.orientationValues, + shouldShowSweep = uiState.shouldShowSweep, + shouldUseCompass = false + ) + } + Column( + verticalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 6.dp, vertical = 4.dp) + ) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + RadarTextTop( + position.azimuth, + stringResource(R.string.radar_az_text), + true + ) + RadarTextTop( + position.elevation, + stringResource(R.string.radar_el_text), + false + ) + } + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + RadarTextBottom( + position.altitude, + stringResource(R.string.radar_alt_text), + true + ) + RadarTextBottom( + position.distance, + stringResource(R.string.radar_dist_text), + false + ) + } + } + } + } + ElevatedCard(modifier = Modifier.fillMaxSize().weight(1f)) { + if (uiState.transmitters.isEmpty()) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Icon( + painter = painterResource(R.drawable.ic_satellite), + contentDescription = null, + modifier = Modifier.size(64.dp) + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = "This satellite doesn't have any known transcievers...", + textAlign = TextAlign.Center, + fontSize = 18.sp, + modifier = Modifier.padding(16.dp) + ) + } + } else { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + TransmittersList(transmitters = uiState.transmitters) + if (uiState.orbitalPos?.eclipsed == true) { + EclipsedIndicator() + } + } + } + } + } } } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satellites/SatellitesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satellites/SatellitesScreen.kt index cd039948..0e9575b9 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satellites/SatellitesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satellites/SatellitesScreen.kt @@ -14,6 +14,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.items import androidx.compose.foundation.text.BasicTextField import androidx.compose.material3.CardDefaults @@ -46,23 +49,25 @@ 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.common.IconCard import com.rtbishop.look4sat.presentation.common.CardLoadingIndicator +import com.rtbishop.look4sat.presentation.common.IconCard import com.rtbishop.look4sat.presentation.common.InfoDialog +import com.rtbishop.look4sat.presentation.common.TopBar +import com.rtbishop.look4sat.presentation.common.isVerticalLayout +import com.rtbishop.look4sat.presentation.common.layoutPadding -fun NavGraphBuilder.satellitesDestination(navigateToPasses: () -> Unit) { +fun NavGraphBuilder.satellitesDestination(navigateUp: () -> Unit) { composable(Screen.Satellites.route) { val viewModel = viewModel( - modelClass = SatellitesViewModel::class.java, - factory = SatellitesViewModel.Factory + modelClass = SatellitesViewModel::class.java, factory = SatellitesViewModel.Factory ) val uiState = viewModel.uiState.collectAsStateWithLifecycle().value - SatellitesScreen(uiState, navigateToPasses) + SatellitesScreen(uiState, navigateUp) } } @Composable -private fun SatellitesScreen(uiState: SatellitesState, navigateToPasses: () -> Unit) { +private fun SatellitesScreen(uiState: SatellitesState, navigateUp: () -> Unit) { val toggleDialog = { uiState.takeAction(SatellitesAction.ToggleTypesDialog) } if (uiState.isDialogShown) { MultiTypesDialog(allTypes = uiState.typesList, types = uiState.currentTypes, toggleDialog) { @@ -79,14 +84,28 @@ private fun SatellitesScreen(uiState: SatellitesState, navigateToPasses: () -> U } val unselectAll = { uiState.takeAction(SatellitesAction.UnselectAll) } val selectAll = { uiState.takeAction(SatellitesAction.SelectAll) } - Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { - TopBar(setQuery = { newQuery: String -> - uiState.takeAction(SatellitesAction.SearchFor(newQuery)) - }, saveSelection = { - uiState.takeAction(SatellitesAction.SaveSelection) - navigateToPasses() - }) - MiddleBar(uiState.currentTypes, { toggleDialog() }, { unselectAll() }, { selectAll() }) + val setQuery = { newQuery: String -> uiState.takeAction(SatellitesAction.SearchFor(newQuery)) } + val saveSelection = { uiState.takeAction(SatellitesAction.SaveSelection).also { navigateUp() } } + Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) { + if (isVerticalLayout()) { + TopBar { + TypeCard(types = uiState.currentTypes, toggleDialog, modifier = Modifier.weight(1f)) + SaveButton(saveSelection = saveSelection, modifier = Modifier.height(48.dp)) + } + 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) + } + } 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) + } + } ElevatedCard(modifier = Modifier.fillMaxSize()) { if (uiState.isLoading) { CardLoadingIndicator() @@ -99,18 +118,6 @@ private fun SatellitesScreen(uiState: SatellitesState, navigateToPasses: () -> U } } -@Preview(showBackground = true) -@Composable -private fun TopBarPreview() = MainTheme { TopBar({}, {}) } - -@Composable -private fun TopBar(setQuery: (String) -> Unit, saveSelection: () -> Unit) { - Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(48.dp)) { - SearchBar(setQuery = { setQuery(it) }, modifier = Modifier.weight(1f)) - SaveButton(saveSelection = { saveSelection() }, modifier = Modifier.height(48.dp)) - } -} - @Composable private fun SearchBar(setQuery: (String) -> Unit, modifier: Modifier = Modifier) { val currentQuery = rememberSaveable { mutableStateOf("") } @@ -168,26 +175,12 @@ private fun SaveButton(saveSelection: () -> Unit, modifier: Modifier = Modifier) } } -@Preview(showBackground = true) -@Composable -private fun MiddleBarPreview() = MainTheme { MiddleBar(listOf("Amateur"), {}, {}, {}) } - -@Composable -private fun MiddleBar( - types: List, navigate: () -> Unit, uncheck: () -> Unit, check: () -> Unit -) { - Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(48.dp)) { - TypeCard(types = types, { navigate() }, modifier = Modifier.weight(1f)) - IconCard(onClick = { uncheck() }, iconId = R.drawable.ic_check_off) - IconCard(onClick = { check() }, iconId = R.drawable.ic_check_on) - } -} - @Composable private fun TypeCard(types: List, onClick: () -> Unit, modifier: Modifier = Modifier) { val typesText = if (types.isEmpty()) "All" else types.joinToString(", ") ElevatedCard(modifier = modifier) { - Row(horizontalArrangement = Arrangement.spacedBy(12.dp), + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically, modifier = Modifier .height(48.dp) @@ -221,7 +214,8 @@ private fun SatellitePreview() { @Composable private fun Satellite(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: Modifier) { val passSatId = stringResource(id = R.string.pass_satId, item.catnum) - Surface(color = MaterialTheme.colorScheme.background, + Surface( + color = MaterialTheme.colorScheme.background, modifier = modifier.clickable { onSelected(item.catnum, item.isSelected) }) { Surface(modifier = Modifier.padding(bottom = 1.dp)) { Row( @@ -263,7 +257,7 @@ private fun SatellitesPreview() { @Composable fun SatellitesCard(items: List, onSelected: (Int, Boolean) -> Unit) { - LazyColumn { + LazyVerticalGrid(columns = GridCells.Adaptive(320.dp)) { items(items = items, key = { item -> item.catnum }) { entry -> Satellite(entry, onSelected, Modifier.animateItem()) } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt index 7f816b6f..6d4b7b2a 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt @@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.material3.ElevatedCard import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator @@ -44,6 +46,7 @@ import com.rtbishop.look4sat.domain.predict.GeoPos import com.rtbishop.look4sat.presentation.MainTheme import com.rtbishop.look4sat.presentation.Screen import com.rtbishop.look4sat.presentation.common.CardButton +import com.rtbishop.look4sat.presentation.common.layoutPadding import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -130,8 +133,10 @@ private fun SettingsScreen(uiState: SettingsState) { } // Screen setup - LazyColumn( - modifier = Modifier.padding(6.dp), + LazyVerticalGrid( + columns = GridCells.Adaptive(320.dp), + modifier = Modifier.layoutPadding(), + horizontalArrangement = Arrangement.spacedBy(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp) ) { item { CardAbout(uiState.appVersionName, uiState.sendSystemAction) } @@ -461,17 +466,17 @@ private fun OtherCard( Text(text = stringResource(id = R.string.other_switch_sensors)) Switch(checked = settings.stateOfSensors, onCheckedChange = { toggleSensor(it) }) } - Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - Text(text = stringResource(id = R.string.other_switch_light_theme)) - Switch( - checked = settings.stateOfLightTheme, - onCheckedChange = { toggleLightTheme(it) } - ) - } +// Row( +// horizontalArrangement = Arrangement.SpaceBetween, +// verticalAlignment = Alignment.CenterVertically, +// modifier = Modifier.fillMaxWidth() +// ) { +// Text(text = stringResource(id = R.string.other_switch_light_theme)) +// Switch( +// checked = settings.stateOfLightTheme, +// onCheckedChange = { toggleLightTheme(it) } +// ) +// } } } } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d460ccb2..b0efc137 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -158,24 +158,24 @@ Формат данных Нет разрешения использовать bluetooth - • Показывать время по UTC - • Обновлять данные автоматически - • Показывать анимацию радара - • Использовать сенсоры устройства - • Использовать светлую тему + Показывать время по UTC + Обновлять данные автоматически + Показывать анимацию радара + Использовать сенсоры устройства + Использовать светлую тему Я хотел бы сказать спасибо: - • Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой! - \n• David A. B. Johnson и + Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой! + \nDavid A. B. Johnson и Dave Moten за работу над predict4java и доступ к ней по GNU GPLv2 - \n• Alexandru Csete за его программу + \nAlexandru Csete за его программу Gpredict, которая послужила для меня вдохновением. - \n• Доктору T.S. Kelso за его сайт + \nДоктору T.S. Kelso за его сайт Celestrak и доступ к файлам орбит Two-Line Element. - \n• Libre Space Foundation за проект + \nLibre Space Foundation за проект SatNOGS, API и базу данных с информацией о спутниках. - \n• DownloadAstro + \nDownloadAstro за интерес к приложению и публикацию интервью на сайте. Это ПО поставляется без гарантий. diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 0dd1d7b9..9b4b3f62 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -158,22 +158,22 @@ දත්ත අකාරය Bluetooth අවසර පරික්‍ෂා ක° - • පසුකර වේලාවන් UTC මගින් - • ස්වයං දත්ත යාවත්කාලීනය - • radar sweep සජීවිකරණය සබල කරන්න - • Radar දර්ශනය කරකැවීමට සංවේදක භාවිතා කරන්න - • සැහැල්ලු තේමාව භාවිතා කරන්න + පසුකර වේලාවන් UTC මගින් + ස්වයං දත්ත යාවත්කාලීනය + radar sweep සජීවිකරණය සබල කරන්න + Radar දර්ශනය කරකැවීමට සංවේදක භාවිතා කරන්න + සැහැල්ලු තේමාව භාවිතා කරන්න මම ස්තුති කිරීමට කැමති: - • බාගෙන භාවිතා කළ ඔබට! \nඅහස සදා ඔබ වෙනුවෙන් පැහැදිලියි! - \n• David A. B. Johnson + බාගෙන භාවිතා කළ ඔබට! \nඅහස සදා ඔබ වෙනුවෙන් පැහැදිලියි! + \nDavid A. B. Johnson සහ Dave Motenඔවුනට ප්‍රයෝජනවත් predict4java පුස්තකාලය නිර්මාණයට. - \n• සහ Alexandru Csete සඳහා Gpredict නිර්මාණයට සහ + \nසහ Alexandru Csete සඳහා Gpredict නිර්මාණයට සහ Look4Sat නිර්මාණය⁣ට පෙළඹවීමට. - \n• Dr T.S. Kelsoට TLE දත්ත සැපයීමට, ඔහුගේ නඩත්තුවෙන් ක්‍රියාත්මක WEB අඩවිට ප්‍රවේශය සැපයීම වෙනුවෙන් + \nDr T.S. Kelsoට TLE දත්ත සැපයීමට, ඔහුගේ නඩත්තුවෙන් ක්‍රියාත්මක WEB අඩවිට ප්‍රවේශය සැපයීම වෙනුවෙන් (Celestrak). - \n• Libre Space Foundationට ඔවුන්ගේ + \nLibre Space Foundationට ඔවුන්ගේ SatNOGSWeb අඩවියට API හා DB විශාල දත්ත ප්‍රමාණයක් ලබාදීම සඳහා. - \n• DownloadAstro කණ්ඩායමට යෙදුම පිළිබඳ ඔවුන්ගේ උනන්දුව සහ කැපවීම ලැබිම සම්බන්ධයෙන්. + \nDownloadAstro කණ්ඩායමට යෙදුම පිළිබඳ ඔවුන්ගේ උනන්දුව සහ කැපවීම ලැබිම සම්බන්ධයෙන්. මෘදුකාංගය වගකීමක් සමග නොලැබේ diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e92aabc4..deb32e76 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -158,25 +158,25 @@ 数据格式 请检查蓝牙权限 - • 以 UTC 时间显示 - • 启用卫星数据自动更新 - • 启用雷达扫描动画 - • 使用传感器旋转雷达视图 - • 使用旧版配色方案 + 以 UTC 时间显示 + 启用卫星数据自动更新 + 启用雷达扫描动画 + 使用传感器旋转雷达视图 + 使用旧版配色方案 我要感谢: - • 感谢下载和使用 Look4Sat!愿你的天空永远晴朗! - \n• 简体中文版翻译由 BA7LWN 于 2022.5.10 创建。 - \n• David A. B. Johnson + 感谢下载和使用 Look4Sat!愿你的天空永远晴朗! + \n简体中文版翻译由 BA7LWN 于 2022.5.10 创建。 + \nDavid A. B. Johnson and Dave Moten for creating predict4java lib under the GNU GPLv2. - \n• Alexandru Csete for creating Gpredict + \nAlexandru Csete for creating Gpredict satellite tracker that inspired the creation of Look4Sat. - \n• Dr T.S. Kelso for maintaining his + \nDr T.S. Kelso for maintaining his Celestrak website that provides access to the TLE data. - \n• Libre Space Foundation for their + \nLibre Space Foundation for their SatNOGS API and DB providing a huge amount of satellite data. - \n• DownloadAstro + \nDownloadAstro team for their interest to the app and the interview published. 该应用程序不提供任何保修. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7af8e7ac..75a8ff0d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -173,23 +173,23 @@ Data format Check your bluetooth permission - • Show pass time in UTC - • Enable automatic data update - • Enable radar sweep animation - • Use sensors to rotate radar view - • Use light theme + Show pass time in UTC + Enable automatic data update + Enable radar sweep animation + Use sensors to rotate radar view + Use light theme I would like to say thanks to: - • You for downloading and using Look4Sat! \nMay your sky always be clear! - \n• David A. B. Johnson + You for downloading and using Look4Sat! \nMay your sky always be clear! + \nDavid A. B. Johnson and Dave Moten for creating a super useful predict4java library. - \n• Alexandru Csete for creating Gpredict + \nAlexandru Csete for creating Gpredict tracker that inspired the creation of Look4Sat. - \n• Dr T.S. Kelso for maintaining his + \nDr T.S. Kelso for maintaining his Celestrak website that provides access to the TLE data. - \n• Libre Space Foundation for their + \nLibre Space Foundation for their SatNOGS API and DB providing a huge amount of data. - \n• DownloadAstro + \nDownloadAstro team for their interest to the app and the interview published. The app comes with no warranty. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2205c403..e5580d71 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive" } +compose-material3-navigation = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite" } 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-adaptive", + "compose-lifecycle", "compose-material3", "compose-material3-navigation", "compose-navigation", "compose-viewmodel" ] composeDebug = ["compose-debug-manifest", "compose-debug-tooling"]