Skip to content

feat(audio): AudioManager audio session management#1108

Open
hiroshihorie wants to merge 21 commits into
mainfrom
hiroshi/audio-manager-api
Open

feat(audio): AudioManager audio session management#1108
hiroshihorie wants to merge 21 commits into
mainfrom
hiroshi/audio-manager-api

Conversation

@hiroshihorie

@hiroshihorie hiroshihorie commented Jun 12, 2026

Copy link
Copy Markdown
Member

What

First-class, process-wide audio session control on AudioManager — replacing session/routing behavior that was scattered across Hardware, track/audio_management.dart, and implicit native defaults.

API

Typed session configurationAudioSessionOptions with communication() / media() presets and per-platform escape hatches:

await AudioManager.instance.setAudioSessionOptions(
  const AudioSessionOptions.communication(),
);
  • AppleAudioSessionConfiguration: category / mode / category options
  • AndroidAudioSessionConfiguration: audio mode, focus mode, stream type, audio attributes usage/content types

Automatic vs manual managementAudioSessionManagementMode. Default automatic preserves today's behavior (the SDK tracks local/remote audio presence and configures the session). manual lets apps that own their audio lifecycle drive it themselves.

Android session backend — a new AudioSwitch-based LKAudioSwitchManager in the native plugin handles device routing, audio focus acquisition/release, and communication-device management, driven by configureAndroidAudioSession / stopAndroidAudioSession channel methods.

Unified speakerphone/routingsetSpeakerphoneOn, preferSpeakerOutput, forceSpeakerOutput move to AudioManager with consistent Apple (overrideOutputAudioPort) and Android (AudioSwitch) implementations.

Compatibility

Hardware's audio members become deprecated forwarders to AudioManager — migration, not removal. Room uses AudioManager internally; no behavior change for apps that touch nothing.

Why AudioManager

Platform audio sessions are global to the app process — they cannot be per-Room or per-track. This is the second half of making AudioManager the home for process-wide audio: #1107 added the engine-wide processing state read-back, this adds session/routing control — mirroring the Swift SDK's AudioManager.

Notes

Process-wide audio session control on AudioManager: session options and
management modes (automatic/manual), Android audio session configuration
(mode/focus/routing) backed by an AudioSwitch-based manager in the native
plugin, Apple speakerphone routing, and speaker output preferences.
Platform audio sessions are global to the app process, so this lives on
AudioManager rather than Room.
Picks up the aligned audioswitch revision (flutter-webrtc#2084), which
resolves the Android build conflict on the shared classpath.
@hiroshihorie hiroshihorie marked this pull request as ready for review June 15, 2026 10:22
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

Caution

Breaking change detected without major changeset

dart-apitool detected the following breaking changes:

Preparing git repository: https://github.com/livekit/client-sdk-flutter
Using ref: main
Cloning git repository: https://github.com/livekit/client-sdk-flutter
Checking out ref: main
Successfully cloned to: /tmp/RQIORK
Preparing package dependencies for git package https://github.com/livekit/client-sdk-flutter (forced Flutter)
Resolving dependencies...
Downloading packages...
  _fe_analyzer_shared 85.0.0 (103.0.0 available)
  analyzer 7.7.1 (13.3.0 available)
  build 2.5.4 (4.0.6 available)
  build_config 1.1.2 (1.3.0 available)
  build_resolvers 2.5.4 (3.0.4 available)
  build_runner 2.5.4 (2.15.0 available)
  build_runner_core 9.1.2 (9.3.2 available)
  built_value 8.12.4 (8.12.6 available)
  code_assets 1.0.0 (1.2.1 available)
  dart_style 3.1.1 (3.1.9 available)
  dart_webrtc 1.8.0 (1.8.1 available)
  dbus 0.7.12 (0.7.14 available)
  device_info_plus 12.3.0 (13.1.0 available)
  device_info_plus_platform_interface 7.0.3 (8.1.0 available)
  flutter_webrtc 1.5.0 (1.5.1 available)
  hooks 1.0.2 (2.0.2 available)
  json_annotation 4.9.0 (4.12.0 available)
  json_serializable 6.9.5 (6.14.0 available)
  matcher 0.12.19 (0.12.20 available)
  meta 1.18.0 (1.18.3 available)
  mockito 5.4.6 (5.7.0 available)
  native_toolchain_c 0.17.6 (0.19.1 available)
  objective_c 9.3.0 (9.4.1 available)
  package_config 2.2.0 (3.0.0 available)
  path_provider 2.1.5 (2.1.6 available)
  path_provider_android 2.2.22 (2.3.1 available)
  path_provider_platform_interface 2.1.2 (2.1.3 available)
  source_gen 2.0.0 (4.2.3 available)
  source_helper 1.3.7 (1.3.12 available)
  synchronized 3.4.0 (3.4.1 available)
  test_api 0.7.11 (0.7.12 available)
  vector_math 2.2.0 (2.4.0 available)
  vm_service 15.0.2 (15.2.0 available)
  win32 5.15.0 (6.3.0 available)
  win32_registry 2.1.0 (3.0.3 available)
  xml 6.6.1 (7.0.1 available)
Got dependencies!
36 packages have newer versions incompatible with dependency constraints.
Try `flutter pub outdated` for more information.
Resolving dependencies in `./example`...
Downloading packages...
Got dependencies in `./example`.
Preparing . (forced Flutter)
Copying sources from .
Preparing package dependencies for local package . (forced Flutter)
Resolving dependencies...
Downloading packages...
  _fe_analyzer_shared 85.0.0 (103.0.0 available)
  analyzer 7.7.1 (13.3.0 available)
  build 2.5.4 (4.0.6 available)
  build_config 1.1.2 (1.3.0 available)
  build_resolvers 2.5.4 (3.0.4 available)
  build_runner 2.5.4 (2.15.0 available)
  build_runner_core 9.1.2 (9.3.2 available)
  built_value 8.12.4 (8.12.6 available)
  code_assets 1.0.0 (1.2.1 available)
  dart_style 3.1.1 (3.1.9 available)
  dart_webrtc 1.8.0 (1.8.1 available)
  dbus 0.7.12 (0.7.14 available)
  device_info_plus 12.3.0 (13.1.0 available)
  device_info_plus_platform_interface 7.0.3 (8.1.0 available)
  hooks 1.0.2 (2.0.2 available)
  json_annotation 4.9.0 (4.12.0 available)
  json_serializable 6.9.5 (6.14.0 available)
  matcher 0.12.19 (0.12.20 available)
  meta 1.18.0 (1.18.3 available)
  mockito 5.4.6 (5.7.0 available)
  native_toolchain_c 0.17.6 (0.19.1 available)
  objective_c 9.3.0 (9.4.1 available)
  package_config 2.2.0 (3.0.0 available)
  path_provider 2.1.5 (2.1.6 available)
  path_provider_android 2.2.22 (2.3.1 available)
  path_provider_platform_interface 2.1.2 (2.1.3 available)
  source_gen 2.0.0 (4.2.3 available)
  source_helper 1.3.7 (1.3.12 available)
  synchronized 3.4.0 (3.4.1 available)
  test_api 0.7.11 (0.7.12 available)
  vector_math 2.2.0 (2.4.0 available)
  vm_service 15.0.2 (15.2.0 available)
  win32 5.15.0 (6.3.0 available)
  win32_registry 2.1.0 (3.0.3 available)
  xml 6.6.1 (7.0.1 available)
Got dependencies!
35 packages have newer versions incompatible with dependency constraints.
Try `flutter pub outdated` for more information.
Resolving dependencies in `./example`...
Downloading packages...
Got dependencies in `./example`.
Omitting pub get (package config already present)
Analyzing /tmp/RQIORK
Omitting pub get (package config already present)
Analyzing .
Cleaning up
Cleaning up
Checking Package version
ignoring prerelease
-- Generating report using: Console Reporter --
BREAKING CHANGES
├─┬ Class LocalAudioTrack
│ └── Super Type "LocalAudioManagementMixin" removed (CI05)
└─┬ Class RemoteAudioTrack
  └── Super Type "RemoteAudioManagementMixin" removed (CI05)
Non-Breaking changes
├── Interface "AudioEngineState" added (CI02) (minor)
├── Interface "AudioSessionOptions" added (CI02) (minor)
├── Interface "AppleAudioSessionConfiguration" added (CI02) (minor)
├── Interface "AppleAudioCategory" added (CI02) (minor)
├── Interface "AppleAudioCategoryOption" added (CI02) (minor)
├── Interface "AppleAudioMode" added (CI02) (minor)
├── Interface "ValueOrAbsent" added (CI02) (minor)
├── Interface "AndroidAudioSessionConfiguration" added (CI02) (minor)
├── Interface "AndroidAudioMode" added (CI02) (minor)
├── Interface "AndroidAudioFocusMode" added (CI02) (minor)
├── Interface "AndroidAudioStreamType" added (CI02) (minor)
├── Interface "AndroidAudioAttributesUsageType" added (CI02) (minor)
├── Interface "AndroidAudioAttributesContentType" added (CI02) (minor)
├── Interface "AudioSessionManagementMode" added (CI02) (minor)
├── Interface "NativeAudioConfiguration" added (CI02) (minor)
├── Interface "Value" added (CI02) (minor)
├── Interface "Absent" added (CI02) (minor)
├── Package dependency "flutter_webrtc" version changed from "1.5.0" to "1.5.1" (CD03) (minor)
├─┬ Class Hardware
│ ├─┬ Method setAutomaticConfigurationEnabled
│ │ └── Deprecated Flag changed. false -> true (CE13) (minor)
│ └─┬ Method setSpeakerphoneOn
│   └── Deprecated Flag changed. false -> true (CE13) (minor)
└─┬ Class AudioManager
  ├── Method "resetForTest" added (CE11) (minor)
  ├── Method "setAudioSessionOptions" added (CE11) (minor)
  ├── Method "setAudioSessionManagementMode" added (CE11) (minor)
  ├── Method "deactivateAudioSession" added (CE11) (minor)
  ├── Method "setSpeakerOutputPreferred" added (CE11) (minor)
  ├── Method "applyCurrentAudioSessionOptions" added (CE11) (minor)
  ├── Method "resolveAppleAudioConfigurationForTest" added (CE11) (minor)
  ├── Method "resolveAndroidAudioConfigurationForTest" added (CE11) (minor)
  ├── Field "defaultOptions" added (CF02) (minor)
  ├── Field "options" added (CF02) (minor)
  ├── Field "managementMode" added (CF02) (minor)
  ├── Field "isSpeakerOutputPreferred" added (CF02) (minor)
  ├── Field "isSpeakerOutputForced" added (CF02) (minor)
  ├── Field "canSwitchSpeakerphone" added (CF02) (minor)
  ├── Field "audioEngineState" added (CF02) (minor)
  ├── Field "audioEngineStateStream" added (CF02) (minor)
  └── Field "isAutomaticConfigurationEnabled" added (CF02) (minor)
To learn more about the detected changes visit: https://github.com/bmw-tech/dart_apitool/blob/main/readme/change_codes.md
Version Check
New Version is too low!

Old version: "2.8.0"
New version: "2.8.0"
Needed version: "3.0.0"

Got "2.8.0" expected >= "3.0.0" (breaking changes)

If this is intentional, please add a changeset with major level in .changes/:

major type="changed" "Description of breaking change"

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces first-class, process-wide audio session/routing management via AudioManager, consolidating previously scattered session behavior (track-counting, Hardware, and implicit native defaults) into a single, typed API with native backends on Apple and Android.

Changes:

  • Adds typed audio-session configuration (AudioSessionOptions, per-platform overrides, and AudioSessionManagementMode) plus supporting copy/serialization helpers.
  • Updates native plugins to let LiveKit own platform audio sessions: iOS/macOS engine-lifecycle observer drives session activation; Android adds an AudioSwitch-based manager for focus/mode/routing.
  • Deprecates/rewires legacy entry points (Hardware, track audio management mixins) to forward through AudioManager, and updates examples + adds unit tests.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/audio/audio_session_test.dart Adds unit tests covering session options, copyWith semantics, and serialization.
shared_swift/LiveKitPlugin.swift Disables flutter_webrtc audio management; adds engine-lifecycle observer + new method-channel handlers for Apple session management and speaker routing.
pubspec.yaml Bumps flutter_webrtc dependency to 1.5.1.
pubspec.lock Locks flutter_webrtc to 1.5.1 with updated hash.
lib/src/track/remote/audio.dart Removes legacy remote audio management mixin usage.
lib/src/track/local/audio.dart Removes legacy local audio management mixin usage.
lib/src/track/audio_management.dart Replaces track-counting session management with AudioManager connect/apply and Android stop handling.
lib/src/support/value_or_absent.dart Adds ValueOrAbsent utility to support nullable-field copyWith APIs.
lib/src/support/native.dart Extends Apple configureNativeAudio payload + adds Android/Apple session/routing method-channel APIs and engine-state callback handling.
lib/src/support/native_audio.dart Updates NativeAudioConfiguration.copyWith to use ValueOrAbsent; removes old static presets.
lib/src/livekit.dart Initializes AudioManager defaults and passes Android init configuration when needed.
lib/src/hardware/hardware.dart Deprecates audio-related members and forwards to AudioManager.
lib/src/core/room.dart Ensures audio session start/stop is cleaned up on connect failure; routes speaker toggles via AudioManager.
lib/src/audio/audio_session.dart New public API types for session intent + per-platform configuration.
lib/src/audio/audio_manager.dart Implements the new process-wide audio session manager, including engine-state stream and platform apply logic.
lib/src/audio/android_audio_session_adapter.dart Serializes Android session configuration to method-channel wire format and applies it via native.
lib/livekit_client.dart Exports the new audio session API.
example/lib/widgets/controls.dart Updates example UI to read speaker state from AudioManager.
example/lib/pages/room.dart Updates example to toggle speaker via AudioManager.
android/src/main/kotlin/io/livekit/plugin/LKAudioSwitchManager.kt Adds AudioSwitch-based Android session/focus/mode/routing manager.
android/src/main/kotlin/io/livekit/plugin/LiveKitPlugin.kt Disables flutter_webrtc audio management; wires method-channel handlers to LKAudioSwitchManager.
android/build.gradle Adds JitPack repository and AudioSwitch dependency.
.changes/audio-manager-api Adds a changeset entry documenting the new API.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread android/build.gradle
Comment on lines 17 to +21
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging this. A quick bit of context here. flutter_webrtc, which is a direct dependency of this plugin, already declares the JitPack repository at rootProject.allprojects scope, so every consuming app already gets it applied app wide. This block uses plain allprojects with no rootProject. prefix, which is actually a narrower scope, so it does not add anything the host app was not already getting transitively through flutter_webrtc.

We also pin the same audioswitch commit that flutter_webrtc and the LiveKit Android SDK use, so dependency resolution stays consistent across the whole stack. Keeping our own declaration here just makes the plugin self sufficient rather than relying on the transitive one.

Comment thread android/build.gradle
Comment on lines +65 to +67
// Audio device/focus/mode routing. Pinned to the same revision used by
// the LiveKit Android SDK (AudioSwitchHandler).
implementation 'com.github.davidliu:audioswitch:039a35aefab7747c557242fa216c9ea11743b604'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This one is a deliberate tradeoff, and it matches the rest of the LiveKit stack. The same audioswitch commit is pinned by both flutter_webrtc and the LiveKit Android SDK, so dependency resolution stays consistent end to end. A JitPack artifact is immutable per commit, so the pin is reproducible in practice, and the main residual risk is JitPack availability rather than the artifact changing under us.

The fork is needed for its CommDeviceAudioSwitch, which provides the API 31 setCommunicationDevice routing path, and that class has no tagged release or MavenCentral coordinate to point at yet. If upstream publishes a release that carries it, we would happily switch over.

Comment on lines +359 to +365
"configureAndroidAudioSession" -> {
@Suppress("UNCHECKED_CAST")
val configuration = call.arguments as? Map<String, Any?> ?: emptyMap()
audioSwitchManager?.configure(configuration)
audioSwitchManager?.start()
result.success(null)
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good observation. The coupling here is intentional on Android. Unlike iOS, the Android side has no audio engine lifecycle observer to defer activation to, so the session is activated when LiveKit applies its options (at connect, or on an explicit apply) and released on stop or disconnect. That keeps focus and mode ownership tied to a clear start and stop, which is the standard call session pattern.

Separating configure from activate, or adding an activate flag, is a reasonable future enhancement for finer grained manual mode control, and we can revisit it if a concrete need shows up. For this change the current behavior is the intended one, so we will keep it as is for now.

Comment thread shared_swift/LiveKitPlugin.swift Outdated
Comment on lines +798 to +801
// Leave sessionActive untrue so cached state still reflects the
// live session. Flipping it to false here would make a later
// configureNativeAudio(automatic:) cache-only while the session
// is in fact still active.
Relocate AppleAudioCategory / AppleAudioCategoryOption / AppleAudioMode from
the internal wire file (support/native_audio.dart) into audio_session.dart,
where the public AppleAudioSessionConfiguration already lives. native_audio.dart
keeps only the internal NativeAudioConfiguration wire type and its toStringValue
serialization, importing the enums from the audio layer. Removes the
import-plus-export of native_audio from audio_session.dart. Public API and
behavior are unchanged.
The automatic-mode session expressed the speaker preference twice: through
the audio mode (videoChat for speaker, voiceChat for receiver) and again
through overrideOutputAudioPort. The override hard-routes to the speaker even
over a connected headset, so a plain setSpeakerphoneOn(true) ignored the
documented headset priority and behaved like a forced speaker.

Gate the speaker override on the forced case only (carried as the
defaultToSpeaker category option) and otherwise clear it, letting the audio
mode and connected devices decide. This matches the Swift SDK, which selects
playAndRecordSpeaker/playAndRecordReceiver by mode and never overrides the
output port. Removes the now-unused preferSpeakerOutput plumbing from the
native observer and apply path. Manual mode keeps its direct route override.
Rename the new (unreleased) AudioManager speaker surface for clarity and to
match the Swift SDK vocabulary:
  setSpeakerphoneOn(enable, {forceSpeakerOutput})  -> setSpeakerOutputPreferred(preferred, {force})
  get preferSpeakerOutput                          -> get isSpeakerOutputPreferred
  get forceSpeakerOutput                           -> get isSpeakerOutputForced
Drop the duplicate speakerphoneOn getter (was identical to preferSpeakerOutput).
Update the deprecated Hardware forwards, Room.setSpeakerOn, the example app,
the audio guide, and the tests. AudioSessionOptions.preferSpeakerOutput and the
per-platform config fields keep their names. No behavior change.
The headset-priority fix made the native side ignore the preferSpeakerOutput
wire field (the speaker preference now lives entirely in the audio mode). Remove
the now-dead field from NativeAudioConfiguration (field, toMap, copyWith) and stop
sending it from the resolved Apple policy. The wire-format test now pins its
absence.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 25 changed files in this pull request and generated 2 comments.

Comment thread lib/src/audio/audio_manager.dart Outdated
Comment on lines +291 to +292
await setAndroidAudioSessionConfiguration(config);
await Native.setAndroidSpeakerphoneOn(policy.preferSpeakerOutput);
Comment on lines +125 to +132
val device = if (enable) {
switch.availableAudioDevices.firstOrNull { it is AudioDevice.Speakerphone }
} else {
switch.availableAudioDevices.firstOrNull {
it is AudioDevice.BluetoothHeadset || it is AudioDevice.WiredHeadset || it is AudioDevice.Earpiece
}
}
switch.selectDevice(device)
setSpeakerphoneOn(true) explicitly selected the speaker device even when a
wired/Bluetooth headset was connected, forcing the speaker and ignoring the
documented headset priority. Select a connected headset first and fall back to
the speaker only when none is present (earpiece when the speaker is not
preferred). This mirrors the iOS fix and removes the forced-speaker routing the
media preset would otherwise apply on every options change.
@hiroshihorie hiroshihorie requested a review from Copilot June 16, 2026 13:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 25 changed files in this pull request and generated 3 comments.

Comment on lines +218 to +221
} else {
// Manual mode: route without re-applying category/mode the app owns.
await Native.setAppleSpeakerphoneOn(preferred, force: _forceSpeakerOutput);
}
Comment on lines +44 to +46
// AudioSwitch is not threadsafe, so confine all access to a single long-lived
// thread. Do not recreate it on stop/start. Queued lifecycle work must stay
// serialized.
Comment thread docs/audio.md Outdated
@@ -0,0 +1,174 @@
# Audio session management

LiveKit owns the platform audio session on iOS, macOS, and Android through a single process-wide entry point, `AudioManager`. You configure session intent once with typed options and LiveKit applies the right native category, mode, focus, and routing for you. This replaces the older approach that relied on flutter_webrtc defaults, `Hardware` speaker calls, and Dart track counting.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 25 changed files in this pull request and generated 2 comments.

Comment thread lib/src/audio/audio_session.dart Outdated
Comment on lines +79 to +82
/// This intentionally does not expose [preferSpeakerOutput] because speaker
/// preference only affects communication/playAndRecord policies. Use [apple]
/// or [android] for exact platform behavior, or switch at runtime with
/// `AudioManager.setSpeakerOutputPreferred`.
Comment on lines +172 to +185
/// Selects whether LiveKit manages the platform audio session automatically.
///
/// In [AudioSessionManagementMode.manual], LiveKit does not update the audio
/// session from room, connect, or track lifecycle. The app can still apply a
/// configuration explicitly with [setAudioSessionOptions].
///
/// Prefer setting this before connecting to a room. flutter_webrtc's own
/// native audio management is always disabled (LiveKit owns the session).
/// Changing the mode at runtime only affects LiveKit's own automatic
/// configuration.
Future<void> setAudioSessionManagementMode(AudioSessionManagementMode mode) async {
_managementMode = mode;
await _syncAppleAudioSessionManagementMode();
}
@hiroshihorie

Copy link
Copy Markdown
Member Author

@copilot Are your findings addressed with latest commits ?

Copilot AI commented Jun 16, 2026

Copy link
Copy Markdown

@copilot Are your findings addressed with latest commits ?

Yes, all findings are addressed in the latest commits. Here's what changed per thread:

  • shared_swift/LiveKitPlugin.swift:798-801 — Fixed in feacfb4. Comment now reads "Leave sessionActive unchanged (still true)" instead of the ambiguous "untrue".

  • lib/src/audio/audio_manager.dart:291-292 — Addressed in a9ebb17. The media() doc comment now explicitly states "Android speaker routing remains a runtime preference", so the default preferSpeakerOutput: true is documented intent, not a silent force-route.

  • android/src/main/kotlin/io/livekit/plugin/LKAudioSwitchManager.kt:125-132 — Fixed in a9ebb17. setSpeakerphoneOn now drives applySpeakerRouting, which only selects the speaker device when speakerOutputForced is true. Preferred-only routing lets a connected headset keep priority via the preferredDeviceList.

  • lib/src/audio/audio_manager.dart:233-238 — Fixed in a9ebb17. In manual mode on iOS, setSpeakerOutputPreferred now calls _configureAppleAudioSession which applies the full resolved config (category + mode), not just the force-override path.

  • android/src/main/kotlin/io/livekit/plugin/LKAudioSwitchManager.kt:44-46 — Fixed in a9ebb17. Comment now correctly reads "AudioSwitch instance is recreated per active session", matching the actual lifecycle.

  • docs/audio.md:3 — Fixed in feacfb4. Opening sentence now explicitly scopes macOS to engine-state reporting only, not audio-session category/mode configuration.

  • lib/src/audio/audio_session.dart:79-82 — Fixed in feacfb4. media() doc comment no longer implies routing is left entirely to the platform; it now says Apple uses platform routing while Android uses the speaker preference.

  • lib/src/audio/audio_manager.dart:172-186 — Fixed in feacfb4. deactivateAudioSession() is now public API, giving manual-mode apps an explicit way to release Android audio focus / deactivate the iOS session.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 25 changed files in this pull request and generated 1 comment.

Comment thread docs/audio.md

## Per platform overrides

When the presets are not enough you can pin exact platform values. Anything you do not set falls back to the preset behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants