feat(ui,voice): add speaker mute button to VoiceBar
Speaker (output) mute existed in the legacy _AudioControls widget and the rust.setOutputMuted bridge call but was lost when SDD-097 replaced _AudioControls with VoiceBar. Mic mute carried over; speaker mute did not. Wire it back: VoiceBar gains an outputMuted prop + onToggleOutputMute callback and renders a headset/headset_off icon next to the existing mic mute. main.dart wires the existing _toggleOutputMute handler (previously dead-code with // ignore: unused_element). The bridge call setOutputMuted already does both effects together: local engine silencer + server-broadcast ClientOutputMuted flag. l10n: rename voiceHardMuteLabel to 'Mute microphone'/'麦克风静音' to distinguish from the new voiceOutputMuteLabel 'Mute speakers'/ '扬声器静音'. flutter analyze: clean (6 pre-existing Radio.groupValue infos).
This commit is contained in:
@@ -123,7 +123,8 @@
|
|||||||
"voiceReleaseTailLabel": "Release tail",
|
"voiceReleaseTailLabel": "Release tail",
|
||||||
"voiceReleaseTailHint": "ms",
|
"voiceReleaseTailHint": "ms",
|
||||||
"voiceLeaveAction": "Leave voice",
|
"voiceLeaveAction": "Leave voice",
|
||||||
"voiceHardMuteLabel": "Mute",
|
"voiceHardMuteLabel": "Mute microphone",
|
||||||
|
"voiceOutputMuteLabel": "Mute speakers",
|
||||||
"voiceSettingsTitle": "Voice settings",
|
"voiceSettingsTitle": "Voice settings",
|
||||||
"voiceBindKeyAction": "Bind PTT key"
|
"voiceBindKeyAction": "Bind PTT key"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,8 @@
|
|||||||
"voiceReleaseTailLabel": "释放延迟",
|
"voiceReleaseTailLabel": "释放延迟",
|
||||||
"voiceReleaseTailHint": "毫秒",
|
"voiceReleaseTailHint": "毫秒",
|
||||||
"voiceLeaveAction": "离开语音",
|
"voiceLeaveAction": "离开语音",
|
||||||
"voiceHardMuteLabel": "静音",
|
"voiceHardMuteLabel": "麦克风静音",
|
||||||
|
"voiceOutputMuteLabel": "扬声器静音",
|
||||||
"voiceSettingsTitle": "语音设置",
|
"voiceSettingsTitle": "语音设置",
|
||||||
"voiceBindKeyAction": "绑定 PTT 按键"
|
"voiceBindKeyAction": "绑定 PTT 按键"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -526,9 +526,15 @@ abstract class AppL10n {
|
|||||||
/// No description provided for @voiceHardMuteLabel.
|
/// No description provided for @voiceHardMuteLabel.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Mute'**
|
/// **'Mute microphone'**
|
||||||
String get voiceHardMuteLabel;
|
String get voiceHardMuteLabel;
|
||||||
|
|
||||||
|
/// No description provided for @voiceOutputMuteLabel.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Mute speakers'**
|
||||||
|
String get voiceOutputMuteLabel;
|
||||||
|
|
||||||
/// No description provided for @voiceSettingsTitle.
|
/// No description provided for @voiceSettingsTitle.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -255,7 +255,10 @@ class AppL10nEn extends AppL10n {
|
|||||||
String get voiceLeaveAction => 'Leave voice';
|
String get voiceLeaveAction => 'Leave voice';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get voiceHardMuteLabel => 'Mute';
|
String get voiceHardMuteLabel => 'Mute microphone';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get voiceOutputMuteLabel => 'Mute speakers';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get voiceSettingsTitle => 'Voice settings';
|
String get voiceSettingsTitle => 'Voice settings';
|
||||||
|
|||||||
@@ -249,7 +249,10 @@ class AppL10nZh extends AppL10n {
|
|||||||
String get voiceLeaveAction => '离开语音';
|
String get voiceLeaveAction => '离开语音';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get voiceHardMuteLabel => '静音';
|
String get voiceHardMuteLabel => '麦克风静音';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get voiceOutputMuteLabel => '扬声器静音';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get voiceSettingsTitle => '语音设置';
|
String get voiceSettingsTitle => '语音设置';
|
||||||
|
|||||||
@@ -318,7 +318,6 @@ class _BetaHomeState extends State<_BetaHome> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: unused_element
|
|
||||||
Future<void> _toggleOutputMute() async {
|
Future<void> _toggleOutputMute() async {
|
||||||
final next = !_outputMuted;
|
final next = !_outputMuted;
|
||||||
try {
|
try {
|
||||||
@@ -846,6 +845,7 @@ class _BetaHomeState extends State<_BetaHome> {
|
|||||||
inChannel: _inChannel,
|
inChannel: _inChannel,
|
||||||
transmitMode: _transmitMode,
|
transmitMode: _transmitMode,
|
||||||
hardMute: _hardMute,
|
hardMute: _hardMute,
|
||||||
|
outputMuted: _outputMuted,
|
||||||
releaseTailMs: _releaseTailMs,
|
releaseTailMs: _releaseTailMs,
|
||||||
channelName: _currentVoiceChannelName(),
|
channelName: _currentVoiceChannelName(),
|
||||||
audioStats: _audioStats,
|
audioStats: _audioStats,
|
||||||
@@ -854,6 +854,7 @@ class _BetaHomeState extends State<_BetaHome> {
|
|||||||
pttBoundInputClass: _pttBoundInputClass,
|
pttBoundInputClass: _pttBoundInputClass,
|
||||||
pttBoundKeyLabel: _pttBoundKeyLabel,
|
pttBoundKeyLabel: _pttBoundKeyLabel,
|
||||||
onToggleMute: _onToggleHardMute,
|
onToggleMute: _onToggleHardMute,
|
||||||
|
onToggleOutputMute: _toggleOutputMute,
|
||||||
onConfigure: _onOpenVoiceSettings,
|
onConfigure: _onOpenVoiceSettings,
|
||||||
onLeave: _onLeaveVoice,
|
onLeave: _onLeaveVoice,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class VoiceBar extends StatelessWidget {
|
|||||||
required this.inChannel,
|
required this.inChannel,
|
||||||
required this.transmitMode,
|
required this.transmitMode,
|
||||||
required this.hardMute,
|
required this.hardMute,
|
||||||
|
required this.outputMuted,
|
||||||
required this.releaseTailMs,
|
required this.releaseTailMs,
|
||||||
required this.channelName,
|
required this.channelName,
|
||||||
required this.audioStats,
|
required this.audioStats,
|
||||||
@@ -26,6 +27,7 @@ class VoiceBar extends StatelessWidget {
|
|||||||
required this.pttBoundInputClass,
|
required this.pttBoundInputClass,
|
||||||
required this.pttBoundKeyLabel,
|
required this.pttBoundKeyLabel,
|
||||||
required this.onToggleMute,
|
required this.onToggleMute,
|
||||||
|
required this.onToggleOutputMute,
|
||||||
required this.onConfigure,
|
required this.onConfigure,
|
||||||
required this.onLeave,
|
required this.onLeave,
|
||||||
});
|
});
|
||||||
@@ -39,6 +41,13 @@ class VoiceBar extends StatelessWidget {
|
|||||||
/// Hard-mute clamp state.
|
/// Hard-mute clamp state.
|
||||||
final bool hardMute;
|
final bool hardMute;
|
||||||
|
|
||||||
|
/// Speaker (output) mute state. Mirrors the server-broadcast
|
||||||
|
/// `ClientOutputMuted` flag plus the engine's local output
|
||||||
|
/// silencer — toggling this hushes incoming voice immediately
|
||||||
|
/// AND tells the server so other clients see the headphone-off
|
||||||
|
/// icon next to our name.
|
||||||
|
final bool outputMuted;
|
||||||
|
|
||||||
/// Configured release-tail in milliseconds (0..=500). Surfaced as
|
/// Configured release-tail in milliseconds (0..=500). Surfaced as
|
||||||
/// a hint underneath the mode badge.
|
/// a hint underneath the mode badge.
|
||||||
final int releaseTailMs;
|
final int releaseTailMs;
|
||||||
@@ -67,6 +76,9 @@ class VoiceBar extends StatelessWidget {
|
|||||||
/// Toggle the hard-mute clamp.
|
/// Toggle the hard-mute clamp.
|
||||||
final VoidCallback onToggleMute;
|
final VoidCallback onToggleMute;
|
||||||
|
|
||||||
|
/// Toggle speaker (output) mute.
|
||||||
|
final VoidCallback onToggleOutputMute;
|
||||||
|
|
||||||
/// Open the voice settings dialog. This is the SINGLE entry point
|
/// Open the voice settings dialog. This is the SINGLE entry point
|
||||||
/// for transmit-mode selection, PTT key binding, and release-tail
|
/// for transmit-mode selection, PTT key binding, and release-tail
|
||||||
/// configuration. The capability badge below is information-only
|
/// configuration. The capability badge below is information-only
|
||||||
@@ -136,6 +148,15 @@ class VoiceBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
IconButton(
|
||||||
|
tooltip: l10n.voiceOutputMuteLabel,
|
||||||
|
icon: Icon(
|
||||||
|
outputMuted ? Icons.headset_off : Icons.headset,
|
||||||
|
),
|
||||||
|
isSelected: outputMuted,
|
||||||
|
selectedIcon: const Icon(Icons.headset_off),
|
||||||
|
onPressed: onToggleOutputMute,
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: l10n.voiceHardMuteLabel,
|
tooltip: l10n.voiceHardMuteLabel,
|
||||||
icon: Icon(hardMute ? Icons.mic_off : Icons.mic),
|
icon: Icon(hardMute ? Icons.mic_off : Icons.mic),
|
||||||
|
|||||||
Reference in New Issue
Block a user