feat(ui,mobile): Plan E voice UI -- status chip + bottom-anchored PTT + AppBar mutes

Restructure the narrow / mobile body layout around three principles
(Hoober thumb-zone research validated, Material 3 components):

  1. Channel tree gets ~85% of the screen height.
  2. Live voice state is always visible in a 2-line status chip
     above the PTT button.
  3. PTT button is wide, bottom-anchored (thumb-natural lower zone).
  4. Frequent toggles (mic mute, headset mute, voice settings) live
     in the AppBar so they don't compete with the channel tree.
  5. Non-essential live readouts (level meter, TX/RX, capability
     badge) live in a modal sheet opened by tapping the status chip
     -- progressive disclosure.

apps/chanora_flutter/lib/widgets/voice_compact.dart  (new file)
  * VoiceStatusChip: 2-line live readout. Line 1 = mode + bind hint;
    Line 2 = release-tail + 'Mic on/off'. Tap opens
    showVoiceDetailsSheet.
  * VoicePttButton: 56 dp wide bottom-anchored Push to Talk button.
    Same touch-and-hold gestures as the previous _PttHoldButton.
  * showVoiceDetailsSheet: modal bottom sheet with mode recap +
    bind/tail hint + level meter + TX/RX counts + PTT capability
    badge (desktop-only).

apps/chanora_flutter/lib/main.dart
  * AppBar gains mic-mute, headset-mute, voice-settings icons when
    in voice channel AND MediaQuery width < 840 dp (mobile only).
    Wide mode keeps these controls inside the existing VoiceBar
    widget unchanged.
  * AppBar title becomes a Row of 'app name + channel chip' when
    in voice channel.
  * Narrow-mode body restructured: Expanded(channelTree) +
    VoiceStatusChip + VoicePttButton (latter only when in voice
    channel AND PTT mode). The old narrow-mode VoiceBar is gone;
    wide-mode VoiceBar is unchanged.
  * New _onOpenVoiceDetailsSheet handler bridges the chip-tap to
    showVoiceDetailsSheet.
  * New top-level helper _isTouchOnlyPttHost mirrors the helpers
    in widgets/voice_bar.dart and widgets/voice_settings.dart so
    the AppBar + narrow-mode chip can branch consistently.

apps/chanora_flutter/lib/l10n/app_en.arb
apps/chanora_flutter/lib/l10n/app_zh.arb
apps/chanora_flutter/lib/l10n/generated/*  (regenerated)
  * New string voicePttHoldHint = 'Hold the button' / '按住按钮'.
    Surfaced in line 1 of VoiceStatusChip on touch-only hosts and
    in the modal sheet's PTT line where the desktop equivalent
    would name a bound key.

Wide-mode (>= 840 dp) layout intentionally unchanged so the
signed-off rc.8 desktop verification still applies.

flutter analyze: clean (6 pre-existing Radio.groupValue infos).
Local cargo + flutter analyze pass; Mac was offline at commit
time so iOS device build verification is pending the next sync.
This commit is contained in:
EdisonJwa
2026-05-16 16:57:08 +08:00
parent 7c0e5caa92
commit 3b08ea9d32
7 changed files with 606 additions and 13 deletions
+1
View File
@@ -127,6 +127,7 @@
"voiceOutputMuteLabel": "Mute speakers",
"voiceSettingsTitle": "Voice settings",
"voiceBindKeyAction": "Bind PTT key",
"voicePttHoldHint": "Hold the button",
"voiceMicOn": "on",
"voiceMicOff": "off",
"channelJoinFailedPermission": "Insufficient permission to join this channel.",
+1
View File
@@ -84,6 +84,7 @@
"voiceOutputMuteLabel": "扬声器静音",
"voiceSettingsTitle": "语音设置",
"voiceBindKeyAction": "绑定 PTT 按键",
"voicePttHoldHint": "按住按钮",
"voiceMicOn": "开启",
"voiceMicOff": "关闭",
"channelJoinFailedPermission": "权限不足,无法加入此频道。",
@@ -547,6 +547,12 @@ abstract class AppL10n {
/// **'Bind PTT key'**
String get voiceBindKeyAction;
/// No description provided for @voicePttHoldHint.
///
/// In en, this message translates to:
/// **'Hold the button'**
String get voicePttHoldHint;
/// No description provided for @voiceMicOn.
///
/// In en, this message translates to:
@@ -266,6 +266,9 @@ class AppL10nEn extends AppL10n {
@override
String get voiceBindKeyAction => 'Bind PTT key';
@override
String get voicePttHoldHint => 'Hold the button';
@override
String get voiceMicOn => 'on';
@@ -260,6 +260,9 @@ class AppL10nZh extends AppL10n {
@override
String get voiceBindKeyAction => '绑定 PTT 按键';
@override
String get voicePttHoldHint => '按住按钮';
@override
String get voiceMicOn => '开启';