Skip to content

Commit b6dea96

Browse files
author
EconomicEgret
authored
[video_player] Add new option for allowBackgroundPlayback (flutter#5013)
1 parent 5691d52 commit b6dea96

4 files changed

Lines changed: 83 additions & 14 deletions

File tree

packages/video_player/video_player/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.3.0
2+
3+
* Adds `allowBackgroundPlayback` to `VideoPlayerOptions`.
4+
15
## 2.2.19
26

37
* Internal code cleanup for stricter analysis options.

packages/video_player/video_player/lib/video_player.dart

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
295295
bool _isDisposed = false;
296296
Completer<void>? _creatingCompleter;
297297
StreamSubscription<dynamic>? _eventSubscription;
298-
late _VideoAppLifeCycleObserver _lifeCycleObserver;
298+
_VideoAppLifeCycleObserver? _lifeCycleObserver;
299299

300300
/// The id of a texture that hasn't been initialized.
301301
@visibleForTesting
@@ -309,8 +309,12 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
309309

310310
/// Attempts to open the given [dataSource] and load metadata about the video.
311311
Future<void> initialize() async {
312-
_lifeCycleObserver = _VideoAppLifeCycleObserver(this);
313-
_lifeCycleObserver.initialize();
312+
final bool allowBackgroundPlayback =
313+
videoPlayerOptions?.allowBackgroundPlayback ?? false;
314+
if (!allowBackgroundPlayback) {
315+
_lifeCycleObserver = _VideoAppLifeCycleObserver(this);
316+
}
317+
_lifeCycleObserver?.initialize();
314318
_creatingCompleter = Completer<void>();
315319

316320
late DataSource dataSourceDescription;
@@ -423,7 +427,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
423427
await _eventSubscription?.cancel();
424428
await _videoPlayerPlatform.dispose(_textureId);
425429
}
426-
_lifeCycleObserver.dispose();
430+
_lifeCycleObserver?.dispose();
427431
}
428432
_isDisposed = true;
429433
super.dispose();

packages/video_player/video_player/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter
33
widgets on Android, iOS, and web.
44
repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player
55
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
6-
version: 2.2.19
6+
version: 2.3.0
77

88
environment:
99
sdk: ">=2.14.0 <3.0.0"
@@ -25,7 +25,7 @@ dependencies:
2525
html: ^0.15.0
2626
video_player_android: ^2.2.17
2727
video_player_avfoundation: ^2.2.17
28-
video_player_platform_interface: ">=4.2.0 <6.0.0"
28+
video_player_platform_interface: ^5.1.0
2929
video_player_web: ^2.0.0
3030

3131
dev_dependencies:

packages/video_player/video_player/test/video_player_test.dart

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,19 @@ class _FakeClosedCaptionFile extends ClosedCaptionFile {
9898
}
9999

100100
void main() {
101+
void _verifyPlayStateRespondsToLifecycle(
102+
VideoPlayerController controller, {
103+
required bool shouldPlayInBackground,
104+
}) {
105+
expect(controller.value.isPlaying, true);
106+
_ambiguate(WidgetsBinding.instance)!
107+
.handleAppLifecycleStateChanged(AppLifecycleState.paused);
108+
expect(controller.value.isPlaying, shouldPlayInBackground);
109+
_ambiguate(WidgetsBinding.instance)!
110+
.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
111+
expect(controller.value.isPlaying, true);
112+
}
113+
101114
testWidgets('update texture', (WidgetTester tester) async {
102115
final FakeController controller = FakeController();
103116
await tester.pumpWidget(VideoPlayer(controller));
@@ -194,6 +207,16 @@ void main() {
194207
});
195208

196209
group('initialize', () {
210+
test('started app lifecycle observing', () async {
211+
final VideoPlayerController controller = VideoPlayerController.network(
212+
'https://127.0.0.1',
213+
);
214+
await controller.initialize();
215+
await controller.play();
216+
_verifyPlayStateRespondsToLifecycle(controller,
217+
shouldPlayInBackground: false);
218+
});
219+
197220
test('asset', () async {
198221
final VideoPlayerController controller = VideoPlayerController.asset(
199222
'a.avi',
@@ -900,6 +923,46 @@ void main() {
900923
});
901924
});
902925

926+
group('VideoPlayerOptions', () {
927+
test('setMixWithOthers', () async {
928+
final VideoPlayerController controller = VideoPlayerController.file(
929+
File(''),
930+
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true));
931+
await controller.initialize();
932+
expect(controller.videoPlayerOptions!.mixWithOthers, true);
933+
});
934+
935+
test('true allowBackgroundPlayback continues playback', () async {
936+
final VideoPlayerController controller = VideoPlayerController.file(
937+
File(''),
938+
videoPlayerOptions: VideoPlayerOptions(
939+
allowBackgroundPlayback: true,
940+
),
941+
);
942+
await controller.initialize();
943+
await controller.play();
944+
_verifyPlayStateRespondsToLifecycle(
945+
controller,
946+
shouldPlayInBackground: true,
947+
);
948+
});
949+
950+
test('false allowBackgroundPlayback pauses playback', () async {
951+
final VideoPlayerController controller = VideoPlayerController.file(
952+
File(''),
953+
videoPlayerOptions: VideoPlayerOptions(
954+
allowBackgroundPlayback: false,
955+
),
956+
);
957+
await controller.initialize();
958+
await controller.play();
959+
_verifyPlayStateRespondsToLifecycle(
960+
controller,
961+
shouldPlayInBackground: false,
962+
);
963+
});
964+
});
965+
903966
test('VideoProgressColors', () {
904967
const Color playedColor = Color.fromRGBO(0, 0, 255, 0.75);
905968
const Color bufferedColor = Color.fromRGBO(0, 255, 0, 0.5);
@@ -914,14 +977,6 @@ void main() {
914977
expect(colors.bufferedColor, bufferedColor);
915978
expect(colors.backgroundColor, backgroundColor);
916979
});
917-
918-
test('setMixWithOthers', () async {
919-
final VideoPlayerController controller = VideoPlayerController.file(
920-
File(''),
921-
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true));
922-
await controller.initialize();
923-
expect(controller.videoPlayerOptions!.mixWithOthers, true);
924-
});
925980
}
926981

927982
class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
@@ -1010,3 +1065,9 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
10101065
calls.add('setMixWithOthers');
10111066
}
10121067
}
1068+
1069+
/// This allows a value of type T or T? to be treated as a value of type T?.
1070+
///
1071+
/// We use this so that APIs that have become non-nullable can still be used
1072+
/// with `!` and `?` on the stable branch.
1073+
T? _ambiguate<T>(T? value) => value;

0 commit comments

Comments
 (0)