diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt
index 73c042a0..6c18ef7a 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/common/Components.kt
@@ -330,6 +330,40 @@ fun InfoDialog(title: String, text: String, onDismiss: () -> Unit) {
}
}
+@Composable
+fun InfoDialog(
+ title: String,
+ onDismiss: () -> Unit,
+ content: @Composable () -> Unit
+) {
+ val padding = LocalSpacing.current.large
+
+ Dialog(onDismissRequest = onDismiss) {
+ ElevatedCard {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(padding)
+ ) {
+ Text(
+ text = title,
+ fontSize = 18.sp,
+ fontWeight = FontWeight.Medium,
+ color = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.padding(start = padding, top = padding, end = padding)
+ )
+ content()
+ Row(modifier = Modifier.padding(start = padding, bottom = padding, end = padding)) {
+ Spacer(modifier = Modifier.weight(1f))
+ CardButton(
+ onClick = onDismiss,
+ text = stringResource(id = R.string.btn_cancel)
+ )
+ }
+ }
+ }
+ }
+}
+
@Composable
fun hasEnoughHeight(): Boolean {
return currentWindowAdaptiveInfo().windowSizeClass.isHeightAtLeastBreakpoint(480)
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsDialog.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsDialog.kt
index e47269e4..f13958cf 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsDialog.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsDialog.kt
@@ -27,6 +27,18 @@ import androidx.compose.ui.tooling.preview.Preview
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.common.SharedDialog
+import com.rtbishop.look4sat.presentation.common.InfoDialog
+import com.rtbishop.look4sat.presentation.common.CardButton
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Switch
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import com.rtbishop.look4sat.presentation.LocalSpacing
@Preview(showBackground = true)
@Composable
@@ -71,3 +83,132 @@ fun LocatorDialog(qthLocator: String, dismiss: () -> Unit, save: (String) -> Uni
OutlinedTextField(value = locator.value, onValueChange = { locator.value = it })
}
}
+
+@Preview(showBackground = true)
+@Composable
+private fun ImportDialogPreview() = MainTheme {
+ ImportDialog(
+ onImportTle = {},
+ onImportTransceivers = {},
+ onDismiss = {}
+ )
+}
+
+@Composable
+fun ImportDialog(
+ onImportTle: () -> Unit,
+ onImportTransceivers: () -> Unit,
+ onDismiss: () -> Unit
+) {
+ val padding = LocalSpacing.current.large
+ InfoDialog(
+ title = stringResource(R.string.prefs_data_import),
+ onDismiss = onDismiss
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ modifier = Modifier
+ .padding(horizontal = padding)
+ .fillMaxWidth()
+ ) {
+ CardButton(
+ onClick = {
+ onImportTle()
+ onDismiss()
+ },
+ text = "TLE",
+ modifier = Modifier.weight(0.7f)
+ )
+ CardButton(
+ onClick = {
+ onImportTransceivers()
+ onDismiss()
+ },
+ text = "Transceivers",
+ modifier = Modifier.weight(1.3f)
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun TransceiversDialogPreview() {
+ MainTheme {
+ DataSourcesDialog(
+ useCustomTle = true,
+ useCustomTransceivers = true,
+ tleUrl = "https://example.com/tle.txt",
+ transceiversUrl = "https://example.com/tx.json",
+ dismiss = {},
+ onSave = { _, _, _, _ -> }
+ )
+ }
+}
+
+@Composable
+fun DataSourcesDialog(
+ useCustomTle: Boolean,
+ useCustomTransceivers: Boolean,
+ tleUrl: String,
+ transceiversUrl: String,
+ dismiss: () -> Unit,
+ onSave: (Boolean, Boolean, String, String) -> Unit
+) {
+ val padding = LocalSpacing.current.large
+ val isEnabledCustomTle = rememberSaveable { mutableStateOf(useCustomTle) }
+ val isEnabledCustomTransceivers = rememberSaveable { mutableStateOf(useCustomTransceivers) }
+ val urlTle = rememberSaveable { mutableStateOf(tleUrl) }
+ val urlTransceivers = rememberSaveable { mutableStateOf(transceiversUrl) }
+ val onAccept = {
+ onSave(isEnabledCustomTle.value, isEnabledCustomTransceivers.value, urlTle.value, urlTransceivers.value)
+ dismiss()
+ }
+ val onCancel = { dismiss() }
+ SharedDialog(
+ title = stringResource(id = R.string.prefs_data_sources_title),
+ onCancel = onCancel,
+ onAccept = onAccept,
+ ) {
+ Column(modifier = Modifier.padding(horizontal = padding)) {
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = stringResource(id = R.string.prefs_data_sources_tle_switch))
+ Switch(
+ checked = isEnabledCustomTle.value,
+ onCheckedChange = { isEnabledCustomTle.value = it }
+ )
+ }
+ OutlinedTextField(
+ value = urlTle.value,
+ onValueChange = { urlTle.value = it },
+ label = { Text(text = stringResource(id = R.string.prefs_data_sources_url_title)) },
+ singleLine = true,
+ modifier = Modifier.fillMaxWidth(),
+ enabled = isEnabledCustomTle.value,
+ )
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = stringResource(id = R.string.prefs_data_sources_transceivers_switch))
+ Switch(
+ checked = isEnabledCustomTransceivers.value,
+ onCheckedChange = { isEnabledCustomTransceivers.value = it }
+ )
+ }
+ OutlinedTextField(
+ value = urlTransceivers.value,
+ onValueChange = { urlTransceivers.value = it },
+ label = { Text(text = stringResource(id = R.string.prefs_data_sources_url_title)) },
+ singleLine = true,
+ modifier = Modifier.fillMaxWidth(),
+ enabled = isEnabledCustomTransceivers.value,
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt
index 878c3c9d..ddc0093d 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt
@@ -112,8 +112,11 @@ private fun SettingsScreen(uiState: SettingsState) {
}
}
val contentContract = ActivityResultContracts.GetContent()
- val contentRequest = rememberLauncherForActivityResult(contentContract) { uri ->
- uri?.let { uiState.sendAction(SettingsAction.UpdateFromFile(uri.toString())) }
+ val contentRequestForTle = rememberLauncherForActivityResult(contentContract) { uri ->
+ uri?.let { uiState.sendAction(SettingsAction.UpdateTLEFromFile(uri.toString())) }
+ }
+ val contentRequestForTransceivers = rememberLauncherForActivityResult(contentContract) { uri ->
+ uri?.let { uiState.sendAction(SettingsAction.UpdateTransceiversFromFile(uri.toString())) }
}
// Position settings
@@ -137,10 +140,46 @@ private fun SettingsScreen(uiState: SettingsState) {
LocatorDialog(posSettings.stationPos.qthLocator, showLocDialog, setQthPos)
}
+ // Data sources dialog
+ val dataSourcesDialogState = rememberSaveable { mutableStateOf(false) }
+ val showDataSourcesDialog = { dataSourcesDialogState.value = true }
+ val dismissDataSourcesDialog = { dataSourcesDialogState.value = false }
+ if (dataSourcesDialogState.value) {
+ DataSourcesDialog(
+ useCustomTle = uiState.dataSourcesSettings.useCustomTLE,
+ useCustomTransceivers = uiState.dataSourcesSettings.useCustomTransceivers,
+ tleUrl = uiState.dataSourcesSettings.tleUrl,
+ transceiversUrl = uiState.dataSourcesSettings.transceiversUrl,
+ dismiss = dismissDataSourcesDialog,
+ onSave = {
+ useCustomTle, useCustomTransceivers, tleUrl, transceiversUrl ->
+ if (!useCustomTle || tleUrl.isNotBlank()) {
+ uiState.sendDataSourcesAction(DataSourcesAction.SetUseCustomTle(useCustomTle))
+ uiState.sendDataSourcesAction(DataSourcesAction.SetTleUrl(tleUrl))
+ }
+ if (!useCustomTransceivers || transceiversUrl.isNotBlank()) {
+ uiState.sendDataSourcesAction(DataSourcesAction.SetUseCustomTransceivers(useCustomTransceivers))
+ uiState.sendDataSourcesAction(DataSourcesAction.SetTransceiversUrl(transceiversUrl))
+ }
+ }
+ )
+ }
+
+ // Import dialog
+ val importDialogState = rememberSaveable { mutableStateOf(false) }
+ val showImportDialog = { importDialogState.value = true }
+ val dismissImportDialog = { importDialogState.value = false }
+ if (importDialogState.value) {
+ ImportDialog(
+ onImportTle = { contentRequestForTle.launch("*/*") },
+ onImportTransceivers = { contentRequestForTransceivers.launch("*/*") },
+ onDismiss = dismissImportDialog
+ )
+ }
+
// Data settings
val dataSettings = uiState.dataSettings
val updateFromWeb: () -> Unit = { uiState.sendAction(SettingsAction.UpdateFromWeb) }
- val updateFromFile = { contentRequest.launch("*/*") }
val clearAllData: () -> Unit = { uiState.sendAction(SettingsAction.ClearAllData) }
// RC settings
@@ -209,7 +248,7 @@ private fun SettingsScreen(uiState: SettingsState) {
item {
LocationCard(posSettings, setGpsPos, showPosDialog, showLocDialog, dismissPos, uiState.sendSystemAction)
}
- item { DataCard(dataSettings, updateFromWeb, updateFromFile, clearAllData) }
+ item { DataCard(dataSettings, updateFromWeb, clearAllData, showImportDialog, showDataSourcesDialog) }
item { NetworkOutputCard(rcSettings, setRotatorState, setRotatorAddress, setRotatorPort) }
item { BluetoothOutputCard(rcSettings, setBluetoothState, setBluetoothAddress, setBluetoothFormat) }
item { OtherCard(otherSettings, toggleUtc, toggleUpdate, toggleSweep, toggleSensor) }
@@ -290,15 +329,16 @@ private fun LocationCard(
@Composable
private fun DataCardPreview() = MainTheme {
val settings = DataSettings(true, 5000, 2500, 0L)
- DataCard(settings = settings, {}, {}) {}
+ DataCard(settings = settings, {}, {},{}) {}
}
@Composable
private fun DataCard(
settings: DataSettings,
updateFromWeb: () -> Unit,
- updateFromFile: () -> Unit,
- clearAllData: () -> Unit
+ clearAllData: () -> Unit,
+ showImportDialog: () -> Unit,
+ showDataSourcesDialog: () -> Unit
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(
@@ -320,25 +360,37 @@ private fun DataCard(
Text(text = stringResource(R.string.prefs_data_radios, settings.radiosTotal))
}
Spacer(modifier = Modifier.height(1.dp))
- Row(horizontalArrangement = Arrangement.SpaceEvenly) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(6.dp),
+ modifier = Modifier.fillMaxWidth()
+ ) {
CardButton(
- onClick = { updateFromWeb() },
+ onClick = updateFromWeb,
text = stringResource(id = R.string.prefs_data_update),
modifier = Modifier.weight(1f)
)
- Spacer(modifier = Modifier.width(6.dp))
CardButton(
- onClick = { updateFromFile() },
+ onClick = showImportDialog,
text = stringResource(id = R.string.prefs_data_import),
modifier = Modifier.weight(1f)
)
- Spacer(modifier = Modifier.width(6.dp))
CardButton(
- onClick = { clearAllData() },
+ onClick = clearAllData,
text = stringResource(id = R.string.prefs_data_clear),
modifier = Modifier.weight(1f)
)
}
+ Spacer(modifier = Modifier.height(6.dp))
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(6.dp),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ CardButton(
+ onClick = showDataSourcesDialog,
+ text = stringResource(id = R.string.prefs_data_sources),
+ modifier = Modifier.weight(1f)
+ )
+ }
}
}
}
@@ -633,3 +685,4 @@ private fun BotCard(onClick: () -> Unit, resId: Int, text: String, modifier: Mod
}
}
}
+
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsState.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsState.kt
index ae6fed54..880d61cf 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsState.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsState.kt
@@ -19,6 +19,7 @@ package com.rtbishop.look4sat.presentation.settings
import com.rtbishop.look4sat.domain.model.OtherSettings
import com.rtbishop.look4sat.domain.model.RCSettings
+import com.rtbishop.look4sat.domain.model.DataSourcesSettings
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.OrbitalPass
@@ -39,9 +40,11 @@ data class SettingsState(
val dataSettings: DataSettings,
val otherSettings: OtherSettings,
val rcSettings: RCSettings,
+ val dataSourcesSettings: DataSourcesSettings,
val sendAction: (SettingsAction) -> Unit,
val sendRCAction: (RCAction) -> Unit,
- val sendSystemAction: (SystemAction) -> Unit
+ val sendSystemAction: (SystemAction) -> Unit,
+ val sendDataSourcesAction: (DataSourcesAction) -> Unit
)
sealed class SettingsAction {
@@ -50,7 +53,8 @@ sealed class SettingsAction {
data class SetQthPosition(val locator: String) : SettingsAction()
data object DismissPosMessages : SettingsAction()
data object UpdateFromWeb : SettingsAction()
- data class UpdateFromFile(val uri: String) : SettingsAction()
+ data class UpdateTLEFromFile(val uri: String) : SettingsAction()
+ data class UpdateTransceiversFromFile(val uri: String) : SettingsAction()
data object ClearAllData : SettingsAction()
data class ToggleUtc(val value: Boolean) : SettingsAction()
data class ToggleUpdate(val value: Boolean) : SettingsAction()
@@ -72,3 +76,10 @@ sealed class RCAction {
data class SetBluetoothName(val value: String) : RCAction()
data class SetBluetoothAddress(val value: String) : RCAction()
}
+
+sealed class DataSourcesAction {
+ data class SetUseCustomTle(val value: Boolean) : DataSourcesAction()
+ data class SetUseCustomTransceivers(val value: Boolean) : DataSourcesAction()
+ data class SetTleUrl(val value: String) : DataSourcesAction()
+ data class SetTransceiversUrl(val value: String) : DataSourcesAction()
+}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt
index 5415e7a9..834c4ffc 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt
@@ -24,6 +24,7 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.R
+import com.rtbishop.look4sat.domain.model.DataSourcesSettings
import com.rtbishop.look4sat.domain.predict.OrbitalPass
import com.rtbishop.look4sat.domain.repository.IDatabaseRepo
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
@@ -51,6 +52,7 @@ class SettingsViewModel(
private val defaultPosSettings = PositionSettings(false, settingsRepo.stationPosition.value, 0)
private val defaultDataSettings = DataSettings(false, 0, 0, 0L)
+ private val defaultDataSourcesSettings = DataSourcesSettings(false, false, settingsRepo.dataSourcesSettings.value.tleUrl, settingsRepo.dataSourcesSettings.value.transceiversUrl)
private val _uiState = MutableStateFlow(
SettingsState(
nextTime = "00:00:00",
@@ -61,9 +63,11 @@ class SettingsViewModel(
dataSettings = defaultDataSettings,
otherSettings = settingsRepo.otherSettings.value,
rcSettings = settingsRepo.rcSettings.value,
+ dataSourcesSettings = defaultDataSourcesSettings,
sendAction = ::handleAction,
sendRCAction = ::handleAction,
- sendSystemAction = ::handleAction
+ sendSystemAction = ::handleAction,
+ sendDataSourcesAction = ::handleAction
)
)
private var processing: Job? = null
@@ -113,6 +117,13 @@ class SettingsViewModel(
_uiState.update { it.copy(otherSettings = settings) }
}
}
+ viewModelScope.launch {
+ settingsRepo.dataSourcesSettings.collect { settings ->
+ _uiState.update {
+ it.copy(dataSourcesSettings = settings)
+ }
+ }
+ }
}
private fun handleAction(action: SettingsAction) {
@@ -122,7 +133,8 @@ class SettingsViewModel(
is SettingsAction.SetQthPosition -> setQthPosition(action.locator)
SettingsAction.DismissPosMessages -> dismissPosMessage()
SettingsAction.UpdateFromWeb -> updateFromWeb()
- is SettingsAction.UpdateFromFile -> updateFromFile(action.uri)
+ is SettingsAction.UpdateTLEFromFile -> updateTLEFromFile(action.uri)
+ is SettingsAction.UpdateTransceiversFromFile -> updateTransceiversFromFile(action.uri)
SettingsAction.ClearAllData -> clearAllData()
is SettingsAction.ToggleUtc -> settingsRepo.setStateOfUtc(action.value)
is SettingsAction.ToggleUpdate -> settingsRepo.setStateOfAutoUpdate(action.value)
@@ -150,6 +162,15 @@ class SettingsViewModel(
}
}
+ private fun handleAction(action: DataSourcesAction) {
+ when (action) {
+ is DataSourcesAction.SetUseCustomTle -> settingsRepo.setUseCustomTle(action.value)
+ is DataSourcesAction.SetUseCustomTransceivers -> settingsRepo.setUseCustomTransceivers(action.value)
+ is DataSourcesAction.SetTleUrl -> settingsRepo.setTleUrl(action.value)
+ is DataSourcesAction.SetTransceiversUrl -> settingsRepo.setTransceiversUrl(action.value)
+ }
+ }
+
private fun setGpsPosition() {
if (settingsRepo.setStationPosition()) {
val messageResId = R.string.prefs_loc_success
@@ -217,11 +238,23 @@ class SettingsViewModel(
}
}
- private fun updateFromFile(uri: String) = viewModelScope.launch {
+ private fun updateTLEFromFile(uri: String) = viewModelScope.launch {
try {
val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = true)
_uiState.update { it.copy(dataSettings = newDataSettings) }
- databaseRepo.updateFromFile(uri)
+ databaseRepo.updateTLEFromFile(uri)
+ } catch (exception: Exception) {
+ val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = false)
+ _uiState.update { it.copy(dataSettings = newDataSettings) }
+ println(exception)
+ }
+ }
+
+ private fun updateTransceiversFromFile(uri: String) = viewModelScope.launch {
+ try {
+ val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = true)
+ _uiState.update { it.copy(dataSettings = newDataSettings) }
+ databaseRepo.updateTransceiversFromFile(uri)
} catch (exception: Exception) {
val newDataSettings = _uiState.value.dataSettings.copy(isUpdating = false)
_uiState.update { it.copy(dataSettings = newDataSettings) }
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 391d04bb..6e6a00fe 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -105,9 +105,15 @@
Actualizar
Importar
Vaciar
+ Custom data sources
Datos eliminados con éxito
Actualización completada con éxito
+ Custom data sources
+ Custom TLE URL
+ Custom transceivers URL
+ URL (https)
+
Salida de datos de red
Habilitar salida
Dirección IP
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 85e6ab95..ffe3aa6f 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -104,9 +104,15 @@
Обновить
Импорт
Очистить
+ Свои источники данных
Очистка прошла успешно
Обновление прошло успешно
+ Свои источники данных
+ Custom TLE URL
+ Custom transceivers URL
+ URL (https)
+
Вывод сетевых данных
Включить вывод
IP адрес
diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml
index 00728c12..1144f44a 100644
--- a/app/src/main/res/values-si/strings.xml
+++ b/app/src/main/res/values-si/strings.xml
@@ -105,9 +105,15 @@
නැවුම් කරන්න
ආදානය
පිරිසිදු ක°
+ Custom data sources
දත්ත ඉවත් කිරීම සාර්ථකයි
යාවත්කාලීනය සම්පූර්ණ කිරීම සාර්ථකයි
+ Custom data sources
+ Custom TLE URL
+ Custom transceivers URL
+ URL (https)
+
ජාල ප්රතිදානය
ප්රතිදානය සක්රීය කරන්න
IP ලිපිනය
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index dd8b5644..9fb4870d 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -105,9 +105,15 @@
Оновити
Імпорт
Очистити
+ Custom data sources
Дані успішно очищено
Оновлення успішне
+ Custom data sources
+ Custom TLE URL
+ Custom transceivers URL
+ URL (https)
+
Вивід мережевих даних
Увімкнути вихід
IP адреса
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index d532cc87..b0acfb07 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -97,9 +97,15 @@
在线更新
导入文件
清空
+ Custom data sources
数据清空成功
更新成功
+ Custom data sources
+ Custom TLE URL
+ Custom transceivers URL
+ URL (https)
+
网络数据输出
启用输出
IP地址
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b1a80767..4a33e8bf 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -128,9 +128,15 @@
Update
Import
Clear
+ Custom data sources
Data was cleared successfully
Update completed successfully
+ Custom data sources
+ Custom TLE URL
+ Custom transceivers URL
+ URL (https)
+
Network data output
Enable output
IP address
diff --git a/data/src/main/java/com/rtbishop/look4sat/data/repository/DatabaseRepo.kt b/data/src/main/java/com/rtbishop/look4sat/data/repository/DatabaseRepo.kt
index 4a1295be..8f9a1e91 100644
--- a/data/src/main/java/com/rtbishop/look4sat/data/repository/DatabaseRepo.kt
+++ b/data/src/main/java/com/rtbishop/look4sat/data/repository/DatabaseRepo.kt
@@ -39,23 +39,46 @@ class DatabaseRepo(
private val settingsRepo: ISettingsRepo
) : IDatabaseRepo {
- override suspend fun updateFromFile(uri: String) = withContext(dispatcher) {
+ override suspend fun updateTLEFromFile(uri: String) = withContext(dispatcher) {
val importedSatellites = remoteSource.getFileStream(uri)?.let { dataParser.parseTLEStream(it) }
importedSatellites?.let { localSource.insertEntries(it) }
setUpdateSuccessful(System.currentTimeMillis())
}
+ override suspend fun updateTransceiversFromFile(uri: String) = withContext(dispatcher) {
+ val radios = remoteSource.getFileStream(uri)?.let { dataParser.parseJSONStream(it) }
+ radios?.let {
+ if(it.isNotEmpty()) {
+ localSource.deleteRadios()
+ localSource.insertRadios(it)
+ }
+ }
+ setUpdateSuccessful(System.currentTimeMillis())
+ }
+
override suspend fun updateFromRemote() = withContext(dispatcher) {
val importedEntries = mutableListOf()
val importedRadios = mutableListOf()
- // fetch
- val jobsMap = Sources.satelliteDataUrls.mapValues { async { remoteSource.getNetworkStream(it.value) } }
- val jobRadios = async { remoteSource.getNetworkStream(Sources.RADIO_DATA_URL) }
+ val tleUrls = Sources.satelliteDataUrls.toMutableMap().apply {
+ if (settingsRepo.dataSourcesSettings.value.useCustomTLE) {
+ this["Other"] = settingsRepo.dataSourcesSettings.value.transceiversUrl
+ }
+ }
+ val jobsMap = tleUrls
+ .filter { (_, url) -> url.isNotEmpty() }
+ .mapValues { (_, url) -> async { remoteSource.getNetworkStream(url) } }
+ val radioUrls = buildList {
+ add(Sources.RADIO_DATA_URL)
+ if (settingsRepo.dataSourcesSettings.value.useCustomTransceivers) {
+ add(settingsRepo.dataSourcesSettings.value.transceiversUrl)
+ }
+ }
+ val jobRadios = radioUrls.associateWith { url -> async { remoteSource.getNetworkStream(url) } }
// parse
jobsMap.mapValues { job -> job.value.await() }.forEach { entry ->
entry.value?.let { stream ->
when (val type = entry.key) {
- "Amsat", "R4UAB" -> {
+ "Amsat", "R4UAB", "Other" -> {
// parse tle stream
val satellites = dataParser.parseTLEStream(stream)
val catnums = satellites.map { it.catnum }
@@ -82,7 +105,11 @@ class DatabaseRepo(
}
}
}
- jobRadios.await()?.let { importedRadios.addAll(dataParser.parseJSONStream(it)) }
+ jobRadios.values.forEach { job ->
+ job.await()?.let {
+ importedRadios.addAll(dataParser.parseJSONStream(it))
+ }
+ }
// insert
localSource.insertEntries(importedEntries)
localSource.insertRadios(importedRadios)
diff --git a/data/src/main/java/com/rtbishop/look4sat/data/repository/SettingsRepo.kt b/data/src/main/java/com/rtbishop/look4sat/data/repository/SettingsRepo.kt
index 1c00bb5e..c4894af8 100644
--- a/data/src/main/java/com/rtbishop/look4sat/data/repository/SettingsRepo.kt
+++ b/data/src/main/java/com/rtbishop/look4sat/data/repository/SettingsRepo.kt
@@ -27,6 +27,7 @@ import com.rtbishop.look4sat.domain.model.DatabaseState
import com.rtbishop.look4sat.domain.model.OtherSettings
import com.rtbishop.look4sat.domain.model.PassesSettings
import com.rtbishop.look4sat.domain.model.RCSettings
+import com.rtbishop.look4sat.domain.model.DataSourcesSettings
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
import com.rtbishop.look4sat.domain.utility.positionToQth
@@ -68,6 +69,10 @@ class SettingsRepo(
private val keyUpdateTimestamp = "updateTimestamp"
private val keyShouldSeeWarning = "shouldSeeWarning"
private val keyShouldSeeWelcome = "shouldSeeWelcome"
+ private val keyUseCustomTle = "useCustomTle"
+ private val keyUseCustomTransceivers = "useCustomTransceivers"
+ private val keyTleUrl = "tleUrl"
+ private val keyTransceiversUrl = "transceiversUrl"
private val separatorComma = ","
//region # Satellites selection settings
@@ -330,4 +335,36 @@ class SettingsRepo(
shouldSeeWelcome = preferences.getBoolean(keyShouldSeeWelcome, true)
)
//endregion
+
+ //region # Data sources settings
+ private val _dataSourcesSettings = MutableStateFlow(getDataSourcesSettings())
+ override val dataSourcesSettings: StateFlow = _dataSourcesSettings
+
+ override fun setUseCustomTle(value: Boolean) {
+ preferences.edit { putBoolean(keyUseCustomTle, value) }
+ _dataSourcesSettings.update { it.copy(useCustomTLE = value) }
+ }
+
+ override fun setUseCustomTransceivers(value: Boolean) {
+ preferences.edit { putBoolean(keyUseCustomTransceivers, value) }
+ _dataSourcesSettings.update { it.copy(useCustomTransceivers = value) }
+ }
+
+ override fun setTleUrl(value: String) {
+ preferences.edit { putString(keyTleUrl, value) }
+ _dataSourcesSettings.update { it.copy(tleUrl = value) }
+ }
+
+ override fun setTransceiversUrl(value: String) {
+ preferences.edit { putString(keyTransceiversUrl, value) }
+ _dataSourcesSettings.update { it.copy(transceiversUrl = value) }
+ }
+
+ private fun getDataSourcesSettings(): DataSourcesSettings = DataSourcesSettings(
+ useCustomTLE = preferences.getBoolean(keyUseCustomTle, false),
+ useCustomTransceivers = preferences.getBoolean(keyUseCustomTransceivers, false),
+ tleUrl = preferences.getString(keyTleUrl, "https://example.com/tle.txt") ?: "",
+ transceiversUrl = preferences.getString(keyTransceiversUrl, "https://example.com/radio.json") ?: ""
+ )
+ //endregion
}
diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/model/Settings.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/model/Settings.kt
index 027a8ec5..dbfef841 100644
--- a/domain/src/main/java/com/rtbishop/look4sat/domain/model/Settings.kt
+++ b/domain/src/main/java/com/rtbishop/look4sat/domain/model/Settings.kt
@@ -48,3 +48,10 @@ data class OtherSettings(
val shouldSeeWarning: Boolean,
val shouldSeeWelcome: Boolean
)
+
+data class DataSourcesSettings(
+ val useCustomTLE: Boolean,
+ val useCustomTransceivers: Boolean,
+ val tleUrl: String,
+ val transceiversUrl: String
+)
diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/repository/IDatabaseRepo.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/repository/IDatabaseRepo.kt
index 9b4bc3a8..39f1c387 100644
--- a/domain/src/main/java/com/rtbishop/look4sat/domain/repository/IDatabaseRepo.kt
+++ b/domain/src/main/java/com/rtbishop/look4sat/domain/repository/IDatabaseRepo.kt
@@ -18,7 +18,8 @@
package com.rtbishop.look4sat.domain.repository
interface IDatabaseRepo {
- suspend fun updateFromFile(uri: String)
+ suspend fun updateTLEFromFile(uri: String)
+ suspend fun updateTransceiversFromFile(uri: String)
suspend fun updateFromRemote()
suspend fun clearAllData()
}
diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/repository/ISettingsRepo.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/repository/ISettingsRepo.kt
index 8dd8c3bf..387939c1 100644
--- a/domain/src/main/java/com/rtbishop/look4sat/domain/repository/ISettingsRepo.kt
+++ b/domain/src/main/java/com/rtbishop/look4sat/domain/repository/ISettingsRepo.kt
@@ -21,6 +21,7 @@ import com.rtbishop.look4sat.domain.model.DatabaseState
import com.rtbishop.look4sat.domain.model.OtherSettings
import com.rtbishop.look4sat.domain.model.PassesSettings
import com.rtbishop.look4sat.domain.model.RCSettings
+import com.rtbishop.look4sat.domain.model.DataSourcesSettings
import com.rtbishop.look4sat.domain.predict.GeoPos
import kotlinx.coroutines.flow.StateFlow
@@ -73,4 +74,12 @@ interface ISettingsRepo {
fun setWarningDismissed()
fun setWelcomeDismissed()
//endregion
+
+ //region # Transceivers settings
+ val dataSourcesSettings: StateFlow
+ fun setUseCustomTle(value: Boolean)
+ fun setUseCustomTransceivers(value: Boolean)
+ fun setTleUrl(value: String)
+ fun setTransceiversUrl(value: String)
+ //endregion
}
diff --git a/domain/src/main/java/com/rtbishop/look4sat/domain/source/Sources.kt b/domain/src/main/java/com/rtbishop/look4sat/domain/source/Sources.kt
index fe30d6e7..c10710e0 100644
--- a/domain/src/main/java/com/rtbishop/look4sat/domain/source/Sources.kt
+++ b/domain/src/main/java/com/rtbishop/look4sat/domain/source/Sources.kt
@@ -46,6 +46,7 @@ object Sources {
"Swarm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=swarm&FORMAT=csv",
"Weather" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=csv",
"X-Comm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv",
- "Amsat" to "https://amsat.org/tle/current/nasabare.txt"
+ "Amsat" to "https://amsat.org/tle/current/nasabare.txt",
+ "Other" to "" // key for sats filter
)
}