diff --git a/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/wavelog/WavelogQueue.kt b/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/wavelog/WavelogQueue.kt index 0fd829bf..3e728c21 100644 --- a/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/wavelog/WavelogQueue.kt +++ b/core/domain/src/main/java/com/rtbishop/look4sat/core/domain/wavelog/WavelogQueue.kt @@ -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) }) } diff --git a/core/presentation/src/main/res/values-in/strings.xml b/core/presentation/src/main/res/values-in/strings.xml index 42aebd52..a055c8b3 100644 --- a/core/presentation/src/main/res/values-in/strings.xml +++ b/core/presentation/src/main/res/values-in/strings.xml @@ -203,6 +203,7 @@ Satelit Panggilan Terkirim + Tidak dikelompokkan Pengaturan lainnya Pengaturan UI Urutan halaman diff --git a/core/presentation/src/main/res/values-tr/strings.xml b/core/presentation/src/main/res/values-tr/strings.xml index 81d1360c..df14a0de 100644 --- a/core/presentation/src/main/res/values-tr/strings.xml +++ b/core/presentation/src/main/res/values-tr/strings.xml @@ -213,6 +213,7 @@ Uydu Çağrı Gönderildi + Gruplanmamış Diğer ayarlar UI Ayarları Sayfa sırası diff --git a/core/presentation/src/main/res/values-zh/strings.xml b/core/presentation/src/main/res/values-zh/strings.xml index 2c789b08..97ae0f51 100644 --- a/core/presentation/src/main/res/values-zh/strings.xml +++ b/core/presentation/src/main/res/values-zh/strings.xml @@ -196,6 +196,7 @@ 卫星 呼号 已传 + 未分组 其他设置 UI设置 页面顺序 diff --git a/core/presentation/src/main/res/values/strings.xml b/core/presentation/src/main/res/values/strings.xml index 14b19369..1324d113 100644 --- a/core/presentation/src/main/res/values/strings.xml +++ b/core/presentation/src/main/res/values/strings.xml @@ -227,6 +227,7 @@ Satellite Call Sent + Ungrouped Other settings UI Settings Page order diff --git a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/LogTab.kt b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/LogTab.kt index e892617c..61cd4209 100644 --- a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/LogTab.kt +++ b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/LogTab.kt @@ -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(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" +} diff --git a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarScreen.kt b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarScreen.kt index e3febb88..ae30c2c9 100644 --- a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarScreen.kt +++ b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/RadarScreen.kt @@ -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 ) } } diff --git a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/WavelogLogScreen.kt b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/WavelogLogScreen.kt index 44b0fef5..4e3c001c 100644 --- a/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/WavelogLogScreen.kt +++ b/feature/radar/src/main/java/com/rtbishop/look4sat/feature/radar/WavelogLogScreen.kt @@ -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 { 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 { + 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