fix(android): clear permission-owned hard mute
This commit is contained in:
@@ -251,6 +251,7 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
rust.BridgeTransmitMode _transmitMode = rust.BridgeTransmitMode.ptt;
|
||||
bool _hardMute = false;
|
||||
bool _hardMuteByPermission = false;
|
||||
bool _permissionHardMuteClearInFlight = false;
|
||||
int _releaseTailMs = 200;
|
||||
BigInt? _currentVoiceChannelId;
|
||||
BigInt? _pendingVoiceChannelId;
|
||||
@@ -310,10 +311,38 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
// events as early as possible so the listen-only banner reflects
|
||||
// the system state on first frame.
|
||||
_androidPermissions.start();
|
||||
_androidPermissions.recordAudioState.addListener(
|
||||
_onRecordAudioPermissionChanged,
|
||||
);
|
||||
unawaited(_reloadBookmarks());
|
||||
unawaited(_hydratePttBinding());
|
||||
}
|
||||
|
||||
void _onRecordAudioPermissionChanged() {
|
||||
if (_androidPermissions.recordAudioState.value ==
|
||||
AndroidRecordAudioPermissionState.granted) {
|
||||
unawaited(_clearPermissionHardMute());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _clearPermissionHardMute() async {
|
||||
if (!_hardMuteByPermission || _permissionHardMuteClearInFlight) return;
|
||||
_permissionHardMuteClearInFlight = true;
|
||||
try {
|
||||
await rust.setHardMute(muted: false);
|
||||
if (!mounted || !_hardMuteByPermission) return;
|
||||
setState(() {
|
||||
_hardMute = false;
|
||||
_hardMuteByPermission = false;
|
||||
});
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() => _error = e.toString());
|
||||
} finally {
|
||||
_permissionHardMuteClearInFlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool _handleFocusedPttKey(KeyEvent event) {
|
||||
final label = _pttDisplayLabelForKey(event.logicalKey);
|
||||
final isBoundKey =
|
||||
@@ -492,16 +521,17 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
// SDD-106 §5/§6 / SRS-209: defensive observer of the
|
||||
// authoritative Rust-side permission stream. The transmit
|
||||
// clamp is already applied inside `chanora_bridge` before
|
||||
// this event is broadcast; here we merely surface the
|
||||
// event so the UI stays consistent if the MethodChannel
|
||||
// path is ever delayed. The existing `AndroidPermissionsService`
|
||||
// remains the canonical Dart-side state holder (driven by
|
||||
// the MethodChannel); a future revision may expose a
|
||||
// setter so both paths converge on a single ValueNotifier.
|
||||
// this event is broadcast. When permission later becomes
|
||||
// granted, release only the permission-owned hard-mute; do
|
||||
// not touch server-side input mute, which may be user-owned.
|
||||
case rust.BridgeEvent_PermissionState(:final permission, :final state):
|
||||
debugPrint(
|
||||
'bridge permission_state: permission=$permission state=$state',
|
||||
);
|
||||
if (permission == 'android.permission.RECORD_AUDIO' &&
|
||||
state == rust.PermissionStateKind.granted) {
|
||||
unawaited(_clearPermissionHardMute());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,6 +563,9 @@ class _BetaHomeState extends State<_BetaHome> {
|
||||
_hostCtl.dispose();
|
||||
_nickCtl.dispose();
|
||||
_passwordCtl.dispose();
|
||||
_androidPermissions.recordAudioState.removeListener(
|
||||
_onRecordAudioPermissionChanged,
|
||||
);
|
||||
// SDD-106: detach the Kotlin -> Dart MethodChannel handler so a
|
||||
// late invokeMethod from the platform side cannot land on this
|
||||
// disposed state.
|
||||
|
||||
Reference in New Issue
Block a user