Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allows switching external playback mode in iOS #1816

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
22 changes: 22 additions & 0 deletions packages/audioplayers/lib/src/audioplayer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ class AudioPlayer {

double get playbackRate => _playbackRate;

bool _allowsExternalPlayback = false;

/// A Boolean value that indicates whether the player allows switching
/// to external playback mode.
///
/// Default is [_allowsExternalPlayback] and
/// switch by calling [setAllowsExternalPlayback]
bool get allowsExternalPlayback => _allowsExternalPlayback;

/// Current mode of the audio player. Can be updated at any time, but is going
/// to take effect only at the next time you play the audio.
PlayerMode _mode = PlayerMode.mediaPlayer;
Expand Down Expand Up @@ -168,6 +177,7 @@ class AudioPlayer {
positionUpdater = FramePositionUpdater(
getPosition: getCurrentPosition,
);
setAllowsExternalPlayback(allows: _allowsExternalPlayback);
}

Future<void> _create() async {
Expand Down Expand Up @@ -230,6 +240,18 @@ class AudioPlayer {
return _platform.setPlayerMode(playerId, mode);
}

/// Switching the external playback mode
///
/// Available in OS 6.0+ | iPadOS 6.0+ | Mac Catalyst 13.1+ |
///
/// macOS 10.11+ | tvOS 9.0+
///
/// Not Available in Android, Linux and Windows
Future<void> setAllowsExternalPlayback({required bool allows}) async {
_allowsExternalPlayback = allows;
return _platform.setAllowsExternalPlayback(playerId, allows: allows);
}

/// Pauses the audio that is currently playing.
///
/// If you call [resume] later, the audio will resume from the point that it
Expand Down
1 change: 1 addition & 0 deletions packages/audioplayers/test/audioplayers_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ void main() {
expect(player.source, null);
await player.creatingCompleter.future;
expect(platform.popCall().method, 'create');
expect(platform.popCall().method, 'setAllowsExternalPlayback');
expect(platform.popLastCall().method, 'getEventStream');
return player;
}
Expand Down
14 changes: 14 additions & 0 deletions packages/audioplayers/test/fake_audioplayers_platform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,18 @@ class FakeAudioplayersPlatform extends AudioplayersPlatformInterface {
calls.add(FakeCall(id: playerId, method: 'getEventStream'));
return eventStreamControllers[playerId]!.stream;
}

@override
Future<void> setAllowsExternalPlayback(
String playerId, {
required bool allows,
}) async {
calls.add(
FakeCall(
id: playerId,
method: 'setAllowsExternalPlayback',
value: allows,
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class AudioplayersPlugin : FlutterPlugin {
player.rate = rate.toFloat()
}

"setAllowsExternalPlayback" -> {
}

"getDuration" -> {
response.success(player.getDuration())
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,17 @@ public class SwiftAudioplayersDarwinPlugin: NSObject, FlutterPlugin {
let currentPosition = player.getCurrentPosition()
result(currentPosition)
return
} else if method == "setAllowsExternalPlayback" {
guard let allows = args["allows"] as? Bool else {
result(
FlutterError(
code: "DarwinAudioError",
message: "Error calling setAllowsExternalPlayback, allows cannot be null",
details: nil))
return
}

player.setAllowsExternalPlayback(allows: allows)
} else if method == "setPlaybackRate" {
guard let playbackRate = args["playbackRate"] as? Double else {
result(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ private let defaultVolume: Double = 1.0

private let defaultLooping: Bool = false

private let defaultAllowsExternalPlayback: Bool = false

typealias Completer = () -> Void

typealias CompleterError = (Error?) -> Void
Expand Down Expand Up @@ -121,6 +123,14 @@ class WrappedMediaPlayer {
}
}

func setAllowsExternalPlayback(allows: Bool) {
if #available(iOS 10.0, macOS 10.12, *) {
player.allowsExternalPlayback = allows
} else {
player.allowsExternalPlayback = defaultAllowsExternalPlayback
}
}

func seek(time: CMTime, completer: Completer? = nil) {
guard let currentItem = player.currentItem else {
completer?()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ static void audioplayers_linux_plugin_handle_method_call(
double playbackRate =
flPlaybackRate == nullptr ? 1.0 : fl_value_get_float(flPlaybackRate);
player->SetPlaybackRate(playbackRate);
} else if (strcmp(method, "setAllowsExternalPlayback") == 0) {
} else if (strcmp(method, "setReleaseMode") == 0) {
auto flReleaseMode = fl_value_lookup_string(args, "releaseMode");
std::string releaseMode =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ mixin MethodChannelAudioplayersPlatform
);
}

@override
Future<void> setAllowsExternalPlayback(
String playerId, {
required bool allows,
}) {
return _call(
'setAllowsExternalPlayback',
playerId,
<String, dynamic>{'allows': allows},
);
}

@override
Future<void> setReleaseMode(String playerId, ReleaseMode releaseMode) {
return _call(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ abstract class MethodChannelAudioplayersPlatformInterface {
/// Android SDK version should be 23 or higher
Future<void> setPlaybackRate(String playerId, double playbackRate);

/// Switching the external playback mode
Future<void> setAllowsExternalPlayback(
String playerId, {
required bool allows,
});

/// Configures the player to read the audio from a URL.
///
/// The resources will start being fetched or buffered as soon as you call
Expand Down
8 changes: 8 additions & 0 deletions packages/audioplayers_web/lib/audioplayers_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ class WebAudioplayersPlatform extends AudioplayersPlatformInterface {
getPlayer(playerId).playbackRate = playbackRate;
}

@override
Future<void> setAllowsExternalPlayback(
String playerId, {
required bool allows,
}) async {
// no-op: web doesn't have external playback, it supports only iOS, macOS
}

@override
Future<void> setReleaseMode(String playerId, ReleaseMode releaseMode) async {
getPlayer(playerId).releaseMode = releaseMode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ void AudioplayersWindowsPlugin::HandleMethodCall(
} else if (method_call.method_name().compare("setPlaybackRate") == 0) {
auto playbackRate = GetArgument<double>("playbackRate", args, 1.0);
player->SetPlaybackSpeed(playbackRate);
} else if (method_call.method_name().compare("setAllowsExternalPlayback") ==
0) {
} else if (method_call.method_name().compare("setReleaseMode") == 0) {
auto releaseMode =
GetArgument<std::string>("releaseMode", args, std::string());
Expand Down
Loading