mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-03 03:17:21 +00:00
Added several tweaks to Radar and RadioControl screens
This commit is contained in:
1 parent
aadc285d7f
commit
4a3a01729b
5 files changed
+116
-125
No files matched your search
@@ -37,9 +37,9 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -89,19 +89,23 @@ fun NavGraphBuilder.radarDestination(
|
|||||||
composable(radarRoute, radarArgs) {
|
composable(radarRoute, radarArgs) {
|
||||||
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
|
val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory)
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
RadarScreen(uiState, navigateUp, navigateToRadioControl)
|
RadarScreen(uiState, viewModel::onAction, navigateUp, navigateToRadioControl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RadarScreen(
|
private fun RadarScreen(
|
||||||
uiState: RadarState,
|
uiState: RadarState,
|
||||||
|
onAction: (RadarAction) -> Unit,
|
||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
navigateToRadioControl: (Int, Long) -> Unit
|
navigateToRadioControl: (Int, Long) -> Unit
|
||||||
) {
|
) {
|
||||||
|
val upcomingPass = uiState.currentPass ?: getDefaultPass()
|
||||||
|
if (upcomingPass.losTime < System.currentTimeMillis()) navigateUp()
|
||||||
|
|
||||||
val addToCalendar: () -> Unit = {
|
val addToCalendar: () -> Unit = {
|
||||||
uiState.currentPass?.let { pass ->
|
uiState.currentPass?.let { pass ->
|
||||||
uiState.sendAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
|
onAction(RadarAction.AddToCalendar(pass.name, pass.aosTime, pass.losTime))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val openRadioControl: () -> Unit = {
|
val openRadioControl: () -> Unit = {
|
||||||
@@ -109,8 +113,6 @@ private fun RadarScreen(
|
|||||||
navigateToRadioControl(pass.catNum, pass.aosTime)
|
navigateToRadioControl(pass.catNum, pass.aosTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val upcomingPass = uiState.currentPass ?: getDefaultPass()
|
|
||||||
if (upcomingPass.losTime < System.currentTimeMillis()) navigateUp()
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.layoutPadding()
|
.layoutPadding()
|
||||||
@@ -120,28 +122,26 @@ private fun RadarScreen(
|
|||||||
val isVertical = isVerticalLayout()
|
val isVertical = isVerticalLayout()
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
TopBar {
|
TopBar {
|
||||||
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
|
||||||
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
||||||
IconCard(action = openRadioControl, resId = R.drawable.ic_radios)
|
IconCard(action = openRadioControl, resId = R.drawable.ic_radios)
|
||||||
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
|
|
||||||
}
|
}
|
||||||
TopBar { NextPassRow(pass = upcomingPass, isUtc = uiState.isUtc) }
|
TopBar { NextPassRow(pass = upcomingPass, isUtc = uiState.isUtc) }
|
||||||
} else {
|
} else {
|
||||||
TopBar {
|
TopBar {
|
||||||
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
|
||||||
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
||||||
NextPassRow(pass = upcomingPass, modifier = Modifier.weight(1f), isUtc = uiState.isUtc)
|
NextPassRow(pass = upcomingPass, modifier = Modifier.weight(1f), isUtc = uiState.isUtc)
|
||||||
IconCard(action = openRadioControl, resId = R.drawable.ic_radios)
|
IconCard(action = openRadioControl, resId = R.drawable.ic_radios)
|
||||||
IconCard(action = addToCalendar, resId = R.drawable.ic_calendar)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
RadarCard(uiState, Modifier.weight(1f))
|
RadarCard(uiState, Modifier.weight(1f))
|
||||||
TransmittersCard(uiState, Modifier.weight(1f))
|
TransmittersCard(uiState.transmitters, uiState.selectedTransmitterUuid, onAction, Modifier.weight(1f))
|
||||||
} else {
|
} else {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
RadarCard(uiState, Modifier.weight(1f))
|
RadarCard(uiState, Modifier.weight(1f))
|
||||||
TransmittersCard(uiState, Modifier.weight(1f))
|
TransmittersCard(uiState.transmitters, uiState.selectedTransmitterUuid, onAction, Modifier.weight(1f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,17 +255,22 @@ private fun RadarLabel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TransmittersCard(uiState: RadarState, modifier: Modifier = Modifier) {
|
private fun TransmittersCard(
|
||||||
|
transmitters: List<SatRadio>,
|
||||||
|
selectedUuid: String?,
|
||||||
|
onAction: (RadarAction) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
ElevatedCard(modifier = modifier) {
|
ElevatedCard(modifier = modifier) {
|
||||||
if (uiState.transmitters.isEmpty()) {
|
if (transmitters.isEmpty()) {
|
||||||
EmptyTransmittersContent()
|
EmptyTransmittersContent()
|
||||||
} else {
|
} else {
|
||||||
TransmittersList(
|
TransmittersList(
|
||||||
transmitters = uiState.transmitters,
|
transmitters = transmitters,
|
||||||
selectedUuid = uiState.selectedTransmitterUuid,
|
selectedUuid = selectedUuid,
|
||||||
onSelect = { uuid ->
|
onSelect = { uuid ->
|
||||||
if (uiState.selectedTransmitterUuid != null) {
|
if (selectedUuid != null) {
|
||||||
uiState.sendAction(RadarAction.SelectTransmitter(uuid))
|
onAction(RadarAction.SelectTransmitter(uuid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -324,7 +329,6 @@ private fun TransmitterItemPreview() {
|
|||||||
MainTheme { TransmitterItem(transmitter, isClickable = true, isSelected = true, onClick = {}) }
|
MainTheme { TransmitterItem(transmitter, isClickable = true, isSelected = true, onClick = {}) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TransmitterItem(
|
private fun TransmitterItem(
|
||||||
radio: SatRadio,
|
radio: SatRadio,
|
||||||
@@ -334,42 +338,42 @@ private fun TransmitterItem(
|
|||||||
) {
|
) {
|
||||||
val title = if (radio.isInverted) "INVERTED: ${radio.info}" else radio.info
|
val title = if (radio.isInverted) "INVERTED: ${radio.info}" else radio.info
|
||||||
val fullTitle = "$title - (${radio.downlinkMode ?: "--"}/${radio.uplinkMode ?: "--"})"
|
val fullTitle = "$title - (${radio.downlinkMode ?: "--"}/${radio.uplinkMode ?: "--"})"
|
||||||
Surface(
|
Column(
|
||||||
color = MaterialTheme.colorScheme.background,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.then(if (isClickable) Modifier.clickable { onClick() } else Modifier)
|
.then(if (isClickable) Modifier.clickable { onClick() } else Modifier)
|
||||||
) {
|
) {
|
||||||
Surface(modifier = Modifier.padding(bottom = 2.dp)) {
|
Column(
|
||||||
Column(
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.fillMaxWidth()
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.padding(horizontal = 8.dp, vertical = 6.dp)
|
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
Text(
|
Text(
|
||||||
text = fullTitle,
|
text = fullTitle,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
modifier = Modifier
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.padding(horizontal = 6.dp)
|
.fillMaxWidth()
|
||||||
.infiniteMarquee()
|
.padding(horizontal = 6.dp)
|
||||||
|
.infiniteMarquee()
|
||||||
|
)
|
||||||
|
if (isSelected) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_radios),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.background(color = MaterialTheme.colorScheme.surface)
|
||||||
)
|
)
|
||||||
if (isSelected) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(id = R.drawable.ic_radios),
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
modifier = Modifier.background(color = MaterialTheme.colorScheme.surface)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FrequencyRow(radio = radio, isDownlink = true)
|
|
||||||
FrequencyRow(radio = radio, isDownlink = false)
|
|
||||||
}
|
}
|
||||||
|
FrequencyRow(radio = radio, isDownlink = true)
|
||||||
|
FrequencyRow(radio = radio, isDownlink = false)
|
||||||
}
|
}
|
||||||
|
HorizontalDivider(thickness = 2.dp, color = MaterialTheme.colorScheme.background)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,19 +22,18 @@ import com.rtbishop.look4sat.core.domain.predict.OrbitalPass
|
|||||||
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
import com.rtbishop.look4sat.core.domain.predict.OrbitalPos
|
||||||
|
|
||||||
data class RadarState(
|
data class RadarState(
|
||||||
val currentPass: OrbitalPass?,
|
val currentPass: OrbitalPass? = null,
|
||||||
val currentTime: String,
|
val currentTime: String = "00:00:00",
|
||||||
val isCurrentTimeAos: Boolean,
|
val isCurrentTimeAos: Boolean = true,
|
||||||
val isUtc: Boolean,
|
val isUtc: Boolean = false,
|
||||||
val orientationValues: Pair<Float, Float>,
|
val orientationValues: Pair<Float, Float> = 0f to 0f,
|
||||||
val orbitalPos: OrbitalPos?,
|
val orbitalPos: OrbitalPos? = null,
|
||||||
val satTrack: List<OrbitalPos>,
|
val satTrack: List<OrbitalPos> = emptyList(),
|
||||||
val shouldShowSweep: Boolean,
|
val shouldShowSweep: Boolean = false,
|
||||||
val shouldUseCompass: Boolean,
|
val shouldUseCompass: Boolean = false,
|
||||||
val transmitters: List<SatRadio>,
|
val transmitters: List<SatRadio> = emptyList(),
|
||||||
val selectedTransmitterUuid: String?,
|
val selectedTransmitterUuid: String? = null,
|
||||||
val selectedFrequency: Long?,
|
val selectedFrequency: Long? = null
|
||||||
val sendAction: (RadarAction) -> Unit
|
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed class RadarAction {
|
sealed class RadarAction {
|
||||||
|
|||||||
@@ -54,25 +54,16 @@ class RadarViewModel(
|
|||||||
private val addToCalendar: IAddToCalendar
|
private val addToCalendar: IAddToCalendar
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(
|
|
||||||
RadarState(
|
|
||||||
currentPass = null,
|
|
||||||
currentTime = "00:00:00",
|
|
||||||
isCurrentTimeAos = true,
|
|
||||||
isUtc = settingsRepo.otherSettings.value.stateOfUtc,
|
|
||||||
orientationValues = sensorsRepo.orientation.value,
|
|
||||||
orbitalPos = null,
|
|
||||||
satTrack = emptyList(),
|
|
||||||
shouldShowSweep = settingsRepo.otherSettings.value.stateOfSweep,
|
|
||||||
shouldUseCompass = settingsRepo.otherSettings.value.stateOfSensors,
|
|
||||||
selectedTransmitterUuid = null,
|
|
||||||
selectedFrequency = null,
|
|
||||||
transmitters = emptyList(),
|
|
||||||
sendAction = ::handleAction,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private val stationPos = settingsRepo.stationPosition.value
|
private val stationPos = settingsRepo.stationPosition.value
|
||||||
private val magDeclination = sensorsRepo.getMagDeclination(stationPos)
|
private val magDeclination = sensorsRepo.getMagDeclination(stationPos)
|
||||||
|
private val _uiState = MutableStateFlow(
|
||||||
|
RadarState(
|
||||||
|
isUtc = settingsRepo.otherSettings.value.stateOfUtc,
|
||||||
|
orientationValues = sensorsRepo.orientation.value,
|
||||||
|
shouldShowSweep = settingsRepo.otherSettings.value.stateOfSweep,
|
||||||
|
shouldUseCompass = settingsRepo.otherSettings.value.stateOfSensors
|
||||||
|
)
|
||||||
|
)
|
||||||
val uiState: StateFlow<RadarState> = _uiState
|
val uiState: StateFlow<RadarState> = _uiState
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -81,7 +72,7 @@ class RadarViewModel(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
sensorsRepo.enableSensor()
|
sensorsRepo.enableSensor()
|
||||||
sensorsRepo.orientation.collect { data ->
|
sensorsRepo.orientation.collect { data ->
|
||||||
val orientationValues = Pair(data.first + magDeclination, data.second)
|
val orientationValues = (data.first + magDeclination) to data.second
|
||||||
_uiState.update { it.copy(orientationValues = orientationValues) }
|
_uiState.update { it.copy(orientationValues = orientationValues) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,41 +83,29 @@ class RadarViewModel(
|
|||||||
_uiState.update { it.copy(isUtc = settings.stateOfUtc) }
|
_uiState.update { it.copy(isUtc = settings.stateOfUtc) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Resolve which pass we're tracking
|
// Resolve which pass we're tracking and start the tick loop
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
|
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
|
||||||
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
|
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
|
||||||
val passes = satelliteRepo.passes.value
|
val passes = satelliteRepo.passes.value
|
||||||
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
|
val currentPass = passes.find { it.catNum == catNum && it.aosTime == aosTime }
|
||||||
val currentPass = pass ?: passes.firstOrNull()
|
?: passes.firstOrNull()
|
||||||
currentPass?.let { satPass ->
|
currentPass?.let { satPass ->
|
||||||
_uiState.update { it.copy(currentPass = satPass) }
|
_uiState.update { it.copy(currentPass = satPass) }
|
||||||
val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
|
val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
|
||||||
// Compute track once (it doesn't change)
|
// Compute track once (it doesn't change for a given pass)
|
||||||
if (!satPass.isDeepSpace) {
|
if (!satPass.isDeepSpace) {
|
||||||
val track = satelliteRepo.getTrack(
|
val track = satelliteRepo.getTrack(
|
||||||
satPass.orbitalObject, stationPos, satPass.aosTime, satPass.losTime
|
satPass.orbitalObject, stationPos, satPass.aosTime, satPass.losTime
|
||||||
)
|
)
|
||||||
_uiState.update { it.copy(satTrack = track) }
|
_uiState.update { it.copy(satTrack = track) }
|
||||||
}
|
}
|
||||||
// Local tick loop — computes position only while the radar screen is alive
|
// Tick loop — position, timer, and radio updates every second
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val timeNow = System.currentTimeMillis()
|
val timeNow = System.currentTimeMillis()
|
||||||
val pos = satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
|
val pos = satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
|
||||||
when {
|
val (time, isAos) = computeTimer(satPass.isDeepSpace, satPass.aosTime, satPass.losTime, timeNow)
|
||||||
satPass.isDeepSpace -> {
|
_uiState.update { it.copy(currentTime = time, isCurrentTimeAos = isAos, orbitalPos = pos) }
|
||||||
val time = 0L.toTimerString()
|
|
||||||
_uiState.update { it.copy(currentTime = time, isCurrentTimeAos = false, orbitalPos = pos) }
|
|
||||||
}
|
|
||||||
satPass.aosTime > timeNow -> {
|
|
||||||
val time = satPass.aosTime.minus(timeNow).toTimerString()
|
|
||||||
_uiState.update { it.copy(currentTime = time, isCurrentTimeAos = true, orbitalPos = pos) }
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val time = satPass.losTime.minus(timeNow).toTimerString()
|
|
||||||
_uiState.update { it.copy(currentTime = time, isCurrentTimeAos = false, orbitalPos = pos) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processRadios(transmitters, satPass.orbitalObject, timeNow)
|
processRadios(transmitters, satPass.orbitalObject, timeNow)
|
||||||
sendPassData(pos)
|
sendPassData(pos)
|
||||||
delay(1000)
|
delay(1000)
|
||||||
@@ -140,10 +119,18 @@ class RadarViewModel(
|
|||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAction(action: RadarAction) {
|
fun onAction(action: RadarAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RadarAction.AddToCalendar -> addToCalendar(action.name, action.aosTime, action.losTime)
|
is RadarAction.AddToCalendar -> addToCalendar(action.name, action.aosTime, action.losTime)
|
||||||
is RadarAction.SelectTransmitter -> { _uiState.update { it.copy(selectedTransmitterUuid = action.uuid) } }
|
is RadarAction.SelectTransmitter -> _uiState.update { it.copy(selectedTransmitterUuid = action.uuid) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeTimer(isDeepSpace: Boolean, aosTime: Long, losTime: Long, timeNow: Long): Pair<String, Boolean> {
|
||||||
|
return when {
|
||||||
|
isDeepSpace -> 0L.toTimerString() to false
|
||||||
|
aosTime > timeNow -> (aosTime - timeNow).toTimerString() to true
|
||||||
|
else -> (losTime - timeNow).toTimerString() to false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,8 +170,10 @@ class RadarViewModel(
|
|||||||
|
|
||||||
private suspend fun processRadios(radios: List<SatRadio>, orbitalObject: OrbitalObject, time: Long) {
|
private suspend fun processRadios(radios: List<SatRadio>, orbitalObject: OrbitalObject, time: Long) {
|
||||||
val transmitters = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
|
val transmitters = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
|
||||||
|
val isFreqEnabled =
|
||||||
|
settingsRepo.rcSettings.value.frequencyState || settingsRepo.rcSettings.value.bluetoothFrequencyState
|
||||||
_uiState.update { state ->
|
_uiState.update { state ->
|
||||||
if (!settingsRepo.rcSettings.value.frequencyState && !settingsRepo.rcSettings.value.bluetoothFrequencyState) {
|
if (!isFreqEnabled) {
|
||||||
return@update state.copy(
|
return@update state.copy(
|
||||||
transmitters = transmitters,
|
transmitters = transmitters,
|
||||||
selectedTransmitterUuid = null,
|
selectedTransmitterUuid = null,
|
||||||
@@ -197,18 +186,12 @@ class RadarViewModel(
|
|||||||
val low = radio.downlinkLow
|
val low = radio.downlinkLow
|
||||||
val high = radio.downlinkHigh
|
val high = radio.downlinkHigh
|
||||||
when {
|
when {
|
||||||
low != null && high != null ->
|
low != null && high != null -> (low + high) / 2
|
||||||
(low + high) / 2
|
low != null -> low
|
||||||
low != null ->
|
|
||||||
low
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.copy(
|
state.copy(transmitters = transmitters, selectedTransmitterUuid = selectedUuid, selectedFrequency = freq)
|
||||||
transmitters = transmitters,
|
|
||||||
selectedTransmitterUuid = selectedUuid,
|
|
||||||
selectedFrequency = freq
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-13
@@ -26,14 +26,12 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
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.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
@@ -59,11 +57,15 @@ import androidx.navigation.navArgument
|
|||||||
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
import com.rtbishop.look4sat.core.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.core.presentation.CardButton
|
import com.rtbishop.look4sat.core.presentation.CardButton
|
||||||
import com.rtbishop.look4sat.core.presentation.IconCard
|
import com.rtbishop.look4sat.core.presentation.IconCard
|
||||||
|
import com.rtbishop.look4sat.core.presentation.NextPassRow
|
||||||
import com.rtbishop.look4sat.core.presentation.R
|
import com.rtbishop.look4sat.core.presentation.R
|
||||||
import com.rtbishop.look4sat.core.presentation.Screen
|
import com.rtbishop.look4sat.core.presentation.Screen
|
||||||
import com.rtbishop.look4sat.core.presentation.TimerRow
|
import com.rtbishop.look4sat.core.presentation.TimerRow
|
||||||
import com.rtbishop.look4sat.core.presentation.TopBar
|
import com.rtbishop.look4sat.core.presentation.TopBar
|
||||||
|
import com.rtbishop.look4sat.core.presentation.getDefaultPass
|
||||||
|
import com.rtbishop.look4sat.core.presentation.isVerticalLayout
|
||||||
import com.rtbishop.look4sat.core.presentation.layoutPadding
|
import com.rtbishop.look4sat.core.presentation.layoutPadding
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
fun NavGraphBuilder.radioControlDestination(navigateUp: () -> Unit) {
|
fun NavGraphBuilder.radioControlDestination(navigateUp: () -> Unit) {
|
||||||
val route = "${Screen.RadioControl.route}?catNum={catNum}&aosTime={aosTime}"
|
val route = "${Screen.RadioControl.route}?catNum={catNum}&aosTime={aosTime}"
|
||||||
@@ -86,16 +88,21 @@ private fun RadioControlScreen(uiState: RadioControlState, navigateUp: () -> Uni
|
|||||||
.keepScreenOn(),
|
.keepScreenOn(),
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
) {
|
) {
|
||||||
TopBar {
|
val currentPass = uiState.currentPass ?: getDefaultPass()
|
||||||
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
val isVertical = isVerticalLayout()
|
||||||
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
if (isVertical) {
|
||||||
uiState.currentPass?.let { pass ->
|
TopBar {
|
||||||
Text(
|
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
||||||
text = pass.name,
|
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
||||||
fontSize = 16.sp,
|
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
||||||
fontWeight = FontWeight.Medium,
|
}
|
||||||
modifier = Modifier.padding(horizontal = 8.dp)
|
TopBar { NextPassRow(pass = currentPass) }
|
||||||
)
|
} else {
|
||||||
|
TopBar {
|
||||||
|
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
||||||
|
TimerRow(timeString = uiState.currentTime, isTimeAos = uiState.isCurrentTimeAos)
|
||||||
|
NextPassRow(pass = currentPass, modifier = Modifier.weight(1f))
|
||||||
|
IconCard(action = navigateUp, resId = R.drawable.ic_back)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +272,7 @@ private fun CtcssSelector(uiState: RadioControlState) {
|
|||||||
FilterChip(
|
FilterChip(
|
||||||
selected = uiState.ctcssTone == tone,
|
selected = uiState.ctcssTone == tone,
|
||||||
onClick = { uiState.sendAction(RadioControlAction.SetCtcssTone(tone)) },
|
onClick = { uiState.sendAction(RadioControlAction.SetCtcssTone(tone)) },
|
||||||
label = { Text(String.format("%.1f", tone)) }
|
label = { Text(String.format(Locale.ENGLISH, "%.1f", tone)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -30,11 +30,9 @@ import com.rtbishop.look4sat.core.domain.repository.IContainerProvider
|
|||||||
import com.rtbishop.look4sat.core.domain.repository.IRadioTrackingService
|
import com.rtbishop.look4sat.core.domain.repository.IRadioTrackingService
|
||||||
import com.rtbishop.look4sat.core.domain.repository.ISatelliteRepo
|
import com.rtbishop.look4sat.core.domain.repository.ISatelliteRepo
|
||||||
import com.rtbishop.look4sat.core.domain.repository.ISettingsRepo
|
import com.rtbishop.look4sat.core.domain.repository.ISettingsRepo
|
||||||
import com.rtbishop.look4sat.core.domain.repository.RadioTrackingState
|
|
||||||
import com.rtbishop.look4sat.core.domain.utility.toTimerString
|
import com.rtbishop.look4sat.core.domain.utility.toTimerString
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@@ -108,8 +106,8 @@ class RadioControlViewModel(
|
|||||||
}
|
}
|
||||||
val isAos = pass != null && !pass.isDeepSpace && pass.aosTime > timeNow
|
val isAos = pass != null && !pass.isDeepSpace && pass.aosTime > timeNow
|
||||||
|
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
it.copy(
|
state.copy(
|
||||||
currentTime = timeStr,
|
currentTime = timeStr,
|
||||||
isCurrentTimeAos = isAos,
|
isCurrentTimeAos = isAos,
|
||||||
azimuth = String.format(Locale.ENGLISH, "%.1f", svc.azimuth),
|
azimuth = String.format(Locale.ENGLISH, "%.1f", svc.azimuth),
|
||||||
|
|||||||
Reference in new issue
Block a user