feat(wavelog): QRZ grid lookup, LoTW satellite list refresh, RST 59
User-prioritized WaveLog fixes (4.5.5, commit-only per instruction): - QRZ 对方网格爬虫: QrzGridClient (domain, pure JVM) fetches https://www.qrz.com/db/{call} with user-supplied cookies (parses EditThisCookie JSON or raw "k=v; k=v"), extracts Grid Square from the Detail table. Cookies NEVER built in - entered in settings. - Settings: WaveLog card top-right gear opens QRZ cookie dialog with test query button (detects logged-in callsign from cookie, looks up its grid, shows result or failure in the dialog). - LogTab: on Enter, async lookup of the other station's grid -> queue.updateGridsquare -> uploaded with QSO (postQso gridsquare). - LoTW satellite list: 112 names embedded (lotw.arrl.org config.tq6), normalizeSatName maps Celestrak TLE names to LoTW names (SAUDISAT-1C -> SO-50, FUNCUBE-1 -> AO-73, DIWATA-2B -> PO-101, ZARYA/ARISS -> ARISS); "Update sats" button in WaveLog card downloads the live list (LotwSatellitesRepo, SharedPreferences persisted, never in build). - RST: rst_sent/rst_rcvd = 59/59 in both v1 ADIF and v2 JSON. - Grid mismatch dialog kept (cloud station grid vs station QTH); QSO gridsquare no longer uses station grid. - New strings in 5 locales; check_strings 9 files OK. Verified: core:domain/data + feature:settings/radar + app compileDebugKotlin BUILD SUCCESSFUL.
This commit is contained in:
1 parent
2c26272e0b
commit
0e767f4250
18 files changed
+375
-20
No files matched your search
@@ -70,6 +70,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import okhttp3.OkHttpClient
|
||||
import com.rtbishop.look4sat.core.data.wavelog.LotwSatellitesRepo
|
||||
|
||||
class MainContainer(private val context: Context) : IMainContainer {
|
||||
|
||||
@@ -112,6 +113,12 @@ class MainContainer(private val context: Context) : IMainContainer {
|
||||
}
|
||||
override fun provideWavelogUploader(): WavelogUploader = WavelogUploader(settingsRepo, wavelogQueue)
|
||||
|
||||
private val lotwRepo: LotwSatellitesRepo by lazy {
|
||||
LotwSatellitesRepo(context).also { it.restore() }
|
||||
}
|
||||
|
||||
override fun provideLotwSatellitesRepo(): com.rtbishop.look4sat.core.domain.wavelog.ILotwSatellitesRepo = lotwRepo
|
||||
|
||||
override fun provideBluetoothReporter(): IReporter {
|
||||
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
val rc = settingsRepo.rcSettings.value
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* LotwSatellitesRepo.kt — LoTW 卫星列表运行时刷新(4.5.5)。
|
||||
* 下载 ARRL 官方 config.tq6(gzip XML), 解析 <satellite name="...">,
|
||||
* 更新 LotwSatellites 动态表 + SharedPreferences 持久化(重启不丢)。
|
||||
* 触发: 设置页 WaveLog 区「更新卫星列表」按钮(用户拍板: 手动触发)。
|
||||
*/
|
||||
package com.rtbishop.look4sat.core.data.wavelog
|
||||
|
||||
import android.content.Context
|
||||
import com.rtbishop.look4sat.core.domain.wavelog.ILotwSatellitesRepo
|
||||
import com.rtbishop.look4sat.core.domain.wavelog.LotwSatellites
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.URL
|
||||
import java.util.zip.GZIPInputStream
|
||||
import kotlin.random.Random
|
||||
|
||||
class LotwSatellitesRepo(private val context: Context) : ILotwSatellitesRepo {
|
||||
|
||||
private val prefs = context.getSharedPreferences("lotw_satellites", Context.MODE_PRIVATE)
|
||||
|
||||
/** 从 SharedPreferences 恢复上次下载的表(进程启动时调用) */
|
||||
override fun restore() {
|
||||
val saved = prefs.getString(KEY_NAMES, null)
|
||||
if (!saved.isNullOrBlank()) {
|
||||
LotwSatellites.updateNames(saved.split(",").filter { it.isNotBlank() }.toSet())
|
||||
}
|
||||
}
|
||||
|
||||
/** 下载 config.tq6 → 解析 → 更新内存 + 持久化。返回结果供 UI 提示 */
|
||||
override suspend fun refresh(): ILotwSatellitesRepo.RefreshResult = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val url = URL("https://lotw.arrl.org/lotw/config.tq6")
|
||||
val conn = url.openConnection()
|
||||
conn.connectTimeout = 10_000
|
||||
conn.readTimeout = 20_000
|
||||
conn.setRequestProperty("User-Agent", "Look4Sat-Pro/4.5.5")
|
||||
val raw = conn.getInputStream()
|
||||
val reader = BufferedReader(InputStreamReader(GZIPInputStream(raw), Charsets.UTF_8))
|
||||
val text = reader.use { it.readText() }
|
||||
// 解析 <satellite name="XXX" ...> — 格式固定(ARRL 官方 XML)
|
||||
val names = Regex("""<satellite name="([^"]+)""").findAll(text)
|
||||
.map { it.groupValues[1].uppercase() }
|
||||
.toSet()
|
||||
if (names.isEmpty()) return@withContext ILotwSatellitesRepo.RefreshResult.Error("列表为空(响应异常)")
|
||||
LotwSatellites.updateNames(names)
|
||||
prefs.edit()
|
||||
.putString(KEY_NAMES, names.joinToString(","))
|
||||
.putLong(KEY_UPDATED, System.currentTimeMillis())
|
||||
.apply()
|
||||
ILotwSatellitesRepo.RefreshResult.Ok(names.size)
|
||||
} catch (e: Exception) {
|
||||
ILotwSatellitesRepo.RefreshResult.Error(e.message ?: "未知错误")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_NAMES = "names"
|
||||
private const val KEY_UPDATED = "updated"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user