From 7aa156a20cd22b668bce12576a9c9ddc1181eef4 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Mon, 9 Oct 2023 20:05:48 +0200 Subject: [PATCH] Put state in Future instead of using Future untracked --- lib/src/channel_manager.dart | 11 +++- lib/src/player.dart | 109 +++++++++++++++-------------------- 2 files changed, 55 insertions(+), 65 deletions(-) diff --git a/lib/src/channel_manager.dart b/lib/src/channel_manager.dart index 543bce24..07314ab9 100644 --- a/lib/src/channel_manager.dart +++ b/lib/src/channel_manager.dart @@ -22,7 +22,14 @@ class ChannelManager { return target; } - static EventChannel registerEventChannel({required String name}) { - return EventChannel(name); + static EventChannel registerEventChannel({ + required String name, + void Function(dynamic event)? onEvent, + }) { + final target = EventChannel(name); + if (onEvent != null) { + target.receiveBroadcastStream().listen(onEvent); + } + return target; } } diff --git a/lib/src/player.dart b/lib/src/player.dart index e30c17ad..9efc1bde 100644 --- a/lib/src/player.dart +++ b/lib/src/player.dart @@ -25,41 +25,37 @@ class Player with PlayerEventHandler implements PlayerApi { Player({ this.config = const PlayerConfig(), }) { - _initializationResult = _completer.future; _uuid = hashCode.toString(); + _nativePlayerHandle = _createNativePlayer(); + } + Future<_NativePlayerHandle> _createNativePlayer() async { final mainChannel = ChannelManager.registerMethodChannel( name: Channels.main, ); - mainChannel - .invokeMethod( - Methods.createPlayer, - Map.from( - { - 'id': id, - 'playerConfig': config.toJson(), - }, - ), - ) - .then((value) { - if (value == null || value == false) { - _completer.complete(false); - return; + final success = await mainChannel.invokeMethod( + Methods.createPlayer, + Map.from( + { + 'id': id, + 'playerConfig': config.toJson(), + }, + ), + ); + if (success == null || success == false) { + throw Exception('Error initializing player on native platform side.'); } - - _methodChannel = ChannelManager.registerMethodChannel( + final methodChannel = ChannelManager.registerMethodChannel( name: '${Channels.player}-$id', handler: _playerMethodCallHandler, ); - - _eventChannel = ChannelManager.registerEventChannel( + final eventChannel = ChannelManager.registerEventChannel( name: '${Channels.playerEvent}-$id', + onEvent: onPlatformEvent, ); - _eventChannel.receiveBroadcastStream().listen(onPlatformEvent); - _completer.complete(true); - }); + return _NativePlayerHandle(methodChannel, eventChannel); } @override @@ -69,19 +65,9 @@ class Player with PlayerEventHandler implements PlayerApi { String get id => _uuid; late String _uuid; - /// Whether the player has been created successfully on the native platform - /// side. If `true`, the player is ready to be used. If `false`, there was an - /// error during player creation on the native platform side. - late Future _initializationResult; - - /// Used to generate the [_initializationResult] future. - final _completer = Completer(); - - /// Private method channel for this player instance - late MethodChannel _methodChannel; - - /// Private method channel for this player instance to receive events - late EventChannel _eventChannel; + /// Player native communication channels. + /// Available once the native player is created. + late Future<_NativePlayerHandle> _nativePlayerHandle; /// Handles Fairplay DRM related method calls. FairplayHandler? _fairplayHandler; @@ -127,12 +113,9 @@ class Player with PlayerEventHandler implements PlayerApi { String methodName, [ dynamic data, ]) async { - final initSuccess = await _initializationResult; - if (!initSuccess) { - throw Exception('Error initializing player on native platform side.'); - } + final methodChannel = (await _nativePlayerHandle).methodChannel; final payload = _buildPayload(data); - final result = await _methodChannel.invokeMethod(methodName, payload); + final result = await methodChannel.invokeMethod(methodName, payload); if (result is! T) { // result is T?, if it `is` not T => T is not nullable and result is null. throw Exception('Native $methodName returned null.'); @@ -142,28 +125,21 @@ class Player with PlayerEventHandler implements PlayerApi { // Can be used to call methods on the platform side that return a complex // object that is not natively supported by the method channel. - Future _invokeObjectMethod( + Future _invokeObjectMethod( String methodName, T Function(Map) fromJson, [ dynamic data, ]) async { - final result = await _initializationResult; - if (!result) { - throw Exception('Error initializing player on native platform side.'); + final String jsonString; + const T? tNull = null; + if (tNull is T) { // T is nullable + final jsonStringOrNull = await _invokeMethod(methodName, data); + if (jsonStringOrNull == null) return tNull; + jsonString = jsonStringOrNull; + } else { + jsonString = await _invokeMethod(methodName, data); } - - final jsonString = await _methodChannel.invokeMethod( - methodName, - _buildPayload(data), - ); - - if (jsonString == null) { - return null; - } - - return fromJson( - jsonDecode(jsonString) as Map, - ); + return fromJson(jsonDecode(jsonString) as Map); } // Can be used to call methods on the platform side that return a list of @@ -173,12 +149,9 @@ class Player with PlayerEventHandler implements PlayerApi { T Function(Map) fromJson, [ dynamic data, ]) async { - final result = await _initializationResult; - if (!result) { - return Future.error('Error initializing player on native platform side.'); - } + final methodChannel = (await _nativePlayerHandle).methodChannel; - final jsonStringList = await _methodChannel.invokeListMethod( + final jsonStringList = await methodChannel.invokeListMethod( methodName, _buildPayload(data), ); @@ -274,7 +247,7 @@ class Player with PlayerEventHandler implements PlayerApi { @override Future get subtitle async => - _invokeObjectMethod( + _invokeObjectMethod( Methods.getSubtitle, SubtitleTrack.fromJson, ); @@ -295,3 +268,13 @@ class _AnalyticsApi implements AnalyticsApi { Future sendCustomDataEvent(CustomData customData) async => _player ._invokeMethod(Methods.sendCustomDataEvent, customData.toJson()); } + +class _NativePlayerHandle { + _NativePlayerHandle(this.methodChannel, this.eventChannel); + + /// Private method channel for this player instance + MethodChannel methodChannel; + + /// Private method channel for this player instance to receive events + EventChannel eventChannel; +}