diff --git a/lib/src/core/better_player_controller.dart b/lib/src/core/better_player_controller.dart index 8bd613758..c85e76305 100644 --- a/lib/src/core/better_player_controller.dart +++ b/lib/src/core/better_player_controller.dart @@ -8,6 +8,7 @@ import 'package:better_player/src/subtitles/better_player_subtitles_factory.dart import 'package:better_player/src/video_player/video_player.dart'; import 'package:better_player/src/video_player/video_player_platform_interface.dart'; import 'package:collection/collection.dart' show IterableExtension; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; @@ -1067,7 +1068,7 @@ class BetterPlayerController { _wasInFullScreenBeforePiP = _isFullScreen; _wasControlsEnabledBeforePiP = _controlsEnabled; setControlsEnabled(false); - if (Platform.isAndroid) { + if (defaultTargetPlatform == TargetPlatform.android) { _wasInFullScreenBeforePiP = _isFullScreen; await videoPlayerController?.enablePictureInPicture( left: 0, top: 0, width: 0, height: 0); @@ -1075,7 +1076,7 @@ class BetterPlayerController { _postEvent(BetterPlayerEvent(BetterPlayerEventType.pipStart)); return; } - if (Platform.isIOS) { + if (defaultTargetPlatform == TargetPlatform.iOS) { final RenderBox? renderBox = betterPlayerGlobalKey.currentContext! .findRenderObject() as RenderBox?; if (renderBox == null) { diff --git a/test/better_player_controller_test.dart b/test/better_player_controller_test.dart index e6117df33..8073f81bd 100644 --- a/test/better_player_controller_test.dart +++ b/test/better_player_controller_test.dart @@ -1,7 +1,10 @@ import 'package:better_player/better_player.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; + import 'better_player_mock_controller.dart'; import 'better_player_test_utils.dart'; +import 'mock_global_key.dart'; import 'mock_method_channel.dart'; import 'mock_video_player_controller.dart'; @@ -431,6 +434,48 @@ void main() { await Future.delayed(const Duration(milliseconds: 3000), () {}); expect(eventCount, 3); }); + + test("enablePictureInPicture sends event on iOS", () async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + final BetterPlayerController betterPlayerMockController = + BetterPlayerTestUtils.setupBetterPlayerMockController(); + final mockVideoPlayerController = + BetterPlayerTestUtils.setupMockVideoPlayerControler(); + mockVideoPlayerController.isPipSupported = true; + betterPlayerMockController.videoPlayerController = + mockVideoPlayerController; + final mockGlobalKey = MockGlobalKey(); + int pipStartCalls = 0; + betterPlayerMockController.addEventsListener((event) { + if (event.betterPlayerEventType == BetterPlayerEventType.pipStart) { + pipStartCalls += 1; + } + }); + betterPlayerMockController.enablePictureInPicture(mockGlobalKey); + await Future.delayed(const Duration(milliseconds: 3000), () {}); + expect(pipStartCalls, 1); + }); + + test("enablePictureInPicture sends event on Android", () async { + debugDefaultTargetPlatformOverride = TargetPlatform.android; + final BetterPlayerController betterPlayerMockController = + BetterPlayerTestUtils.setupBetterPlayerMockController(); + final mockVideoPlayerController = + BetterPlayerTestUtils.setupMockVideoPlayerControler(); + mockVideoPlayerController.isPipSupported = true; + betterPlayerMockController.videoPlayerController = + mockVideoPlayerController; + final mockGlobalKey = MockGlobalKey(); + int pipStartCalls = 0; + betterPlayerMockController.addEventsListener((event) { + if (event.betterPlayerEventType == BetterPlayerEventType.pipStart) { + pipStartCalls += 1; + } + }); + betterPlayerMockController.enablePictureInPicture(mockGlobalKey); + await Future.delayed(const Duration(milliseconds: 3000), () {}); + expect(pipStartCalls, 1); + }); }, ); } diff --git a/test/mock_build_context.dart b/test/mock_build_context.dart new file mode 100644 index 000000000..ddd964f23 --- /dev/null +++ b/test/mock_build_context.dart @@ -0,0 +1,96 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'mock_render_box.dart'; + +class MockBuildContext extends BuildContext { + @override + bool get debugDoingBuild => throw UnimplementedError(); + + @override + InheritedWidget dependOnInheritedElement(InheritedElement ancestor, + {Object? aspect}) { + throw UnimplementedError(); + } + + @override + T? dependOnInheritedWidgetOfExactType( + {Object? aspect}) { + throw UnimplementedError(); + } + + @override + DiagnosticsNode describeElement(String name, + {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty}) { + throw UnimplementedError(); + } + + @override + List describeMissingAncestor( + {required Type expectedAncestorType}) { + throw UnimplementedError(); + } + + @override + DiagnosticsNode describeOwnershipChain(String name) { + throw UnimplementedError(); + } + + @override + DiagnosticsNode describeWidget(String name, + {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty}) { + throw UnimplementedError(); + } + + @override + void dispatchNotification(Notification notification) {} + + @override + T? findAncestorRenderObjectOfType() { + throw UnimplementedError(); + } + + @override + T? findAncestorStateOfType>() { + throw UnimplementedError(); + } + + @override + T? findAncestorWidgetOfExactType() { + throw UnimplementedError(); + } + + @override + RenderObject? findRenderObject() { + return MockRenderBox(); + } + + @override + T? findRootAncestorStateOfType>() { + throw UnimplementedError(); + } + + @override + InheritedElement? + getElementForInheritedWidgetOfExactType() { + throw UnimplementedError(); + } + + @override + bool get mounted => throw UnimplementedError(); + + @override + BuildOwner? get owner => throw UnimplementedError(); + + @override + Size? get size => throw UnimplementedError(); + + @override + void visitAncestorElements(bool Function(Element element) visitor) {} + + @override + void visitChildElements(ElementVisitor visitor) {} + + @override + Widget get widget => throw UnimplementedError(); +} diff --git a/test/mock_global_key.dart b/test/mock_global_key.dart new file mode 100644 index 000000000..a6d5ca9d6 --- /dev/null +++ b/test/mock_global_key.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; +import 'mock_build_context.dart'; + +class MockGlobalKey extends GlobalKey { + MockGlobalKey() : super.constructor(); + + @override + BuildContext? get currentContext => MockBuildContext(); +} diff --git a/test/mock_render_box.dart b/test/mock_render_box.dart new file mode 100644 index 000000000..077215fce --- /dev/null +++ b/test/mock_render_box.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +class MockRenderBox extends RenderBox { + @override + Offset localToGlobal(Offset point, {RenderObject? ancestor}) { + return Offset(0, 0); + } + + @override + Size get size => Size.zero; +} diff --git a/test/mock_video_player_controller.dart b/test/mock_video_player_controller.dart index b9bceebab..46f6e59b9 100644 --- a/test/mock_video_player_controller.dart +++ b/test/mock_video_player_controller.dart @@ -9,6 +9,7 @@ class MockVideoPlayerController extends VideoPlayerController { bool isLoopingState = false; double volume = 0.0; double speed = 1.0; + bool isPipSupported = false; @override Future play() async { @@ -75,4 +76,9 @@ class MockVideoPlayerController extends VideoPlayerController { String? clearKey, String? videoExtension, }) async {} + + @override + Future isPictureInPictureSupported() { + return Future.value(isPipSupported); + } }