mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 19:17:21 +00:00
feat(wavelog): group log entries by pass session (satellite + AOS timestamp)
User request: logs should be separated by satellite/pass. Each pass session gets an ID = satellite name + AOS timestamp (the second the elevation hits 0, from OrbitalPass.aosTime), e.g. "ASRTU-1-20260804-2014". Log page groups entries by session: group title (satellite - local time) + thick divider line between groups (md --- style); legacy entries without sessionId fall into "Ungrouped" at the end. - WavelogQso: +sessionId (persisted in queue JSON) - LogTab: sessionId built from satelliteName + aosTimeMs (radar page passes currentPass.aosTime) - WavelogLogScreen: grouped rendering + wavelog_ungrouped string (4 locales) - sessionId UTC yyyyMMdd-HHmm; display converts to local time Verified: check_strings OK (9 files); :app:compileDebugKotlin BUILD SUCCESSFUL. Not released (batched).
This commit is contained in:
1 parent
a23bf34ccf
commit
dd378990fe
8 files changed
+79
-3
No files matched your search
@@ -25,6 +25,7 @@ data class WavelogQso(
|
||||
val freqTxHz: Long, // 上行(回车那一秒多普勒修正)
|
||||
val freqRxHz: Long, // 下行
|
||||
val satName: String,
|
||||
val sessionId: String = "", // 场次 ID: 卫星名-AOS 时间戳(过境仰角 0 秒), 空=未分组(旧数据)
|
||||
val uploaded: Boolean = false // 是否已成功上传(4.5.2 修复: 成功后保留标记, 表格打勾)
|
||||
)
|
||||
|
||||
@@ -46,6 +47,7 @@ class WavelogQueue(private val store: IWavelogQueueStore) {
|
||||
freqTxHz = o.optLong("freqTxHz"),
|
||||
freqRxHz = o.optLong("freqRxHz"),
|
||||
satName = o.optString("satName"),
|
||||
sessionId = o.optString("sessionId"),
|
||||
uploaded = o.optBoolean("uploaded", false)
|
||||
)
|
||||
}
|
||||
@@ -89,6 +91,7 @@ class WavelogQueue(private val store: IWavelogQueueStore) {
|
||||
put("id", q.id); put("timeUtcMs", q.timeUtcMs); put("call", q.call)
|
||||
put("mode", q.mode); put("freqTxHz", q.freqTxHz)
|
||||
put("freqRxHz", q.freqRxHz); put("satName", q.satName)
|
||||
put("sessionId", q.sessionId)
|
||||
put("uploaded", q.uploaded)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -203,6 +203,7 @@
|
||||
<string name="wavelog_col_sat">Satelit</string>
|
||||
<string name="wavelog_col_call">Panggilan</string>
|
||||
<string name="wavelog_col_uploaded">Terkirim</string>
|
||||
<string name="wavelog_ungrouped">Tidak dikelompokkan</string>
|
||||
<string name="prefs_other_title">Pengaturan lainnya</string>
|
||||
<string name="prefs_ui_title">Pengaturan UI</string>
|
||||
<string name="prefs_ui_order_title">Urutan halaman</string>
|
||||
|
||||
@@ -213,6 +213,7 @@
|
||||
<string name="wavelog_col_sat">Uydu</string>
|
||||
<string name="wavelog_col_call">Çağrı</string>
|
||||
<string name="wavelog_col_uploaded">Gönderildi</string>
|
||||
<string name="wavelog_ungrouped">Gruplanmamış</string>
|
||||
<string name="prefs_other_title">Diğer ayarlar</string>
|
||||
<string name="prefs_ui_title">UI Ayarları</string>
|
||||
<string name="prefs_ui_order_title">Sayfa sırası</string>
|
||||
|
||||
@@ -196,6 +196,7 @@
|
||||
<string name="wavelog_col_sat">卫星</string>
|
||||
<string name="wavelog_col_call">呼号</string>
|
||||
<string name="wavelog_col_uploaded">已传</string>
|
||||
<string name="wavelog_ungrouped">未分组</string>
|
||||
<string name="prefs_other_title">其他设置</string>
|
||||
<string name="prefs_ui_title">UI设置</string>
|
||||
<string name="prefs_ui_order_title">页面顺序</string>
|
||||
|
||||
@@ -227,6 +227,7 @@
|
||||
<string name="wavelog_col_sat">Satellite</string>
|
||||
<string name="wavelog_col_call">Call</string>
|
||||
<string name="wavelog_col_uploaded">Sent</string>
|
||||
<string name="wavelog_ungrouped">Ungrouped</string>
|
||||
<string name="prefs_other_title">Other settings</string>
|
||||
<string name="prefs_ui_title">UI Settings</string>
|
||||
<string name="prefs_ui_order_title">Page order</string>
|
||||
|
||||
@@ -88,6 +88,7 @@ fun LogTab(
|
||||
wavelogConfigured: Boolean,
|
||||
showToast: (String) -> Unit,
|
||||
txBaseFrequencyHz: Long? = null,
|
||||
aosTimeMs: Long = 0L,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var selectedUuid by remember { mutableStateOf<String?>(null) }
|
||||
@@ -159,6 +160,7 @@ fun LogTab(
|
||||
queue = queue,
|
||||
showToast = showToast,
|
||||
txBaseFrequencyHz = txBaseFrequencyHz,
|
||||
aosTimeMs = aosTimeMs,
|
||||
onSaved = { refreshTick++ }
|
||||
)
|
||||
}
|
||||
@@ -225,6 +227,7 @@ private fun ExpandedLogInput(
|
||||
queue: WavelogQueue,
|
||||
showToast: (String) -> Unit,
|
||||
txBaseFrequencyHz: Long? = null,
|
||||
aosTimeMs: Long = 0L,
|
||||
onSaved: () -> Unit
|
||||
) {
|
||||
var callsign by remember { mutableStateOf("") }
|
||||
@@ -246,7 +249,8 @@ private fun ExpandedLogInput(
|
||||
mode = mode.trim().ifBlank { "FM" }.uppercase(),
|
||||
freqTxHz = tx,
|
||||
freqRxHz = rx,
|
||||
satName = satelliteName
|
||||
satName = satelliteName,
|
||||
sessionId = buildSessionId(satelliteName, aosTimeMs)
|
||||
)
|
||||
)
|
||||
callsign = ""
|
||||
@@ -448,3 +452,14 @@ private fun formatLocalTime(utcMs: Long): String {
|
||||
cal.timeInMillis = utcMs
|
||||
return "%02d:%02d".format(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE))
|
||||
}
|
||||
|
||||
/** 场次 ID: 卫星名-AOS 时间戳(UTC yyyyMMdd-HHmm); 无 AOS 时用回车时刻 */
|
||||
private fun buildSessionId(satName: String, aosTimeMs: Long): String {
|
||||
val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
||||
cal.timeInMillis = if (aosTimeMs > 0) aosTimeMs else System.currentTimeMillis()
|
||||
val stamp = "%04d%02d%02d-%02d%02d".format(
|
||||
cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH),
|
||||
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)
|
||||
)
|
||||
return "$satName-$stamp"
|
||||
}
|
||||
@@ -299,7 +299,8 @@ private fun PagerCard(
|
||||
queue = wavelogQueue,
|
||||
wavelogConfigured = wavelogConfigured,
|
||||
showToast = showToast,
|
||||
txBaseFrequencyHz = uiState.radioControl.txBaseFrequencyHz
|
||||
txBaseFrequencyHz = uiState.radioControl.txBaseFrequencyHz,
|
||||
aosTimeMs = uiState.currentPass?.aosTime ?: 0L
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+54
-1
@@ -95,7 +95,27 @@ fun WavelogLogScreen(
|
||||
modifier = Modifier.padding(horizontal = 10.dp, vertical = 12.dp)
|
||||
)
|
||||
} else {
|
||||
entries.forEachIndexed { index, entry ->
|
||||
// 按场次分组(sessionId = 卫星名-AOS 时间戳); 空 sessionId 归"未分组"放最后
|
||||
val groups = entries.groupBy { it.sessionId.ifBlank { UNGROUPED } }
|
||||
.toSortedMap(compareByDescending<String> { id ->
|
||||
entries.filter { it.sessionId.ifBlank { UNGROUPED } == id }.minOfOrNull { it.timeUtcMs } ?: 0L
|
||||
}.thenByDescending { it })
|
||||
groups.forEach { (sessionId, groupEntries) ->
|
||||
// 组标题(场次名 + 本地时间)
|
||||
val (satPart, timePart) = parseSession(sessionId)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = if (sessionId == UNGROUPED) stringResource(id = R.string.wavelog_ungrouped)
|
||||
else "$satPart - ${formatSessionTime(timePart)}",
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp)
|
||||
)
|
||||
}
|
||||
// 组间横线(md --- 效果)
|
||||
HorizontalDivider(thickness = 1.5.dp, color = GridLineColor)
|
||||
groupEntries.forEach { entry ->
|
||||
SwipeDeleteRow(
|
||||
onDelete = {
|
||||
queue.remove(entry.id)
|
||||
@@ -133,6 +153,7 @@ fun WavelogLogScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,6 +194,38 @@ private fun RowScope.Cell(text: String, width: Dp, weight: Float = 0f) {
|
||||
)
|
||||
}
|
||||
|
||||
private const val UNGROUPED = "__ungrouped__"
|
||||
|
||||
/** 解析场次 ID: "ASRTU-1-20260804-2014" → (卫星名, "20260804-2014") */
|
||||
private fun parseSession(sessionId: String): Pair<String, String> {
|
||||
val parts = sessionId.split('-')
|
||||
if (parts.size < 3) return sessionId to ""
|
||||
val timePart = parts.takeLast(2).joinToString("-")
|
||||
val satPart = parts.dropLast(2).joinToString("-")
|
||||
return satPart to timePart
|
||||
}
|
||||
|
||||
/** 场次时间(UTC "20260804-2014") → 本地 "MM-dd HH:mm" */
|
||||
private fun formatSessionTime(timePart: String): String {
|
||||
if (timePart.length != 13 || timePart[8] != '-') return timePart
|
||||
return try {
|
||||
val year = timePart.substring(0, 4).toInt()
|
||||
val month = timePart.substring(4, 6).toInt()
|
||||
val day = timePart.substring(6, 8).toInt()
|
||||
val hour = timePart.substring(9, 11).toInt()
|
||||
val minute = timePart.substring(11, 13).toInt()
|
||||
val cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
||||
cal.set(year, month - 1, day, hour, minute, 0)
|
||||
cal.timeInMillis = cal.timeInMillis + TimeZone.getDefault().getOffset(cal.timeInMillis)
|
||||
"%02d-%02d %02d:%02d".format(
|
||||
cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH),
|
||||
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
timePart
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatLocalTime(utcMs: Long): String {
|
||||
val cal = Calendar.getInstance(TimeZone.getDefault())
|
||||
cal.timeInMillis = utcMs
|
||||
|
||||
Reference in new issue
Block a user