chore(i18n): translate all code comments to English

All Chinese comments (//, /* */, KDoc) across core/app/feature/build-
logic translated to English (550 lines, 73 files after FT8 rollback).
Code logic untouched - comment text only. Verified: all modules
compileDebugKotlin BUILD SUCCESSFUL.
This commit is contained in:
mckero committed 2026-08-05 07:59:35 +00:00
1 parent 19926c58ac
commit c9cab8457d
65 files changed
+443 -443

No files matched your search

@@ -9,8 +9,8 @@ import java.net.InetSocketAddress
import java.net.Socket
/**
* APRS-IS TCP 客户端(从 APRSdroid TcpUploader.scala 逆向移植)。
* 纯文本协议:登录一行 + 每行一个包;断线 30 秒重连。
* APRS-IS TCP client (reverse-ported from APRSdroid TcpUploader.scala).
* Plain-text protocol: one login line + one packet per line; 30 s reconnect after drop.
*/
class AprsIsClient(
private val host: String,
@@ -30,7 +30,7 @@ class AprsIsClient(
val isConnected: Boolean
get() = synchronized(lock) { socket?.isConnected == true && !socket!!.isClosed }
/** 建立连接 + 登录(同步阻塞,调用方放后台线程) */
/** Connect + login (synchronous/blocking; call from a background thread) */
@Throws(Exception::class)
fun connect() {
disconnect()
@@ -43,10 +43,10 @@ class AprsIsClient(
writer = PrintWriter(OutputStreamWriter(s.getOutputStream(), Charsets.ISO_8859_1), true)
reader = BufferedReader(InputStreamReader(s.getInputStream(), Charsets.ISO_8859_1), 256)
}
// 登录行
// Login line
val login = AprsPacket.formatLogin(callsign, ssid, passcode, version) + filter
writer?.println(login)
// 读登录响应(aprsc 返回 # logresp ... verified/unverified)
// Read the login response (aprsc replies # logresp ... verified/unverified)
runCatching {
s.soTimeout = 8000
val resp = reader?.readLine()
@@ -54,14 +54,14 @@ class AprsIsClient(
resp.contains("unverified", ignoreCase = true))) {
throw IllegalArgumentException(resp.trim())
}
// 恢复超时
// Restore timeout
s.soTimeout = timeoutSec * 1000
}
}
/**
* 发送一个 APRS 包(一行)并尝试读服务器确认。
* 返回 null=发送失败;Pair(ok, detail)=发送结果(服务器错误原文在 detail)
* Sends one APRS packet (one line) and tries to read the server ack.
* Returns null=failed to send; Pair(ok, detail)=result (server error text lives in detail)
*/
fun sendPacket(packetLine: String): Pair<Boolean, String>? {
synchronized(lock) {
@@ -69,7 +69,7 @@ class AprsIsClient(
w.println(packetLine)
if (w.checkError()) return Pair(false, "write failed")
}
// 尝试读服务器响应(短超时 3s;APRS-IS 对格式错误会回错误行)
// Try to read the server response (short 3 s timeout; APRS-IS replies with an error line on bad format)
return runCatching {
val s = socket ?: return@runCatching Pair(true, "OK")
val oldTimeout = s.soTimeout
@@ -88,7 +88,7 @@ class AprsIsClient(
}.getOrElse { Pair(true, "OK") }
}
/** 读一行(服务器响应,超时抛异常) */
/** Read one line (server response; throws on timeout) */
fun readLine(): String? {
return reader?.readLine()
}
@@ -10,10 +10,10 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
/** APRS 连接状态 */
/** APRS connection state */
enum class AprsState { Idle, Connecting, Connected, Disconnected, Error }
/** APRS 配置(SharedPreferences 持久化,填入即保存) */
/** APRS config (persisted in SharedPreferences, saved as filled) */
data class AprsConfig(
val enabled: Boolean = false,
val server: String = "euro.aprs2.net",
@@ -29,7 +29,7 @@ data class AprsConfig(
val includeAltitude: Boolean = true
)
/** APRS 上报结果 */
/** APRS report result */
data class AprsReport(
val timestamp: Long,
val packet: String,
@@ -37,7 +37,7 @@ data class AprsReport(
val detail: String
)
/** 上报调度器(周期上报 + 手动触发),连接管理放在前台服务 */
/** Report scheduler (periodic + manual trigger); connection management lives in the foreground service */
class AprsReporter(
private val configProvider: () -> AprsConfig,
private val positionProvider: () -> Pair<Double, Double>? = { null },
@@ -51,7 +51,7 @@ class AprsReporter(
val isRunning: Boolean get() = job?.isActive == true
/** 启动周期上报(前台服务调用) */
/** Start periodic reporting (called by the foreground service) */
fun start() {
stop()
val cfg = configProvider()
@@ -75,7 +75,7 @@ class AprsReporter(
onState(AprsState.Idle)
}
/** 手动触发一次(立即上报,不等待周期) */
/** Trigger one report manually (immediately, without waiting for the cycle) */
fun reportNow() {
manualJob?.cancel()
manualJob = scope.launch { reportOnce() }
@@ -112,7 +112,7 @@ class AprsReporter(
}
}
/** 构造位置包:BG7NTA-5>APRS:=DDMM.MMN/DDDMM.MME<状态文本 */
/** Build position packet: BG7NTA-5>APRS:=DDMM.MMN/DDDMM.MME<status text */
private fun buildPositionPacket(cfg: AprsConfig, lat: Double? = null, lon: Double? = null): String {
val source = AprsPacket.formatCallSsid(cfg.callsign, cfg.ssid)
val pos = AprsPosition(
@@ -3,8 +3,8 @@ package com.rtbishop.look4sat.core.data.aprs
import android.content.Context
/**
* APRS 配置存取 + 服务 action 常量(被 feature/settings 和 app 服务共用,
* 避免 feature 反向依赖 app)。
* APRS config storage + service action constants (shared by feature/settings and the app service,
* so feature never depends on app).
*/
object AprsStore {
@@ -28,7 +28,7 @@ object AprsStore {
private const val KEY_LAST_OK = "last_report_ok"
private const val KEY_LAST_DETAIL = "last_report_detail"
/** 读取配置(填入即保存,无需每次打开重填) */
/** Read config (saved as filled; no need to re-enter each time) */
fun loadConfig(context: Context): AprsConfig {
val p = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
return AprsConfig(
@@ -45,7 +45,7 @@ object AprsStore {
)
}
/** 上次上报结果(供设置卡片显示) */
/** Last report result (shown on the settings card) */
data class LastReport(val time: Long = 0L, val ok: Boolean = false, val detail: String = "")
fun loadLastReport(context: Context): LastReport {
@@ -65,7 +65,7 @@ object AprsStore {
.apply()
}
/** 保存配置 */
/** Save config */
fun saveConfig(context: Context, cfg: AprsConfig) {
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit()
.putBoolean(KEY_ENABLED, cfg.enabled)
@@ -103,7 +103,7 @@ class MainContainer(private val context: Context) : IMainContainer {
override fun provideSaveImage(): ISaveImage = SaveImage(context)
// WaveLog 日志(4.5.2): 本地队列 + 上传器(共享实例)
// WaveLog logging (4.5.2): local queue + uploader (shared instance)
override val wavelogQueue: WavelogQueue by lazy {
val prefs = context.getSharedPreferences("wavelog", Context.MODE_PRIVATE)
WavelogQueue(object : IWavelogQueueStore {
@@ -5,7 +5,7 @@ import com.rtbishop.look4sat.core.domain.model.SatStatusPage
import com.rtbishop.look4sat.core.domain.repository.IAmSatRepository
import com.rtbishop.look4sat.core.domain.source.IRemoteSource
/** AMSAT 状态仓库: 抓 HTML → 解析 → SatStatusPage */
/** AMSAT status repository: fetch HTML -> parse -> SatStatusPage */
class AmSatRepository(private val remoteSource: IRemoteSource) : IAmSatRepository {
override suspend fun fetchStatus(): SatStatusPage? {
@@ -69,7 +69,7 @@ class DatabaseRepo(
val dataSourcesSettings = settingsRepo.dataSourcesSettings.value
val tleUrls = buildMap {
putAll(Sources.satelliteDataUrls)
// 开关开且 URL 非空 -> All 用自定义 URL;否则用默认 URL(在线更新默认源)
// Switch on + non-empty URL -> All uses the custom URL; otherwise the default URL (online-update default source)
put("All", if (dataSourcesSettings.useCustomTLE && dataSourcesSettings.tleUrl.isNotBlank())
dataSourcesSettings.tleUrl else Sources.defaultTleUrl)
}.filterValues { it.isNotBlank() }
@@ -81,7 +81,7 @@ class DatabaseRepo(
// launch all network requests concurrently
val tleJobs = tleUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } }
val radioJobs = radioUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } }
// 统计成功源数: 0 成功视为更新失败(不刷时间戳, 抛异常让 UI 提示)
// Count successful sources: zero successes = update failed (timestamp untouched, exception surfaced in the UI)
val tleResults = tleJobs.awaitAll()
val radioResults = radioJobs.awaitAll()
val successCount = tleResults.count { it.second != null } + radioResults.count { it.second != null }
@@ -187,9 +187,9 @@ class SettingsRepo(
return true
}
/** GPS 定位: 一次性 getCurrentLocation(GPS 优先, 15 秒超时), 拿到位置才返回 true */
/** GPS fix: one-shot getCurrentLocation (GPS first, 15 s timeout); returns true only with a fix */
override suspend fun setStationPosition(): Boolean {
// 权限前置: 无定位权限直接失败(不吞异常)
// Permission gate: fail fast without location permission (no swallowed exceptions)
if (androidx.core.content.ContextCompat.checkSelfPermission(
context, android.Manifest.permission.ACCESS_FINE_LOCATION
) != android.content.pm.PackageManager.PERMISSION_GRANTED
@@ -202,7 +202,7 @@ class SettingsRepo(
val signal = android.os.CancellationSignal()
val handler = android.os.Handler(android.os.Looper.getMainLooper())
val executor = java.util.concurrent.Executor { handler.post(it) }
// 15 秒超时
// 15 s timeout
val timeout = handler.postDelayed({
signal.cancel()
if (cont.isActive) cont.resume(false) { }
@@ -432,8 +432,8 @@ class SettingsRepo(
}
private fun getDataSourcesSettings(): DataSourcesSettings {
// 4.4.8 修复: 旧版 example.com 占位 URL 视为未配置 -> 替换为真实默认 URL 且开关强制关闭,
// 否则在线更新的 All/SatNOGS 源会指向错误地址导致更新失败
// 4.4.8 fix: legacy example.com placeholder URLs count as unconfigured -> replaced with the real default URL and the switch forced off,
// otherwise the online All/SatNOGS sources would point at the wrong address and fail to update
val storedTleUrl = preferences.getString(keyTleUrl, Sources.defaultTleUrl) ?: Sources.defaultTleUrl
val storedTxUrl = preferences.getString(keyTransceiversUrl, Sources.defaultTransceiversUrl) ?: Sources.defaultTransceiversUrl
val tleUrl = if (storedTleUrl == "https://example.com/tle.txt") Sources.defaultTleUrl else storedTleUrl
@@ -8,17 +8,17 @@ import com.rtbishop.look4sat.core.domain.model.SatStatusPage
import java.util.regex.Pattern
/**
* AMSAT 卫星状态页解析器(https://amsat.org/status/)
* AMSAT satellite status page parser (https://amsat.org/status/)
*
* 页面结构(静态 HTML, 2026-08 实测):
* - 状态表: <table> 内 48 行, 表头 = Name + 6 天(每个 colspan=12)
* 数据行 73 格: [0]=卫星名, [1..72] = 6 天 × 12 个 2 小时槽(新→旧)
* 每格 <td width=9 bgcolor="颜色">数字</td>, 有报告时数字带
* docTips.show('id') 链接
* - 报告详情在页面内嵌 JS:
* tips.a885153 = new Array(5,5,120,'状态<br>呼号<br>网格<br>日期<br>时间段 UTC')
* Page structure (static HTML, verified 2026-08):
* - Status table: 48 rows in <table>, header = Name + 6 days (each colspan=12)
* Data row has 73 cells: [0]=satellite name, [1..72] = 6 days x 12 two-hour slots (new->old)
* Each cell: <td width=9 bgcolor="color">count</td>; when reports exist the count carries a
* docTips.show('id') link
* - Report details live in the page's inline JS:
* tips.a885153 = new Array(5,5,120,'status<br>callsign<br>grid<br>date<br>time span UTC')
*
* 状态色: #648fff=活跃, #ffb000=仅遥测, #dc267f=未听到, #fe6100=矛盾
* Status colors: #648fff=active, #ffb000=telemetry only, #dc267f=not heard, #fe6100=conflicting
*/
object AmSatParser {
@@ -38,14 +38,14 @@ object AmSatParser {
"tips\\.(a\\d+)\\s*=\\s*new\\s+Array\\(\\s*\\d+,\\s*\\d+,\\s*\\d+,\\s*'([^']*)'"
)
/** 解析完整页面 */
/** Parse the full page */
fun parse(html: String, fetchedAtUtcMs: Long): SatStatusPage {
val reports = parseReports(html)
val statuses = parseStatusTable(html, reports)
return SatStatusPage(fetchedAtUtcMs, statuses, reports)
}
/** 提取全部报告(tips.* JS 数组) */
/** Extract all reports (tips.* JS arrays) */
fun parseReports(html: String): Map<String, SatReport> {
val map = mutableMapOf<String, SatReport>()
val m = tipRe.matcher(html)
@@ -64,7 +64,7 @@ object AmSatParser {
return map
}
/** 解析状态表(48 行卫星) */
/** Parse the status table (48 satellite rows) */
fun parseStatusTable(html: String, reports: Map<String, SatReport>): List<SatStatus> {
val result = mutableListOf<SatStatus>()
val tables = extractTables(html)
@@ -79,7 +79,7 @@ object AmSatParser {
val cellList = mutableListOf<String>()
while (cells.find()) cellList.add(cells.group(1))
if (rowIndex == 0) {
// 表头: Name + 6 天(每个 colspan=12)
// Header: Name + 6 days (each colspan=12)
dayHeaders = cellList.drop(1).take(6).map { stripHtml(it) }
rowIndex++
continue
@@ -1,7 +1,7 @@
/* LotwSatellitesRepo.kt — LoTW 卫星列表运行时刷新(4.5.5)。
* 下载 ARRL 官方 config.tq6(gzip XML), 解析 <satellite name="...">,
* 更新 LotwSatellites 动态表 + SharedPreferences 持久化(重启不丢)。
* 触发: 设置页 WaveLog 区「更新卫星列表」按钮(用户拍板: 手动触发)。
/* LotwSatellitesRepo.kt - runtime refresh of the LoTW satellite list (4.5.5).
* Downloads ARRL's official config.tq6 (gzip XML), parses <satellite name="...">,
* updates the LotwSatellites dynamic set + persists to SharedPreferences (survives restarts).
* Trigger: the "Update sats" button in the settings WaveLog section (user decided: manual).
*/
package com.rtbishop.look4sat.core.data.wavelog
@@ -20,7 +20,7 @@ class LotwSatellitesRepo(private val context: Context) : ILotwSatellitesRepo {
private val prefs = context.getSharedPreferences("lotw_satellites", Context.MODE_PRIVATE)
/** 从 SharedPreferences 恢复上次下载的表(进程启动时调用) */
/** Restore the last downloaded list from SharedPreferences (call at process start) */
override fun restore() {
val saved = prefs.getString(KEY_NAMES, null)
if (!saved.isNullOrBlank()) {
@@ -28,7 +28,7 @@ class LotwSatellitesRepo(private val context: Context) : ILotwSatellitesRepo {
}
}
/** 下载 config.tq6 → 解析 → 更新内存 + 持久化。返回结果供 UI 提示 */
/** Download config.tq6 -> parse -> update memory + persist. Result is surfaced in the UI */
override suspend fun refresh(): ILotwSatellitesRepo.RefreshResult = withContext(Dispatchers.IO) {
try {
val url = URL("https://lotw.arrl.org/lotw/config.tq6")
@@ -39,7 +39,7 @@ class LotwSatellitesRepo(private val context: Context) : ILotwSatellitesRepo {
val raw = conn.getInputStream()
val reader = BufferedReader(InputStreamReader(GZIPInputStream(raw), Charsets.UTF_8))
val text = reader.use { it.readText() }
// 解析 <satellite name="XXX" ...> — 格式固定(ARRL 官方 XML)
// Parse <satellite name="XXX" ...> - fixed format (official ARRL XML)
val names = Regex("""<satellite name="([^"]+)""").findAll(text)
.map { it.groupValues[1].uppercase() }
.toSet()