ui: slim the More menu and drop dead controls from the CW page
用户反馈三处 UI 问题, 一并处理。
1. 更多菜单风格不符 + 遮盖感重
- 去掉全屏 scrim 遮罩(0.35 alpha 压暗整页), 改为透明点击层, 点外部仍可关闭
- Card 限宽 232dp 靠右下角, 从"全宽卡片"变成竖长条
- 容器色 surfaceContainerHigh -> surfaceContainer, 与导航栏一致; 加 1dp 细边框
- 动画从全屏 expandVertically + spring 弹跳改为右下角原点 150ms scaleIn
- Card 加 clickable(enabled=false) 吞掉点击, 避免点卡片空白区误触关闭
2. CW 页左上角退出键点击无效 -> 删除
根因: CwDecodeScreen(navigateUp: () -> Unit = {}) 是默认空实现, 而
MainScreen 的 entry<Screen.CwDecode> 调用 CwDecodeScreen() 从未传入
navigateUp, 所以点击必然无反应。按 AGENTS.md 无死代码原则删除按钮 +
navigateUp 参数 + cw_back 字符串(五语)。
3. CW 页"正在监听"状态行在停止解码后仍显示 -> 删除
estimatedPitch 在暂停后保留上次值, 状态行不会消失, 属误导。删除该 Text
后 estimatedPitch / lastInferenceMs 两个 collector 成为死代码, 一并清理;
cw_status_listening / cw_status_tone 字符串(五语)同步删除。
signalStrength(瀑布图) 与 errorMessage(错误提示) 仍在用, 保留。
验证:
- :app:compileDebugKotlin + :feature:cw:compileDebugKotlin => BUILD SUCCESSFUL
- :core:domain:test => 31 个 CW 测试全绿 (7+13+3+8)
This commit is contained in:
1 parent
f9fd7a2cfa
commit
5d89190348
8 files changed
+32
-55
No files matched your search
@@ -22,18 +22,14 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.animation.togetherWith
|
||||
@@ -67,6 +63,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -346,14 +343,18 @@ fun MainScreen(navigateToRadar: () -> Unit = {}) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// More-menu popup panel (overlays content above the bottom bar; spring bounce)
|
||||
// More-menu popup panel (slim strip anchored to the bottom-right corner)
|
||||
AnimatedVisibility(
|
||||
visible = moreExpanded,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
enter = expandVertically(
|
||||
animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy)
|
||||
) + fadeIn(),
|
||||
exit = shrinkVertically() + fadeOut()
|
||||
enter = scaleIn(
|
||||
animationSpec = tween(150),
|
||||
transformOrigin = TransformOrigin(1f, 1f)
|
||||
) + fadeIn(animationSpec = tween(150)),
|
||||
exit = scaleOut(
|
||||
animationSpec = tween(120),
|
||||
transformOrigin = TransformOrigin(1f, 1f)
|
||||
) + fadeOut(animationSpec = tween(120))
|
||||
) {
|
||||
MoreMenuPopup(
|
||||
items = moreNavItems,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* MoreMenuPopup.kt - bottom-nav "More" second-level menu popup panel (4.5.1).
|
||||
*
|
||||
* Overlays the content area (above the bottom bar, right-aligned), vertical menu items (icon+text+arrow),
|
||||
* current page highlighted; tap the scrim to close, tap an item to navigate. Open/close animation is driven by the caller
|
||||
* (MainScreen's AnimatedVisibility + spring).
|
||||
* A slim right-aligned strip above the bottom bar, vertical menu items (icon+text+arrow),
|
||||
* current page highlighted; tap outside to close, tap an item to navigate. No dimming scrim, so the
|
||||
* page stays readable. Open/close animation is driven by the caller (MainScreen's AnimatedVisibility).
|
||||
*/
|
||||
package com.rtbishop.look4sat
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
@@ -45,16 +46,22 @@ fun MoreMenuPopup(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.35f))
|
||||
// Transparent catcher: taps outside the card dismiss the menu without
|
||||
// dimming the page behind it.
|
||||
.clickable(onClick = onDismiss)
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(12.dp),
|
||||
.padding(12.dp)
|
||||
.widthIn(max = 232.dp)
|
||||
// Swallow taps on the card so they do not reach the dismiss
|
||||
// catcher underneath.
|
||||
.clickable(enabled = false) {},
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f)),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||
)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(vertical = 4.dp)) {
|
||||
|
||||
@@ -77,7 +77,7 @@ import com.rtbishop.look4sat.core.presentation.R as CoreR
|
||||
* 400-1200 Hz window and tracks speed on its own, so there is nothing to tune.
|
||||
*/
|
||||
@Composable
|
||||
fun CwDecodeScreen(navigateUp: () -> Unit = {}) {
|
||||
fun CwDecodeScreen() {
|
||||
val context = LocalContext.current
|
||||
val container = remember { (context.applicationContext as IContainerProvider).getMainContainer() }
|
||||
val decoder = remember { container.provideCwDecoder() }
|
||||
@@ -95,9 +95,7 @@ fun CwDecodeScreen(navigateUp: () -> Unit = {}) {
|
||||
|
||||
val decodedText by decoder.decodedText.collectAsState()
|
||||
val historyText by decoder.historyText.collectAsState()
|
||||
val estimatedPitch by decoder.estimatedPitch.collectAsState()
|
||||
val signalStrength by decoder.signalStrength.collectAsState()
|
||||
val inferenceMs by decoder.lastInferenceMs.collectAsState()
|
||||
val errorMessage by decoder.errorMessage.collectAsState()
|
||||
|
||||
val permissionLauncher = rememberLauncherForActivityResult(
|
||||
@@ -139,27 +137,13 @@ fun CwDecodeScreen(navigateUp: () -> Unit = {}) {
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
IconButton(onClick = { isListening = false; navigateUp() }) {
|
||||
Icon(
|
||||
painter = painterResource(CoreR.drawable.ic_back),
|
||||
contentDescription = stringResource(R.string.cw_back)
|
||||
)
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringResource(CoreR.string.nav_cw),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = if (estimatedPitch != null) {
|
||||
stringResource(R.string.cw_status_tone, estimatedPitch!!.toInt(), inferenceMs)
|
||||
} else {
|
||||
stringResource(R.string.cw_status_listening)
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(CoreR.string.nav_cw),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 12.dp)
|
||||
)
|
||||
IconButton(onClick = { isListening = !isListening }) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="cw_back">Kembali</string>
|
||||
<string name="cw_pause">Jeda pendekodean</string>
|
||||
<string name="cw_resume">Lanjutkan pendekodean</string>
|
||||
<string name="cw_clear">Hapus teks hasil dekode</string>
|
||||
<string name="cw_status_listening">Mendengarkan…</string>
|
||||
<string name="cw_status_tone">%1$d Hz · %2$d ms</string>
|
||||
<string name="cw_mic_permission">Izin mikrofon diperlukan untuk pendekodean CW</string>
|
||||
<string name="cw_grant_permission">Berikan izin</string>
|
||||
<string name="cw_open_settings">Buka pengaturan aplikasi</string>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="cw_back">Kembali</string>
|
||||
<string name="cw_pause">Jeda pendekodean</string>
|
||||
<string name="cw_resume">Lanjutkan pendekodean</string>
|
||||
<string name="cw_clear">Hapus teks hasil dekode</string>
|
||||
<string name="cw_status_listening">Mendengarkan…</string>
|
||||
<string name="cw_status_tone">%1$d Hz · %2$d ms</string>
|
||||
<string name="cw_mic_permission">Izin mikrofon diperlukan untuk pendekodean CW</string>
|
||||
<string name="cw_grant_permission">Berikan izin</string>
|
||||
<string name="cw_open_settings">Buka pengaturan aplikasi</string>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="cw_back">Geri</string>
|
||||
<string name="cw_pause">Çözmeyi duraklat</string>
|
||||
<string name="cw_resume">Çözmeyi sürdür</string>
|
||||
<string name="cw_clear">Çözülen metni temizle</string>
|
||||
<string name="cw_status_listening">Dinleniyor…</string>
|
||||
<string name="cw_status_tone">%1$d Hz · %2$d ms</string>
|
||||
<string name="cw_mic_permission">CW çözümü için mikrofon izni gerekli</string>
|
||||
<string name="cw_grant_permission">İzin ver</string>
|
||||
<string name="cw_open_settings">Uygulama ayarlarını aç</string>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="cw_back">返回</string>
|
||||
<string name="cw_pause">暂停解码</string>
|
||||
<string name="cw_resume">继续解码</string>
|
||||
<string name="cw_clear">清空解码文本</string>
|
||||
<string name="cw_status_listening">正在监听…</string>
|
||||
<string name="cw_status_tone">%1$d Hz · %2$d 毫秒</string>
|
||||
<string name="cw_mic_permission">CW 解码需要麦克风权限</string>
|
||||
<string name="cw_grant_permission">授予权限</string>
|
||||
<string name="cw_open_settings">打开应用设置</string>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="cw_back">Back</string>
|
||||
<string name="cw_pause">Pause decoding</string>
|
||||
<string name="cw_resume">Resume decoding</string>
|
||||
<string name="cw_clear">Clear decoded text</string>
|
||||
<string name="cw_status_listening">Listening…</string>
|
||||
<string name="cw_status_tone">%1$d Hz · %2$d ms</string>
|
||||
<string name="cw_mic_permission">Microphone permission is required for CW decoding</string>
|
||||
<string name="cw_grant_permission">Grant permission</string>
|
||||
<string name="cw_open_settings">Open app settings</string>
|
||||
|
||||
Reference in new issue
Block a user