Skip to content

Commit

Permalink
[webview_flutter_android] Adds a WebViewFlutterApi (flutter#3324)
Browse files Browse the repository at this point in the history
[webview_flutter_android] Adds a WebViewFlutterApi
  • Loading branch information
bparrishMines authored Mar 8, 2023
1 parent 9bfef95 commit a4d3d16
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 56 deletions.
5 changes: 5 additions & 0 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 3.4.1

* Fixes a potential bug where a `WebView` that was not added to the `InstanceManager` could be
returned by a `WebViewClient` or `WebChromeClient`.

## 3.4.0

* Adds support to set text zoom of a page. See `AndroidWebViewController.setTextZoom`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,16 @@ private static GeneratedAndroidWebView.FileChooserModeEnumData toFileChooserEnum
/**
* Stores the FileChooserParams instance and notifies Dart to create a new FileChooserParams
* instance that is attached to this one.
*
* @return the instanceId of the stored instance
*/
public long create(WebChromeClient.FileChooserParams instance, Reply<Void> callback) {
final long instanceId = instanceManager.addHostCreatedInstance(instance);
create(
instanceId,
instance.isCaptureEnabled(),
Arrays.asList(instance.getAcceptTypes()),
toFileChooserEnumData(instance.getMode()),
instance.getFilenameHint(),
callback);
return instanceId;
public void create(WebChromeClient.FileChooserParams instance, Reply<Void> callback) {
if (!instanceManager.containsInstance(instance)) {
create(
instanceManager.addHostCreatedInstance(instance),
instance.isCaptureEnabled(),
Arrays.asList(instance.getAcceptTypes()),
toFileChooserEnumData(instance.getMode()),
instance.getFilenameHint(),
callback);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1574,6 +1574,42 @@ public void error(Throwable error) {
}
}
}
/**
* Flutter API for `WebView`.
*
* <p>This class may handle instantiating and adding Dart instances that are attached to a native
* instance or receiving callback methods from an overridden native class.
*
* <p>See https://developer.android.com/reference/android/webkit/WebView.
*
* <p>Generated class from Pigeon that represents Flutter messages that can be called from Java.
*/
public static class WebViewFlutterApi {
private final BinaryMessenger binaryMessenger;

public WebViewFlutterApi(BinaryMessenger argBinaryMessenger) {
this.binaryMessenger = argBinaryMessenger;
}

public interface Reply<T> {
void reply(T reply);
}
/** The codec used by WebViewFlutterApi. */
static MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
}
/** Create a new Dart instance and add it to the `InstanceManager`. */
public void create(@NonNull Long identifierArg, Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger, "dev.flutter.pigeon.WebViewFlutterApi.create", getCodec());
channel.send(
new ArrayList<Object>(Collections.singletonList(identifierArg)),
channelReply -> {
callback.reply(null);
});
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface WebSettingsHostApi {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
private final BinaryMessenger binaryMessenger;
private final InstanceManager instanceManager;
private final WebViewFlutterApiImpl webViewFlutterApi;

/**
* Creates a Flutter api that sends messages to Dart.
Expand All @@ -33,15 +34,16 @@ public WebChromeClientFlutterApiImpl(
super(binaryMessenger);
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
webViewFlutterApi = new WebViewFlutterApiImpl(binaryMessenger, instanceManager);
}

/** Passes arguments from {@link WebChromeClient#onProgressChanged} to Dart. */
public void onProgressChanged(
WebChromeClient webChromeClient, WebView webView, Long progress, Reply<Void> callback) {
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
if (webViewIdentifier == null) {
throw new IllegalStateException("Could not find identifier for WebView.");
}
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
super.onProgressChanged(
getIdentifierForClient(webChromeClient), webViewIdentifier, progress, callback);
}
Expand All @@ -53,17 +55,15 @@ public void onShowFileChooser(
WebView webView,
WebChromeClient.FileChooserParams fileChooserParams,
Reply<List<String>> callback) {
Long paramsInstanceId = instanceManager.getIdentifierForStrongReference(fileChooserParams);
if (paramsInstanceId == null) {
final FileChooserParamsFlutterApiImpl flutterApi =
new FileChooserParamsFlutterApiImpl(binaryMessenger, instanceManager);
paramsInstanceId = flutterApi.create(fileChooserParams, reply -> {});
}
webViewFlutterApi.create(webView, reply -> {});

new FileChooserParamsFlutterApiImpl(binaryMessenger, instanceManager)
.create(fileChooserParams, reply -> {});

onShowFileChooser(
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webChromeClient)),
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView)),
paramsInstanceId,
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(fileChooserParams)),
callback);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientFlutterApi;
import java.util.HashMap;
import java.util.Objects;

/**
* Flutter Api implementation for {@link WebViewClient}.
*
* <p>Passes arguments of callbacks methods from a {@link WebViewClient} to Dart.
*/
public class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi {
private final BinaryMessenger binaryMessenger;
private final InstanceManager instanceManager;
private final WebViewFlutterApiImpl webViewFlutterApi;

@RequiresApi(api = Build.VERSION_CODES.M)
static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData(
Expand Down Expand Up @@ -71,26 +74,28 @@ static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestDa
public WebViewClientFlutterApiImpl(
BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
super(binaryMessenger);
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
webViewFlutterApi = new WebViewFlutterApiImpl(binaryMessenger, instanceManager);
}

/** Passes arguments from {@link WebViewClient#onPageStarted} to Dart. */
public void onPageStarted(
WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
if (webViewIdentifier == null) {
throw new IllegalStateException("Could not find identifier for WebView.");
}
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
onPageStarted(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
}

/** Passes arguments from {@link WebViewClient#onPageFinished} to Dart. */
public void onPageFinished(
WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
if (webViewIdentifier == null) {
throw new IllegalStateException("Could not find identifier for WebView.");
}
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
onPageFinished(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
}

Expand All @@ -105,10 +110,10 @@ public void onReceivedRequestError(
WebResourceRequest request,
WebResourceError error,
Reply<Void> callback) {
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
if (webViewIdentifier == null) {
throw new IllegalStateException("Could not find identifier for WebView.");
}
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
onReceivedRequestError(
getIdentifierForClient(webViewClient),
webViewIdentifier,
Expand All @@ -128,10 +133,10 @@ public void onReceivedRequestError(
WebResourceRequest request,
WebResourceErrorCompat error,
Reply<Void> callback) {
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
if (webViewIdentifier == null) {
throw new IllegalStateException("Could not find identifier for WebView.");
}
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
onReceivedRequestError(
getIdentifierForClient(webViewClient),
webViewIdentifier,
Expand All @@ -151,10 +156,10 @@ public void onReceivedError(
String descriptionArg,
String failingUrlArg,
Reply<Void> callback) {
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
if (webViewIdentifier == null) {
throw new IllegalStateException("Could not find identifier for WebView.");
}
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
onReceivedError(
getIdentifierForClient(webViewClient),
webViewIdentifier,
Expand All @@ -174,10 +179,10 @@ public void requestLoading(
WebView webView,
WebResourceRequest request,
Reply<Void> callback) {
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
if (webViewIdentifier == null) {
throw new IllegalStateException("Could not find identifier for WebView.");
}
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
requestLoading(
getIdentifierForClient(webViewClient),
webViewIdentifier,
Expand All @@ -190,10 +195,10 @@ public void requestLoading(
*/
public void urlLoading(
WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
if (webViewIdentifier == null) {
throw new IllegalStateException("Could not find identifier for WebView.");
}
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
urlLoading(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// 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.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewFlutterApi;

/**
* Flutter API implementation for `WebView`.
*
* <p>This class may handle adding native instances that are attached to a Dart instance or passing
* arguments of callbacks methods to a Dart instance.
*/
public class WebViewFlutterApiImpl {
// To ease adding additional methods, this value is added prematurely.
@SuppressWarnings({"unused", "FieldCanBeLocal"})
private final BinaryMessenger binaryMessenger;

private final InstanceManager instanceManager;
private WebViewFlutterApi api;

/**
* Constructs a {@link WebViewFlutterApiImpl}.
*
* @param binaryMessenger used to communicate with Dart over asynchronous messages
* @param instanceManager maintains instances stored to communicate with attached Dart objects
*/
public WebViewFlutterApiImpl(
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
this.binaryMessenger = binaryMessenger;
this.instanceManager = instanceManager;
api = new WebViewFlutterApi(binaryMessenger);
}

/**
* Stores the `WebView` instance and notifies Dart to create and store a new `WebView` instance
* that is attached to this one. If `instance` has already been added, this method does nothing.
*/
public void create(@NonNull WebView instance, @NonNull WebViewFlutterApi.Reply<Void> callback) {
if (!instanceManager.containsInstance(instance)) {
api.create(instanceManager.addHostCreatedInstance(instance), callback);
}
}

/**
* Sets the Flutter API used to send messages to Dart.
*
* <p>This is only visible for testing.
*/
@VisibleForTesting
void setApi(@NonNull WebViewFlutterApi api) {
this.api = api;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ public class WebChromeClientTest {
public void setUp() {
instanceManager = InstanceManager.open(identifier -> {});

instanceManager.addDartCreatedInstance(mockWebView, 0L);

final WebChromeClientCreator webChromeClientCreator =
new WebChromeClientCreator() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ public class WebViewClientTest {
public void setUp() {
instanceManager = InstanceManager.open(identifier -> {});

instanceManager.addDartCreatedInstance(mockWebView, 0L);

final WebViewClientCreator webViewClientCreator =
new WebViewClientCreator() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
Expand All @@ -18,6 +19,7 @@
import android.webkit.WebChromeClient;
import android.webkit.WebViewClient;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewFlutterApi;
import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.WebViewPlatformView;
import java.util.HashMap;
import java.util.Objects;
Expand Down Expand Up @@ -314,4 +316,23 @@ public void destroy() {

assertTrue(destroyCalled[0]);
}

@Test
public void flutterApiCreate() {
final InstanceManager instanceManager = InstanceManager.open(identifier -> {});

final WebViewFlutterApiImpl flutterApiImpl =
new WebViewFlutterApiImpl(mockBinaryMessenger, instanceManager);

final WebViewFlutterApi mockFlutterApi = mock(WebViewFlutterApi.class);
flutterApiImpl.setApi(mockFlutterApi);

flutterApiImpl.create(mockWebView, reply -> {});

final long instanceIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(mockWebView));
verify(mockFlutterApi).create(eq(instanceIdentifier), any());

instanceManager.close();
}
}
Loading

0 comments on commit a4d3d16

Please sign in to comment.