feat(voice): add iOS VAD runtime support
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/// iOS microphone permission integration for AVAudioSession.
|
||||
///
|
||||
/// Trace:
|
||||
/// - SRS-164 (iOS system permission presentation and settings path).
|
||||
/// - SRS-114 / SRS-138 (iOS platform-service behaviour for audio).
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'android_permissions_service.dart';
|
||||
|
||||
@visibleForTesting
|
||||
const String iosPlatformChannelName = 'chanora/ios_platform';
|
||||
|
||||
@visibleForTesting
|
||||
const String methodGetMicrophonePermissionState =
|
||||
'getMicrophonePermissionState';
|
||||
|
||||
@visibleForTesting
|
||||
const String methodRequestMicrophonePermission = 'requestMicrophonePermission';
|
||||
|
||||
@visibleForTesting
|
||||
const String methodIosOpenAppSettings = 'openAppSettings';
|
||||
|
||||
/// Dart-side integration for iOS microphone permission state.
|
||||
class IosPermissionsService {
|
||||
IosPermissionsService({MethodChannel? channel})
|
||||
: _channel =
|
||||
channel ??
|
||||
(_isIOS ? const MethodChannel(iosPlatformChannelName) : null);
|
||||
|
||||
static bool get _isIOS {
|
||||
if (kIsWeb) return false;
|
||||
return Platform.isIOS;
|
||||
}
|
||||
|
||||
final MethodChannel? _channel;
|
||||
|
||||
final ValueNotifier<AndroidRecordAudioPermissionState> _state =
|
||||
ValueNotifier<AndroidRecordAudioPermissionState>(
|
||||
_isIOS
|
||||
? AndroidRecordAudioPermissionState.unknown
|
||||
: AndroidRecordAudioPermissionState.granted,
|
||||
);
|
||||
|
||||
ValueListenable<AndroidRecordAudioPermissionState> get recordAudioState =>
|
||||
_state;
|
||||
|
||||
Future<void> start() async {
|
||||
final ch = _channel;
|
||||
if (ch == null) return;
|
||||
try {
|
||||
_state.value = _parseState(
|
||||
await ch.invokeMethod<String>(methodGetMicrophonePermissionState),
|
||||
);
|
||||
} catch (_) {
|
||||
_state.value = AndroidRecordAudioPermissionState.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void stop() {}
|
||||
|
||||
Future<AndroidRecordAudioPermissionState> ensureRecordAudio() async {
|
||||
final ch = _channel;
|
||||
if (ch == null) return AndroidRecordAudioPermissionState.granted;
|
||||
try {
|
||||
final state = _parseState(
|
||||
await ch.invokeMethod<String>(methodRequestMicrophonePermission),
|
||||
);
|
||||
_state.value = state;
|
||||
return state;
|
||||
} catch (_) {
|
||||
return _state.value;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> openAppSettings() async {
|
||||
final ch = _channel;
|
||||
if (ch == null) return;
|
||||
try {
|
||||
await ch.invokeMethod<bool>(methodIosOpenAppSettings);
|
||||
} catch (e, st) {
|
||||
developer.log(
|
||||
'openAppSettings failed',
|
||||
name: 'IosPermissionsService',
|
||||
error: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
void dispose() {
|
||||
_state.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
AndroidRecordAudioPermissionState _parseState(String? raw) {
|
||||
switch (raw) {
|
||||
case 'Granted':
|
||||
return AndroidRecordAudioPermissionState.granted;
|
||||
case 'Denied':
|
||||
return AndroidRecordAudioPermissionState.permanentlyDenied;
|
||||
case 'NotDetermined':
|
||||
return AndroidRecordAudioPermissionState.denied;
|
||||
default:
|
||||
return AndroidRecordAudioPermissionState.unknown;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user