Implemented landscape support for MapScreen

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:47 +00:00
1 parent 5a0ff019e9
commit 609c83ed34
13 files changed
+87 -48

No files matched your search

@@ -113,7 +113,7 @@ private val darkScheme = darkColorScheme(
// surfaceContainerHigh = Color(0xFF121212), // surfaceContainerHigh = Color(0xFF121212),
surfaceContainerHighest = Color(0xFF242424), // filled card background surfaceContainerHighest = Color(0xFF242424), // filled card background
surfaceContainerLow = Color(0xFF202020), // elevated card background surfaceContainerLow = Color(0xFF202020), // elevated card background
// surfaceContainerLowest = Color(0xFF121212), surfaceContainerLowest = Color(0xCC121212),
// surfaceDim = Color(0xFF121212), // surfaceDim = Color(0xFF121212),
) )
@@ -1,6 +1,7 @@
package com.rtbishop.look4sat.presentation.common package com.rtbishop.look4sat.presentation.common
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -117,7 +118,8 @@ fun NextPassRow(pass: OrbitalPass, modifier: Modifier = Modifier) {
text = pass.name, text = pass.name,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.padding(end = 6.dp), .padding(end = 6.dp)
.basicMarquee(),
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@@ -11,8 +11,12 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -26,7 +30,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
@@ -99,7 +102,11 @@ private fun MapScreen(uiState: State<MapState>, mapView: MapView) {
val rotateMod = Modifier.rotate(180f) val rotateMod = Modifier.rotate(180f)
val timeString = uiState.value.mapData?.aosTime ?: "00:00:00" val timeString = uiState.value.mapData?.aosTime ?: "00:00:00"
val isTimeAos = uiState.value.mapData?.isTimeAos ?: true val isTimeAos = uiState.value.mapData?.isTimeAos ?: true
val osmInfo = "© OpenStreetMap contributors" LaunchedEffect(uiState.value.track) {
val latitude = uiState.value.track?.get(0)?.get(0)?.latitude ?: 0.0
val longitude = uiState.value.track?.get(0)?.get(0)?.longitude ?: 0.0
mapView.controller.animateTo(GeoPoint(latitude, longitude))
}
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) { Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
if (isVerticalLayout()) { if (isVerticalLayout()) {
TopBar { TopBar {
@@ -118,50 +125,80 @@ private fun MapScreen(uiState: State<MapState>, mapView: MapView) {
} }
ElevatedCard(modifier = Modifier.weight(1f)) { ElevatedCard(modifier = Modifier.weight(1f)) {
Box(contentAlignment = Alignment.BottomCenter) { Box(contentAlignment = Alignment.BottomCenter) {
LaunchedEffect(uiState.value.track) {
val latitude = uiState.value.track?.get(0)?.get(0)?.latitude ?: 0.0
val longitude = uiState.value.track?.get(0)?.get(0)?.longitude ?: 0.0
mapView.controller.animateTo(GeoPoint(latitude, longitude))
}
AndroidView({ mapView }) { mapView -> AndroidView({ mapView }) { mapView ->
uiState.value.stationPosition?.let { setStationPosition(it, mapView) } uiState.value.stationPosition?.let { setStationPosition(it, mapView) }
uiState.value.positions?.let { setPositions(it, mapView, onItemClick) } uiState.value.positions?.let { setPositions(it, mapView, onItemClick) }
uiState.value.track?.let { setSatelliteTrack(it, mapView) } uiState.value.track?.let { setSatelliteTrack(it, mapView) }
uiState.value.footprint?.let { setFootprint(it, mapView) } uiState.value.footprint?.let { setFootprint(it, mapView) }
} }
Text(text = osmInfo, textAlign = TextAlign.Center, fontSize = 14.sp) uiState.value.mapData?.let { mapData ->
} if (isVerticalLayout()) MapDataCard(mapData) else MapDataCards(mapData)
} }
uiState.value.mapData?.let { mapData ->
ElevatedCard {
MapDataCard(mapData)
} }
} }
} }
} }
@Composable @Composable
private fun MapDataCard(mapData: MapData) { private fun MapDataCard(data: MapData) {
Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) { val textColor = MaterialTheme.colorScheme.primary
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { val cardColors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest)
Text(text = stringResource(R.string.map_period, mapData.period), color = MaterialTheme.colorScheme.primary) Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceBetween) {
Text(text = stringResource(R.string.map_phase, mapData.phase), color = MaterialTheme.colorScheme.primary) Text(text = "© OpenStreetMap contributors", fontSize = 14.sp)
Card(colors = cardColors) {
Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) {
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
Text(text = stringResource(R.string.map_azimuth, data.azimuth))
Text(text = stringResource(R.string.map_elevation, data.elevation))
}
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
Text(text = stringResource(R.string.map_altitude, data.altitude), color = textColor)
Text(text = stringResource(R.string.map_distance, data.range), color = textColor)
}
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
Text(text = stringResource(R.string.map_latitude, data.osmPos.latitude))
Text(text = stringResource(R.string.map_longitude, data.osmPos.longitude))
}
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
Text(text = stringResource(R.string.map_qth, data.qthLoc), color = textColor)
Text(text = stringResource(R.string.map_phase, data.phase), color = textColor)
}
}
} }
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { }
Text(text = stringResource(R.string.map_azimuth, mapData.azimuth)) }
Text(text = stringResource(R.string.map_elevation, mapData.elevation))
@Composable
private fun MapDataCards(data: MapData) {
val cardColors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest)
val paddingMod = Modifier.padding(horizontal = 8.dp, vertical = 4.dp).width(160.dp)
val osmText = "© OpenStreetMap contributors"
val textColor = MaterialTheme.colorScheme.primary
Box(modifier = Modifier.fillMaxSize()) {
Card(colors = cardColors, modifier = Modifier.align(Alignment.TopStart)) {
Column(horizontalAlignment = Alignment.Start, modifier = paddingMod) {
Text(text = stringResource(R.string.map_azimuth, data.azimuth))
Text(text = stringResource(R.string.map_elevation, data.elevation), color = textColor)
}
} }
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Card(colors = cardColors, modifier = Modifier.align(Alignment.TopEnd)) {
Text(text = stringResource(R.string.map_altitude, mapData.altitude), color = MaterialTheme.colorScheme.primary) Column(horizontalAlignment = Alignment.End, modifier = paddingMod) {
Text(text = stringResource(R.string.map_distance, mapData.range), color = MaterialTheme.colorScheme.primary) Text(text = stringResource(R.string.map_altitude, data.altitude))
Text(text = stringResource(R.string.map_distance, data.range), color = textColor)
}
} }
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Card(colors = cardColors, modifier = Modifier.align(Alignment.BottomStart)) {
Text(text = stringResource(R.string.map_latitude, mapData.osmPos.latitude)) Column(horizontalAlignment = Alignment.Start, modifier = paddingMod) {
Text(text = stringResource(R.string.map_longitude, mapData.osmPos.longitude)) Text(text = stringResource(R.string.map_phase, data.phase))
Text(text = stringResource(R.string.map_qth, data.qthLoc), color = textColor)
}
} }
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Text(text = osmText, fontSize = 14.sp, modifier = Modifier.align(Alignment.BottomCenter))
Text(text = stringResource(R.string.map_qth, mapData.qthLoc), color = MaterialTheme.colorScheme.primary) Card(colors = cardColors, modifier = Modifier.align(Alignment.BottomEnd)) {
Text(text = stringResource(R.string.map_velocity, mapData.velocity), color = MaterialTheme.colorScheme.primary) Column(horizontalAlignment = Alignment.End, modifier = paddingMod) {
Text(text = stringResource(R.string.map_latitude, data.osmPos.latitude))
Text(text = stringResource(R.string.map_longitude, data.osmPos.longitude), color = textColor)
}
} }
} }
} }
@@ -306,6 +343,8 @@ private fun getColorFilter(): ColorMatrixColorFilter {
return ColorMatrixColorFilter(negative) return ColorMatrixColorFilter(negative)
} }
@Composable
private fun getMinZoom(screenHeight: Int): Double { private fun getMinZoom(screenHeight: Int): Double {
if (!isVerticalLayout()) return 3.5
return MapView.getTileSystem().getLatitudeZoom(maxLat, minLat, screenHeight) return MapView.getTileSystem().getLatitudeZoom(maxLat, minLat, screenHeight)
} }
@@ -148,7 +148,7 @@ fun RadiosDialog(modes: List<String>, cancel: () -> Unit, accept: (List<String>)
val onAccept = { accept(selected.toList()).also { cancel() } } val onAccept = { accept(selected.toList()).also { cancel() } }
SharedDialog(title = "Radios", onCancel = cancel, onAccept = onAccept) { SharedDialog(title = "Radios", onCancel = cancel, onAccept = onAccept) {
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(1), columns = GridCells.Adaptive(240.dp),
modifier = Modifier modifier = Modifier
.fillMaxHeight(0.69f) .fillMaxHeight(0.69f)
.background(MaterialTheme.colorScheme.background), .background(MaterialTheme.colorScheme.background),
@@ -1,6 +1,7 @@
package com.rtbishop.look4sat.presentation.passes package com.rtbishop.look4sat.presentation.passes
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -201,7 +202,8 @@ private fun NearEarthPass(
text = pass.name, text = pass.name,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.padding(end = 6.dp), .padding(end = 6.dp)
.basicMarquee(),
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@@ -82,6 +82,7 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
} }
} }
val upcomingPass = uiState.currentPass ?: getDefaultPass() val upcomingPass = uiState.currentPass ?: getDefaultPass()
if (upcomingPass.losTime < System.currentTimeMillis()) navigateUp()
Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) { Column(modifier = Modifier.layoutPadding(), verticalArrangement = Arrangement.spacedBy(6.dp)) {
if (isVerticalLayout()) { if (isVerticalLayout()) {
TopBar { TopBar {
@@ -13,11 +13,9 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width 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.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width 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.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
-1
View File
@@ -117,7 +117,6 @@
<string name="map_elevation">Элевация: %.1f°</string> <string name="map_elevation">Элевация: %.1f°</string>
<string name="map_latitude">Широта: %.1f°</string> <string name="map_latitude">Широта: %.1f°</string>
<string name="map_longitude">Долгота: %.1f°</string> <string name="map_longitude">Долгота: %.1f°</string>
<string name="map_qth">QTH Локатор: %s</string>
<string name="map_visible">Видимый</string> <string name="map_visible">Видимый</string>
<string name="map_eclipsed">В тени</string> <string name="map_eclipsed">В тени</string>
<string name="map_visibility">Видимость: %s</string> <string name="map_visibility">Видимость: %s</string>
-1
View File
@@ -117,7 +117,6 @@
<string name="map_elevation">උත්තෝලනය: %.1f°</string> <string name="map_elevation">උත්තෝලනය: %.1f°</string>
<string name="map_latitude">අක්‍ෂාංශ: %.1f°</string> <string name="map_latitude">අක්‍ෂාංශ: %.1f°</string>
<string name="map_longitude">දේශාංශ: %.1f°</string> <string name="map_longitude">දේශාංශ: %.1f°</string>
<string name="map_qth">QTH Locator: %s</string>
<string name="map_visible">දෘශ්‍ය</string> <string name="map_visible">දෘශ්‍ය</string>
<string name="map_eclipsed">අදෘශ්‍ය</string> <string name="map_eclipsed">අදෘශ්‍ය</string>
<string name="map_visibility">දෘශ්‍යතාව: %s</string> <string name="map_visibility">දෘශ්‍යතාව: %s</string>
@@ -117,7 +117,6 @@
<string name="map_elevation">仰角:%.1f°</string> <string name="map_elevation">仰角:%.1f°</string>
<string name="map_latitude">纬度:%.1f°</string> <string name="map_latitude">纬度:%.1f°</string>
<string name="map_longitude">经度:%.1f°</string> <string name="map_longitude">经度:%.1f°</string>
<string name="map_qth">QTH 网格:%s</string>
<string name="map_visible">日照中</string> <string name="map_visible">日照中</string>
<string name="map_eclipsed">地影中</string> <string name="map_eclipsed">地影中</string>
<string name="map_visibility">能见度:%s</string> <string name="map_visibility">能见度:%s</string>
+1 -1
View File
@@ -130,7 +130,7 @@
<string name="map_elevation">Elevation: %.1f°</string> <string name="map_elevation">Elevation: %.1f°</string>
<string name="map_latitude">Latitude: %.1f°</string> <string name="map_latitude">Latitude: %.1f°</string>
<string name="map_longitude">Longitude: %.1f°</string> <string name="map_longitude">Longitude: %.1f°</string>
<string name="map_qth">QTH Locator: %s</string> <string name="map_qth" translatable="false">QTH: %s</string>
<string name="map_visible">Visible</string> <string name="map_visible">Visible</string>
<string name="map_eclipsed">Eclipsed</string> <string name="map_eclipsed">Eclipsed</string>
<string name="map_visibility">Visibility: %s</string> <string name="map_visibility">Visibility: %s</string>
@@ -51,7 +51,8 @@ class SatelliteRepo(
settingsRepo.selectedIds.collect { selectedIds -> settingsRepo.selectedIds.collect { selectedIds ->
_satellites.update { localStorage.getEntriesWithIds(selectedIds) } _satellites.update { localStorage.getEntriesWithIds(selectedIds) }
val (hoursAhead, minElevation, modes) = settingsRepo.passesSettings.value val (hoursAhead, minElevation, modes) = settingsRepo.passesSettings.value
calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation, modes) val timeNow = 1000 * ((System.currentTimeMillis() + 500) / 1000)
calculatePasses(timeNow, hoursAhead, minElevation, modes)
} }
} }
@@ -195,10 +196,10 @@ class SatelliteRepo(
} }
} while (satPos.elevation < 0.0) } while (satPos.elevation < 0.0)
// refine to 3 seconds // refine to 1 second
calendarTimeMillis += -60L * 1000L calendarTimeMillis += -60L * 1000L
do { do {
calendarTimeMillis += 3L * 1000L calendarTimeMillis += 1L * 500L
satPos = sat.getPosition(pos, calendarTimeMillis) satPos = sat.getPosition(pos, calendarTimeMillis)
elevation = satPos.elevation elevation = satPos.elevation
if (elevation > maxElevation) { if (elevation > maxElevation) {
@@ -207,7 +208,7 @@ class SatelliteRepo(
} }
} while (satPos.elevation < 0.0) } while (satPos.elevation < 0.0)
val aos = satPos.time val aos = 1000 * ((satPos.time + 500) / 1000)
val aosAz = satPos.azimuth.toDegrees().round(1) val aosAz = satPos.azimuth.toDegrees().round(1)
// find when sat goes below // find when sat goes below
@@ -221,10 +222,10 @@ class SatelliteRepo(
} }
} while (satPos.elevation > 0.0) } while (satPos.elevation > 0.0)
// refine to 3 seconds // refine to 1 second
calendarTimeMillis += -30L * 1000L calendarTimeMillis += -30L * 1000L
do { do {
calendarTimeMillis += 3L * 1000L calendarTimeMillis += 1L * 500L
satPos = sat.getPosition(pos, calendarTimeMillis) satPos = sat.getPosition(pos, calendarTimeMillis)
elevation = satPos.elevation elevation = satPos.elevation
if (elevation > maxElevation) { if (elevation > maxElevation) {
@@ -233,7 +234,7 @@ class SatelliteRepo(
} }
} while (satPos.elevation > 0.0) } while (satPos.elevation > 0.0)
val los = satPos.time // val tca = (aos + los) / 2 val los = 1000 * ((satPos.time + 500) / 1000) // val tca = (aos + los) / 2
val losAz = satPos.azimuth.toDegrees().round(1) val losAz = satPos.azimuth.toDegrees().round(1)
val elev = maxElevation.toDegrees().round(1) val elev = maxElevation.toDegrees().round(1)
return OrbitalPass(aos, aosAz, los, losAz, alt.toInt(), elev, sat) return OrbitalPass(aos, aosAz, los, losAz, alt.toInt(), elev, sat)