From 5498f0ba24cc5d1be8a8308133d1f7c5072ef3d3 Mon Sep 17 00:00:00 2001 From: Paul Staats Date: Tue, 2 May 2023 10:33:54 +0200 Subject: [PATCH] Added fullscreen capabilities to the android webview --- .../webview_flutter_android/CHANGELOG.md | 4 + .../WebChromeClientHostApiImpl.java | 102 +++++++++++++++++- .../webviewflutter/WebViewFlutterPlugin.java | 15 ++- .../webviewflutter/WebChromeClientTest.java | 34 +++++- .../webview_flutter_android/pubspec.yaml | 2 +- 5 files changed, 144 insertions(+), 13 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 82dbbd49f215..62939d468d35 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.7.0 + +* Adds support for fullscreen video. + ## 3.6.3 * Updates gradle, AGP and fixes some lint errors. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java index 38ebcb8932b8..b68e30327bb8 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java @@ -4,15 +4,25 @@ package io.flutter.plugins.webviewflutter; +import android.app.Activity; +import android.content.Context; import android.net.Uri; import android.os.Build; import android.os.Message; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowInsets; +import android.view.WindowManager; import android.webkit.PermissionRequest; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.FrameLayout; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -29,6 +39,7 @@ public class WebChromeClientHostApiImpl implements WebChromeClientHostApi { private final InstanceManager instanceManager; private final WebChromeClientCreator webChromeClientCreator; private final WebChromeClientFlutterApiImpl flutterApi; + private Context context; /** * Implementation of {@link WebChromeClient} that passes arguments of callback methods to Dart. @@ -36,14 +47,18 @@ public class WebChromeClientHostApiImpl implements WebChromeClientHostApi { public static class WebChromeClientImpl extends SecureWebChromeClient { private final WebChromeClientFlutterApiImpl flutterApi; private boolean returnValueForOnShowFileChooser = false; + private View mFullscreenView; + private Context context; /** * Creates a {@link WebChromeClient} that passes arguments of callbacks methods to Dart. * * @param flutterApi handles sending messages to Dart + * @param context the Activity or application context */ - public WebChromeClientImpl(@NonNull WebChromeClientFlutterApiImpl flutterApi) { + public WebChromeClientImpl(@NonNull WebChromeClientFlutterApiImpl flutterApi, Context context) { this.flutterApi = flutterApi; + this.context = context; } @Override @@ -51,6 +66,67 @@ public void onProgressChanged(@NonNull WebView view, int progress) { flutterApi.onProgressChanged(this, view, (long) progress, reply -> {}); } + @Override + public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) { + if (mFullscreenView != null) { + ((ViewGroup) mFullscreenView.getParent()).removeView(mFullscreenView); + } + if (context instanceof Activity) { + Window window = ((Activity)context).getWindow(); + mFullscreenView = view; + setFullscreenStatus(true, window); + window.addContentView(mFullscreenView, + new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER)); + } + } + + @Override + public void onHideCustomView() { + if (mFullscreenView == null) { + return; + } + if (context instanceof Activity) { + setFullscreenStatus(false, ((Activity)context).getWindow()); + ((ViewGroup) mFullscreenView.getParent()).removeView(mFullscreenView); + mFullscreenView = null; + } + } + + @SuppressWarnings("deprecation") + void activateFullscreenDeprecated(Boolean fullscreen, Window window) { + View decorView = window.getDecorView(); + if (fullscreen) { + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN); + + window.addFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + decorView.setSystemUiVisibility(View.STATUS_BAR_VISIBLE); + + window.clearFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + } + + @RequiresApi(api = Build.VERSION_CODES.R) // >= 30 + void setFullScreenCurrent(Boolean fullscreen, Window window) { + View decorView = window.getDecorView(); + if (fullscreen) { + decorView.getWindowInsetsController().hide(WindowInsets.Type.statusBars()); + } else { + decorView.getWindowInsetsController().show(WindowInsets.Type.statusBars()); + } + } + + void setFullscreenStatus(Boolean fullscreen, Window window) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + activateFullscreenDeprecated(fullscreen, window); + } else { + setFullScreenCurrent(fullscreen, window); + } + } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @SuppressWarnings("LambdaLast") @Override @@ -87,6 +163,11 @@ public void onPermissionRequest(@NonNull PermissionRequest request) { public void setReturnValueForOnShowFileChooser(boolean value) { returnValueForOnShowFileChooser = value; } + + @VisibleForTesting + public View getmFullscreenView() { + return this.mFullscreenView; + } } /** @@ -183,12 +264,13 @@ public static class WebChromeClientCreator { * Creates a {@link WebChromeClientHostApiImpl.WebChromeClientImpl}. * * @param flutterApi handles sending messages to Dart + * @param context the Activity or application context * @return the created {@link WebChromeClientHostApiImpl.WebChromeClientImpl} */ @NonNull public WebChromeClientImpl createWebChromeClient( - @NonNull WebChromeClientFlutterApiImpl flutterApi) { - return new WebChromeClientImpl(flutterApi); + @NonNull WebChromeClientFlutterApiImpl flutterApi, Context context) { + return new WebChromeClientImpl(flutterApi, context); } } @@ -202,16 +284,26 @@ public WebChromeClientImpl createWebChromeClient( public WebChromeClientHostApiImpl( @NonNull InstanceManager instanceManager, @NonNull WebChromeClientCreator webChromeClientCreator, - @NonNull WebChromeClientFlutterApiImpl flutterApi) { + @NonNull WebChromeClientFlutterApiImpl flutterApi, + @NonNull Context context) { this.instanceManager = instanceManager; this.webChromeClientCreator = webChromeClientCreator; this.flutterApi = flutterApi; + this.context = context; + } + + /** + * Sets the context to construct {@link WebChromeClientImpl}s. + * @param context the new context + */ + public void setContext(Context context) { + this.context = context; } @Override public void create(@NonNull Long instanceId) { final WebChromeClient webChromeClient = - webChromeClientCreator.createWebChromeClient(flutterApi); + webChromeClientCreator.createWebChromeClient(flutterApi, context); instanceManager.addDartCreatedInstance(webChromeClient, instanceId); } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index af34649211ab..53215b899923 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -40,6 +40,7 @@ public class WebViewFlutterPlugin implements FlutterPlugin, ActivityAware { private FlutterPluginBinding pluginBinding; private WebViewHostApiImpl webViewHostApi; private JavaScriptChannelHostApiImpl javaScriptChannelHostApi; + private WebChromeClientHostApiImpl webChromeClientHostApi; /** * Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to @@ -100,6 +101,14 @@ private void setUp( new JavaScriptChannelFlutterApiImpl(binaryMessenger, instanceManager), new Handler(context.getMainLooper())); + webChromeClientHostApi = + new WebChromeClientHostApiImpl( + instanceManager, + new WebChromeClientHostApiImpl.WebChromeClientCreator(), + new WebChromeClientFlutterApiImpl(binaryMessenger, instanceManager), + context); + + JavaObjectHostApi.setup(binaryMessenger, new JavaObjectHostApiImpl(instanceManager)); WebViewHostApi.setup(binaryMessenger, webViewHostApi); JavaScriptChannelHostApi.setup(binaryMessenger, javaScriptChannelHostApi); @@ -111,10 +120,7 @@ private void setUp( new WebViewClientFlutterApiImpl(binaryMessenger, instanceManager))); WebChromeClientHostApi.setup( binaryMessenger, - new WebChromeClientHostApiImpl( - instanceManager, - new WebChromeClientHostApiImpl.WebChromeClientCreator(), - new WebChromeClientFlutterApiImpl(binaryMessenger, instanceManager))); + webChromeClientHostApi); DownloadListenerHostApi.setup( binaryMessenger, new DownloadListenerHostApiImpl( @@ -181,6 +187,7 @@ public void onDetachedFromActivity() { private void updateContext(Context context) { webViewHostApi.setContext(context); javaScriptChannelHostApi.setPlatformThreadHandler(new Handler(context.getMainLooper())); + webChromeClientHostApi.setContext(context); } /** Maintains instances used to communicate with the corresponding objects in Dart. */ diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java index 9a97cd6f37a6..fff9906cd462 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java @@ -5,6 +5,7 @@ package io.flutter.plugins.webviewflutter; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -13,8 +14,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Activity; +import android.content.Context; import android.net.Uri; import android.os.Message; +import android.view.Window; import android.webkit.PermissionRequest; import android.webkit.WebResourceRequest; import android.webkit.WebView; @@ -41,6 +45,12 @@ public class WebChromeClientTest { @Mock public WebViewClient mockWebViewClient; + @Mock public Context mockContext; + + @Mock public Activity mockActivity; + + @Mock public Window mockWindow; + InstanceManager instanceManager; WebChromeClientHostApiImpl hostApiImpl; WebChromeClientImpl webChromeClient; @@ -54,15 +64,20 @@ public void setUp() { @Override @NonNull public WebChromeClientImpl createWebChromeClient( - @NonNull WebChromeClientFlutterApiImpl flutterApi) { - webChromeClient = super.createWebChromeClient(flutterApi); + @NonNull WebChromeClientFlutterApiImpl flutterApi, @NonNull Context context) { + webChromeClient = super.createWebChromeClient(flutterApi, context); return webChromeClient; } }; hostApiImpl = - new WebChromeClientHostApiImpl(instanceManager, webChromeClientCreator, mockFlutterApi); + new WebChromeClientHostApiImpl(instanceManager, webChromeClientCreator, mockFlutterApi, mockContext); + hostApiImpl.setContext(mockActivity); hostApiImpl.create(2L); + + when(mockActivity.getWindow()).thenReturn(mockWindow); + when(mockWebView.getParent()).thenReturn(mockWebView); + when(mockWindow.getDecorView()).thenReturn(mockWebView); } @After @@ -124,5 +139,18 @@ public void onPermissionRequest() { webChromeClient.onPermissionRequest(mockRequest); verify(mockFlutterApi).onPermissionRequest(eq(webChromeClient), eq(mockRequest), any()); + } + + @Test + public void onShowCustomView() { + webChromeClient.onShowCustomView(mockWebView, null); + assertNotNull(webChromeClient.getmFullscreenView()); + } + + @Test + public void onHideCustomView() { + webChromeClient.onShowCustomView(mockWebView, null); + webChromeClient.onHideCustomView(); + assertNull(webChromeClient.getmFullscreenView()); } } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index d9053493cd93..b674a5a6b645 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.6.3 +version: 3.7.0 environment: sdk: ">=2.18.0 <4.0.0"