Skip to content

Commit

Permalink
refactor!: improve separation of global audioplayer interface (#1443)
Browse files Browse the repository at this point in the history
# Description

AudioPlayers platform communication consists of two parts: the
`AudioPlayer` scope and the `Global` scope.
These were not separated consistently, so one was used as implementation
and the other as interface.
Now both scopes are handled the same way:

* `AudioPlayer`: player specific processing and interaction via
`AudioplayersPlatformInterface`
* `GlobalAudioScope`: global processing and interaction via
`GlobalAudioplayersPlatformInterface`
* `AudioplayersPlatformInterface`: interface to player platform channel
(methods and events), holds an instance implementation like
`AudioplayersPlatform` or `WebAudioplayersPlatform`
* `GlobalAudioplayersPlatformInterface`: interface to global platform
channel (methods and events), holds an instance implementation like
`GlobalAudioplayersPlatform` or `WebGlobalAudioplayersPlatform`
* `AudioplayersPlatform`: platform channel implementation of
`AudioplayersPlatformInterface`
* `GlobalAudioplayersPlatform`: platform channel implementation of
`GlobalAudioplayersPlatformInterface`

Also deprecated `AudioPlayer.global.setGlobalAudioContext()` in favor of
`AudioPlayer.global.setAudioContext()`.

## Migration instructions

**audioplayers**:
| Before | After |
|---|---|
| `GlobalPlatformInterface` | `GlobalAudioScope` |
| `AudioPlayer.global.setGlobalAudioContext()` |
`AudioPlayer.global.setAudioContext()` |

**audioplayers_platform_interface**:
| Before | After |
|---|---|
| `AudioplayersPlatform` | `AudioplayersPlatformInterface` |
| `MethodChannelAudioplayersPlatform` | `AudioplayersPlatform` |
| `GlobalPlatformInterface` | `GlobalAudioplayersPlatformInterface` |
| `MethodChannelGlobalPlatform` | `GlobalAudioplayersPlatform` |

**audioplayers_web**:
| Before | After |
|---|---|
| `AudioplayersPlugin` | `AudioplayersPlugin`, `WebAudioplayersPlatform` and `WebGlobalAudioplayersPlatform` |
  • Loading branch information
Gustl22 authored Mar 20, 2023
1 parent 3b5de50 commit c0b3f85
Show file tree
Hide file tree
Showing 14 changed files with 108 additions and 49 deletions.
8 changes: 4 additions & 4 deletions packages/audioplayers/example/integration_test/lib_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ void main() {
//ignore: avoid_redundant_argument_values
respectSilence: false,
).build();
await AudioPlayer.global.setGlobalAudioContext(audioContext);
await AudioPlayer.global.setAudioContext(audioContext);
await player.setAudioContext(audioContext);

await player.play(td.source);
Expand All @@ -143,7 +143,7 @@ void main() {
forceSpeaker: false,
respectSilence: true,
).build();
await AudioPlayer.global.setGlobalAudioContext(audioContext);
await AudioPlayer.global.setAudioContext(audioContext);
await player.setAudioContext(audioContext);

await player.resume();
Expand Down Expand Up @@ -172,7 +172,7 @@ void main() {
//ignore: avoid_redundant_argument_values
respectSilence: false,
).build();
await AudioPlayer.global.setGlobalAudioContext(audioContext);
await AudioPlayer.global.setAudioContext(audioContext);
await player.setAudioContext(audioContext);

await player.setSource(td.source);
Expand All @@ -187,7 +187,7 @@ void main() {
forceSpeaker: false,
respectSilence: true,
).build();
await AudioPlayer.global.setGlobalAudioContext(audioContext);
await AudioPlayer.global.setAudioContext(audioContext);
await player.setAudioContext(audioContext);

await player.resume();
Expand Down
4 changes: 2 additions & 2 deletions packages/audioplayers/example/lib/tabs/audio_context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class AudioContextTab extends StatefulWidget {

class _AudioContextTabState extends State<AudioContextTab>
with AutomaticKeepAliveClientMixin<AudioContextTab> {
static GlobalPlatformInterface get _global => AudioPlayer.global;
static GlobalAudioScope get _global => AudioPlayer.global;

AudioPlayer get player => widget.player;

Expand All @@ -43,7 +43,7 @@ class _AudioContextTabState extends State<AudioContextTab>
ElevatedButton.icon(
icon: const Icon(Icons.public),
label: const Text('Global'),
onPressed: () => _global.setGlobalAudioContext(audioContext),
onPressed: () => _global.setAudioContext(audioContext),
),
ElevatedButton.icon(
icon: const Icon(Icons.looks_one),
Expand Down
2 changes: 1 addition & 1 deletion packages/audioplayers/example/lib/tabs/logger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class LoggerTab extends StatefulWidget {
}

class _LoggerTabState extends State<LoggerTab> {
static GlobalPlatformInterface get _logger => AudioPlayer.global;
static GlobalAudioScope get _logger => AudioPlayer.global;

LogLevel currentLogLevel = _logger.logLevel;

Expand Down
2 changes: 1 addition & 1 deletion packages/audioplayers/lib/audioplayers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ export 'package:audioplayers_platform_interface/src/api/log_level.dart';
export 'package:audioplayers_platform_interface/src/api/player_mode.dart';
export 'package:audioplayers_platform_interface/src/api/player_state.dart';
export 'package:audioplayers_platform_interface/src/api/release_mode.dart';
export 'package:audioplayers_platform_interface/src/global_platform_interface.dart';

export 'src/audio_cache.dart';
export 'src/audio_pool.dart';
export 'src/audioplayer.dart';
export 'src/global_audio_scope.dart';
export 'src/source.dart';
4 changes: 2 additions & 2 deletions packages/audioplayers/lib/src/audioplayer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const _uuid = Uuid();
/// It holds methods to play, loop, pause, stop, seek the audio, and some useful
/// hooks for handlers and callbacks.
class AudioPlayer {
static final global = GlobalPlatformInterface.instance;
static final _platform = AudioplayersPlatform.instance;
static final global = GlobalAudioScope();
static final _platform = AudioplayersPlatformInterface.instance;

/// This is the [AudioCache] instance used by this player.
/// Unless you want to control multiple caches separately, you don't need to
Expand Down
24 changes: 24 additions & 0 deletions packages/audioplayers/lib/src/global_audio_scope.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:audioplayers_platform_interface/audioplayers_platform_interface.dart';

/// Handle global audio scope like calls and events concerning all AudioPlayers.
class GlobalAudioScope {
static final _platform = GlobalAudioplayersPlatformInterface.instance;

LogLevel get logLevel => _platform.logLevel;

Future<void> changeLogLevel(LogLevel level) =>
_platform.changeLogLevel(level);

void log(LogLevel level, String message) => _platform.log(level, message);

void info(String message) => _platform.info(message);

void error(String message) => _platform.error(message);

Future<void> setAudioContext(AudioContext ctx) =>
_platform.setGlobalAudioContext(ctx);

@Deprecated('Use `setAudioContext()` instead.')
Future<void> setGlobalAudioContext(AudioContext ctx) =>
_platform.setGlobalAudioContext(ctx);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export 'src/api/player_mode.dart';
export 'src/api/player_state.dart';
export 'src/api/release_mode.dart';
export 'src/audioplayers_platform_interface.dart';
export 'src/global_platform_interface.dart';
export 'src/global_audioplayers_platform_interface.dart';
export 'src/streams_interface.dart';
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';

// TODO(gustl22): remove when upgrading min Flutter version to >=3.3.0
// ignore: unnecessary_import
import 'dart:typed_data';
Expand All @@ -7,21 +8,21 @@ import 'package:audioplayers_platform_interface/src/api/audio_context.dart';
import 'package:audioplayers_platform_interface/src/api/player_mode.dart';
import 'package:audioplayers_platform_interface/src/api/release_mode.dart';
import 'package:audioplayers_platform_interface/src/audioplayers_platform_interface.dart';
import 'package:audioplayers_platform_interface/src/global_platform_interface.dart';
import 'package:audioplayers_platform_interface/src/global_audioplayers_platform_interface.dart';
import 'package:audioplayers_platform_interface/src/method_channel_interface.dart';
import 'package:audioplayers_platform_interface/src/streams_interface.dart';
import 'package:flutter/services.dart';

class MethodChannelAudioplayersPlatform extends AudioplayersPlatform
class AudioplayersPlatform extends AudioplayersPlatformInterface
with StreamsInterface {
final MethodChannel _channel = const MethodChannel('xyz.luan/audioplayers');

MethodChannelAudioplayersPlatform() {
AudioplayersPlatform() {
_channel.setMethodCallHandler(platformCallHandler);
}

static GlobalPlatformInterface get _logger =>
GlobalPlatformInterface.instance;
static GlobalAudioplayersPlatformInterface get _logger =>
GlobalAudioplayersPlatformInterface.instance;

@override
Future<int?> getCurrentPosition(String playerId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:audioplayers_platform_interface/src/api/audio_context.dart';
import 'package:audioplayers_platform_interface/src/api/for_player.dart';
import 'package:audioplayers_platform_interface/src/api/player_mode.dart';
import 'package:audioplayers_platform_interface/src/api/release_mode.dart';
import 'package:audioplayers_platform_interface/src/method_channel_audioplayers_platform.dart';
import 'package:audioplayers_platform_interface/src/audioplayers_platform.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

/// The interface that implementations of audioplayers must implement.
Expand All @@ -15,18 +15,19 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart';
/// changes. Extending this class (using `extends`) ensures that the subclass
/// will get the default implementation, while platform implementations that
/// `implements` this interface will be broken by newly added
/// [AudioplayersPlatform] methods.
abstract class AudioplayersPlatform extends PlatformInterface {
AudioplayersPlatform() : super(token: _token);
/// [AudioplayersPlatformInterface] methods.
abstract class AudioplayersPlatformInterface extends PlatformInterface {
AudioplayersPlatformInterface() : super(token: _token);

static final Object _token = Object();

/// The default instance of [AudioplayersPlatform] to use.
/// The default instance of [AudioplayersPlatformInterface] to use.
///
/// Defaults to [MethodChannelAudioplayersPlatform].
/// Defaults to [AudioplayersPlatform].
/// Platform-specific plugins should set this with their own platform-specific
/// class that extends [AudioplayersPlatform] when they register themselves.
static AudioplayersPlatform instance = MethodChannelAudioplayersPlatform();
/// class that extends [AudioplayersPlatformInterface] when they register
/// themselves.
static AudioplayersPlatformInterface instance = AudioplayersPlatform();

/// Pauses the audio that is currently playing.
///
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,10 @@
import 'package:audioplayers_platform_interface/src/api/audio_context.dart';
import 'package:audioplayers_platform_interface/src/api/log_level.dart';
import 'package:audioplayers_platform_interface/src/global_audioplayers_platform_interface.dart';
import 'package:audioplayers_platform_interface/src/method_channel_interface.dart';
import 'package:flutter/services.dart';

abstract class GlobalPlatformInterface {
static GlobalPlatformInterface instance = MethodChannelGlobalPlatform();

LogLevel get logLevel;

Future<void> changeLogLevel(LogLevel value);

Future<void> setGlobalAudioContext(AudioContext ctx);

void log(LogLevel level, String message) {
if (level.getLevel() <= logLevel.getLevel()) {
// ignore: avoid_print
print(message);
}
}

void info(String message) => log(LogLevel.info, message);

void error(String message) => log(LogLevel.error, message);
}

class MethodChannelGlobalPlatform extends GlobalPlatformInterface {
class GlobalAudioplayersPlatform extends GlobalAudioplayersPlatformInterface {
static const MethodChannel _channel =
MethodChannel('xyz.luan/audioplayers.global');

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:audioplayers_platform_interface/src/api/audio_context.dart';
import 'package:audioplayers_platform_interface/src/api/log_level.dart';
import 'package:audioplayers_platform_interface/src/global_audioplayers_platform.dart';

abstract class GlobalAudioplayersPlatformInterface {
static GlobalAudioplayersPlatformInterface instance =
GlobalAudioplayersPlatform();

LogLevel get logLevel;

Future<void> changeLogLevel(LogLevel value);

Future<void> setGlobalAudioContext(AudioContext ctx);

void log(LogLevel level, String message) {
if (level.getLevel() <= logLevel.getLevel()) {
// ignore: avoid_print
print(message);
}
}

void info(String message) => log(LogLevel.info, message);

void error(String message) => log(LogLevel.error, message);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ void main() {
});

final _print = OverridePrint();
final _logger = GlobalPlatformInterface.instance;
final _logger = GlobalAudioplayersPlatformInterface.instance;

group('Logger', () {
setUp(_print.clear);
Expand Down
10 changes: 8 additions & 2 deletions packages/audioplayers_web/lib/audioplayers_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ import 'dart:async';
import 'dart:typed_data';

import 'package:audioplayers_platform_interface/audioplayers_platform_interface.dart';
import 'package:audioplayers_web/global_audioplayers_web.dart';
import 'package:audioplayers_web/num_extension.dart';
import 'package:audioplayers_web/wrapped_player.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';

class AudioplayersPlugin extends AudioplayersPlatform with StreamsInterface {
class AudioplayersPlugin with StreamsInterface {
/// The entrypoint called by the generated plugin registrant.
static void registerWith(Registrar registrar) {
AudioplayersPlatform.instance = AudioplayersPlugin();
AudioplayersPlatformInterface.instance = WebAudioplayersPlatform();
GlobalAudioplayersPlatformInterface.instance =
WebGlobalAudioplayersPlatform();
}
}

class WebAudioplayersPlatform extends AudioplayersPlatformInterface
with StreamsInterface {
// players by playerId
Map<String, WrappedPlayer> players = {};

Expand Down
22 changes: 22 additions & 0 deletions packages/audioplayers_web/lib/global_audioplayers_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'dart:async';

import 'package:audioplayers_platform_interface/audioplayers_platform_interface.dart';

class WebGlobalAudioplayersPlatform
extends GlobalAudioplayersPlatformInterface {
// Web implementation currently does not log anything
LogLevel _level = LogLevel.error;

@override
Future<void> changeLogLevel(LogLevel value) async {
_level = value;
}

@override
LogLevel get logLevel => _level;

@override
Future<void> setGlobalAudioContext(AudioContext ctx) async {
// no-op: web does not support changing audio context
}
}

0 comments on commit c0b3f85

Please sign in to comment.