Update comments and provide default implementations of platform interface.

This commit is contained in:
Ryan Heise 2020-09-27 14:00:37 +10:00
parent 1be1c212f6
commit aa57e7fad8
3 changed files with 86 additions and 40 deletions

View File

@ -1,26 +1,15 @@
# just_audio_platform_interface # just_audio_platform_interface
A common platform interface for the [`just_audio`][../just_audio] plugin. A common platform interface for the [`just_audio`](../just_audio) plugin.
This interface allows platform-specific implementations of the `just_audio` This interface allows platform-specific implementations of the `just_audio` plugin, as well as the plugin itself, to ensure they are supporting the same interface.
plugin, as well as the plugin itself, to ensure they are supporting the
same interface.
# Usage # Usage
To implement a new platform-specific implementation of `just_audio`, extend To implement a new platform-specific implementation of `just_audio`, extend [`JustAudioPlatform`](lib/just_audio_platform_interface.dart) with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `JustAudioPlatform` by calling `JustAudioPlatform.instance = MyPlatformJustAudio()`.
[`JustAudioPlatform`][2] with an implementation that performs the
platform-specific behavior, and when you register your plugin, set the default
`JustAudioPlatform` by calling
`JustAudioPlatform.instance = MyPlatformJustAudio()`.
# Note on breaking changes # Note on breaking changes
Strongly prefer non-breaking changes (such as adding a method to the interface) Strongly prefer non-breaking changes (such as adding a method to the interface) over breaking changes for this package.
over breaking changes for this package.
See https://flutter.dev/go/platform-interface-breaking-changes for a discussion See https://flutter.dev/go/platform-interface-breaking-changes for a discussion on why a less-clean interface is preferable to a breaking change.
on why a less-clean interface is preferable to a breaking change.
[1]: ../just_audio
[2]: lib/just_audio_platform_interface.dart

View File

@ -36,7 +36,7 @@ abstract class JustAudioPlatform extends PlatformInterface {
_instance = instance; _instance = instance;
} }
/// Creates a new player and returns a nested platform interface for /// Creates a new platform player and returns a nested platform interface for
/// communicating with that player. /// communicating with that player.
Future<AudioPlayerPlatform> init(InitRequest request) { Future<AudioPlayerPlatform> init(InitRequest request) {
throw UnimplementedError('init() has not been implemented.'); throw UnimplementedError('init() has not been implemented.');
@ -45,30 +45,88 @@ abstract class JustAudioPlatform extends PlatformInterface {
/// A nested platform interface for communicating with a particular player /// A nested platform interface for communicating with a particular player
/// instance. /// instance.
///
/// Platform implementations should extend this class rather than implement it
/// as `just_audio` does not consider newly added methods to be breaking
/// 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
/// [AudioPlayerPlatform] methods.
abstract class AudioPlayerPlatform { abstract class AudioPlayerPlatform {
Stream<PlaybackEventMessage> get playbackEventMessageStream; Stream<PlaybackEventMessage> get playbackEventMessageStream {
Future<LoadResponse> load(LoadRequest request); throw UnimplementedError(
Future<PlayResponse> play(PlayRequest request); 'playbackEventMessageStream has not been implemented.');
Future<PauseResponse> pause(PauseRequest request); }
Future<SetVolumeResponse> setVolume(SetVolumeRequest request);
Future<SetSpeedResponse> setSpeed(SetSpeedRequest request); Future<LoadResponse> load(LoadRequest request) {
Future<SetLoopModeResponse> setLoopMode(SetLoopModeRequest request); throw UnimplementedError("load() has not been implemented.");
Future<SetShuffleModeResponse> setShuffleMode(SetShuffleModeRequest request); }
Future<PlayResponse> play(PlayRequest request) {
throw UnimplementedError("play() has not been implemented.");
}
Future<PauseResponse> pause(PauseRequest request) {
throw UnimplementedError("pause() has not been implemented.");
}
Future<SetVolumeResponse> setVolume(SetVolumeRequest request) {
throw UnimplementedError("setVolume() has not been implemented.");
}
Future<SetSpeedResponse> setSpeed(SetSpeedRequest request) {
throw UnimplementedError("setSpeed() has not been implemented.");
}
Future<SetLoopModeResponse> setLoopMode(SetLoopModeRequest request) {
throw UnimplementedError("setLoopMode() has not been implemented.");
}
Future<SetShuffleModeResponse> setShuffleMode(SetShuffleModeRequest request) {
throw UnimplementedError("setShuffleMode() has not been implemented.");
}
Future<SetAutomaticallyWaitsToMinimizeStallingResponse> Future<SetAutomaticallyWaitsToMinimizeStallingResponse>
setAutomaticallyWaitsToMinimizeStalling( setAutomaticallyWaitsToMinimizeStalling(
SetAutomaticallyWaitsToMinimizeStallingRequest request); SetAutomaticallyWaitsToMinimizeStallingRequest request) {
Future<SeekResponse> seek(SeekRequest request); throw UnimplementedError(
"setAutomaticallyWaitsToMinimizeStalling() has not been implemented.");
}
Future<SeekResponse> seek(SeekRequest request) {
throw UnimplementedError("seek() has not been implemented.");
}
Future<SetAndroidAudioAttributesResponse> setAndroidAudioAttributes( Future<SetAndroidAudioAttributesResponse> setAndroidAudioAttributes(
SetAndroidAudioAttributesRequest request); SetAndroidAudioAttributesRequest request) {
Future<DisposeResponse> dispose(DisposeRequest request); throw UnimplementedError(
"setAndroidAudioAttributes() has not been implemented.");
}
Future<DisposeResponse> dispose(DisposeRequest request) {
throw UnimplementedError("dispose() has not been implemented.");
}
Future<ConcatenatingInsertAllResponse> concatenatingInsertAll( Future<ConcatenatingInsertAllResponse> concatenatingInsertAll(
ConcatenatingInsertAllRequest request); ConcatenatingInsertAllRequest request) {
throw UnimplementedError(
"concatenatingInsertAll() has not been implemented.");
}
Future<ConcatenatingRemoveRangeResponse> concatenatingRemoveRange( Future<ConcatenatingRemoveRangeResponse> concatenatingRemoveRange(
ConcatenatingRemoveRangeRequest request); ConcatenatingRemoveRangeRequest request) {
throw UnimplementedError(
"concatenatingRemoveRange() has not been implemented.");
}
Future<ConcatenatingMoveResponse> concatenatingMove( Future<ConcatenatingMoveResponse> concatenatingMove(
ConcatenatingMoveRequest request); ConcatenatingMoveRequest request) {
throw UnimplementedError("concatenatingMove() has not been implemented.");
}
} }
/// A playback event communicated from the platform implementation to the
/// Flutter plugin.
class PlaybackEventMessage { class PlaybackEventMessage {
final ProcessingStateMessage processingState; final ProcessingStateMessage processingState;
final DateTime updateTime; final DateTime updateTime;
@ -94,11 +152,8 @@ class PlaybackEventMessage {
PlaybackEventMessage( PlaybackEventMessage(
processingState: ProcessingStateMessage.values[map['processingState']], processingState: ProcessingStateMessage.values[map['processingState']],
updateTime: DateTime.fromMillisecondsSinceEpoch(map['updateTime']), updateTime: DateTime.fromMillisecondsSinceEpoch(map['updateTime']),
// TODO: Ensure all platforms pass a microsecond value.
updatePosition: Duration(microseconds: map['updatePosition']), updatePosition: Duration(microseconds: map['updatePosition']),
// TODO: Ensure all platforms pass a microsecond value.
bufferedPosition: Duration(microseconds: map['bufferedPosition']), bufferedPosition: Duration(microseconds: map['bufferedPosition']),
// TODO: Ensure all platforms pass a microsecond value.
duration: map['duration'] == null || map['duration'] < 0 duration: map['duration'] == null || map['duration'] < 0
? null ? null
: Duration(microseconds: map['duration']), : Duration(microseconds: map['duration']),
@ -110,6 +165,7 @@ class PlaybackEventMessage {
); );
} }
/// A processing state communicated from the platform implementation.
enum ProcessingStateMessage { enum ProcessingStateMessage {
none, none,
loading, loading,
@ -118,6 +174,7 @@ enum ProcessingStateMessage {
completed, completed,
} }
/// Icy metadata communicated from the platform implementation.
class IcyMetadataMessage { class IcyMetadataMessage {
final IcyInfoMessage info; final IcyInfoMessage info;
final IcyHeadersMessage headers; final IcyHeadersMessage headers;
@ -137,6 +194,7 @@ class IcyMetadataMessage {
); );
} }
/// Icy info communicated from the platform implementation.
class IcyInfoMessage { class IcyInfoMessage {
final String title; final String title;
final String url; final String url;
@ -147,6 +205,7 @@ class IcyInfoMessage {
IcyInfoMessage(title: json['title'], url: json['url']); IcyInfoMessage(title: json['title'], url: json['url']);
} }
/// Icy headers communicated from the platform implementation.
class IcyHeadersMessage { class IcyHeadersMessage {
final int bitrate; final int bitrate;
final String genre; final String genre;
@ -419,6 +478,8 @@ class ConcatenatingMoveResponse {
ConcatenatingMoveResponse(); ConcatenatingMoveResponse();
} }
/// Information about an audio source to be communicated with the platform
/// implementation.
abstract class AudioSourceMessage { abstract class AudioSourceMessage {
final String id; final String id;
@ -504,7 +565,6 @@ class ConcatenatingAudioSourceMessage extends AudioSourceMessage {
Map<dynamic, dynamic> toMap() => { Map<dynamic, dynamic> toMap() => {
'type': 'concatenating', 'type': 'concatenating',
'id': id, 'id': id,
// TODO: ensure platform implementation uses this key
'children': children.map((child) => child.toMap()).toList(), 'children': children.map((child) => child.toMap()).toList(),
'useLazyPreparation': useLazyPreparation, 'useLazyPreparation': useLazyPreparation,
}; };
@ -526,11 +586,8 @@ class ClippingAudioSourceMessage extends IndexedAudioSourceMessage {
Map<dynamic, dynamic> toMap() => { Map<dynamic, dynamic> toMap() => {
'type': 'clipping', 'type': 'clipping',
'id': id, 'id': id,
// TODO: ensure platform implementation uses this key
'child': child.toMap(), 'child': child.toMap(),
// TODO: ensure platform implementation interprets in Us.
'start': start.inMicroseconds, 'start': start.inMicroseconds,
// TODO: ensure platform implementation interprets in Us.
'end': end.inMicroseconds, 'end': end.inMicroseconds,
}; };
} }
@ -549,7 +606,6 @@ class LoopingAudioSourceMessage extends AudioSourceMessage {
Map<dynamic, dynamic> toMap() => { Map<dynamic, dynamic> toMap() => {
'type': 'looping', 'type': 'looping',
'id': id, 'id': id,
// TODO: ensure platform implementation uses this key
'child': child.toMap(), 'child': child.toMap(),
'count': count, 'count': count,
}; };

View File

@ -15,6 +15,7 @@ class MethodChannelJustAudio extends JustAudioPlatform {
} }
} }
/// An implementation of [AudioPlayerPlatform] that uses method channels.
class MethodChannelAudioPlayer extends AudioPlayerPlatform { class MethodChannelAudioPlayer extends AudioPlayerPlatform {
final String id; final String id;
final MethodChannel _channel; final MethodChannel _channel;