diff --git a/just_audio_platform_interface/lib/just_audio_platform_interface.dart b/just_audio_platform_interface/lib/just_audio_platform_interface.dart index 6685cfd..34a4700 100644 --- a/just_audio_platform_interface/lib/just_audio_platform_interface.dart +++ b/just_audio_platform_interface/lib/just_audio_platform_interface.dart @@ -46,6 +46,7 @@ abstract class JustAudioPlatform extends PlatformInterface { /// A nested platform interface for communicating with a particular player /// instance. abstract class AudioPlayerPlatform { + Stream get playbackEventMessageStream; Future load(LoadRequest request); Future play(PlayRequest request); Future pause(PauseRequest request); @@ -68,6 +69,112 @@ abstract class AudioPlayerPlatform { ConcatenatingMoveRequest request); } +class PlaybackEventMessage { + final ProcessingStateMessage processingState; + final DateTime updateTime; + final Duration updatePosition; + final Duration bufferedPosition; + final Duration duration; + final IcyMetadataMessage icyMetadata; + final int currentIndex; + final int androidAudioSessionId; + + PlaybackEventMessage({ + @required this.processingState, + @required this.updateTime, + @required this.updatePosition, + @required this.bufferedPosition, + @required this.duration, + @required this.icyMetadata, + @required this.currentIndex, + @required this.androidAudioSessionId, + }); + + static PlaybackEventMessage fromMap(Map map) => + PlaybackEventMessage( + processingState: ProcessingStateMessage.values[map['processingState']], + updateTime: DateTime.fromMillisecondsSinceEpoch(map['updateTime']), + // TODO: Ensure all platforms pass a microsecond value. + updatePosition: Duration(microseconds: map['updatePosition']), + // TODO: Ensure all platforms pass a microsecond value. + bufferedPosition: Duration(microseconds: map['bufferedPosition']), + // TODO: Ensure all platforms pass a microsecond value. + duration: map['duration'] == null || map['duration'] < 0 + ? null + : Duration(microseconds: map['duration']), + icyMetadata: map['icyMetadata'] == null + ? null + : IcyMetadataMessage.fromMap(map['icyMetadata']), + currentIndex: map['currentIndex'], + androidAudioSessionId: map['androidAudioSessionId'], + ); +} + +enum ProcessingStateMessage { + none, + loading, + buffering, + ready, + completed, +} + +class IcyMetadataMessage { + final IcyInfoMessage info; + final IcyHeadersMessage headers; + + IcyMetadataMessage({ + @required this.info, + @required this.headers, + }); + + static IcyMetadataMessage fromMap(Map json) => + IcyMetadataMessage( + info: + json['info'] == null ? null : IcyInfoMessage.fromMap(json['info']), + headers: json['headers'] == null + ? null + : IcyHeadersMessage.fromMap(json['headers']), + ); +} + +class IcyInfoMessage { + final String title; + final String url; + + IcyInfoMessage({@required this.title, @required this.url}); + + static IcyInfoMessage fromMap(Map json) => + IcyInfoMessage(title: json['title'], url: json['url']); +} + +class IcyHeadersMessage { + final int bitrate; + final String genre; + final String name; + final int metadataInterval; + final String url; + final bool isPublic; + + IcyHeadersMessage({ + @required this.bitrate, + @required this.genre, + @required this.name, + @required this.metadataInterval, + @required this.url, + @required this.isPublic, + }); + + static IcyHeadersMessage fromMap(Map json) => + IcyHeadersMessage( + bitrate: json['bitrate'], + genre: json['genre'], + name: json['name'], + metadataInterval: json['metadataInterval'], + url: json['url'], + isPublic: json['isPublic'], + ); +} + class InitRequest { final String id; @@ -344,6 +451,7 @@ class ProgressiveAudioSourceMessage extends UriAudioSourceMessage { @override Map toMap() => { + 'type': 'progressive', 'id': id, 'uri': uri, 'headers': headers, @@ -359,6 +467,7 @@ class DashAudioSourceMessage extends UriAudioSourceMessage { @override Map toMap() => { + 'type': 'dash', 'id': id, 'uri': uri, 'headers': headers, @@ -374,6 +483,7 @@ class HlsAudioSourceMessage extends UriAudioSourceMessage { @override Map toMap() => { + 'type': 'hls', 'id': id, 'uri': uri, 'headers': headers, @@ -392,7 +502,9 @@ class ConcatenatingAudioSourceMessage extends AudioSourceMessage { @override Map toMap() => { + 'type': 'concatenating', 'id': id, + // TODO: ensure platform implementation uses this key 'children': children.map((child) => child.toMap()).toList(), 'useLazyPreparation': useLazyPreparation, }; @@ -412,9 +524,13 @@ class ClippingAudioSourceMessage extends IndexedAudioSourceMessage { @override Map toMap() => { + 'type': 'clipping', 'id': id, + // TODO: ensure platform implementation uses this key 'child': child.toMap(), + // TODO: ensure platform implementation interprets in Us. 'start': start.inMicroseconds, + // TODO: ensure platform implementation interprets in Us. 'end': end.inMicroseconds, }; } @@ -431,7 +547,9 @@ class LoopingAudioSourceMessage extends AudioSourceMessage { @override Map toMap() => { + 'type': 'looping', 'id': id, + // TODO: ensure platform implementation uses this key 'child': child.toMap(), 'count': count, }; diff --git a/just_audio_platform_interface/lib/method_channel_just_audio.dart b/just_audio_platform_interface/lib/method_channel_just_audio.dart index 0cac213..4fd368c 100644 --- a/just_audio_platform_interface/lib/method_channel_just_audio.dart +++ b/just_audio_platform_interface/lib/method_channel_just_audio.dart @@ -22,94 +22,101 @@ class MethodChannelAudioPlayer extends AudioPlayerPlatform { MethodChannelAudioPlayer(this.id) : _channel = MethodChannel('com.ryanheise.just_audio.methods.$id'); + @override + Stream get playbackEventMessageStream => + EventChannel('com.ryanheise.just_audio.events.$id') + .receiveBroadcastStream() + .map((map) => PlaybackEventMessage.fromMap(map)); + @override Future load(LoadRequest request) async { - return (await _channel.invokeMethod('load', request?.toMap()))?.fromMap(); + return LoadResponse.fromMap( + await _channel.invokeMethod('load', request?.toMap())); } @override Future play(PlayRequest request) async { - return (await _channel.invokeMethod('play', request?.toMap()))?.fromMap(); + return PlayResponse.fromMap( + await _channel.invokeMethod('play', request?.toMap())); } @override Future pause(PauseRequest request) async { - return (await _channel.invokeMethod('pause', request?.toMap()))?.fromMap(); + return PauseResponse.fromMap( + await _channel.invokeMethod('pause', request?.toMap())); } @override Future setVolume(SetVolumeRequest request) async { - return (await _channel.invokeMethod('setVolume', request?.toMap())) - ?.fromMap(); + return SetVolumeResponse.fromMap( + await _channel.invokeMethod('setVolume', request?.toMap())); } @override Future setSpeed(SetSpeedRequest request) async { - return (await _channel.invokeMethod('setSpeed', request?.toMap())) - ?.fromMap(); + return SetSpeedResponse.fromMap( + await _channel.invokeMethod('setSpeed', request?.toMap())); } @override Future setLoopMode(SetLoopModeRequest request) async { - return (await _channel.invokeMethod('setLoopMode', request?.toMap())) - ?.fromMap(); + return SetLoopModeResponse.fromMap( + await _channel.invokeMethod('setLoopMode', request?.toMap())); } @override Future setShuffleMode( SetShuffleModeRequest request) async { - return (await _channel.invokeMethod('setShuffleMode', request?.toMap())) - ?.fromMap(); + return SetShuffleModeResponse.fromMap( + await _channel.invokeMethod('setShuffleMode', request?.toMap())); } @override Future setAutomaticallyWaitsToMinimizeStalling( SetAutomaticallyWaitsToMinimizeStallingRequest request) async { - return (await _channel.invokeMethod( - 'setAutomaticallyWaitsToMinimizeStalling', request?.toMap())) - ?.fromMap(); + return SetAutomaticallyWaitsToMinimizeStallingResponse.fromMap( + await _channel.invokeMethod( + 'setAutomaticallyWaitsToMinimizeStalling', request?.toMap())); } @override Future seek(SeekRequest request) async { - return (await _channel.invokeMethod('seek', request?.toMap()))?.fromMap(); + return SeekResponse.fromMap( + await _channel.invokeMethod('seek', request?.toMap())); } @override Future setAndroidAudioAttributes( SetAndroidAudioAttributesRequest request) async { - return (await _channel.invokeMethod( - 'setAndroidAudioAttributes', request?.toMap())) - ?.fromMap(); + return SetAndroidAudioAttributesResponse.fromMap(await _channel + .invokeMethod('setAndroidAudioAttributes', request?.toMap())); } @override Future dispose(DisposeRequest request) async { - return (await _channel.invokeMethod('dispose', request?.toMap())) - ?.fromMap(); + return DisposeResponse.fromMap( + await _channel.invokeMethod('dispose', request?.toMap())); } @override Future concatenatingInsertAll( ConcatenatingInsertAllRequest request) async { - return (await _channel.invokeMethod( - 'concatenatingInsertAll', request?.toMap())) - ?.fromMap(); + return ConcatenatingInsertAllResponse.fromMap(await _channel.invokeMethod( + 'concatenatingInsertAll', request?.toMap())); } @override Future concatenatingRemoveRange( ConcatenatingRemoveRangeRequest request) async { - return (await _channel.invokeMethod( - 'concatenatingRemoveRange', request?.toMap())) - ?.fromMap(); + return ConcatenatingRemoveRangeResponse.fromMap(await _channel.invokeMethod( + 'concatenatingRemoveRange', request?.toMap())); } @override Future concatenatingMove( ConcatenatingMoveRequest request) async { - return (await _channel.invokeMethod('concatenatingMove', request?.toMap())) - ?.fromMap(); + return ConcatenatingMoveResponse.fromMap( + await _channel.invokeMethod('concatenatingMove', request?.toMap())); } }