feat(settings): custom URL now overrides the online-update default source

The "Custom URL" dialog's two URL fields now control the default
source used by the online update, per the user's intent (one-time
setup, no extra steps per update):

- Defaults are real URLs now: TLE = Celestrak "All" (active group
  CSV), transceivers = SatNOGS API. The example.com placeholders are
  gone.
- updateFromRemote(): the "All" TLE entry and the "SatNOGS" entry are
  replaced by the user's URLs when the corresponding custom toggle is
  ON and the URL is non-blank; otherwise the real defaults are used.
  The old "Other" appended-source logic is removed - the toggle now
  means "use my URL for this source" instead of "download an extra
  source". Each source (TLE / transceivers) is independent.
- Each URL field gained a reset (loop) icon button on the right that
  restores the default URL text (new ic_reset drawable, Autorenew
  vector). Strings added in en/zh/tr.

Test updated: custom TLE URL data now lands under the "All" type
instead of "Other". Verified: core:data tests pass, settings + app
compile. Version stays 4.4.8 (覆盖 per user).
This commit is contained in:
mckero committed 2026-08-03 16:41:14 +00:00
1 parent 59d6e2ae05
commit 8d88100b15
8 files changed
+47 -5

No files matched your search

@@ -69,11 +69,14 @@ class DatabaseRepo(
val dataSourcesSettings = settingsRepo.dataSourcesSettings.value
val tleUrls = buildMap {
putAll(Sources.satelliteDataUrls)
if (dataSourcesSettings.useCustomTLE) put(customSourceType, dataSourcesSettings.tleUrl)
// 开关开且 URL 非空 -> All 用自定义 URL;否则用默认 URL(在线更新默认源)
put("All", if (dataSourcesSettings.useCustomTLE && dataSourcesSettings.tleUrl.isNotBlank())
dataSourcesSettings.tleUrl else Sources.defaultTleUrl)
}.filterValues { it.isNotBlank() }
val radioUrls = buildMap {
putAll(Sources.transceiversDataUrls)
if (dataSourcesSettings.useCustomTransceivers) put(customSourceType, dataSourcesSettings.transceiversUrl)
put("SatNOGS", if (dataSourcesSettings.useCustomTransceivers && dataSourcesSettings.transceiversUrl.isNotBlank())
dataSourcesSettings.transceiversUrl else Sources.defaultTransceiversUrl)
}.filterValues { it.isNotBlank() }
// launch all network requests concurrently
val tleJobs = tleUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } }
@@ -29,6 +29,7 @@ import com.rtbishop.look4sat.core.domain.model.OtherSettings
import com.rtbishop.look4sat.core.domain.model.PassesSettings
import com.rtbishop.look4sat.core.domain.model.RCSettings
import com.rtbishop.look4sat.core.domain.model.RadioControlSettings
import com.rtbishop.look4sat.core.domain.source.Sources
import com.rtbishop.look4sat.core.domain.predict.GeoPos
import com.rtbishop.look4sat.core.domain.repository.ISettingsRepo
import com.rtbishop.look4sat.core.domain.utility.positionToQth
@@ -393,8 +394,8 @@ class SettingsRepo(
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") ?: ""
tleUrl = preferences.getString(keyTleUrl, Sources.defaultTleUrl) ?: "",
transceiversUrl = preferences.getString(keyTransceiversUrl, Sources.defaultTransceiversUrl) ?: ""
)
//endregion
@@ -102,7 +102,8 @@ class DatabaseRepoTest {
repository.updateFromRemote()
assertTrue(localSource.insertedEntries.any { it.catnum == 25544 })
assertEquals(listOf(25544), settingsRepo.satelliteTypeIdsByType["Other"])
// 新语义: 开关开 + URL 非空 -> All 源用自定义 URL, 数据归入 All 类型
assertEquals(listOf(25544), settingsRepo.satelliteTypeIdsByType["All"])
}
private fun validCsvStream(): InputStream = """
@@ -18,6 +18,10 @@
package com.rtbishop.look4sat.core.domain.source
object Sources {
// 在线更新的默认 URL(用户可通过"自定义URL"对话框修改/重置)
val defaultTleUrl = "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv"
val defaultTransceiversUrl = "https://db.satnogs.org/api/transmitters/?format=json&status=active"
val satelliteDataUrls = mapOf(
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z" />
</vector>
@@ -147,6 +147,7 @@
<string name="prefs_data_sources_tle_switch">Özel TLE URL</string>
<string name="prefs_data_sources_transceivers_switch">Özel transceiver URL</string>
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
<string name="prefs_data_reset_url">Varsayılan URL\'ye sıfırla</string>
<string name="prefs_data_output_title">Veri aktarımı</string>
<string name="prefs_net_output">Ağ</string>
@@ -161,6 +161,7 @@
<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_url_title" translatable="false">URL (HTTPS)</string>
<string name="prefs_data_reset_url">Reset to default URL</string>
<string name="prefs_data_output_title">Data output</string>
<string name="prefs_net_output">Network</string>
@@ -30,6 +30,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
@@ -41,11 +43,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.rtbishop.look4sat.core.domain.model.RCSettings
import com.rtbishop.look4sat.core.domain.source.Sources
import com.rtbishop.look4sat.core.domain.model.RadioControlSettings
import com.rtbishop.look4sat.core.presentation.CardButton
import com.rtbishop.look4sat.core.presentation.LocalSpacing
@@ -203,6 +207,14 @@ fun DataSourcesDialog(
singleLine = true,
modifier = Modifier.fillMaxWidth(),
enabled = isEnabledCustomTle.value,
trailingIcon = {
IconButton(onClick = { urlTle.value = Sources.defaultTleUrl }) {
Icon(
painter = painterResource(id = R.drawable.ic_reset),
contentDescription = stringResource(id = R.string.prefs_data_reset_url)
)
}
},
)
Spacer(modifier = Modifier.height(6.dp))
Row(
@@ -232,6 +244,14 @@ fun DataSourcesDialog(
singleLine = true,
modifier = Modifier.fillMaxWidth(),
enabled = isEnabledCustomTransceivers.value,
trailingIcon = {
IconButton(onClick = { urlTransceivers.value = Sources.defaultTransceiversUrl }) {
Icon(
painter = painterResource(id = R.drawable.ic_reset),
contentDescription = stringResource(id = R.string.prefs_data_reset_url)
)
}
},
)
Spacer(modifier = Modifier.height(12.dp))
}