diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index adf696ad0120..36af95d3e7e5 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Adds `allowBackgroundPlayback` to `VideoPlayerOptions`. + ## 2.2.18 * Moves Android and iOS implementations to federated packages. diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 8fd216286fca..3788772cb563 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -294,7 +294,7 @@ class VideoPlayerController extends ValueNotifier { bool _isDisposed = false; Completer? _creatingCompleter; StreamSubscription? _eventSubscription; - late _VideoAppLifeCycleObserver _lifeCycleObserver; + _VideoAppLifeCycleObserver? _lifeCycleObserver; /// The id of a texture that hasn't been initialized. @visibleForTesting @@ -308,8 +308,12 @@ class VideoPlayerController extends ValueNotifier { /// Attempts to open the given [dataSource] and load metadata about the video. Future initialize() async { - _lifeCycleObserver = _VideoAppLifeCycleObserver(this); - _lifeCycleObserver.initialize(); + final allowBackgroundPlayback = + videoPlayerOptions?.allowBackgroundPlayback ?? false; + if (!allowBackgroundPlayback) { + _lifeCycleObserver = _VideoAppLifeCycleObserver(this); + } + _lifeCycleObserver?.initialize(); _creatingCompleter = Completer(); late DataSource dataSourceDescription; @@ -424,7 +428,7 @@ class VideoPlayerController extends ValueNotifier { await _eventSubscription?.cancel(); await _videoPlayerPlatform.dispose(_textureId); } - _lifeCycleObserver.dispose(); + _lifeCycleObserver?.dispose(); } _isDisposed = true; super.dispose(); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index a444fc69c912..8a7f1dfbf3d0 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.18 +version: 2.3.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 3bce821e8fe3..e91a8818d7a4 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -98,6 +98,22 @@ class _FakeClosedCaptionFile extends ClosedCaptionFile { } void main() { + void verifyPlayingWhenAppLifecyclePaused( + VideoPlayerController controller, { + required bool isObserving, + }) { + final wasPlayingBeforePause = controller.value.isPlaying; + WidgetsBinding.instance! + .handleAppLifecycleStateChanged(AppLifecycleState.paused); + expect( + controller.value.isPlaying, + isObserving ? false : wasPlayingBeforePause, + ); + WidgetsBinding.instance! + .handleAppLifecycleStateChanged(AppLifecycleState.resumed); + expect(controller.value.isPlaying, wasPlayingBeforePause); + } + testWidgets('update texture', (WidgetTester tester) async { final FakeController controller = FakeController(); await tester.pumpWidget(VideoPlayer(controller)); @@ -192,6 +208,15 @@ void main() { }); group('initialize', () { + test('started app lifecycle observing', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + await controller.initialize(); + await controller.play(); + verifyPlayingWhenAppLifecyclePaused(controller, isObserving: true); + }); + test('asset', () async { final VideoPlayerController controller = VideoPlayerController.asset( 'a.avi', @@ -899,6 +924,33 @@ void main() { }); }); + group('VideoPlayerOptions', () { + test('setMixWithOthers', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File(''), + videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true)); + await controller.initialize(); + expect(controller.videoPlayerOptions!.mixWithOthers, true); + }); + + [true, false].forEach((allowBackgroundPlayback) { + test('allowBackgroundPlayback is $allowBackgroundPlayback', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File(''), + videoPlayerOptions: VideoPlayerOptions( + allowBackgroundPlayback: allowBackgroundPlayback, + ), + ); + await controller.initialize(); + await controller.play(); + verifyPlayingWhenAppLifecyclePaused( + controller, + isObserving: !allowBackgroundPlayback, + ); + }); + }); + }); + test('VideoProgressColors', () { const Color playedColor = Color.fromRGBO(0, 0, 255, 0.75); const Color bufferedColor = Color.fromRGBO(0, 255, 0, 0.5); @@ -913,14 +965,6 @@ void main() { expect(colors.bufferedColor, bufferedColor); expect(colors.backgroundColor, backgroundColor); }); - - test('setMixWithOthers', () async { - final VideoPlayerController controller = VideoPlayerController.file( - File(''), - videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true)); - await controller.initialize(); - expect(controller.videoPlayerOptions!.mixWithOthers, true); - }); } class FakeVideoPlayerPlatform extends VideoPlayerPlatform {