blob: 77d535b78aed2d0601c422e15c654760475d442e [file] [log] [blame]
// 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.
package io.flutter.plugins.webviewflutter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.view.View;
import android.webkit.DownloadListener;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewHostApi;
import java.util.Map;
import java.util.Objects;
/**
* Host api implementation for {@link WebView}.
*
* <p>Handles creating {@link WebView}s that intercommunicate with a paired Dart object.
*/
public class WebViewHostApiImpl implements WebViewHostApi {
private final InstanceManager instanceManager;
private final WebViewProxy webViewProxy;
// Only used with WebView using virtual displays.
@Nullable private final View containerView;
private final BinaryMessenger binaryMessenger;
private Context context;
/** Handles creating and calling static methods for {@link WebView}s. */
public static class WebViewProxy {
/**
* Creates a {@link WebViewPlatformView}.
*
* @param context an Activity Context to access application assets
* @param binaryMessenger used to communicate with Dart over asynchronous messages
* @param instanceManager mangages instances used to communicate with the corresponding objects
* in Dart
* @return the created {@link WebViewPlatformView}
*/
public WebViewPlatformView createWebView(
Context context, BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
return new WebViewPlatformView(context, binaryMessenger, instanceManager);
}
/**
* Creates a {@link InputAwareWebViewPlatformView}.
*
* @param context an Activity Context to access application assets
* @param containerView parent View of the WebView
* @return the created {@link InputAwareWebViewPlatformView}
*/
public InputAwareWebViewPlatformView createInputAwareWebView(
Context context,
BinaryMessenger binaryMessenger,
InstanceManager instanceManager,
@Nullable View containerView) {
return new InputAwareWebViewPlatformView(
context, binaryMessenger, instanceManager, containerView);
}
/**
* Forwards call to {@link WebView#setWebContentsDebuggingEnabled}.
*
* @param enabled whether debugging should be enabled
*/
public void setWebContentsDebuggingEnabled(boolean enabled) {
WebView.setWebContentsDebuggingEnabled(enabled);
}
}
/** Implementation of {@link WebView} that can be used as a Flutter {@link PlatformView}s. */
public static class WebViewPlatformView extends WebView implements PlatformView {
private WebViewClient currentWebViewClient;
private WebChromeClientHostApiImpl.SecureWebChromeClient currentWebChromeClient;
/**
* Creates a {@link WebViewPlatformView}.
*
* @param context an Activity Context to access application assets. This value cannot be null.
*/
public WebViewPlatformView(
Context context, BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
super(context);
currentWebViewClient = new WebViewClient();
currentWebChromeClient = new WebChromeClientHostApiImpl.SecureWebChromeClient();
setWebViewClient(currentWebViewClient);
setWebChromeClient(currentWebChromeClient);
}
@Override
public View getView() {
return this;
}
@Override
public void dispose() {}
@Override
public void setWebViewClient(WebViewClient webViewClient) {
super.setWebViewClient(webViewClient);
currentWebViewClient = webViewClient;
currentWebChromeClient.setWebViewClient(webViewClient);
}
@Override
public void setWebChromeClient(WebChromeClient client) {
super.setWebChromeClient(client);
if (!(client instanceof WebChromeClientHostApiImpl.SecureWebChromeClient)) {
throw new AssertionError("Client must be a SecureWebChromeClient.");
}
currentWebChromeClient = (WebChromeClientHostApiImpl.SecureWebChromeClient) client;
currentWebChromeClient.setWebViewClient(currentWebViewClient);
}
// When running unit tests, the parent `WebView` class is replaced by a stub that returns null
// for every method. This is overridden so that this returns the current WebChromeClient during
// unit tests. This should only remain overridden as long as `setWebChromeClient` is overridden.
@Nullable
@Override
public WebChromeClient getWebChromeClient() {
return currentWebChromeClient;
}
}
/**
* Implementation of {@link InputAwareWebView} that can be used as a Flutter {@link
* PlatformView}s.
*/
@SuppressLint("ViewConstructor")
public static class InputAwareWebViewPlatformView extends InputAwareWebView
implements PlatformView {
private WebViewClient currentWebViewClient;
private WebChromeClientHostApiImpl.SecureWebChromeClient currentWebChromeClient;
/**
* Creates a {@link InputAwareWebViewPlatformView}.
*
* @param context an Activity Context to access application assets. This value cannot be null.
*/
public InputAwareWebViewPlatformView(
Context context,
BinaryMessenger binaryMessenger,
InstanceManager instanceManager,
View containerView) {
super(context, containerView);
currentWebViewClient = new WebViewClient();
currentWebChromeClient = new WebChromeClientHostApiImpl.SecureWebChromeClient();
setWebViewClient(currentWebViewClient);
setWebChromeClient(currentWebChromeClient);
}
@Override
public View getView() {
return this;
}
@Override
public void onFlutterViewAttached(@NonNull View flutterView) {
setContainerView(flutterView);
}
@Override
public void onFlutterViewDetached() {
setContainerView(null);
}
@Override
public void dispose() {
super.dispose();
destroy();
}
@Override
public void onInputConnectionLocked() {
lockInputConnection();
}
@Override
public void onInputConnectionUnlocked() {
unlockInputConnection();
}
@Override
public void setWebViewClient(WebViewClient webViewClient) {
super.setWebViewClient(webViewClient);
currentWebViewClient = webViewClient;
currentWebChromeClient.setWebViewClient(webViewClient);
}
@Override
public void setWebChromeClient(WebChromeClient client) {
super.setWebChromeClient(client);
if (!(client instanceof WebChromeClientHostApiImpl.SecureWebChromeClient)) {
throw new AssertionError("Client must be a SecureWebChromeClient.");
}
currentWebChromeClient = (WebChromeClientHostApiImpl.SecureWebChromeClient) client;
currentWebChromeClient.setWebViewClient(currentWebViewClient);
}
}
/**
* Creates a host API that handles creating {@link WebView}s and invoking its methods.
*
* @param instanceManager maintains instances stored to communicate with Dart objects
* @param binaryMessenger used to communicate with Dart over asynchronous messages
* @param webViewProxy handles creating {@link WebView}s and calling its static methods
* @param context an Activity Context to access application assets. This value cannot be null.
* @param containerView parent of the webView
*/
public WebViewHostApiImpl(
InstanceManager instanceManager,
BinaryMessenger binaryMessenger,
WebViewProxy webViewProxy,
Context context,
@Nullable View containerView) {
this.instanceManager = instanceManager;
this.binaryMessenger = binaryMessenger;
this.webViewProxy = webViewProxy;
this.context = context;
this.containerView = containerView;
}
/**
* Sets the context to construct {@link WebView}s.
*
* @param context the new context.
*/
public void setContext(Context context) {
this.context = context;
}
@Override
public void create(Long instanceId, Boolean useHybridComposition) {
DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
DisplayManager displayManager =
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
displayListenerProxy.onPreWebViewInitialization(displayManager);
final WebView webView =
useHybridComposition
? webViewProxy.createWebView(context, binaryMessenger, instanceManager)
: webViewProxy.createInputAwareWebView(
context, binaryMessenger, instanceManager, containerView);
displayListenerProxy.onPostWebViewInitialization(displayManager);
instanceManager.addDartCreatedInstance(webView, instanceId);
}
@Override
public void loadData(Long instanceId, String data, String mimeType, String encoding) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.loadData(data, mimeType, encoding);
}
@Override
public void loadDataWithBaseUrl(
Long instanceId,
String baseUrl,
String data,
String mimeType,
String encoding,
String historyUrl) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@Override
public void loadUrl(Long instanceId, String url, Map<String, String> headers) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.loadUrl(url, headers);
}
@Override
public void postUrl(Long instanceId, String url, byte[] data) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.postUrl(url, data);
}
@Override
public String getUrl(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
return webView.getUrl();
}
@Override
public Boolean canGoBack(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
return webView.canGoBack();
}
@Override
public Boolean canGoForward(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
return webView.canGoForward();
}
@Override
public void goBack(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.goBack();
}
@Override
public void goForward(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.goForward();
}
@Override
public void reload(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.reload();
}
@Override
public void clearCache(Long instanceId, Boolean includeDiskFiles) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.clearCache(includeDiskFiles);
}
@Override
public void evaluateJavascript(
Long instanceId, String javascriptString, GeneratedAndroidWebView.Result<String> result) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.evaluateJavascript(javascriptString, result::success);
}
@Override
public String getTitle(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
return webView.getTitle();
}
@Override
public void scrollTo(Long instanceId, Long x, Long y) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.scrollTo(x.intValue(), y.intValue());
}
@Override
public void scrollBy(Long instanceId, Long x, Long y) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.scrollBy(x.intValue(), y.intValue());
}
@Override
public Long getScrollX(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
return (long) webView.getScrollX();
}
@Override
public Long getScrollY(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
return (long) webView.getScrollY();
}
@NonNull
@Override
public GeneratedAndroidWebView.WebViewPoint getScrollPosition(@NonNull Long instanceId) {
final WebView webView = Objects.requireNonNull(instanceManager.getInstance(instanceId));
return new GeneratedAndroidWebView.WebViewPoint.Builder()
.setX((long) webView.getScrollX())
.setY((long) webView.getScrollY())
.build();
}
@Override
public void setWebContentsDebuggingEnabled(Boolean enabled) {
webViewProxy.setWebContentsDebuggingEnabled(enabled);
}
@Override
public void setWebViewClient(Long instanceId, Long webViewClientInstanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.setWebViewClient((WebViewClient) instanceManager.getInstance(webViewClientInstanceId));
}
@Override
public void addJavaScriptChannel(Long instanceId, Long javaScriptChannelInstanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
final JavaScriptChannel javaScriptChannel =
(JavaScriptChannel) instanceManager.getInstance(javaScriptChannelInstanceId);
webView.addJavascriptInterface(javaScriptChannel, javaScriptChannel.javaScriptChannelName);
}
@Override
public void removeJavaScriptChannel(Long instanceId, Long javaScriptChannelInstanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
final JavaScriptChannel javaScriptChannel =
(JavaScriptChannel) instanceManager.getInstance(javaScriptChannelInstanceId);
webView.removeJavascriptInterface(javaScriptChannel.javaScriptChannelName);
}
@Override
public void setDownloadListener(Long instanceId, Long listenerInstanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.setDownloadListener((DownloadListener) instanceManager.getInstance(listenerInstanceId));
}
@Override
public void setWebChromeClient(Long instanceId, Long clientInstanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.setWebChromeClient((WebChromeClient) instanceManager.getInstance(clientInstanceId));
}
@Override
public void setBackgroundColor(Long instanceId, Long color) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.setBackgroundColor(color.intValue());
}
/** Maintains instances used to communicate with the corresponding WebView Dart object. */
public InstanceManager getInstanceManager() {
return instanceManager;
}
}