Implemented extended data output (#195)

This commit is contained in:
londre7 authored and GitHub committed 2026-02-20 18:42:55 +00:00
1 parent 7adbf951bc
commit 85ffe94266
19 files changed
+965 -322

No files matched your search

@@ -25,6 +25,7 @@ import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
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
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -33,8 +34,10 @@ 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.size import androidx.compose.foundation.layout.size
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.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -55,6 +58,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
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.graphics.Brush
import androidx.lifecycle.compose.collectAsStateWithLifecycle 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
@@ -194,7 +198,15 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
} }
} else { } else {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
TransmittersList(transmitters = uiState.transmitters) TransmittersList(
transmitters = uiState.transmitters,
selectedUuid = uiState.selectedTransmitterUuid,
onSelect = { uuid ->
if (uiState.selectedTransmitterUuid != null) {
uiState.sendAction(RadarAction.SelectTransmitter(uuid))
}
}
)
if (uiState.orbitalPos?.eclipsed == true) { if (uiState.orbitalPos?.eclipsed == true) {
EclipsedIndicator() EclipsedIndicator()
} }
@@ -282,7 +294,15 @@ private fun RadarScreen(uiState: RadarState, navigateUp: () -> Unit) {
} }
} else { } else {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
TransmittersList(transmitters = uiState.transmitters) TransmittersList(
transmitters = uiState.transmitters,
selectedUuid = uiState.selectedTransmitterUuid,
onSelect = { uuid ->
if (uiState.selectedTransmitterUuid != null) {
uiState.sendAction(RadarAction.SelectTransmitter(uuid))
}
}
)
if (uiState.orbitalPos?.eclipsed == true) { if (uiState.orbitalPos?.eclipsed == true) {
EclipsedIndicator() EclipsedIndicator()
} }
@@ -315,10 +335,19 @@ private fun RadarTextBottom(value: Double, text: String, isLeft: Boolean) {
} }
@Composable @Composable
private fun TransmittersList(transmitters: List<SatRadio>) { private fun TransmittersList(
transmitters: List<SatRadio>,
selectedUuid: String?,
onSelect: (String) -> Unit
) {
LazyColumn(modifier = Modifier.fillMaxSize()) { LazyColumn(modifier = Modifier.fillMaxSize()) {
items(items = transmitters, key = { item -> item.uuid }) { radio -> items(items = transmitters, key = { item -> item.uuid }) { radio ->
TransmitterItem(radio) TransmitterItem(
radio,
(selectedUuid != null),
(radio.uuid == selectedUuid),
{ onSelect(radio.uuid) }
)
} }
} }
} }
@@ -330,7 +359,7 @@ private fun TransmitterItemPreview() {
"", "Extremely powerful transmitter", true, 10000000000L, 10000000000L, "", "Extremely powerful transmitter", true, 10000000000L, 10000000000L,
null, 10000000000L, 10000000000L, "FSK AX.100 Mode 5", true, 0 null, 10000000000L, 10000000000L, "FSK AX.100 Mode 5", true, 0
) )
MainTheme { TransmitterItem(transmitter) } MainTheme { TransmitterItem(transmitter, true, true,{}) }
} }
@Composable @Composable
@@ -370,13 +399,24 @@ private fun EclipsedIndicator() {
} }
@Composable @Composable
private fun TransmitterItem(radio: SatRadio) { private fun TransmitterItem(
radio: SatRadio,
isClickable: Boolean,
isSelected: Boolean,
onClick: () -> Unit
) {
// LaunchedEffect(radio) { // LaunchedEffect(radio) {
// BluetoothCIV.updateOnce(radio) // BluetoothCIV.updateOnce(radio)
// } // }
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(color = MaterialTheme.colorScheme.background, Surface(
color = MaterialTheme.colorScheme.background,
modifier = Modifier
.fillMaxWidth()
.then(
if (isClickable) Modifier.clickable { onClick() } else Modifier
)
// modifier = Modifier.clickable(onClick = { // modifier = Modifier.clickable(onClick = {
// Log.d("BluetoothCivManager", radio.toString()) // Log.d("BluetoothCivManager", radio.toString())
// if(radio.uuid == BluetoothCIV.selected) BluetoothCIV.selected = "NONE" else // if(radio.uuid == BluetoothCIV.selected) BluetoothCIV.selected = "NONE" else
@@ -387,21 +427,51 @@ private fun TransmitterItem(radio: SatRadio) {
// }) // })
) { ) {
Surface(modifier = Modifier.padding(bottom = 2.dp)) { Surface(modifier = Modifier.padding(bottom = 2.dp)) {
Column( Box {
verticalArrangement = Arrangement.spacedBy(6.dp), Column(
modifier = Modifier verticalArrangement = Arrangement.spacedBy(6.dp),
.background(MaterialTheme.colorScheme.surface)
.padding(horizontal = 8.dp, vertical = 6.dp)
) {
Text(
text = fullTitle,
textAlign = TextAlign.Center,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .background(MaterialTheme.colorScheme.surface)
.infiniteMarquee() .padding(horizontal = 8.dp, vertical = 6.dp)
) ) {
FrequencyRow(radio = radio, isDownlink = true) Text(
FrequencyRow(radio = radio, isDownlink = false) text = fullTitle,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.infiniteMarquee()
)
FrequencyRow(radio = radio, isDownlink = true)
FrequencyRow(radio = radio, isDownlink = false)
}
if (isSelected) {
Box(
modifier = Modifier
.align(Alignment.TopStart)
.padding(start = 4.dp, top = 5.dp)
.background(
brush = Brush.radialGradient(
colors = listOf(
MaterialTheme.colorScheme.surface,
MaterialTheme.colorScheme.surface.copy(alpha = 0f)
),
radius = 80f
),
shape = CircleShape
)
.width(30.dp)
) {
Icon(
painter = painterResource(id = R.drawable.ic_radios),
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.align(Alignment.TopStart)
.padding(start = 4.dp)
.size(24.dp)
)
}
}
} }
} }
} }
@@ -31,9 +31,12 @@ data class RadarState(
val shouldShowSweep: Boolean, val shouldShowSweep: Boolean,
val shouldUseCompass: Boolean, val shouldUseCompass: Boolean,
val transmitters: List<SatRadio>, val transmitters: List<SatRadio>,
val selectedTransmitterUuid: String?,
val selectedFrequency: Long?,
val sendAction: (RadarAction) -> Unit val sendAction: (RadarAction) -> Unit
) )
sealed class RadarAction { sealed class RadarAction {
data class AddToCalendar(val name: String, val aosTime: Long, val losTime: Long) : RadarAction() data class AddToCalendar(val name: String, val aosTime: Long, val losTime: Long) : RadarAction()
data class SelectTransmitter(val uuid: String) : RadarAction()
} }
@@ -25,7 +25,10 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.data.framework.WithoutExtParams
import com.rtbishop.look4sat.data.framework.BluetoothReporter import com.rtbishop.look4sat.data.framework.BluetoothReporter
import com.rtbishop.look4sat.data.framework.BtService
import com.rtbishop.look4sat.data.framework.ExtendedParams
import com.rtbishop.look4sat.data.framework.NetworkReporter import com.rtbishop.look4sat.data.framework.NetworkReporter
import com.rtbishop.look4sat.domain.model.SatRadio import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.predict.OrbitalObject import com.rtbishop.look4sat.domain.predict.OrbitalObject
@@ -65,6 +68,8 @@ class RadarViewModel(
satTrack = emptyList(), satTrack = emptyList(),
shouldShowSweep = settingsRepo.otherSettings.value.stateOfSweep, shouldShowSweep = settingsRepo.otherSettings.value.stateOfSweep,
shouldUseCompass = settingsRepo.otherSettings.value.stateOfSensors, shouldUseCompass = settingsRepo.otherSettings.value.stateOfSensors,
selectedTransmitterUuid = null,
selectedFrequency = null,
transmitters = emptyList(), transmitters = emptyList(),
sendAction = ::handleAction, sendAction = ::handleAction,
) )
@@ -109,9 +114,9 @@ class RadarViewModel(
_uiState.update { it.copy(currentTime = time, isCurrentTimeAos = false) } _uiState.update { it.copy(currentTime = time, isCurrentTimeAos = false) }
} }
} }
processRadios(transmitters, satPass.orbitalObject, timeNow)
sendPassData(satPass, pos, satPass.orbitalObject) sendPassData(satPass, pos, satPass.orbitalObject)
sendPassDataBT(pos) sendPassDataBT(pos)
processRadios(transmitters, satPass.orbitalObject, timeNow)
delay(1000) delay(1000)
} }
} }
@@ -126,6 +131,7 @@ class RadarViewModel(
private fun handleAction(action: RadarAction) { private fun handleAction(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) } }
} }
} }
@@ -141,25 +147,47 @@ class RadarViewModel(
if (settingsRepo.rcSettings.value.rotatorState) { if (settingsRepo.rcSettings.value.rotatorState) {
val server = settingsRepo.rcSettings.value.rotatorAddress val server = settingsRepo.rcSettings.value.rotatorAddress
val port = settingsRepo.rcSettings.value.rotatorPort.toInt() val port = settingsRepo.rcSettings.value.rotatorPort.toInt()
val format = settingsRepo.rcSettings.value.rotatorFormat
val azimuth = orbitalPos.azimuth.toDegrees().round(2) val azimuth = orbitalPos.azimuth.toDegrees().round(2)
val elevation = orbitalPos.elevation.toDegrees().round(2) val elevation = orbitalPos.elevation.toDegrees().round(2)
networkReporter.reportRotation(server, port, azimuth, elevation) networkReporter.reportRotation(format, azimuth, elevation, ExtendedParams(server, port))
}
if (settingsRepo.rcSettings.value.frequencyState) {
_uiState.value.selectedFrequency?.let { freq ->
val server = settingsRepo.rcSettings.value.frequencyAddress
val port = settingsRepo.rcSettings.value.frequencyPort.toInt()
val format = settingsRepo.rcSettings.value.frequencyFormat
networkReporter.reportFrequency(format,freq, ExtendedParams(server, port))
}
} }
} }
} }
private fun sendPassDataBT(orbitalPos: OrbitalPos) { private fun sendPassDataBT(orbitalPos: OrbitalPos) {
viewModelScope.launch { viewModelScope.launch {
if (settingsRepo.rcSettings.value.bluetoothState) { if (settingsRepo.rcSettings.value.bluetoothRotatorState) {
val btDevice = settingsRepo.rcSettings.value.bluetoothAddress val btRotatorDevice = settingsRepo.rcSettings.value.bluetoothRotatorAddress
if (bluetoothReporter.isConnected()) { if (bluetoothReporter.isConnected(BtService.ROTATOR)) {
val format = settingsRepo.rcSettings.value.bluetoothFormat val format = settingsRepo.rcSettings.value.bluetoothRotatorFormat
val azimuth = orbitalPos.azimuth.toDegrees().round(0).toInt() val azimuth = orbitalPos.azimuth.toDegrees().round(0)
val elevation = orbitalPos.elevation.toDegrees().round(0).toInt() val elevation = orbitalPos.elevation.toDegrees().round(0)
bluetoothReporter.reportRotation(format, azimuth, elevation) bluetoothReporter.reportRotation(format, azimuth, elevation, WithoutExtParams(0))
} else if (!bluetoothReporter.isConnecting()) { } else if (!bluetoothReporter.isConnecting(BtService.ROTATOR)) {
// Log.i("BTReporter", "BTReporter: Attempting to connect...") // Log.i("BTReporter", "BTReporter (rotator): Attempting to connect...")
bluetoothReporter.connectBTDevice(btDevice) bluetoothReporter.connect(BtService.ROTATOR,btRotatorDevice)
}
}
if (settingsRepo.rcSettings.value.bluetoothFrequencyState) {
val btFrequencyDevice = settingsRepo.rcSettings.value.bluetoothFrequencyAddress
if (bluetoothReporter.isConnected(BtService.FREQUENCY)) {
val format = settingsRepo.rcSettings.value.bluetoothFrequencyFormat
val frequency = _uiState.value.selectedFrequency
frequency?.let { freq ->
bluetoothReporter.reportFrequency(format, freq, WithoutExtParams(0))
}
} else if (!bluetoothReporter.isConnecting(BtService.FREQUENCY)) {
// Log.i("BTReporter", "BTReporter (frequency): Attempting to connect...")
bluetoothReporter.connect(BtService.FREQUENCY, btFrequencyDevice)
} }
} }
} }
@@ -167,7 +195,33 @@ 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)
_uiState.update { it.copy(transmitters = transmitters) } _uiState.update { state ->
if (!settingsRepo.rcSettings.value.frequencyState && !settingsRepo.rcSettings.value.bluetoothFrequencyState) {
return@update state.copy(
transmitters = transmitters,
selectedTransmitterUuid = null,
selectedFrequency = null
)
}
val selectedUuid = state.selectedTransmitterUuid ?: transmitters.firstOrNull()?.uuid
val selectedRadio = transmitters.firstOrNull { it.uuid == selectedUuid }
val freq = selectedRadio?.let { radio ->
val low = radio.downlinkLow
val high = radio.downlinkHigh
when {
low != null && high != null ->
(low + high) / 2
low != null ->
low
else -> null
}
}
state.copy(
transmitters = transmitters,
selectedTransmitterUuid = selectedUuid,
selectedFrequency = freq
)
}
} }
companion object { companion object {
@@ -40,6 +40,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.rtbishop.look4sat.presentation.LocalSpacing import com.rtbishop.look4sat.presentation.LocalSpacing
import com.rtbishop.look4sat.domain.model.RCSettings
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
@@ -199,3 +200,283 @@ fun DataSourcesDialog(
} }
} }
} }
@Preview(showBackground = true)
@Composable
fun PreviewNetworkOutputDialog() {
MainTheme {
NetworkOutputDialog(
initialSettings = RCSettings(
rotatorState = false,
rotatorAddress = "127.0.0.1",
rotatorPort = "4533",
rotatorFormat = $$"P $AZ $EL",
frequencyState = false,
frequencyAddress = "127.0.0.1",
frequencyPort = "4532",
frequencyFormat = $$"set_freq $FREQ",
bluetoothRotatorState = false,
bluetoothRotatorFormat = $$"W$AZ $EL",
bluetoothRotatorName = "Default",
bluetoothRotatorAddress = "00:0C:BF:13:80:5D",
bluetoothFrequencyState = false,
bluetoothFrequencyAddress = "00:0C:BF:13:80:5D",
bluetoothFrequencyFormat = $$"FA$FREQ"
),
onDismiss = {},
onSave = { _, _, _, _, _, _, _, _ -> }
)
}
}
@Composable
fun NetworkOutputDialog(
initialSettings: RCSettings,
onDismiss: () -> Unit,
onSave: (
Boolean, String, String, String,
Boolean, String, String, String
) -> Unit
) {
val rotatorState = rememberSaveable { mutableStateOf(initialSettings.rotatorState) }
val rotatorAddress = rememberSaveable { mutableStateOf(initialSettings.rotatorAddress) }
val rotatorPort = rememberSaveable { mutableStateOf(initialSettings.rotatorPort) }
val rotatorFormat = rememberSaveable { mutableStateOf(initialSettings.rotatorFormat) }
val frequencyState = rememberSaveable { mutableStateOf(initialSettings.frequencyState) }
val frequencyAddress = rememberSaveable { mutableStateOf(initialSettings.frequencyAddress) }
val frequencyPort = rememberSaveable { mutableStateOf(initialSettings.frequencyPort) }
val frequencyFormat = rememberSaveable { mutableStateOf(initialSettings.frequencyFormat) }
val onAccept = {
onSave(
rotatorState.value,
rotatorAddress.value,
rotatorPort.value,
rotatorFormat.value,
frequencyState.value,
frequencyAddress.value,
frequencyPort.value,
frequencyFormat.value
)
onDismiss()
}
SharedDialog(
title = stringResource(R.string.prefs_net_title),
onCancel = onDismiss,
onAccept = onAccept
) {
Column(modifier = Modifier.padding(8.dp)) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.prefs_net_rotator_switch))
Switch(
checked = rotatorState.value,
onCheckedChange = { rotatorState.value = it }
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxWidth()
) {
OutlinedTextField(
value = rotatorAddress.value,
onValueChange = { rotatorAddress.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_net_rotator_ip_hint)) },
modifier = Modifier.weight(1.5f),
enabled = rotatorState.value
)
OutlinedTextField(
value = rotatorPort.value,
onValueChange = { rotatorPort.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_net_rotator_port_hint)) },
modifier = Modifier.weight(1f),
enabled = rotatorState.value
)
}
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = rotatorFormat.value,
onValueChange = { rotatorFormat.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_net_rotator_format_hint)) },
modifier = Modifier.fillMaxWidth(),
enabled = rotatorState.value
)
Spacer(modifier = Modifier.height(12.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.prefs_net_frequency_switch))
Switch(
checked = frequencyState.value,
onCheckedChange = { frequencyState.value = it }
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxWidth()
) {
OutlinedTextField(
value = frequencyAddress.value,
onValueChange = { frequencyAddress.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_net_frequency_ip_hint)) },
modifier = Modifier.weight(1.5f),
enabled = frequencyState.value
)
OutlinedTextField(
value = frequencyPort.value,
onValueChange = { frequencyPort.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_net_frequency_port_hint)) },
modifier = Modifier.weight(1f),
enabled = frequencyState.value
)
}
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = frequencyFormat.value,
onValueChange = { frequencyFormat.value = it },
maxLines = 2,
label = { Text(stringResource(R.string.prefs_net_frequency_format_hint)) },
modifier = Modifier.fillMaxWidth(),
enabled = frequencyState.value
)
}
}
}
@Preview(showBackground = true)
@Composable
fun PreviewBluetoothOutputDialog() {
MainTheme {
BluetoothOutputDialog(
initialSettings = RCSettings(
rotatorState = false,
rotatorAddress = "127.0.0.1",
rotatorPort = "4533",
rotatorFormat = $$"P $AZ $EL",
frequencyState = false,
frequencyAddress = "127.0.0.1",
frequencyPort = "4532",
frequencyFormat = $$"set_freq $FREQ",
bluetoothRotatorState = false,
bluetoothRotatorFormat = $$"W$AZ $EL",
bluetoothRotatorName = "Default",
bluetoothRotatorAddress = "00:0C:BF:13:80:5D",
bluetoothFrequencyState = false,
bluetoothFrequencyAddress = "00:0C:BF:13:80:5D",
bluetoothFrequencyFormat = $$"FA$FREQ"
),
onDismiss = {},
onSave = { _, _, _, _, _, _ -> }
)
}
}
@Composable
fun BluetoothOutputDialog(
initialSettings: RCSettings,
onDismiss: () -> Unit,
onSave: (
Boolean, String, String,
Boolean, String, String
) -> Unit
) {
val rotatorState = rememberSaveable { mutableStateOf(initialSettings.bluetoothRotatorState) }
val rotatorAddress = rememberSaveable { mutableStateOf(initialSettings.bluetoothRotatorAddress) }
val rotatorFormat = rememberSaveable { mutableStateOf(initialSettings.bluetoothRotatorFormat) }
val frequencyState = rememberSaveable { mutableStateOf(initialSettings.bluetoothFrequencyState) }
val frequencyAddress = rememberSaveable { mutableStateOf(initialSettings.bluetoothFrequencyAddress) }
val frequencyFormat = rememberSaveable { mutableStateOf(initialSettings.bluetoothFrequencyFormat) }
val onAccept = {
onSave(
rotatorState.value,
rotatorAddress.value,
rotatorFormat.value,
frequencyState.value,
frequencyAddress.value,
frequencyFormat.value
)
onDismiss()
}
SharedDialog(
title = stringResource(R.string.prefs_bt_title),
onCancel = onDismiss,
onAccept = onAccept
) {
Column(modifier = Modifier.padding(8.dp)) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.prefs_bt_rotator_switch))
Switch(
checked = rotatorState.value,
onCheckedChange = { rotatorState.value = it }
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxWidth()
) {
OutlinedTextField(
value = rotatorAddress.value,
onValueChange = { rotatorAddress.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_bt_rotator_device_hint)) },
modifier = Modifier.weight(1.5f),
enabled = rotatorState.value
)
OutlinedTextField(
value = rotatorFormat.value,
onValueChange = { rotatorFormat.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_bt_rotator_output_hint)) },
modifier = Modifier.weight(1f),
enabled = rotatorState.value
)
}
Spacer(modifier = Modifier.height(12.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.prefs_bt_frequency_switch))
Switch(
checked = frequencyState.value,
onCheckedChange = { frequencyState.value = it }
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxWidth()
) {
OutlinedTextField(
value = frequencyAddress.value,
onValueChange = { frequencyAddress.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_bt_frequency_device_hint)) },
modifier = Modifier.weight(1.5f),
enabled = frequencyState.value
)
OutlinedTextField(
value = frequencyFormat.value,
onValueChange = { frequencyFormat.value = it },
singleLine = true,
label = { Text(stringResource(R.string.prefs_bt_frequency_output_hint)) },
modifier = Modifier.weight(1f),
enabled = frequencyState.value
)
}
}
}
}
@@ -37,7 +37,6 @@ import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -60,7 +59,6 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.model.OtherSettings import com.rtbishop.look4sat.domain.model.OtherSettings
import com.rtbishop.look4sat.domain.model.RCSettings
import com.rtbishop.look4sat.domain.predict.GeoPos import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.presentation.MainTheme import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.Screen import com.rtbishop.look4sat.presentation.Screen
@@ -87,19 +85,6 @@ fun NavGraphBuilder.settingsDestination() {
@Composable @Composable
private fun SettingsScreen(uiState: SettingsState) { private fun SettingsScreen(uiState: SettingsState) {
// Permissions setup
val bluetoothContract = ActivityResultContracts.RequestPermission()
val bluetoothError = stringResource(R.string.prefs_bt_perm_error)
val bluetoothPerm = when {
Build.VERSION.SDK_INT < Build.VERSION_CODES.S -> Manifest.permission.BLUETOOTH
else -> Manifest.permission.BLUETOOTH_CONNECT
}
val bluetoothRequest = rememberLauncherForActivityResult(bluetoothContract) { isGranted ->
if (!isGranted) {
uiState.sendRCAction(RCAction.SetBluetoothState(false))
uiState.sendSystemAction(SystemAction.ShowToast(bluetoothError))
}
}
val locationContract = ActivityResultContracts.RequestMultiplePermissions() val locationContract = ActivityResultContracts.RequestMultiplePermissions()
val locationError = stringResource(R.string.prefs_loc_gps_error) val locationError = stringResource(R.string.prefs_loc_gps_error)
val locationPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION val locationPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
@@ -180,12 +165,84 @@ private fun SettingsScreen(uiState: SettingsState) {
val setRotatorState = { value: Boolean -> uiState.sendRCAction(RCAction.SetRotatorState(value)) } val setRotatorState = { value: Boolean -> uiState.sendRCAction(RCAction.SetRotatorState(value)) }
val setRotatorAddress = { value: String -> uiState.sendRCAction(RCAction.SetRotatorAddress(value)) } val setRotatorAddress = { value: String -> uiState.sendRCAction(RCAction.SetRotatorAddress(value)) }
val setRotatorPort = { value: String -> uiState.sendRCAction(RCAction.SetRotatorPort(value)) } val setRotatorPort = { value: String -> uiState.sendRCAction(RCAction.SetRotatorPort(value)) }
val setBluetoothState = { value: Boolean -> val setRotatorFormat = { value: String -> uiState.sendRCAction(RCAction.SetRotatorFormat(value)) }
bluetoothRequest.launch(bluetoothPerm) val setFrequencyState = { value: Boolean -> uiState.sendRCAction(RCAction.SetFrequencyState(value)) }
uiState.sendRCAction(RCAction.SetBluetoothState(value)) val setFrequencyAddress = { value: String -> uiState.sendRCAction(RCAction.SetFrequencyAddress(value)) }
val setFrequencyPort = { value: String -> uiState.sendRCAction(RCAction.SetFrequencyPort(value)) }
val setFrequencyFormat = { value: String -> uiState.sendRCAction(RCAction.SetFrequencyFormat(value)) }
val setBluetoothRotatorState = { value: Boolean -> uiState.sendRCAction(RCAction.SetBluetoothRotatorState(value)) }
val setBluetoothRotatorAddress = { value: String -> uiState.sendRCAction(RCAction.SetBluetoothRotatorAddress(value)) }
val setBluetoothRotatorFormat = { value: String -> uiState.sendRCAction(RCAction.SetBluetoothRotatorFormat(value)) }
val setBluetoothFrequencyState = { value: Boolean -> uiState.sendRCAction(RCAction.SetBluetoothFrequencyState(value)) }
val setBluetoothFrequencyAddress = { value: String -> uiState.sendRCAction(RCAction.SetBluetoothFrequencyAddress(value)) }
val setBluetoothFrequencyFormat = { value: String -> uiState.sendRCAction(RCAction.SetBluetoothFrequencyFormat(value)) }
// Network data output
val networkDialogState = rememberSaveable { mutableStateOf(false) }
val showNetworkDialog = { networkDialogState.value = true }
val dismissNetworkDialog = { networkDialogState.value = false }
if (networkDialogState.value) {
NetworkOutputDialog(
initialSettings = rcSettings,
onDismiss = dismissNetworkDialog,
onSave = { rotatorState,
rotatorAddress,
rotatorPort,
rotatorFormat,
frequencyState,
frequencyAddress,
frequencyPort,
frequencyFormat ->
setRotatorState(rotatorState)
setRotatorAddress(rotatorAddress)
setRotatorPort(rotatorPort)
setRotatorFormat(rotatorFormat)
setFrequencyState(frequencyState)
setFrequencyAddress(frequencyAddress)
setFrequencyPort(frequencyPort)
setFrequencyFormat(frequencyFormat)
}
)
}
// Bluetooth data output
val bluetoothDialogState = rememberSaveable { mutableStateOf(false) }
val bluetoothContract = ActivityResultContracts.RequestPermission()
val bluetoothError = stringResource(R.string.prefs_bt_perm_error)
val bluetoothPerm = when {
Build.VERSION.SDK_INT < Build.VERSION_CODES.S -> Manifest.permission.BLUETOOTH
else -> Manifest.permission.BLUETOOTH_CONNECT
}
val bluetoothRequest = rememberLauncherForActivityResult(bluetoothContract) { isGranted ->
if (!isGranted)
{
uiState.sendRCAction(RCAction.SetBluetoothRotatorState(false))
uiState.sendRCAction(RCAction.SetBluetoothFrequencyState(false))
uiState.sendSystemAction(SystemAction.ShowToast(bluetoothError))
}
else { bluetoothDialogState.value = true }
}
val showBluetoothDialog = { bluetoothRequest.launch(bluetoothPerm) }
val dismissBluetoothDialog = { bluetoothDialogState.value = false }
if (bluetoothDialogState.value) {
BluetoothOutputDialog(
initialSettings = rcSettings,
onDismiss = dismissBluetoothDialog,
onSave = { rotatorState,
rotatorAddress,
rotatorFormat,
frequencyState,
frequencyAddress,
frequencyFormat ->
setBluetoothRotatorState(rotatorState)
setBluetoothRotatorAddress(rotatorAddress)
setBluetoothRotatorFormat(rotatorFormat)
setBluetoothFrequencyState(frequencyState)
setBluetoothFrequencyAddress(frequencyAddress)
setBluetoothFrequencyFormat(frequencyFormat)
}
)
} }
val setBluetoothAddress = { value: String -> uiState.sendRCAction(RCAction.SetBluetoothAddress(value)) }
val setBluetoothFormat = { value: String -> uiState.sendRCAction(RCAction.SetBluetoothFormat(value)) }
// Other settings // Other settings
val otherSettings = uiState.otherSettings val otherSettings = uiState.otherSettings
@@ -242,8 +299,7 @@ private fun SettingsScreen(uiState: SettingsState) {
LocationCard(posSettings, setGpsPos, showPosDialog, showLocDialog, dismissPos, uiState.sendSystemAction) LocationCard(posSettings, setGpsPos, showPosDialog, showLocDialog, dismissPos, uiState.sendSystemAction)
} }
item { DataCard(dataSettings, updateFromWeb, clearAllData, showDataSourcesDialog) } item { DataCard(dataSettings, updateFromWeb, clearAllData, showDataSourcesDialog) }
item { NetworkOutputCard(rcSettings, setRotatorState, setRotatorAddress, setRotatorPort) } item { OutputCard({ showNetworkDialog() }, { showBluetoothDialog() }) }
item { BluetoothOutputCard(rcSettings, setBluetoothState, setBluetoothAddress, setBluetoothFormat) }
item { OtherCard(otherSettings, toggleUtc, toggleUpdate, toggleSweep, toggleSensor) } item { OtherCard(otherSettings, toggleUtc, toggleUpdate, toggleSweep, toggleSensor) }
item { CardCredits() } item { CardCredits() }
} }
@@ -375,126 +431,32 @@ private fun DataCard(
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun NetworkOutputCardPreview() = MainTheme { private fun OutputCardPreview() = MainTheme { OutputCard({}, {}) }
val settings = RCSettings(
rotatorState = false,
rotatorAddress = "127.0.0.1",
rotatorPort = "4533",
bluetoothState = false,
bluetoothFormat = $$"W$AZ $EL",
bluetoothName = "Name",
bluetoothAddress = "00:0C:BF:13:80:5D"
)
NetworkOutputCard(settings)
}
@Composable @Composable
private fun NetworkOutputCard( private fun OutputCard(
settings: RCSettings, onNetworkClick: () -> Unit,
setRotatorState: (Boolean) -> Unit = {}, onBluetoothClick: () -> Unit
setRotatorAddress: (String) -> Unit = {},
setRotatorPort: (String) -> Unit = {}
) { ) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) { ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) { Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) {
Text( Text(
text = stringResource(id = R.string.prefs_net_title), text = stringResource(id = R.string.prefs_data_output_title),
color = MaterialTheme.colorScheme.primary color = MaterialTheme.colorScheme.primary
) )
Row( Spacer(modifier = Modifier.height(6.dp))
horizontalArrangement = Arrangement.SpaceBetween, Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
verticalAlignment = Alignment.CenterVertically, CardButton(
modifier = Modifier.fillMaxWidth() onClick = onNetworkClick,
) { text = stringResource(id = R.string.prefs_net_output),
Text(text = stringResource(R.string.prefs_net_switch)) modifier = Modifier.weight(1f)
Switch(checked = settings.rotatorState, onCheckedChange = { setRotatorState(it) })
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
OutlinedTextField(
value = settings.rotatorAddress,
singleLine = true,
label = { Text(text = stringResource(R.string.prefs_net_ip_hint)) },
onValueChange = { setRotatorAddress(it) },
modifier = Modifier.weight(1.5f),
enabled = settings.rotatorState
) )
OutlinedTextField( CardButton(
value = settings.rotatorPort, onClick = onBluetoothClick,
onValueChange = { setRotatorPort(it) }, text = stringResource(id = R.string.prefs_bt_output),
label = { Text(text = stringResource(R.string.prefs_net_port_hint)) }, modifier = Modifier.weight(1f)
singleLine = true,
modifier = Modifier.weight(1f),
enabled = settings.rotatorState
) )
} }
Spacer(modifier = Modifier.height(4.dp))
}
}
}
@Preview(showBackground = true)
@Composable
private fun BluetoothOutputCardPreview() = MainTheme {
val settings = RCSettings(
rotatorState = false,
rotatorAddress = "127.0.0.1",
rotatorPort = "4533",
bluetoothState = false,
bluetoothFormat = $$"W$AZ $EL",
bluetoothName = "Name",
bluetoothAddress = "00:0C:BF:13:80:5D"
)
BluetoothOutputCard(settings)
}
@Composable
private fun BluetoothOutputCard(
settings: RCSettings,
setBluetoothState: (Boolean) -> Unit = {},
setBluetoothAddress: (String) -> Unit = {},
setBluetoothFormat: (String) -> Unit = {}
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) {
Text(
text = stringResource(id = R.string.prefs_bt_title),
color = MaterialTheme.colorScheme.primary
)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.prefs_bt_switch))
Switch(checked = settings.bluetoothState, onCheckedChange = { setBluetoothState(it) })
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
OutlinedTextField(
value = settings.bluetoothAddress,
singleLine = true,
label = { Text(text = stringResource(R.string.prefs_bt_device_hint)) },
onValueChange = { setBluetoothAddress(it) },
modifier = Modifier.weight(1.5f),
enabled = settings.bluetoothState
)
OutlinedTextField(
value = settings.bluetoothFormat,
onValueChange = { setBluetoothFormat(it) },
label = { Text(text = stringResource(R.string.prefs_bt_output_hint)) },
singleLine = true,
modifier = Modifier.weight(1f),
enabled = settings.bluetoothState
)
}
Spacer(modifier = Modifier.height(4.dp))
} }
} }
} }
@@ -71,10 +71,18 @@ sealed class RCAction {
data class SetRotatorState(val value: Boolean) : RCAction() data class SetRotatorState(val value: Boolean) : RCAction()
data class SetRotatorAddress(val value: String) : RCAction() data class SetRotatorAddress(val value: String) : RCAction()
data class SetRotatorPort(val value: String) : RCAction() data class SetRotatorPort(val value: String) : RCAction()
data class SetBluetoothState(val value: Boolean) : RCAction() data class SetRotatorFormat(val value: String) : RCAction()
data class SetBluetoothFormat(val value: String) : RCAction() data class SetFrequencyState(val value: Boolean) : RCAction()
data class SetBluetoothName(val value: String) : RCAction() data class SetFrequencyAddress(val value: String) : RCAction()
data class SetBluetoothAddress(val value: String) : RCAction() data class SetFrequencyPort(val value: String) : RCAction()
data class SetFrequencyFormat(val value: String) : RCAction()
data class SetBluetoothRotatorState(val value: Boolean) : RCAction()
data class SetBluetoothRotatorFormat(val value: String) : RCAction()
data class SetBluetoothRotatorName(val value: String) : RCAction()
data class SetBluetoothRotatorAddress(val value: String) : RCAction()
data class SetBluetoothFrequencyState(val value: Boolean) : RCAction()
data class SetBluetoothFrequencyFormat(val value: String) : RCAction()
data class SetBluetoothFrequencyAddress(val value: String) : RCAction()
} }
sealed class DataSourcesAction { sealed class DataSourcesAction {
@@ -152,10 +152,18 @@ class SettingsViewModel(
is RCAction.SetRotatorState -> settingsRepo.setRotatorState(action.value) is RCAction.SetRotatorState -> settingsRepo.setRotatorState(action.value)
is RCAction.SetRotatorAddress -> settingsRepo.setRotatorAddress(action.value) is RCAction.SetRotatorAddress -> settingsRepo.setRotatorAddress(action.value)
is RCAction.SetRotatorPort -> settingsRepo.setRotatorPort(action.value) is RCAction.SetRotatorPort -> settingsRepo.setRotatorPort(action.value)
is RCAction.SetBluetoothState -> settingsRepo.setBluetoothState(action.value) is RCAction.SetRotatorFormat -> settingsRepo.setRotatorFormat(action.value)
is RCAction.SetBluetoothAddress -> settingsRepo.setBluetoothAddress(action.value) is RCAction.SetFrequencyState -> settingsRepo.setFrequencyState(action.value)
is RCAction.SetBluetoothFormat -> settingsRepo.setBluetoothFormat(action.value) is RCAction.SetFrequencyAddress -> settingsRepo.setFrequencyAddress(action.value)
is RCAction.SetBluetoothName -> settingsRepo.setBluetoothName(action.value) is RCAction.SetFrequencyPort -> settingsRepo.setFrequencyPort(action.value)
is RCAction.SetFrequencyFormat -> settingsRepo.setFrequencyFormat(action.value)
is RCAction.SetBluetoothRotatorState -> settingsRepo.setBluetoothRotatorState(action.value)
is RCAction.SetBluetoothRotatorAddress -> settingsRepo.setBluetoothRotatorAddress(action.value)
is RCAction.SetBluetoothRotatorFormat -> settingsRepo.setBluetoothRotatorFormat(action.value)
is RCAction.SetBluetoothRotatorName -> settingsRepo.setBluetoothRotatorName(action.value)
is RCAction.SetBluetoothFrequencyState -> settingsRepo.setBluetoothFrequencyState(action.value)
is RCAction.SetBluetoothFrequencyFormat -> settingsRepo.setBluetoothFrequencyFormat(action.value)
is RCAction.SetBluetoothFrequencyAddress -> settingsRepo.setBluetoothFrequencyAddress(action.value)
} }
} }
+18 -6
View File
@@ -112,15 +112,27 @@
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string> <string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string> <string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
<string name="prefs_data_output_title">Data output</string>
<string name="prefs_net_output">Network</string>
<string name="prefs_bt_output">Bluetooth</string>
<string name="prefs_net_title">Salida de datos de red</string> <string name="prefs_net_title">Salida de datos de red</string>
<string name="prefs_net_switch">Habilitar salida</string> <string name="prefs_net_rotator_switch">Enable rotation output</string>
<string name="prefs_net_ip_hint">Dirección IP</string> <string name="prefs_net_rotator_ip_hint">Dirección IP</string>
<string name="prefs_net_port_hint">Puerto</string> <string name="prefs_net_rotator_port_hint">Puerto</string>
<string name="prefs_net_rotator_format_hint">Formato de datos</string>
<string name="prefs_net_frequency_switch">Enable frequency output</string>
<string name="prefs_net_frequency_ip_hint">Dirección IP</string>
<string name="prefs_net_frequency_port_hint">Puerto</string>
<string name="prefs_net_frequency_format_hint">Formato de datos</string>
<string name="prefs_bt_title">Salida por Bluetooth</string> <string name="prefs_bt_title">Salida por Bluetooth</string>
<string name="prefs_bt_switch">Habilitar salida</string> <string name="prefs_bt_rotator_switch">Enable rotation output</string>
<string name="prefs_bt_device_hint">Id dispositivo</string> <string name="prefs_bt_rotator_device_hint">Id dispositivo</string>
<string name="prefs_bt_output_hint">Formato de datos</string> <string name="prefs_bt_rotator_output_hint">Formato de datos</string>
<string name="prefs_bt_frequency_switch">Enable frequency output</string>
<string name="prefs_bt_frequency_device_hint">Id dispositivo</string>
<string name="prefs_bt_frequency_output_hint">Formato de datos</string>
<string name="prefs_bt_perm_error">Revisa los permisos de Bluetooth</string> <string name="prefs_bt_perm_error">Revisa los permisos de Bluetooth</string>
<string name="prefs_other_title">Otros ajustes</string> <string name="prefs_other_title">Otros ajustes</string>
+18 -6
View File
@@ -111,15 +111,27 @@
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string> <string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
<string name="prefs_data_sources_transceivers_switch">Custom Transceivers URL</string> <string name="prefs_data_sources_transceivers_switch">Custom Transceivers URL</string>
<string name="prefs_data_output_title">Вывод данных</string>
<string name="prefs_net_output">Сеть</string>
<string name="prefs_bt_output">Bluetooth</string>
<string name="prefs_net_title">Вывод сетевых данных</string> <string name="prefs_net_title">Вывод сетевых данных</string>
<string name="prefs_net_switch">Включить вывод</string> <string name="prefs_net_rotator_switch">Включить вывод ротатора</string>
<string name="prefs_net_ip_hint">IP адрес</string> <string name="prefs_net_rotator_ip_hint">IP адрес</string>
<string name="prefs_net_port_hint">Порт</string> <string name="prefs_net_rotator_port_hint">Порт</string>
<string name="prefs_net_rotator_format_hint">Формат</string>
<string name="prefs_net_frequency_switch">Включить вывод частоты</string>
<string name="prefs_net_frequency_ip_hint">IP адрес</string>
<string name="prefs_net_frequency_port_hint">Порт</string>
<string name="prefs_net_frequency_format_hint">Формат</string>
<string name="prefs_bt_title">Вывод данных Bluetooth</string> <string name="prefs_bt_title">Вывод данных Bluetooth</string>
<string name="prefs_bt_switch">Включить вывод</string> <string name="prefs_bt_rotator_switch">Включить вывод ротатора</string>
<string name="prefs_bt_device_hint">Id устройства</string> <string name="prefs_bt_rotator_device_hint">Id устройства</string>
<string name="prefs_bt_output_hint">Формат данных</string> <string name="prefs_bt_rotator_output_hint">Формат</string>
<string name="prefs_bt_frequency_switch">Включить вывод частоты</string>
<string name="prefs_bt_frequency_device_hint">Id устройства</string>
<string name="prefs_bt_frequency_output_hint">Формат</string>
<string name="prefs_bt_perm_error">Нет разрешения использовать bluetooth</string> <string name="prefs_bt_perm_error">Нет разрешения использовать bluetooth</string>
<string name="prefs_other_title">Другие настройки</string> <string name="prefs_other_title">Другие настройки</string>
+18 -6
View File
@@ -112,15 +112,27 @@
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string> <string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string> <string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
<string name="prefs_data_output_title">Data output</string>
<string name="prefs_net_output">Network</string>
<string name="prefs_bt_output">Bluetooth</string>
<string name="prefs_net_title">ජාල ප්‍රතිදානය</string> <string name="prefs_net_title">ජාල ප්‍රතිදානය</string>
<string name="prefs_net_switch">ප්‍රතිදානය සක්‍රීය කරන්න</string> <string name="prefs_net_rotator_switch">Enable rotation output</string>
<string name="prefs_net_ip_hint">IP ලිපිනය</string> <string name="prefs_net_rotator_ip_hint">IP ලිපිනය</string>
<string name="prefs_net_port_hint">Port</string> <string name="prefs_net_rotator_port_hint">Port</string>
<string name="prefs_net_rotator_format_hint">දත්ත අකාරය</string>
<string name="prefs_net_frequency_switch">Enable frequency output</string>
<string name="prefs_net_frequency_ip_hint">IP ලිපිනය</string>
<string name="prefs_net_frequency_port_hint">Port</string>
<string name="prefs_net_frequency_format_hint">දත්ත අකාරය</string>
<string name="prefs_bt_title">Bluetooth දත්ත ප්‍රතිදානය</string> <string name="prefs_bt_title">Bluetooth දත්ත ප්‍රතිදානය</string>
<string name="prefs_bt_switch">ප්‍රතිදානය සක්‍රීය කරන්න</string> <string name="prefs_bt_rotator_switch">Enable rotation output</string>
<string name="prefs_bt_device_hint">උපාංග id</string> <string name="prefs_bt_rotator_device_hint">උපාංග id</string>
<string name="prefs_bt_output_hint">දත්ත අකාරය</string> <string name="prefs_bt_rotator_output_hint">දත්ත අකාරය</string>
<string name="prefs_bt_frequency_switch">Enable frequency output</string>
<string name="prefs_bt_frequency_device_hint">උපාංග id</string>
<string name="prefs_bt_frequency_output_hint">දත්ත අකාරය</string>
<string name="prefs_bt_perm_error">Bluetooth අවසර පරික්‍ෂා ක°</string> <string name="prefs_bt_perm_error">Bluetooth අවසර පරික්‍ෂා ක°</string>
<string name="prefs_other_title">වෙනත් සැකසුම්</string> <string name="prefs_other_title">වෙනත් සැකසුම්</string>
+18 -6
View File
@@ -112,15 +112,27 @@
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string> <string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string> <string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
<string name="prefs_data_output_title">Data output</string>
<string name="prefs_net_output">Network</string>
<string name="prefs_bt_output">Bluetooth</string>
<string name="prefs_net_title">Вивід мережевих даних</string> <string name="prefs_net_title">Вивід мережевих даних</string>
<string name="prefs_net_switch">Увімкнути вихід</string> <string name="prefs_net_rotator_switch">Enable rotation output</string>
<string name="prefs_net_ip_hint">IP адреса</string> <string name="prefs_net_rotator_ip_hint">IP адреса</string>
<string name="prefs_net_port_hint">Порт</string> <string name="prefs_net_rotator_port_hint">Порт</string>
<string name="prefs_net_rotator_format_hint">Формат даних</string>
<string name="prefs_net_frequency_switch">Enable frequency output</string>
<string name="prefs_net_frequency_ip_hint">IP адреса</string>
<string name="prefs_net_frequency_port_hint">Порт</string>
<string name="prefs_net_frequency_format_hint">Формат даних</string>
<string name="prefs_bt_title">Вивід даних Bluetooth</string> <string name="prefs_bt_title">Вивід даних Bluetooth</string>
<string name="prefs_bt_switch">Увімкнути вихід</string> <string name="prefs_bt_rotator_switch">Enable rotation output</string>
<string name="prefs_bt_device_hint">Id пристрою</string> <string name="prefs_bt_rotator_device_hint">Id пристрою</string>
<string name="prefs_bt_output_hint">Формат даних</string> <string name="prefs_bt_rotator_output_hint">Формат даних</string>
<string name="prefs_bt_frequency_switch">Enable frequency output</string>
<string name="prefs_bt_frequency_device_hint">Id пристрою</string>
<string name="prefs_bt_frequency_output_hint">Формат даних</string>
<string name="prefs_bt_perm_error">Перевірте дозвіл Bluetooth</string> <string name="prefs_bt_perm_error">Перевірте дозвіл Bluetooth</string>
<string name="prefs_other_title">Інші налаштування</string> <string name="prefs_other_title">Інші налаштування</string>
+18 -6
View File
@@ -104,15 +104,27 @@
<string name="prefs_data_sources_tle_switch">自定义TLE URL</string> <string name="prefs_data_sources_tle_switch">自定义TLE URL</string>
<string name="prefs_data_sources_transceivers_switch">自定义收发器URL</string> <string name="prefs_data_sources_transceivers_switch">自定义收发器URL</string>
<string name="prefs_data_output_title">Data output</string>
<string name="prefs_net_output">Network</string>
<string name="prefs_bt_output">Bluetooth</string>
<string name="prefs_net_title">网络数据输出</string> <string name="prefs_net_title">网络数据输出</string>
<string name="prefs_net_switch">启用输出</string> <string name="prefs_net_rotator_switch">Enable rotation output</string>
<string name="prefs_net_ip_hint">IP地址</string> <string name="prefs_net_rotator_ip_hint">IP地址</string>
<string name="prefs_net_port_hint">端口</string> <string name="prefs_net_rotator_port_hint">端口</string>
<string name="prefs_net_rotator_format_hint">数据格式</string>
<string name="prefs_net_frequency_switch">Enable frequency output</string>
<string name="prefs_net_frequency_ip_hint">IP地址</string>
<string name="prefs_net_frequency_port_hint">端口</string>
<string name="prefs_net_frequency_format_hint">数据格式</string>
<string name="prefs_bt_title">蓝牙数据输出</string> <string name="prefs_bt_title">蓝牙数据输出</string>
<string name="prefs_bt_switch">启用输出</string> <string name="prefs_bt_rotator_switch">Enable rotation output</string>
<string name="prefs_bt_device_hint">蓝牙设备 ID</string> <string name="prefs_bt_rotator_device_hint">蓝牙设备 ID</string>
<string name="prefs_bt_output_hint">数据格式</string> <string name="prefs_bt_rotator_output_hint">数据格式</string>
<string name="prefs_bt_frequency_switch">Enable frequency output</string>
<string name="prefs_bt_frequency_device_hint">蓝牙设备 ID</string>
<string name="prefs_bt_frequency_output_hint">数据格式</string>
<string name="prefs_bt_perm_error">请检查蓝牙权限</string> <string name="prefs_bt_perm_error">请检查蓝牙权限</string>
<string name="prefs_other_title">其他设置</string> <string name="prefs_other_title">其他设置</string>
+18 -6
View File
@@ -136,15 +136,27 @@
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string> <string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string> <string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
<string name="prefs_data_output_title">Data output</string>
<string name="prefs_net_output">Network</string>
<string name="prefs_bt_output">Bluetooth</string>
<string name="prefs_net_title">Network data output</string> <string name="prefs_net_title">Network data output</string>
<string name="prefs_net_switch">Enable output</string> <string name="prefs_net_rotator_switch">Enable rotation output</string>
<string name="prefs_net_ip_hint">IP address</string> <string name="prefs_net_rotator_ip_hint">IP address</string>
<string name="prefs_net_port_hint">Port</string> <string name="prefs_net_rotator_port_hint">Port</string>
<string name="prefs_net_rotator_format_hint">Format</string>
<string name="prefs_net_frequency_switch">Enable frequency output</string>
<string name="prefs_net_frequency_ip_hint">IP address</string>
<string name="prefs_net_frequency_port_hint">Port</string>
<string name="prefs_net_frequency_format_hint">Format</string>
<string name="prefs_bt_title">Bluetooth data output</string> <string name="prefs_bt_title">Bluetooth data output</string>
<string name="prefs_bt_switch">Enable output</string> <string name="prefs_bt_rotator_switch">Enable rotation output</string>
<string name="prefs_bt_device_hint">Device id</string> <string name="prefs_bt_rotator_device_hint">Device id</string>
<string name="prefs_bt_output_hint">Data format</string> <string name="prefs_bt_rotator_output_hint">Data format</string>
<string name="prefs_bt_frequency_switch">Enable frequency output</string>
<string name="prefs_bt_frequency_device_hint">Device id</string>
<string name="prefs_bt_frequency_output_hint">Data format</string>
<string name="prefs_bt_perm_error">Check your bluetooth permission</string> <string name="prefs_bt_perm_error">Check your bluetooth permission</string>
<string name="prefs_other_title">Other settings</string> <string name="prefs_other_title">Other settings</string>
@@ -18,6 +18,7 @@
package com.rtbishop.look4sat.data.framework package com.rtbishop.look4sat.data.framework
import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothSocket
import android.util.Log import android.util.Log
import java.io.OutputStream import java.io.OutputStream
import java.util.* import java.util.*
@@ -25,69 +26,110 @@ import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import com.rtbishop.look4sat.domain.repository.IReporterParams
import com.rtbishop.look4sat.domain.repository.IReporterRepo
data class WithoutExtParams(
val dummy: Int
) : IReporterParams
enum class BtService { ROTATOR, FREQUENCY }
data class DeviceConnection(
var socket: BluetoothSocket? = null,
var outputStream: OutputStream? = null,
var connected: Boolean = false,
var connecting: Boolean = false,
var connectionJob: Job? = null
)
class BluetoothReporter( class BluetoothReporter(
private val bluetoothManager: BluetoothManager, private val bluetoothManager: BluetoothManager,
private val reporterScope: CoroutineScope private val reporterScope: CoroutineScope
) { ) : IReporterRepo<WithoutExtParams> {
private val tag = "BTReporter" private val tag = "BTReporter"
private val sppid: UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb") private val sppid: UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb")
private lateinit var outputStream: OutputStream
private var rotationConnectionJob: Job? = null
private var rotationReportingJob: Job? = null
private var connected = false
private var connecting = false
fun isConnected(): Boolean = connected private val serviceToDevice = mutableMapOf<BtService, String>()
private val deviceConnections = mutableMapOf<String, DeviceConnection>()
private val writeMutex = Mutex()
fun isConnecting(): Boolean = connecting fun isConnected(service: BtService): Boolean {
val deviceId = serviceToDevice[service] ?: return false
return deviceConnections[deviceId]?.connected == true
}
fun isConnecting(service: BtService): Boolean {
val deviceId = serviceToDevice[service] ?: return false
return deviceConnections[deviceId]?.connecting == true
}
fun connectBTDevice(deviceId: String) { fun connect(service: BtService, deviceId: String) {
if (!connected) { serviceToDevice[service] = deviceId
rotationConnectionJob = reporterScope.launch { val connection = deviceConnections.getOrPut(deviceId) {
try { DeviceConnection()
bluetoothManager.adapter.getRemoteDevice(deviceId)?.let { device -> }
device.createInsecureRfcommSocketToServiceRecord(sppid)?.let { socket -> if (connection.connected || connection.connecting) return
connecting = true connection.connectionJob = reporterScope.launch {
socket.connect() try {
outputStream = socket.outputStream connection.connecting = true
connected = true val device = bluetoothManager.adapter.getRemoteDevice(deviceId)
connecting = false val socket = device.createInsecureRfcommSocketToServiceRecord(sppid)
Log.i(tag, "$tag: Connected!") socket.connect()
} connection.socket = socket
} connection.outputStream = socket.outputStream
} catch (e: SecurityException) { connection.connected = true
Log.e(tag, "$tag: ${e.message}") Log.i(tag, "$tag: Connected to $deviceId")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, "$tag: ${e.message}") Log.e(tag, "$tag: ${e.message}")
} connection.connected = false
} finally {
connection.connecting = false
} }
} }
} }
fun reportRotation(format: String, azimuth: Int, elevation: Int) { private suspend fun write(service: BtService, buffer: String) {
if (connected) { val deviceId = serviceToDevice[service] ?: return
rotationReportingJob = reporterScope.launch { val connection = deviceConnections[deviceId] ?: return
val newElevation = if (elevation > 0) elevation else 0 if (!connection.connected) return
try { try {
val azimuthString = intToStringWithLeadingZeroes(azimuth) writeMutex.withLock {
val elevationString = intToStringWithLeadingZeroes(newElevation) connection.outputStream?.write(buffer.toByteArray())
val crChar = '\r'
val nlChar = '\n'
val tbChar = '\t'
var buffer = format.replace("\$AZ", azimuthString)
buffer = buffer.replace("\$EL", elevationString)
buffer = buffer.replace("\\r", crChar.toString())
buffer = buffer.replace("\\n", nlChar.toString())
buffer = buffer.replace("\\t", tbChar.toString())
Log.i(tag, "$tag: Sending $buffer")
if (connected) outputStream.write(buffer.toByteArray())
} catch (e: Exception) {
Log.e(tag, "$tag: ${e.message}")
connected = false
}
} }
} catch (e: Exception) {
Log.e(tag, "$tag: Write failed ${e.message}")
connection.connected = false
}
}
override fun reportRotation(format: String, azimuth: Double, elevation: Double, params: WithoutExtParams) {
reporterScope.launch {
if (!isConnected(BtService.ROTATOR)) return@launch
val newElevation = if (elevation > 0.0) elevation else 0.0
val azimuthString = intToStringWithLeadingZeroes(azimuth.toInt())
val elevationString = intToStringWithLeadingZeroes(newElevation.toInt())
var buffer = format
.replace("\$AZ", azimuthString)
.replace("\$EL", elevationString)
.replace("\\r", "\r")
.replace("\\n", "\n")
.replace("\\t", "\t")
write(BtService.ROTATOR, buffer)
}
}
override fun reportFrequency(format: String, frequency: Long, params: WithoutExtParams) {
reporterScope.launch {
if (!isConnected(BtService.FREQUENCY)) return@launch
var buffer = format
.replace("\$FREQ", frequency.toString())
.replace("\\r", "\r")
.replace("\\n", "\n")
.replace("\\t", "\t")
write(BtService.FREQUENCY, buffer)
} }
} }
@@ -22,49 +22,94 @@ import java.nio.ByteBuffer
import java.nio.channels.SocketChannel import java.nio.channels.SocketChannel
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import com.rtbishop.look4sat.domain.repository.IReporterParams
import com.rtbishop.look4sat.domain.repository.IReporterRepo
class NetworkReporter(private val reporterScope: CoroutineScope) { data class ExtendedParams(
val server: String,
val port: Int
) : IReporterParams
private var rotationSocketChannel: SocketChannel? = null enum class NetworkService { ROTATOR, FREQUENCY }
private var rotationReporting: Job? = null
// private var frequencySocketChannel: SocketChannel? = null data class SocketConnection(
// private var frequencyReporting: Job? = null var socket: SocketChannel? = null,
// var connected: Boolean = false,
// fun reportFrequency(server: String, port: Int, frequency: Long) { var connecting: Boolean = false,
// frequencyReporting = reporterScope.launch { var connectionJob: Job? = null
// runCatching { )
// if (frequencySocketChannel == null) {
// frequencySocketChannel = SocketChannel.open(InetSocketAddress(server, port))
// } else {
// val buffer = ByteBuffer.wrap("\\set_freq $frequency\n".toByteArray())
// frequencySocketChannel?.write(buffer)
// }
// }.onFailure { error ->
// println(error.localizedMessage)
// frequencySocketChannel = null
// frequencyReporting?.cancelAndJoin()
// }
// }
// }
fun reportRotation(server: String, port: Int, azimuth: Double, elevation: Double) { class NetworkReporter(private val reporterScope: CoroutineScope) : IReporterRepo<ExtendedParams> {
rotationReporting = reporterScope.launch { private val serviceToAddress = mutableMapOf<NetworkService, String>()
val newElevation = if (elevation > 0.0) elevation else 0.0 private val connections = mutableMapOf<String, SocketConnection>()
runCatching { private val writeMutex = Mutex()
if (rotationSocketChannel == null) {
rotationSocketChannel = SocketChannel.open(InetSocketAddress(server, port)) fun isConnected(service: NetworkService): Boolean {
} else { val addr = serviceToAddress[service] ?: return false
val buffer = ByteBuffer.wrap("\\P $azimuth $newElevation\n".toByteArray()) return connections[addr]?.connected == true
rotationSocketChannel?.write(buffer) }
}
}.onFailure { error -> private fun getOrCreateConnection(addr: String): SocketConnection {
println(error.localizedMessage) return connections.getOrPut(addr) { SocketConnection() }
rotationSocketChannel = null }
rotationReporting?.cancelAndJoin()
fun connect(service: NetworkService, server: String, port: Int) {
val addr = "$server:$port"
serviceToAddress[service] = addr
val connection = getOrCreateConnection(addr)
if (connection.connected || connection.connecting) return
connection.connectionJob = reporterScope.launch {
try {
connection.connecting = true
val socket = SocketChannel.open(InetSocketAddress(server, port))
connection.socket = socket
connection.connected = true
println("NetworkReporter: $service connected to $addr")
} catch (e: Exception) {
println("NetworkReporter connect error: ${e.message}")
connection.connected = false
} finally {
connection.connecting = false
} }
} }
} }
private suspend fun write(service: NetworkService, command: String) {
val addr = serviceToAddress[service] ?: return
val connection = connections[addr] ?: return
if (!connection.connected) return
try {
writeMutex.withLock {
val buffer = ByteBuffer.wrap("\\$command\n".toByteArray())
connection.socket?.write(buffer)
}
} catch (e: Exception) {
println("NetworkReporter write error: ${e.message}")
connection.connected = false
}
}
override fun reportRotation(format: String, azimuth: Double, elevation: Double, params: ExtendedParams) {
reporterScope.launch {
connect(NetworkService.ROTATOR, params.server, params.port)
if (!isConnected(NetworkService.ROTATOR)) return@launch
val el = if (elevation > 0.0) elevation else 0.0
val command = format
.replace("\$AZ", azimuth.toString())
.replace("\$EL", el.toString())
write(NetworkService.ROTATOR, command)
}
}
override fun reportFrequency(format: String, frequency: Long, params: ExtendedParams) {
reporterScope.launch {
connect(NetworkService.FREQUENCY, params.server, params.port)
if (!isConnected(NetworkService.FREQUENCY)) return@launch
val command = format.replace("\$FREQ", frequency.toString())
write(NetworkService.FREQUENCY, command)
}
}
} }
@@ -42,10 +42,13 @@ class SettingsRepo(
private val preferences: SharedPreferences private val preferences: SharedPreferences
) : ISettingsRepo, LocationListenerCompat { ) : ISettingsRepo, LocationListenerCompat {
private val keyBluetoothAddress = "bluetoothAddress" private val keyBluetoothRotatorAddress = "bluetoothAddress"
private val keyBluetoothName = "bluetoothName" private val keyBluetoothRotatorName = "bluetoothName"
private val keyBluetoothFormat = "bluetoothFormat" private val keyBluetoothRotatorFormat = "bluetoothFormat"
private val keyBluetoothState = "bluetoothState" private val keyBluetoothRotatorState = "bluetoothState"
private val keyBluetoothFrequencyState = "bluetoothFrequencyState"
private val keyBluetoothFrequencyAddress = "bluetoothFrequencyAddress"
private val keyBluetoothFrequencyFormat = "bluetoothFrequencyFormat"
private val keyFilterHoursAhead = "filterHoursAhead" private val keyFilterHoursAhead = "filterHoursAhead"
private val keyFilterMinElevation = "filterMinElevation" private val keyFilterMinElevation = "filterMinElevation"
private val keyNumberOfRadios = "numberOfRadios" private val keyNumberOfRadios = "numberOfRadios"
@@ -53,6 +56,11 @@ class SettingsRepo(
private val keyRotatorAddress = "rotatorAddress" private val keyRotatorAddress = "rotatorAddress"
private val keyRotatorPort = "rotatorPort" private val keyRotatorPort = "rotatorPort"
private val keyRotatorState = "rotatorState" private val keyRotatorState = "rotatorState"
private val keyRotatorFormat = "rotatorFormat"
private val keyFrequencyState = "frequencyState"
private val keyFrequencyAddress = "frequencyAddress"
private val keyFrequencyPort = "frequencyPort"
private val keyFrequencyFormat = "frequencyFormat"
private val keySelectedIds = "selectedIds" private val keySelectedIds = "selectedIds"
private val keySelectedTypes = "selectedTypes" private val keySelectedTypes = "selectedTypes"
private val keySelectedModes = "selectedModes" private val keySelectedModes = "selectedModes"
@@ -240,24 +248,39 @@ class SettingsRepo(
private val _rcSettings = MutableStateFlow(getRCSettings()) private val _rcSettings = MutableStateFlow(getRCSettings())
override val rcSettings: StateFlow<RCSettings> = _rcSettings override val rcSettings: StateFlow<RCSettings> = _rcSettings
override fun setBluetoothAddress(value: String) { override fun setBluetoothRotatorAddress(value: String) {
preferences.edit { putString(keyBluetoothAddress, value) } preferences.edit { putString(keyBluetoothRotatorAddress, value) }
_rcSettings.update { it.copy(bluetoothAddress = value) } _rcSettings.update { it.copy(bluetoothRotatorAddress = value) }
} }
override fun setBluetoothFormat(value: String) { override fun setBluetoothRotatorFormat(value: String) {
preferences.edit { putString(keyBluetoothFormat, value) } preferences.edit { putString(keyBluetoothRotatorFormat, value) }
_rcSettings.update { it.copy(bluetoothFormat = value) } _rcSettings.update { it.copy(bluetoothRotatorFormat = value) }
} }
override fun setBluetoothName(value: String) { override fun setBluetoothRotatorName(value: String) {
preferences.edit { putString(keyBluetoothName, value) } preferences.edit { putString(keyBluetoothRotatorName, value) }
_rcSettings.update { it.copy(bluetoothName = value) } _rcSettings.update { it.copy(bluetoothRotatorName = value) }
} }
override fun setBluetoothState(value: Boolean) { override fun setBluetoothRotatorState(value: Boolean) {
preferences.edit { putBoolean(keyBluetoothState, value) } preferences.edit { putBoolean(keyBluetoothRotatorState, value) }
_rcSettings.update { it.copy(bluetoothState = value) } _rcSettings.update { it.copy(bluetoothRotatorState = value) }
}
override fun setBluetoothFrequencyState(value: Boolean) {
preferences.edit { putBoolean(keyBluetoothFrequencyState, value) }
_rcSettings.update { it.copy(bluetoothFrequencyState = value) }
}
override fun setBluetoothFrequencyAddress(value: String) {
preferences.edit { putString(keyBluetoothFrequencyAddress, value) }
_rcSettings.update { it.copy(bluetoothFrequencyAddress = value) }
}
override fun setBluetoothFrequencyFormat(value: String) {
preferences.edit { putString(keyBluetoothFrequencyFormat, value) }
_rcSettings.update { it.copy(bluetoothFrequencyFormat = value) }
} }
override fun setRotatorAddress(value: String) { override fun setRotatorAddress(value: String) {
@@ -275,14 +298,47 @@ class SettingsRepo(
_rcSettings.update { it.copy(rotatorState = value) } _rcSettings.update { it.copy(rotatorState = value) }
} }
override fun setRotatorFormat(value: String) {
preferences.edit { putString(keyRotatorFormat, value) }
_rcSettings.update { it.copy(rotatorFormat = value) }
}
override fun setFrequencyState(value: Boolean) {
preferences.edit { putBoolean(keyFrequencyState, value) }
_rcSettings.update { it.copy(frequencyState = value) }
}
override fun setFrequencyAddress(value: String) {
preferences.edit { putString(keyFrequencyAddress, value) }
_rcSettings.update { it.copy(frequencyAddress = value) }
}
override fun setFrequencyPort(value: String) {
preferences.edit { putString(keyFrequencyPort, value) }
_rcSettings.update { it.copy(frequencyPort = value) }
}
override fun setFrequencyFormat(value: String) {
preferences.edit { putString(keyFrequencyFormat, value) }
_rcSettings.update { it.copy(frequencyFormat = value) }
}
private fun getRCSettings(): RCSettings = RCSettings( private fun getRCSettings(): RCSettings = RCSettings(
rotatorState = preferences.getBoolean(keyRotatorState, false), rotatorState = preferences.getBoolean(keyRotatorState, false),
rotatorAddress = preferences.getString(keyRotatorAddress, null) ?: "127.0.0.1", rotatorAddress = preferences.getString(keyRotatorAddress, null) ?: "127.0.0.1",
rotatorPort = preferences.getString(keyRotatorPort, null) ?: "4533", rotatorPort = preferences.getString(keyRotatorPort, null) ?: "4533",
bluetoothState = preferences.getBoolean(keyBluetoothState, false), rotatorFormat = preferences.getString(keyRotatorFormat, null) ?: $$"P $AZ $EL",
bluetoothFormat = preferences.getString(keyBluetoothFormat, null) ?: $$"W$AZ $EL", frequencyState = preferences.getBoolean(keyFrequencyState, false),
bluetoothName = preferences.getString(keyBluetoothName, null) ?: "Default", frequencyAddress = preferences.getString(keyFrequencyAddress, null) ?: "127.0.0.1",
bluetoothAddress = preferences.getString(keyBluetoothAddress, null) ?: "00:0C:BF:13:80:5D" frequencyPort = preferences.getString(keyFrequencyPort, null) ?: "4532",
frequencyFormat = preferences.getString(keyFrequencyFormat, null) ?: $$"set_freq $FREQ",
bluetoothRotatorState = preferences.getBoolean(keyBluetoothRotatorState, false),
bluetoothRotatorFormat = preferences.getString(keyBluetoothRotatorFormat, null) ?: $$"W$AZ $EL",
bluetoothRotatorName = preferences.getString(keyBluetoothRotatorName, null) ?: "Default",
bluetoothRotatorAddress = preferences.getString(keyBluetoothRotatorAddress, null) ?: "00:0C:BF:13:80:5D",
bluetoothFrequencyState = preferences.getBoolean(keyBluetoothFrequencyState, false),
bluetoothFrequencyAddress = preferences.getString(keyBluetoothFrequencyAddress, null) ?: "00:0C:BF:13:80:5D",
bluetoothFrequencyFormat = preferences.getString(keyBluetoothFrequencyFormat, null) ?: $$"FA$FREQ"
) )
//endregion //endregion
@@ -33,10 +33,18 @@ data class RCSettings(
val rotatorState: Boolean, val rotatorState: Boolean,
val rotatorAddress: String, val rotatorAddress: String,
val rotatorPort: String, val rotatorPort: String,
val bluetoothState: Boolean, val rotatorFormat: String,
val bluetoothFormat: String, val frequencyState: Boolean,
val bluetoothName: String, val frequencyAddress: String,
val bluetoothAddress: String, val frequencyPort: String,
val frequencyFormat: String,
val bluetoothRotatorState: Boolean,
val bluetoothRotatorFormat: String,
val bluetoothRotatorName: String,
val bluetoothRotatorAddress: String,
val bluetoothFrequencyState: Boolean,
val bluetoothFrequencyFormat: String,
val bluetoothFrequencyAddress: String
) )
data class OtherSettings( data class OtherSettings(
@@ -0,0 +1,25 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2026 Arty Bishop and contributors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.domain.repository
interface IReporterParams
interface IReporterRepo<T : IReporterParams> {
fun reportRotation(format: String, azimuth: Double, elevation: Double, params: T)
fun reportFrequency(format: String, frequency: Long, params: T)
}
@@ -55,13 +55,22 @@ interface ISettingsRepo {
//region # RC settings //region # RC settings
val rcSettings: StateFlow<RCSettings> val rcSettings: StateFlow<RCSettings>
fun setBluetoothAddress(value: String) fun setBluetoothRotatorAddress(value: String)
fun setBluetoothFormat(value: String) fun setBluetoothRotatorFormat(value: String)
fun setBluetoothName(value: String) fun setBluetoothRotatorName(value: String)
fun setBluetoothState(value: Boolean) fun setBluetoothRotatorState(value: Boolean)
fun setBluetoothFrequencyAddress(value: String)
fun setBluetoothFrequencyFormat(value: String)
fun setBluetoothFrequencyState(value: Boolean)
fun setRotatorAddress(value: String) fun setRotatorAddress(value: String)
fun setRotatorPort(value: String) fun setRotatorPort(value: String)
fun setRotatorState(value: Boolean) fun setRotatorState(value: Boolean)
fun setRotatorFormat(value: String)
fun setFrequencyAddress(value: String)
fun setFrequencyPort(value: String)
fun setFrequencyState(value: Boolean)
fun setFrequencyFormat(value: String)
//endregion //endregion
//region # Other settings //region # Other settings