Skip to content

Commit

Permalink
Added fullscreen capabilities to the android webview
Browse files Browse the repository at this point in the history
  • Loading branch information
paulppn committed May 2, 2023
1 parent b017367 commit 5498f0b
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 13 deletions.
4 changes: 4 additions & 0 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.7.0

* Adds support for fullscreen video.

## 3.6.3

* Updates gradle, AGP and fixes some lint errors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,28 +39,94 @@ 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.
*/
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
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
Expand Down Expand Up @@ -87,6 +163,11 @@ public void onPermissionRequest(@NonNull PermissionRequest request) {
public void setReturnValueForOnShowFileChooser(boolean value) {
returnValueForOnShowFileChooser = value;
}

@VisibleForTesting
public View getmFullscreenView() {
return this.mFullscreenView;
}
}

/**
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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(
Expand Down Expand Up @@ -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. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 5498f0b

Please sign in to comment.