fix(aprs): upload feedback, manual report reliability, station position
User testing round 3 (4.5.4): no success feedback on upload, aprs.fi shows nothing, passcode calculator OK, notification present. - Manual report now works even when service not running: ACTION_REPORT_NOW starts the service first (Toast "not configured" if missing callsign) - Upload result feedback guaranteed: Toast always shows (short OK / long fail+reason), last result persisted (time/ok/detail) and shown in the settings card "Last report: HH:mm:ss OK/failed - detail" - Position source: station position from settingsRepo (user decision) with live GPS last-known as fallback - sendPacket reads the server confirmation line (short 3s timeout); server error text (Invalid/error) surfaces in Toast + card - Strings EN/ZH/TR/IN/ID +4 keys Verified: compileDebugKotlin all modules BUILD SUCCESSFUL, check_strings 9 files OK.
This commit is contained in:
1 parent
012ea1eeb6
commit
c9d87be289
10 files changed
+131
-19
No files matched your search
@@ -59,13 +59,33 @@ class AprsIsClient(
|
||||
}
|
||||
}
|
||||
|
||||
/** 发送一个 APRS 包(一行) */
|
||||
fun sendPacket(packetLine: String): Boolean {
|
||||
/**
|
||||
* 发送一个 APRS 包(一行)并尝试读服务器确认。
|
||||
* 返回 null=发送失败;Pair(ok, detail)=发送结果(服务器错误原文在 detail)
|
||||
*/
|
||||
fun sendPacket(packetLine: String): Pair<Boolean, String>? {
|
||||
synchronized(lock) {
|
||||
val w = writer ?: return false
|
||||
val w = writer ?: return null
|
||||
w.println(packetLine)
|
||||
return !w.checkError()
|
||||
if (w.checkError()) return Pair(false, "write failed")
|
||||
}
|
||||
// 尝试读服务器响应(短超时 3s;APRS-IS 对格式错误会回错误行)
|
||||
return runCatching {
|
||||
val s = socket ?: return@runCatching Pair(true, "OK")
|
||||
val oldTimeout = s.soTimeout
|
||||
s.soTimeout = 3000
|
||||
try {
|
||||
val resp = reader?.readLine()
|
||||
if (resp != null && (resp.contains("Invalid", ignoreCase = true) ||
|
||||
resp.contains("error", ignoreCase = true))) {
|
||||
Pair(false, resp.trim())
|
||||
} else {
|
||||
Pair(true, if (resp.isNullOrBlank()) "OK" else resp.trim())
|
||||
}
|
||||
} finally {
|
||||
s.soTimeout = oldTimeout
|
||||
}
|
||||
}.getOrElse { Pair(true, "OK") }
|
||||
}
|
||||
|
||||
/** 读一行(服务器响应,超时抛异常) */
|
||||
|
||||
@@ -99,9 +99,10 @@ class AprsReporter(
|
||||
|
||||
val pos = positionProvider()
|
||||
val packetLine = buildPositionPacket(cfg, pos?.first, pos?.second)
|
||||
val ok = c.sendPacket(packetLine)
|
||||
onReport(AprsReport(System.currentTimeMillis(), packetLine, ok,
|
||||
if (ok) "OK" else "send failed"))
|
||||
val result = c.sendPacket(packetLine)
|
||||
val ok = result?.first == true
|
||||
val detail = result?.second ?: "no connection"
|
||||
onReport(AprsReport(System.currentTimeMillis(), packetLine, ok, detail))
|
||||
if (ok) onState(AprsState.Connected) else onState(AprsState.Error)
|
||||
} catch (e: Exception) {
|
||||
runCatching { client?.disconnect() }
|
||||
|
||||
@@ -24,6 +24,9 @@ object AprsStore {
|
||||
private const val KEY_STATUS = "status"
|
||||
private const val KEY_SYMBOL_TABLE = "symbol_table"
|
||||
private const val KEY_SYMBOL_CODE = "symbol_code"
|
||||
private const val KEY_LAST_TIME = "last_report_time"
|
||||
private const val KEY_LAST_OK = "last_report_ok"
|
||||
private const val KEY_LAST_DETAIL = "last_report_detail"
|
||||
|
||||
/** 读取配置(填入即保存,无需每次打开重填) */
|
||||
fun loadConfig(context: Context): AprsConfig {
|
||||
@@ -42,6 +45,26 @@ object AprsStore {
|
||||
)
|
||||
}
|
||||
|
||||
/** 上次上报结果(供设置卡片显示) */
|
||||
data class LastReport(val time: Long = 0L, val ok: Boolean = false, val detail: String = "")
|
||||
|
||||
fun loadLastReport(context: Context): LastReport {
|
||||
val p = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
return LastReport(
|
||||
time = p.getLong(KEY_LAST_TIME, 0L),
|
||||
ok = p.getBoolean(KEY_LAST_OK, false),
|
||||
detail = p.getString(KEY_LAST_DETAIL, "") ?: ""
|
||||
)
|
||||
}
|
||||
|
||||
fun saveLastReport(context: Context, ok: Boolean, detail: String) {
|
||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit()
|
||||
.putLong(KEY_LAST_TIME, System.currentTimeMillis())
|
||||
.putBoolean(KEY_LAST_OK, ok)
|
||||
.putString(KEY_LAST_DETAIL, detail)
|
||||
.apply()
|
||||
}
|
||||
|
||||
/** 保存配置 */
|
||||
fun saveConfig(context: Context, cfg: AprsConfig) {
|
||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit()
|
||||
|
||||
Reference in new issue
Block a user