Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[webview_flutter] Added 'allowsInlineMediaPlayback' property #2227

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/webview_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.3.15+2

* Added `allowsInlineMediaPlayback` property.

## 0.3.15+1

* Revert the prior embedding support add since it requires an API that hasn't
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ private void applySettings(Map<String, Object> settings) {

webView.setWebContentsDebuggingEnabled(debuggingEnabled);
break;
case "allowsInlineMediaPlayback":
// no-op inline media playback is always allowed on Android.
break;
case "userAgent":
updateUserAgent((String) settings.get(key));
break;
Expand Down
Binary file not shown.
Binary file not shown.
6 changes: 2 additions & 4 deletions packages/webview_flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,9 @@ class _WebViewExampleState extends State<WebViewExample> {
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// ignore: prefer_collection_literals
javascriptChannels: <JavascriptChannel>[
javascriptChannels: <JavascriptChannel>{
_toasterJavascriptChannel(context),
].toSet(),
},
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
print('blocking navigation to $request}');
Expand Down
6 changes: 3 additions & 3 deletions packages/webview_flutter/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: webview_flutter_example
description: Demonstrates how to use the webview_flutter plugin.

version: 1.0.3
version: 1.0.4

environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
sdk: ">=2.2.0 <3.0.0"

dependencies:
flutter:
Expand All @@ -21,4 +21,4 @@ dev_dependencies:
flutter:
uses-material-design: true
assets:
- assets/sample_audio.ogg
- assets/sample_video.mp4
120 changes: 92 additions & 28 deletions packages/webview_flutter/example/test_driver/webview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -113,16 +114,14 @@ void main() {
controllerCompleter.complete(controller);
},
javascriptMode: JavascriptMode.unrestricted,
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// ignore: prefer_collection_literals
javascriptChannels: <JavascriptChannel>[
javascriptChannels: <JavascriptChannel>{
JavascriptChannel(
name: 'Echo',
onMessageReceived: (JavascriptMessage message) {
messagesReceived.add(message.message);
},
),
].toSet(),
},
onPageFinished: (String url) {
pageLoaded.complete(null);
},
Expand Down Expand Up @@ -168,16 +167,14 @@ void main() {
onWebViewCreated: (WebViewController controller) {
controllerCompleter.complete(controller);
},
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// ignore: prefer_collection_literals
javascriptChannels: <JavascriptChannel>[
javascriptChannels: <JavascriptChannel>{
JavascriptChannel(
name: 'Resize',
onMessageReceived: (JavascriptMessage message) {
resizeCompleter.complete(true);
},
),
].toSet(),
},
onPageFinished: (String url) {
pageLoaded.complete(null);
},
Expand Down Expand Up @@ -311,34 +308,38 @@ void main() {
});

group('Media playback policy', () {
String audioTestBase64;
String videoTestBase64;
setUpAll(() async {
final ByteData audioData =
await rootBundle.load('assets/sample_audio.ogg');
final String base64AudioData =
base64Encode(Uint8List.view(audioData.buffer));
final String audioTest = '''
final ByteData videoData =
await rootBundle.load('assets/sample_video.mp4');
final String base64VideoData =
base64Encode(Uint8List.view(videoData.buffer));
final String videoTest = '''
<!DOCTYPE html><html>
<head><title>Audio auto play</title>
<head><title>Video auto play</title>
<script type="text/javascript">
function play() {
var audio = document.getElementById("audio");
audio.play();
var video = document.getElementById("video");
video.play();
}
function isPaused() {
var audio = document.getElementById("audio");
return audio.paused;
var video = document.getElementById("video");
return video.paused;
}
function isFullScreen() {
var video = document.getElementById("video");
return video.webkitDisplayingFullscreen;
}
</script>
</head>
<body onload="play();">
<audio controls id="audio">
<source src="data:audio/ogg;charset=utf-8;base64,$base64AudioData">
</audio>
<video controls playsinline autoplay id="video">
<source src="data:video/mp4;charset=utf-8;base64,$base64VideoData">
</video>
</body>
</html>
''';
audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest));
videoTestBase64 = base64Encode(const Utf8Encoder().convert(videoTest));
});

test('Auto media playback', () async {
Expand All @@ -351,7 +352,7 @@ void main() {
textDirection: TextDirection.ltr,
child: WebView(
key: GlobalKey(),
initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
onWebViewCreated: (WebViewController controller) {
controllerCompleter.complete(controller);
},
Expand All @@ -378,7 +379,7 @@ void main() {
textDirection: TextDirection.ltr,
child: WebView(
key: GlobalKey(),
initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
onWebViewCreated: (WebViewController controller) {
controllerCompleter.complete(controller);
},
Expand All @@ -399,7 +400,7 @@ void main() {
expect(isPaused, _webviewBool(true));
});

test('Changes to initialMediaPlaybackPolocy are ignored', () async {
test('Changes to initialMediaPlaybackPolicy are ignored', () async {
final Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
Completer<void> pageLoaded = Completer<void>();
Expand All @@ -410,7 +411,7 @@ void main() {
textDirection: TextDirection.ltr,
child: WebView(
key: key,
initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
onWebViewCreated: (WebViewController controller) {
controllerCompleter.complete(controller);
},
Expand All @@ -435,7 +436,7 @@ void main() {
textDirection: TextDirection.ltr,
child: WebView(
key: key,
initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
onWebViewCreated: (WebViewController controller) {
controllerCompleter.complete(controller);
},
Expand All @@ -456,6 +457,69 @@ void main() {
isPaused = await controller.evaluateJavascript('isPaused();');
expect(isPaused, _webviewBool(false));
});

test('Video plays inline when allowsInlineMediaPlayback is true', () async {
if (Platform.isIOS) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should run on all platforms (so that we can be sure future plugin / platform versions don't break).

Can you clearly observe a different behavior on Android? If so, perhaps this could be a separate else branch to verify that.

Completer<WebViewController> controllerCompleter =
Completer<WebViewController>();
Completer<void> pageLoaded = Completer<void>();

await pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
key: GlobalKey(),
initialUrl:
'data:text/html;charset=utf-8;base64,$videoTestBase64',
onWebViewCreated: (WebViewController controller) {
controllerCompleter.complete(controller);
},
javascriptMode: JavascriptMode.unrestricted,
onPageFinished: (String url) {
pageLoaded.complete(null);
},
initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
allowsInlineMediaPlayback: true,
),
),
);
WebViewController controller = await controllerCompleter.future;
await pageLoaded.future;

String isFullScreen =
await controller.evaluateJavascript('isFullScreen();');
expect(isFullScreen, _webviewBool(false));

controllerCompleter = Completer<WebViewController>();
pageLoaded = Completer<void>();

await pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: WebView(
key: GlobalKey(),
initialUrl:
'data:text/html;charset=utf-8;base64,$videoTestBase64',
onWebViewCreated: (WebViewController controller) {
controllerCompleter.complete(controller);
},
javascriptMode: JavascriptMode.unrestricted,
onPageFinished: (String url) {
pageLoaded.complete(null);
},
initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
allowsInlineMediaPlayback: false,
),
),
);

controller = await controllerCompleter.future;
await pageLoaded.future;

isFullScreen = await controller.evaluateJavascript('isFullScreen();');
expect(isFullScreen, _webviewBool(true));
}
});
});

test('getTitle', () async {
Expand Down
3 changes: 3 additions & 0 deletions packages/webview_flutter/ios/Classes/FlutterWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ - (NSString*)applySettings:(NSDictionary<NSString*, id>*)settings {
} else if ([key isEqualToString:@"userAgent"]) {
NSString* userAgent = settings[key];
[self updateUserAgent:[userAgent isEqual:[NSNull null]] ? nil : userAgent];
} else if ([key isEqualToString:@"allowsInlineMediaPlayback"]) {
NSNumber* allowsInlineMediaPlayback = settings[key];
_webView.configuration.allowsInlineMediaPlayback = [allowsInlineMediaPlayback boolValue];
} else {
[unknownKeys addObject:key];
}
Expand Down
8 changes: 7 additions & 1 deletion packages/webview_flutter/lib/platform_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ class WebSettings {
this.javascriptMode,
this.hasNavigationDelegate,
this.debuggingEnabled,
this.allowsInlineMediaPlayback,
@required this.userAgent,
}) : assert(userAgent != null);

Expand All @@ -238,6 +239,11 @@ class WebSettings {
/// See also: [WebView.debuggingEnabled].
final bool debuggingEnabled;

/// Whether to play HTML5 videos inline or use the native full-screen controller on iOS.
///
/// This will have no effect on Android.
final bool allowsInlineMediaPlayback;

/// The value used for the HTTP `User-Agent:` request header.
///
/// If [userAgent.value] is null the platform's default user agent should be used.
Expand All @@ -250,7 +256,7 @@ class WebSettings {

@override
String toString() {
return 'WebSettings(javascriptMode: $javascriptMode, hasNavigationDelegate: $hasNavigationDelegate, debuggingEnabled: $debuggingEnabled, userAgent: $userAgent,)';
return 'WebSettings(javascriptMode: $javascriptMode, hasNavigationDelegate: $hasNavigationDelegate, debuggingEnabled: $debuggingEnabled, userAgent: $userAgent, allowsInlineMediaPlayback: $allowsInlineMediaPlayback)';
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/webview_flutter/lib/src/webview_method_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
_addIfNonNull('jsMode', settings.javascriptMode?.index);
_addIfNonNull('hasNavigationDelegate', settings.hasNavigationDelegate);
_addIfNonNull('debuggingEnabled', settings.debuggingEnabled);
_addIfNonNull(
'allowsInlineMediaPlayback', settings.allowsInlineMediaPlayback);
_addSettingIfPresent('userAgent', settings.userAgent);
return map;
}
Expand Down
13 changes: 10 additions & 3 deletions packages/webview_flutter/lib/webview_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class WebView extends StatefulWidget {
this.userAgent,
this.initialMediaPlaybackPolicy =
AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
this.allowsInlineMediaPlayback = false,
}) : assert(javascriptMode != null),
assert(initialMediaPlaybackPolicy != null),
super(key: key);
Expand Down Expand Up @@ -300,6 +301,13 @@ class WebView extends StatefulWidget {
/// The default policy is [AutoMediaPlaybackPolicy.require_user_action_for_all_media_types].
final AutoMediaPlaybackPolicy initialMediaPlaybackPolicy;

/// Controls whether inline playback of HTML5 videos is allowed on iOS.
///
/// This field is ignored on Android.
///
/// By default `allowsInlineMediaPlayback` is false.
final bool allowsInlineMediaPlayback;

@override
State<StatefulWidget> createState() => _WebViewState();
}
Expand Down Expand Up @@ -372,6 +380,7 @@ WebSettings _webSettingsFromWidget(WebView widget) {
javascriptMode: widget.javascriptMode,
hasNavigationDelegate: widget.navigationDelegate != null,
debuggingEnabled: widget.debuggingEnabled,
allowsInlineMediaPlayback: widget.allowsInlineMediaPlayback,
userAgent: WebSetting<String>.of(widget.userAgent),
);
}
Expand Down Expand Up @@ -415,9 +424,7 @@ WebSettings _clearUnchangedWebSettings(

Set<String> _extractChannelNames(Set<JavascriptChannel> channels) {
final Set<String> channelNames = channels == null
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// ignore: prefer_collection_literals
? Set<String>()
? <String>{}
: channels.map((JavascriptChannel channel) => channel.name).toSet();
return channelNames;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/webview_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: webview_flutter
description: A Flutter plugin that provides a WebView widget on Android and iOS.
version: 0.3.15+1
version: 0.3.15+2
author: Flutter Team <[email protected]>
homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter

environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
sdk: ">=2.2.0 <3.0.0"
flutter: ">=1.5.0 <2.0.0"

dependencies:
Expand Down
Loading