feat(macos): add macOS audio lifecycle MethodChannel

apps/chanora_flutter/macos/Runner/MacOSAudioLifecycle.swift (new): native MethodChannel handler for chanora/macos_audio_lifecycle. Observes Core Audio HAL default-input and default-output device property changes via AudioObjectAddPropertyListener; posts handleDefaultDeviceChange events with {role: input|output} payload. Mirrors the iOS chanora/ios_audio_lifecycle event surface minus the AVAudioSession-specific events (no interruption / no media services reset equivalents on macOS — no AVAudioSession).

apps/chanora_flutter/macos/Runner/MainFlutterWindow.swift: register MacOSAudioLifecycle next to MacOSPermissionsHandler in awakeFromNib. Closes the iOS/macOS asymmetry noted in SysRS-051.

apps/chanora_flutter/lib/services/audio_lifecycle_service.dart: add wireMacosAudioLifecycle() parallel to wireIosAudioLifecycle() / wireAndroidAudioLifecycle(). The current implementation captures and logs the events; the FRB function that triggers a VPIO re-bind on the engine is a follow-up. Event-shape mirrors the iOS side so a future caller can switch on platform without changing the dispatch shape.

apps/chanora_flutter/test/services/audio_lifecycle_service_test.dart: smoke test for wireMacosAudioLifecycle (4 tests pass, including the new one).
This commit is contained in:
Edison Jwa
2026-06-07 23:27:46 +09:00
parent 581353b6d2
commit fe5dc1cda4
4 changed files with 161 additions and 0 deletions
@@ -39,5 +39,17 @@ void main() {
// than misclassify as Speaker.
expect(parseBridgeAudioRoute('Hdmi'), rust.BridgeAudioRoute.unknown);
});
test('wireMacosAudioLifecycle is a no-op on non-macOS and registers on macOS', () {
// MethodChannel needs a binary messenger, which requires the test
// binding to be initialised first.
TestWidgetsFlutterBinding.ensureInitialized();
// Channel name constant matches the Swift side (MacOSAudioLifecycle.swift).
expect(macosAudioLifecycleChannelName, 'chanora/macos_audio_lifecycle');
// The wire is a no-op on the test platform (CI defaults to host OS
// which may be macOS or Linux). On Linux it returns early; on macOS
// it installs a handler. Either way, it must not throw.
expect(() => wireMacosAudioLifecycle(), returnsNormally);
});
}