diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index f75c5a10f94e..e988a210f74d 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 6.2.0 * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. +* Adds web options to customize control's list and displaying context menu. ## 6.1.0 diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 32410660ce8e..46b992f58c61 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -102,6 +102,11 @@ abstract class VideoPlayerPlatform extends PlatformInterface { Future setMixWithOthers(bool mixWithOthers) { throw UnimplementedError('setMixWithOthers() has not been implemented.'); } + + /// Sets additional options on web + Future setWebOptions(int textureId, VideoPlayerWebOptions options) { + throw UnimplementedError('setWebOptions() has not been implemented.'); + } } class _PlaceholderImplementation extends VideoPlayerPlatform {} @@ -365,7 +370,7 @@ class DurationRange { /// [VideoPlayerOptions] can be optionally used to set additional player settings @immutable class VideoPlayerOptions { - /// set additional optional player settings + /// Set additional optional player settings // TODO(stuartmorgan): Temporarily suppress warnings about not using const // in all of the other video player packages, fix this, and then update // the other packages to use const. @@ -373,6 +378,7 @@ class VideoPlayerOptions { VideoPlayerOptions({ this.mixWithOthers = false, this.allowBackgroundPlayback = false, + this.webOptions, }); /// Set this to true to keep playing video in background, when app goes in background. @@ -385,4 +391,86 @@ class VideoPlayerOptions { /// Note: This option will be silently ignored in the web platform (there is /// currently no way to implement this feature in this platform). final bool mixWithOthers; + + /// Additional web controls + final VideoPlayerWebOptions? webOptions; +} + +/// [VideoPlayerWebOptions] can be optionally used to set additional web settings +@immutable +class VideoPlayerWebOptions { + /// [VideoPlayerWebOptions] can be optionally used to set additional web settings + const VideoPlayerWebOptions({ + this.controls = const VideoPlayerWebOptionsControls.disabled(), + this.allowContextMenu = true, + this.allowRemotePlayback = true, + }); + + /// Additional settings for how control options are displayed + final VideoPlayerWebOptionsControls controls; + + /// Whether context menu (right click) is allowed + final bool allowContextMenu; + + /// Whether remote playback is allowed + final bool allowRemotePlayback; +} + +/// [VideoPlayerWebOptions] can be used to set how control options are displayed +@immutable +class VideoPlayerWebOptionsControls { + /// Enables controls and sets how the options are displayed + const VideoPlayerWebOptionsControls.enabled({ + this.allowDownload = true, + this.allowFullscreen = true, + this.allowPlaybackRate = true, + this.allowPictureInPicture = true, + }) : enabled = true; + + /// Disables control options. Default behavior. + const VideoPlayerWebOptionsControls.disabled() + : enabled = false, + allowDownload = false, + allowFullscreen = false, + allowPlaybackRate = false, + allowPictureInPicture = false; + + /// Whether native controls are enabled + final bool enabled; + + /// Whether downloaded control is displayed + /// + /// Only applicable when [controlsEnabled] is true + final bool allowDownload; + + /// Whether fullscreen control is enabled + /// + /// Only applicable when [controlsEnabled] is true + final bool allowFullscreen; + + /// Whether playback rate control is displayed + /// + /// Only applicable when [controlsEnabled] is true + final bool allowPlaybackRate; + + /// Whether picture in picture control is displayed + /// + /// Only applicable when [controlsEnabled] is true + final bool allowPictureInPicture; + + /// A string representation of disallowed controls + String get controlsList { + final List controlsList = []; + if (!allowDownload) { + controlsList.add('nodownload'); + } + if (!allowFullscreen) { + controlsList.add('nofullscreen'); + } + if (!allowPlaybackRate) { + controlsList.add('noplaybackrate'); + } + + return controlsList.join(' '); + } } diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index f266f6eaded3..b3ab22640f9c 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/video_player/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 6.1.0 +version: 6.2.0 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart index 8aa7ad9bd3c1..7b643de83e3e 100644 --- a/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart +++ b/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart @@ -9,7 +9,17 @@ void main() { // Store the initial instance before any tests change it. final VideoPlayerPlatform initialInstance = VideoPlayerPlatform.instance; - test('default implementation throws uninimpletemented', () async { + test('default implementation init throws unimplemented', () async { await expectLater(() => initialInstance.init(), throwsUnimplementedError); }); + + test('default implementation setWebOptions throws unimplemented', () async { + await expectLater( + () => initialInstance.setWebOptions( + 1, + const VideoPlayerWebOptions(), + ), + throwsUnimplementedError, + ); + }); } diff --git a/packages/video_player/video_player_platform_interface/test/video_player_web_options_controls_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_web_options_controls_test.dart new file mode 100644 index 000000000000..1d6ea15f0914 --- /dev/null +++ b/packages/video_player/video_player_platform_interface/test/video_player_web_options_controls_test.dart @@ -0,0 +1,82 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +void main() { + group('VideoPlayerWebOptionsControls', () { + late VideoPlayerWebOptionsControls controls; + + group('when disabled', () { + setUp(() { + controls = const VideoPlayerWebOptionsControls.disabled(); + }); + + test( + 'expect enabled isFalse', + () { + expect(controls.enabled, isFalse); + }, + ); + }); + + group('when enabled', () { + group('and all options are allowed', () { + setUp(() { + controls = const VideoPlayerWebOptionsControls.enabled(); + }); + + test( + 'expect enabled isTrue', + () { + expect(controls.enabled, isTrue); + expect(controls.allowDownload, isTrue); + expect(controls.allowFullscreen, isTrue); + expect(controls.allowPlaybackRate, isTrue); + expect(controls.allowPictureInPicture, isTrue); + }, + ); + + test( + 'expect controlsList isEmpty', + () { + expect(controls.controlsList, isEmpty); + }, + ); + }); + + group('and some options are disallowed', () { + setUp(() { + controls = const VideoPlayerWebOptionsControls.enabled( + allowDownload: false, + allowFullscreen: false, + allowPlaybackRate: false, + ); + }); + + test( + 'expect enabled isTrue', + () { + expect(controls.enabled, isTrue); + expect(controls.allowDownload, isFalse); + expect(controls.allowFullscreen, isFalse); + expect(controls.allowPlaybackRate, isFalse); + expect(controls.allowPictureInPicture, isTrue); + }, + ); + + test( + 'expect controlsList is correct', + () { + expect( + controls.controlsList, + 'nodownload nofullscreen noplaybackrate', + ); + }, + ); + }); + }); + }); +} diff --git a/packages/video_player/video_player_platform_interface/test/video_player_web_options_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_web_options_test.dart new file mode 100644 index 000000000000..ae37fa399331 --- /dev/null +++ b/packages/video_player/video_player_platform_interface/test/video_player_web_options_test.dart @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +void main() { + test( + 'VideoPlayerOptions controls defaults to VideoPlayerWebOptionsControls.disabled()', + () { + const VideoPlayerWebOptions options = VideoPlayerWebOptions(); + expect(options.controls, const VideoPlayerWebOptionsControls.disabled()); + }, + ); + + test( + 'VideoPlayerOptions allowContextMenu defaults to true', + () { + const VideoPlayerWebOptions options = VideoPlayerWebOptions(); + expect(options.allowContextMenu, isTrue); + }, + ); + + test( + 'VideoPlayerOptions allowRemotePlayback defaults to true', + () { + const VideoPlayerWebOptions options = VideoPlayerWebOptions(); + expect(options.allowRemotePlayback, isTrue); + }, + ); +}