mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Added multiple fixes to MapScreen and components
This commit is contained in:
1 parent
5b62474c27
commit
880fe10bfd
6 files changed
+192
-134
No files matched your search
@@ -209,11 +209,12 @@ fun CardButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier)
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CardIcon(onClick: () -> Unit, iconId: Int, description: String? = null) {
|
fun CardIcon(onClick: () -> Unit, iconId: Int, modifier: Modifier = Modifier) {
|
||||||
val clickableModifier = Modifier.clickable { onClick() }
|
val clickableModifier = Modifier.clickable { onClick() }
|
||||||
|
val iconRes = ImageVector.vectorResource(iconId)
|
||||||
ElevatedCard(modifier = Modifier.size(48.dp)) {
|
ElevatedCard(modifier = Modifier.size(48.dp)) {
|
||||||
Box(modifier = clickableModifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = clickableModifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
Icon(imageVector = ImageVector.vectorResource(iconId), contentDescription = description)
|
Icon(imageVector = iconRes, contentDescription = null, modifier = modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
@@ -26,6 +27,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
@@ -38,7 +40,6 @@ import com.rtbishop.look4sat.presentation.components.CardIcon
|
|||||||
import com.rtbishop.look4sat.presentation.components.NextPassRow
|
import com.rtbishop.look4sat.presentation.components.NextPassRow
|
||||||
import com.rtbishop.look4sat.presentation.components.TimerBar
|
import com.rtbishop.look4sat.presentation.components.TimerBar
|
||||||
import com.rtbishop.look4sat.presentation.components.TimerRow
|
import com.rtbishop.look4sat.presentation.components.TimerRow
|
||||||
import com.rtbishop.look4sat.presentation.components.getDefaultPass
|
|
||||||
import org.osmdroid.tileprovider.tilesource.XYTileSource
|
import org.osmdroid.tileprovider.tilesource.XYTileSource
|
||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
import org.osmdroid.views.CustomZoomButtonsController
|
import org.osmdroid.views.CustomZoomButtonsController
|
||||||
@@ -70,31 +71,34 @@ private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|||||||
private val labelRect = Rect()
|
private val labelRect = Rect()
|
||||||
|
|
||||||
fun NavGraphBuilder.mapDestination() {
|
fun NavGraphBuilder.mapDestination() {
|
||||||
composable(Screen.Map.route) { MapScreen() }
|
composable(Screen.Map.route) {
|
||||||
|
val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory)
|
||||||
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
MapScreen(uiState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MapScreen() {
|
private fun MapScreen(uiState: State<MapState>) {
|
||||||
val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory)
|
val onItemClick = { item: OrbitalObject -> uiState.value.sendAction(MapAction.SelectItem(item)) }
|
||||||
viewModel.selectDefaultSatellite(-1)
|
val selectPrev = { uiState.value.sendAction(MapAction.SelectPrev) }
|
||||||
val stationPos = viewModel.stationPosition.collectAsState(initial = null)
|
val selectNext = { uiState.value.sendAction(MapAction.SelectNext) }
|
||||||
val positions = viewModel.positions.collectAsState(initial = null)
|
val rotateMod = Modifier.rotate(180f)
|
||||||
val satTrack = viewModel.track.collectAsState(initial = null)
|
val timeString = uiState.value.mapData?.aosTime ?: "00:00:00"
|
||||||
val footprint = viewModel.footprint.collectAsState(initial = null)
|
val isTimeAos = uiState.value.mapData?.isTimeAos ?: true
|
||||||
val positionClick = { orbitalObject: OrbitalObject -> viewModel.selectSatellite(orbitalObject) }
|
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
TimerRow {
|
TimerRow {
|
||||||
CardIcon(onClick = {}, iconId = R.drawable.ic_filter)
|
CardIcon(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod)
|
||||||
TimerBar(timeString = "88:88:88", isTimeAos = true)
|
TimerBar(timeString = timeString, isTimeAos = isTimeAos)
|
||||||
CardIcon(onClick = {}, iconId = R.drawable.ic_satellite)
|
CardIcon(onClick = selectNext, iconId = R.drawable.ic_arrow)
|
||||||
}
|
}
|
||||||
NextPassRow(pass = getDefaultPass())
|
NextPassRow(pass = uiState.value.orbitalPass)
|
||||||
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
MapView(modifier = Modifier.fillMaxSize(), isLightTheme = viewModel.stateOfLightTheme) { mapView ->
|
MapView(modifier = Modifier.fillMaxSize(), isLightUi = uiState.value.isLightUi) { mapView ->
|
||||||
stationPos.value?.let { setStationPosition(it, mapView) }
|
uiState.value.stationPosition?.let { setStationPosition(it, mapView) }
|
||||||
positions.value?.let { setPositions(it, mapView, positionClick) }
|
uiState.value.positions?.let { setPositions(it, mapView, onItemClick) }
|
||||||
satTrack.value?.let { setSatelliteTrack(it, mapView) }
|
uiState.value.track?.let { setSatelliteTrack(it, mapView) }
|
||||||
footprint.value?.let { setFootprint(it, mapView) }
|
uiState.value.footprint?.let { setFootprint(it, mapView) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,10 +222,10 @@ private fun setFootprint(orbitalPos: OrbitalPos, mapView: MapView) {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun MapView(
|
private fun MapView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
isLightTheme: Boolean,
|
isLightUi: Boolean,
|
||||||
update: ((map: MapView) -> Unit)? = null
|
update: ((map: MapView) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val mapView = rememberMapViewWithLifecycle(isLightTheme)
|
val mapView = rememberMapViewWithLifecycle(isLightUi)
|
||||||
AndroidView({ mapView }, modifier) { update?.invoke(it) }
|
AndroidView({ mapView }, modifier) { update?.invoke(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
@@ -18,11 +18,33 @@
|
|||||||
package com.rtbishop.look4sat.presentation.map
|
package com.rtbishop.look4sat.presentation.map
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
|
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||||
|
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||||
|
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||||
|
|
||||||
|
data class MapState(
|
||||||
|
val mapData: MapData?,
|
||||||
|
val isLightUi: Boolean,
|
||||||
|
val stationPosition: GeoPos?,
|
||||||
|
val orbitalPass: OrbitalPass,
|
||||||
|
val track: List<List<GeoPos>>?,
|
||||||
|
val footprint: OrbitalPos?,
|
||||||
|
val positions: Map<OrbitalObject, GeoPos>?,
|
||||||
|
val sendAction: (MapAction) -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class MapAction {
|
||||||
|
data object SelectPrev: MapAction()
|
||||||
|
data object SelectNext: MapAction()
|
||||||
|
data class SelectItem(val item: OrbitalObject): MapAction()
|
||||||
|
data class SelectDefaultItem(val catnum: Int): MapAction()
|
||||||
|
}
|
||||||
|
|
||||||
data class MapData(
|
data class MapData(
|
||||||
val catNum: Int,
|
val catNum: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val aosTime: String,
|
val aosTime: String,
|
||||||
|
val isTimeAos: Boolean,
|
||||||
val azimuth: Double,
|
val azimuth: Double,
|
||||||
val elevation: Double,
|
val elevation: Double,
|
||||||
val range: Double,
|
val range: Double,
|
||||||
@@ -25,7 +25,6 @@ import androidx.lifecycle.viewmodel.viewModelFactory
|
|||||||
import com.rtbishop.look4sat.MainApplication
|
import com.rtbishop.look4sat.MainApplication
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
|
||||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||||
import com.rtbishop.look4sat.domain.utility.clipLat
|
import com.rtbishop.look4sat.domain.utility.clipLat
|
||||||
@@ -33,60 +32,62 @@ import com.rtbishop.look4sat.domain.utility.clipLon
|
|||||||
import com.rtbishop.look4sat.domain.utility.positionToQth
|
import com.rtbishop.look4sat.domain.utility.positionToQth
|
||||||
import com.rtbishop.look4sat.domain.utility.toDegrees
|
import com.rtbishop.look4sat.domain.utility.toDegrees
|
||||||
import com.rtbishop.look4sat.domain.utility.toTimerString
|
import com.rtbishop.look4sat.domain.utility.toTimerString
|
||||||
import java.util.Date
|
import com.rtbishop.look4sat.presentation.components.getDefaultPass
|
||||||
import kotlin.collections.set
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.cancelAndJoin
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.Date
|
||||||
|
import kotlin.collections.set
|
||||||
|
|
||||||
class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISettingsRepo) : ViewModel() {
|
class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISettingsRepo) :
|
||||||
|
ViewModel() {
|
||||||
|
|
||||||
private val stationPos = settingsRepo.stationPosition.value
|
private val stationPos = settingsRepo.stationPosition.value
|
||||||
|
private val defaultPass = getDefaultPass()
|
||||||
|
private val _uiState = MutableStateFlow(
|
||||||
|
MapState(
|
||||||
|
mapData = null,
|
||||||
|
isLightUi = settingsRepo.otherSettings.value.stateOfLightTheme,
|
||||||
|
stationPosition = null,
|
||||||
|
orbitalPass = defaultPass,
|
||||||
|
track = null,
|
||||||
|
footprint = null,
|
||||||
|
positions = null,
|
||||||
|
sendAction = ::handleAction
|
||||||
|
)
|
||||||
|
)
|
||||||
private var allPasses = satelliteRepo.passes.value
|
private var allPasses = satelliteRepo.passes.value
|
||||||
private var allSatellites = satelliteRepo.satellites.value
|
private var allSatellites = satelliteRepo.satellites.value
|
||||||
private var dataUpdateJob: Job? = null
|
private var dataUpdateJob: Job? = null
|
||||||
private var dataUpdateRate = 1000L
|
private var dataUpdateRate = 1000L
|
||||||
private var selectedOrbitalObject: OrbitalObject? = null
|
private var selectedOrbitalObject: OrbitalObject? = null
|
||||||
|
val uiState: StateFlow<MapState> = _uiState
|
||||||
|
|
||||||
val stateOfLightTheme = settingsRepo.otherSettings.value.stateOfLightTheme
|
init {
|
||||||
|
selectDefaultSatellite(-1)
|
||||||
val stationPosition = flow {
|
|
||||||
val osmLat = clipLat(stationPos.latitude)
|
|
||||||
val osmLon = clipLon(stationPos.longitude)
|
|
||||||
emit(GeoPos(osmLat, osmLon))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _track = MutableSharedFlow<List<List<GeoPos>>>()
|
private fun handleAction(action: MapAction) {
|
||||||
val track: SharedFlow<List<List<GeoPos>>> = _track
|
when (action) {
|
||||||
|
MapAction.SelectPrev -> scrollSelection(true)
|
||||||
|
MapAction.SelectNext -> scrollSelection(false)
|
||||||
|
is MapAction.SelectItem -> selectSatellite(action.item)
|
||||||
|
is MapAction.SelectDefaultItem -> selectDefaultSatellite(action.catnum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val _footprint = MutableSharedFlow<OrbitalPos>()
|
private fun getStationPosition() {
|
||||||
val footprint: SharedFlow<OrbitalPos> = _footprint
|
val osmLat = clipLat(stationPos.latitude)
|
||||||
|
val osmLon = clipLon(stationPos.longitude)
|
||||||
|
_uiState.update { it.copy(stationPosition = GeoPos(osmLat, osmLon)) }
|
||||||
|
}
|
||||||
|
|
||||||
private val _mapData = MutableSharedFlow<MapData>()
|
private fun selectDefaultSatellite(catnum: Int) {
|
||||||
// val mapData: SharedFlow<MapData> = _mapData
|
|
||||||
|
|
||||||
private val _positions = MutableSharedFlow<Map<OrbitalObject, GeoPos>>()
|
|
||||||
val positions: SharedFlow<Map<OrbitalObject, GeoPos>> = _positions
|
|
||||||
|
|
||||||
// fun scrollSelection(decrement: Boolean) {
|
|
||||||
// if (allSatellites.isNotEmpty()) {
|
|
||||||
// val index = allSatellites.indexOf(selectedSatellite)
|
|
||||||
// if (decrement) {
|
|
||||||
// if (index > 0) selectSatellite(allSatellites[index - 1])
|
|
||||||
// else selectSatellite(allSatellites[allSatellites.size - 1])
|
|
||||||
// } else {
|
|
||||||
// if (index < allSatellites.size - 1) selectSatellite(allSatellites[index + 1])
|
|
||||||
// else selectSatellite(allSatellites[0])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun selectDefaultSatellite(catnum: Int) {
|
|
||||||
if (allSatellites.isNotEmpty()) {
|
if (allSatellites.isNotEmpty()) {
|
||||||
if (catnum == -1) {
|
if (catnum == -1) {
|
||||||
allPasses.find { pass -> pass.progress < 100 && !pass.isDeepSpace }
|
allPasses.find { pass -> pass.progress < 100 && !pass.isDeepSpace }
|
||||||
@@ -97,12 +98,26 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectSatellite(orbitalObject: OrbitalObject, updateFreq: Long = dataUpdateRate) {
|
private fun scrollSelection(decrement: Boolean) {
|
||||||
|
if (allSatellites.isNotEmpty()) {
|
||||||
|
val index = allSatellites.indexOf(selectedOrbitalObject)
|
||||||
|
if (decrement) {
|
||||||
|
if (index > 0) selectSatellite(allSatellites[index - 1])
|
||||||
|
else selectSatellite(allSatellites[allSatellites.size - 1])
|
||||||
|
} else {
|
||||||
|
if (index < allSatellites.size - 1) selectSatellite(allSatellites[index + 1])
|
||||||
|
else selectSatellite(allSatellites[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectSatellite(orbitalObject: OrbitalObject, updateFreq: Long = dataUpdateRate) {
|
||||||
selectedOrbitalObject = orbitalObject
|
selectedOrbitalObject = orbitalObject
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
dataUpdateJob?.cancelAndJoin()
|
dataUpdateJob?.cancelAndJoin()
|
||||||
dataUpdateJob = launch {
|
dataUpdateJob = launch {
|
||||||
val dateNow = Date()
|
val dateNow = Date()
|
||||||
|
getStationPosition()
|
||||||
getSatTrack(orbitalObject, stationPos, dateNow)
|
getSatTrack(orbitalObject, stationPos, dateNow)
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
dateNow.time = System.currentTimeMillis()
|
dateNow.time = System.currentTimeMillis()
|
||||||
@@ -141,7 +156,7 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
|||||||
currentTrack.add(currentPosition)
|
currentTrack.add(currentPosition)
|
||||||
}
|
}
|
||||||
satTracks.add(currentTrack)
|
satTracks.add(currentTrack)
|
||||||
_track.emit(satTracks)
|
_uiState.update { it.copy(track = satTracks) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getPositions(orbitalObjects: List<OrbitalObject>, pos: GeoPos, date: Date) {
|
private suspend fun getPositions(orbitalObjects: List<OrbitalObject>, pos: GeoPos, date: Date) {
|
||||||
@@ -152,24 +167,29 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
|||||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||||
positions[satellite] = GeoPos(osmLat, osmLon)
|
positions[satellite] = GeoPos(osmLat, osmLon)
|
||||||
}
|
}
|
||||||
_positions.emit(positions)
|
_uiState.update { it.copy(positions = positions) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSatFootprint(orbitalObject: OrbitalObject, pos: GeoPos, date: Date) {
|
private suspend fun getSatFootprint(orbitalObject: OrbitalObject, pos: GeoPos, date: Date) {
|
||||||
val satPos = satelliteRepo.getPosition(orbitalObject, pos, date.time)
|
val satPos = satelliteRepo.getPosition(orbitalObject, pos, date.time)
|
||||||
_footprint.emit(satPos)
|
_uiState.update { it.copy(footprint = satPos) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSatData(sat: OrbitalObject, pos: GeoPos, date: Date) {
|
private suspend fun getSatData(sat: OrbitalObject, pos: GeoPos, date: Date) {
|
||||||
|
var orbitalPass = defaultPass
|
||||||
var aosTime = 0L.toTimerString()
|
var aosTime = 0L.toTimerString()
|
||||||
|
var isTimeAos = true
|
||||||
allPasses.find { pass -> pass.catNum == sat.data.catnum && pass.progress < 100 }
|
allPasses.find { pass -> pass.catNum == sat.data.catnum && pass.progress < 100 }
|
||||||
?.let { satPass ->
|
?.let { satPass ->
|
||||||
|
orbitalPass = satPass
|
||||||
if (!satPass.isDeepSpace) {
|
if (!satPass.isDeepSpace) {
|
||||||
aosTime = if (date.time < satPass.aosTime) {
|
aosTime = if (date.time < satPass.aosTime) {
|
||||||
val millisBeforeStart = satPass.aosTime.minus(date.time)
|
val millisBeforeStart = satPass.aosTime.minus(date.time)
|
||||||
|
isTimeAos = true
|
||||||
millisBeforeStart.toTimerString()
|
millisBeforeStart.toTimerString()
|
||||||
} else {
|
} else {
|
||||||
val millisBeforeEnd = satPass.losTime.minus(date.time)
|
val millisBeforeEnd = satPass.losTime.minus(date.time)
|
||||||
|
isTimeAos = false
|
||||||
millisBeforeEnd.toTimerString()
|
millisBeforeEnd.toTimerString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,6 +208,7 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
|||||||
sat.data.catnum,
|
sat.data.catnum,
|
||||||
sat.data.name,
|
sat.data.name,
|
||||||
aosTime,
|
aosTime,
|
||||||
|
isTimeAos,
|
||||||
azimuth,
|
azimuth,
|
||||||
elevation,
|
elevation,
|
||||||
satPos.distance,
|
satPos.distance,
|
||||||
@@ -199,7 +220,7 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
|||||||
phase,
|
phase,
|
||||||
visibility
|
visibility
|
||||||
)
|
)
|
||||||
_mapData.emit(satData)
|
_uiState.update { it.copy(mapData = satData, orbitalPass = orbitalPass) }
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.compose.foundation.MarqueeSpacing
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.basicMarquee
|
import androidx.compose.foundation.basicMarquee
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
@@ -38,6 +39,7 @@ import androidx.navigation.compose.composable
|
|||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
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.MainTheme
|
||||||
import com.rtbishop.look4sat.presentation.Screen
|
import com.rtbishop.look4sat.presentation.Screen
|
||||||
import com.rtbishop.look4sat.presentation.components.CardIcon
|
import com.rtbishop.look4sat.presentation.components.CardIcon
|
||||||
@@ -78,15 +80,53 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
|
|||||||
CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
|
CardIcon(onClick = addToCalendar, iconId = R.drawable.ic_calendar)
|
||||||
}
|
}
|
||||||
NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
|
NextPassRow(pass = uiState.currentPass ?: getDefaultPass())
|
||||||
ElevatedCard(modifier = Modifier.aspectRatio(1f)) {
|
Box(contentAlignment = Alignment.Center, modifier = Modifier.aspectRatio(1f)) {
|
||||||
uiState.orbitalPos?.let { item ->
|
uiState.orbitalPos?.let { position ->
|
||||||
RadarViewCompose(
|
ElevatedCard {
|
||||||
item = item,
|
RadarViewCompose(
|
||||||
items = uiState.satTrack,
|
item = position,
|
||||||
azimElev = uiState.orientationValues,
|
items = uiState.satTrack,
|
||||||
shouldShowSweep = uiState.shouldShowSweep,
|
azimElev = uiState.orientationValues,
|
||||||
shouldUseCompass = uiState.shouldUseCompass
|
shouldShowSweep = uiState.shouldShowSweep,
|
||||||
)
|
shouldUseCompass = uiState.shouldUseCompass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
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()) {
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
@@ -96,6 +136,26 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RadarTextTop(value: Double, text: String, isLeft: Boolean) {
|
||||||
|
val alignment = if (isLeft) Alignment.Start else Alignment.End
|
||||||
|
val degValue = stringResource(R.string.radar_az_value, value.toDegrees())
|
||||||
|
Column(horizontalAlignment = alignment) {
|
||||||
|
Text(text = degValue, fontSize = 18.sp)
|
||||||
|
Text(text = text, fontSize = 15.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RadarTextBottom(value: Double, text: String, isLeft: Boolean) {
|
||||||
|
val alignment = if (isLeft) Alignment.Start else Alignment.End
|
||||||
|
val degValue = stringResource(R.string.radar_alt_value, value)
|
||||||
|
Column(horizontalAlignment = alignment) {
|
||||||
|
Text(text = text, fontSize = 15.sp)
|
||||||
|
Text(text = degValue, fontSize = 18.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TransmittersList(transmitters: List<SatRadio>) {
|
private fun TransmittersList(transmitters: List<SatRadio>) {
|
||||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||||
@@ -120,19 +180,19 @@ private fun TransmitterItem(radio: SatRadio) {
|
|||||||
Surface(color = MaterialTheme.colorScheme.background) {
|
Surface(color = MaterialTheme.colorScheme.background) {
|
||||||
Surface(modifier = Modifier.padding(bottom = 2.dp)) {
|
Surface(modifier = Modifier.padding(bottom = 2.dp)) {
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.padding(6.dp)
|
.padding(horizontal = 6.dp, vertical = 8.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.ic_back),
|
painter = painterResource(id = R.drawable.ic_arrow),
|
||||||
tint = Color.Green,
|
tint = Color.Green,
|
||||||
contentDescription = null, modifier = Modifier.rotate(-90f)
|
contentDescription = null, modifier = Modifier.rotate(90f).weight(0.15f)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = if (radio.isInverted) "INVERTED: ${radio.info} " else "${radio.info} ",
|
text = if (radio.isInverted) "INVERTED: ${radio.info} " else "${radio.info} ",
|
||||||
@@ -140,14 +200,14 @@ private fun TransmitterItem(radio: SatRadio) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.basicMarquee(
|
.basicMarquee(
|
||||||
iterations = Int.MAX_VALUE,
|
iterations = Int.MAX_VALUE,
|
||||||
spacing = MarqueeSpacing(0.dp)
|
spacing = MarqueeSpacing(16.dp)
|
||||||
)
|
)
|
||||||
.weight(1f)
|
.weight(0.7f)
|
||||||
)
|
)
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.ic_back),
|
painter = painterResource(id = R.drawable.ic_arrow),
|
||||||
tint = Color.Red,
|
tint = Color.Red,
|
||||||
contentDescription = null, modifier = Modifier.rotate(90f)
|
contentDescription = null, modifier = Modifier.rotate(-90f).weight(0.15f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
FrequencyRow(satRadio = radio)
|
FrequencyRow(satRadio = radio)
|
||||||
@@ -185,7 +245,6 @@ private fun RowScope.ModeText(mode: String?) {
|
|||||||
Text(
|
Text(
|
||||||
text = mode ?: "- - : - -",
|
text = mode ?: "- - : - -",
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
fontSize = 15.sp,
|
|
||||||
modifier = Modifier.weight(0.15f)
|
modifier = Modifier.weight(0.15f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -203,53 +262,3 @@ private fun FrequencyText(frequency: Long?) {
|
|||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//private val divider = 1000000f
|
|
||||||
//radioDownlink.text = String.format(Locale.ENGLISH, link, downlink / divider)
|
|
||||||
//radioUplink.text = String.format(Locale.ENGLISH, link, uplink / divider)
|
|
||||||
|
|
||||||
//private fun setupObservers() {
|
|
||||||
// viewModel.getPass(45000, System.currentTimeMillis()).observe(viewLifecycleOwner) { pass ->
|
|
||||||
// binding?.run {
|
|
||||||
// radarView = RadarView(requireContext()).apply {
|
|
||||||
// setShowAim(viewModel.getUseCompass())
|
|
||||||
// setScanning(viewModel.getShowSweep())
|
|
||||||
// }
|
|
||||||
// viewModel.radarData.observe(viewLifecycleOwner) { passData ->
|
|
||||||
// setPassText(pass, passData.satPos)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//private fun setPassText(satPass: SatPass, satPos: SatPos) {
|
|
||||||
// binding?.run {
|
|
||||||
// val timeNow = System.currentTimeMillis()
|
|
||||||
// val radarAzim = getString(R.string.radar_az_value)
|
|
||||||
// val radarElev = getString(R.string.radar_el_value)
|
|
||||||
// val radarAlt = getString(R.string.radar_alt_value)
|
|
||||||
// val radarDist = getString(R.string.radar_dist_value)
|
|
||||||
// radarAzValue.text = String.format(radarAzim, satPos.azimuth.toDegrees())
|
|
||||||
// radarElValue.text = String.format(radarElev, satPos.elevation.toDegrees())
|
|
||||||
// radarAltValue.text = String.format(radarAlt, satPos.altitude)
|
|
||||||
// radarDstValue.text = String.format(radarDist, satPos.distance)
|
|
||||||
// if (satPos.eclipsed) {
|
|
||||||
// radarVisibility.text = getText(R.string.radar_eclipsed)
|
|
||||||
// } else {
|
|
||||||
// radarVisibility.text = getText(R.string.radar_visible)
|
|
||||||
// }
|
|
||||||
// if (!satPass.isDeepSpace) {
|
|
||||||
// if (timeNow < satPass.aosTime) {
|
|
||||||
// val millisBeforeStart = satPass.aosTime.minus(timeNow)
|
|
||||||
// radarTimer.text = millisBeforeStart.toTimerString()
|
|
||||||
// } else {
|
|
||||||
// val millisBeforeEnd = satPass.losTime.minus(timeNow)
|
|
||||||
// radarTimer.text = millisBeforeEnd.toTimerString()
|
|
||||||
// if (timeNow > satPass.losTime) {
|
|
||||||
// radarTimer.text = 0L.toTimerString()
|
|
||||||
//// findNavController().navigateUp()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else radarTimer.text = 0L.toTimerString()
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@@ -195,6 +195,7 @@ private fun TypeCard(types: List<String>, onClick: () -> Unit, modifier: Modifie
|
|||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
.basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(16.dp))
|
.basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(16.dp))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user