Skip to content

Commit

Permalink
feat: create, dispose & reuse event stream (#1609)
Browse files Browse the repository at this point in the history
# Description

Currently if an event stream for a `playerId` is requested, a new stream
was created, conflicting with the existing one. Now the
`createEventStream`, `disposeEventStream` and `getEventStream` methods
allow more control of handling the stream and also reusing the existing
stream on `getEventStream`.
  • Loading branch information
Gustl22 authored Aug 17, 2023
1 parent 25fbec0 commit efbabf5
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ import 'package:flutter/services.dart';
class AudioplayersPlatform extends AudioplayersPlatformInterface
with MethodChannelAudioplayersPlatform, EventChannelAudioplayersPlatform {
AudioplayersPlatform();

@override
Future<void> create(String playerId) async {
await super.create(playerId);
createEventStream(playerId);
}

@override
Future<void> dispose(String playerId) async {
await super.dispose(playerId);
disposeEventStream(playerId);
}
}

mixin MethodChannelAudioplayersPlatform
Expand Down Expand Up @@ -212,12 +224,12 @@ mixin MethodChannelAudioplayersPlatform

mixin EventChannelAudioplayersPlatform
implements EventChannelAudioplayersPlatformInterface {
@override
Stream<AudioEvent> getEventStream(String playerId) {
// Only can be used after have created the event channel on the native side.
final eventChannel = EventChannel('xyz.luan/audioplayers/events/$playerId');
final Map<String, Stream<AudioEvent>> streams = {};

return eventChannel.receiveBroadcastStream().map(
// Only can be used after have created the event channel on the native side.
void createEventStream(String playerId) {
final eventChannel = EventChannel('xyz.luan/audioplayers/events/$playerId');
streams[playerId] = eventChannel.receiveBroadcastStream().map(
(dynamic event) {
final map = event as Map<dynamic, dynamic>;
final eventType = map.getString('event');
Expand Down Expand Up @@ -260,4 +272,15 @@ mixin EventChannelAudioplayersPlatform
},
);
}

void disposeEventStream(String playerId) {
if (streams.containsKey(playerId)) {
streams.remove(playerId);
}
}

@override
Stream<AudioEvent> getEventStream(String playerId) {
return streams[playerId]!;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,6 @@ void main() {
final platform = AudioplayersPlatformInterface.instance;

final methodCalls = <MethodCall>[];
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
const MethodChannel('xyz.luan/audioplayers'),
(MethodCall methodCall) async {
methodCalls.add(methodCall);
return 0;
},
);

void clear() {
methodCalls.clear();
Expand All @@ -36,7 +28,24 @@ void main() {
}

group('AudioPlayers Method Channel', () {
setUp(clear);
setUp(() {
clear();

createNativeMethodHandler(
channel: 'xyz.luan/audioplayers',
handler: (MethodCall methodCall) async {
methodCalls.add(methodCall);
switch (methodCall.method) {
case 'getDuration':
return 0;
case 'getCurrentPosition':
return 0;
default:
return null;
}
},
);
});

test('#setSource', () async {
await platform.setSourceUrl('p1', 'internet.com/file.mp3');
Expand Down Expand Up @@ -83,14 +92,17 @@ void main() {
group('AudioPlayers Event Channel', () {
test('emit events', () async {
final eventController = StreamController<ByteData>.broadcast();
const playerId = 'p1';

createNativeEventStream(
channel: 'xyz.luan/audioplayers/events/p1',
channel: 'xyz.luan/audioplayers/events/$playerId',
byteDataStream: eventController.stream,
);

await platform.create(playerId);

expect(
platform.getEventStream('p1'),
platform.getEventStream(playerId),
emitsInOrder(<AudioEvent>[
const AudioEvent(
eventType: AudioEventType.duration,
Expand Down Expand Up @@ -140,6 +152,7 @@ void main() {
}

await eventController.close();
await platform.dispose(playerId);
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ void main() {
}

group('Global Method Channel', () {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
const MethodChannel('xyz.luan/audioplayers.global'),
(MethodCall methodCall) async {
methodCalls.add(methodCall);
return 1;
},
);

setUp(clear);
setUp(() {
clear();
createNativeMethodHandler(
channel: 'xyz.luan/audioplayers.global',
handler: (MethodCall methodCall) async {
methodCalls.add(methodCall);
return null;
},
);
});

test('set AudioContext for Windows', () async {
debugDefaultTargetPlatformOverride = TargetPlatform.windows;
Expand Down
11 changes: 11 additions & 0 deletions packages/audioplayers_platform_interface/test/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ extension MethodCallParser on MethodCall {
Map<Object?, Object?> get args => arguments as Map<Object?, Object?>;
}

void createNativeMethodHandler({
required String channel,
Future<Object?>? Function(MethodCall message)? handler,
}) {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
MethodChannel(channel),
handler,
);
}

// See: https://github.com/flutter/packages/blob/12609a2abbb0a30b9d32af7b73599bfc834e609e/packages/video_player/video_player_android/test/android_video_player_test.dart#L270
void createNativeEventStream({
required String channel,
Expand Down

0 comments on commit efbabf5

Please sign in to comment.