[webview_flutter_android] Fix bugs in Java Implementation of the Pigeon API (#4459)
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java
new file mode 100644
index 0000000..2dd98c4
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java
@@ -0,0 +1,64 @@
+// 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.DownloadListener;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerFlutterApi;
+
+/**
+ * Flutter Api implementation for {@link DownloadListener}.
+ *
+ * <p>Passes arguments of callbacks methods from a {@link DownloadListener} to Dart.
+ */
+public class DownloadListenerFlutterApiImpl extends DownloadListenerFlutterApi {
+ private final InstanceManager instanceManager;
+
+ /**
+ * Creates a Flutter api that sends messages to Dart.
+ *
+ * @param binaryMessenger handles sending messages to Dart
+ * @param instanceManager maintains instances stored to communicate with Dart objects
+ */
+ public DownloadListenerFlutterApiImpl(
+ BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
+ super(binaryMessenger);
+ this.instanceManager = instanceManager;
+ }
+
+ /** Passes arguments from {@link DownloadListener#onDownloadStart} to Dart. */
+ public void onDownloadStart(
+ DownloadListener downloadListener,
+ String url,
+ String userAgent,
+ String contentDisposition,
+ String mimetype,
+ long contentLength,
+ Reply<Void> callback) {
+ onDownloadStart(
+ instanceManager.getInstanceId(downloadListener),
+ url,
+ userAgent,
+ contentDisposition,
+ mimetype,
+ contentLength,
+ callback);
+ }
+
+ /**
+ * Communicates to Dart that the reference to a {@link DownloadListener} was removed.
+ *
+ * @param downloadListener the instance whose reference will be removed
+ * @param callback reply callback with return value from Dart
+ */
+ public void dispose(DownloadListener downloadListener, Reply<Void> callback) {
+ final Long instanceId = instanceManager.removeInstance(downloadListener);
+ if (instanceId != null) {
+ dispose(instanceId, callback);
+ } else {
+ callback.reply(null);
+ }
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java
index 202be87..9694f39 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java
@@ -5,40 +5,92 @@
package io.flutter.plugins.webviewflutter;
import android.webkit.DownloadListener;
-import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerFlutterApi;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerHostApi;
-class DownloadListenerHostApiImpl implements GeneratedAndroidWebView.DownloadListenerHostApi {
+/**
+ * Host api implementation for {@link DownloadListener}.
+ *
+ * <p>Handles creating {@link DownloadListener}s that intercommunicate with a paired Dart object.
+ */
+public class DownloadListenerHostApiImpl implements DownloadListenerHostApi {
private final InstanceManager instanceManager;
private final DownloadListenerCreator downloadListenerCreator;
- private final GeneratedAndroidWebView.DownloadListenerFlutterApi downloadListenerFlutterApi;
+ private final DownloadListenerFlutterApiImpl flutterApi;
- static class DownloadListenerCreator {
- DownloadListener createDownloadListener(
- Long instanceId, DownloadListenerFlutterApi downloadListenerFlutterApi) {
- return (url, userAgent, contentDisposition, mimetype, contentLength) ->
- downloadListenerFlutterApi.onDownloadStart(
- instanceId, url, userAgent, contentDisposition, mimetype, contentLength, reply -> {});
+ /**
+ * Implementation of {@link DownloadListener} that passes arguments of callback methods to Dart.
+ *
+ * <p>No messages are sent to Dart after {@link DownloadListenerImpl#release} is called.
+ */
+ public static class DownloadListenerImpl implements DownloadListener, Releasable {
+ @Nullable private DownloadListenerFlutterApiImpl flutterApi;
+
+ /**
+ * Creates a {@link DownloadListenerImpl} that passes arguments of callbacks methods to Dart.
+ *
+ * @param flutterApi handles sending messages to Dart
+ */
+ public DownloadListenerImpl(@NonNull DownloadListenerFlutterApiImpl flutterApi) {
+ this.flutterApi = flutterApi;
+ }
+
+ @Override
+ public void onDownloadStart(
+ String url,
+ String userAgent,
+ String contentDisposition,
+ String mimetype,
+ long contentLength) {
+ if (flutterApi != null) {
+ flutterApi.onDownloadStart(
+ this, url, userAgent, contentDisposition, mimetype, contentLength, reply -> {});
+ }
+ }
+
+ @Override
+ public void release() {
+ if (flutterApi != null) {
+ flutterApi.dispose(this, reply -> {});
+ }
+ flutterApi = null;
}
}
- DownloadListenerHostApiImpl(
+ /** Handles creating {@link DownloadListenerImpl}s for a {@link DownloadListenerHostApiImpl}. */
+ public static class DownloadListenerCreator {
+ /**
+ * Creates a {@link DownloadListenerImpl}.
+ *
+ * @param flutterApi handles sending messages to Dart
+ * @return the created {@link DownloadListenerImpl}
+ */
+ public DownloadListenerImpl createDownloadListener(DownloadListenerFlutterApiImpl flutterApi) {
+ return new DownloadListenerImpl(flutterApi);
+ }
+ }
+
+ /**
+ * Creates a host API that handles creating {@link DownloadListener}s.
+ *
+ * @param instanceManager maintains instances stored to communicate with Dart objects
+ * @param downloadListenerCreator handles creating {@link DownloadListenerImpl}s
+ * @param flutterApi handles sending messages to Dart
+ */
+ public DownloadListenerHostApiImpl(
InstanceManager instanceManager,
DownloadListenerCreator downloadListenerCreator,
- DownloadListenerFlutterApi downloadListenerFlutterApi) {
+ DownloadListenerFlutterApiImpl flutterApi) {
this.instanceManager = instanceManager;
this.downloadListenerCreator = downloadListenerCreator;
- this.downloadListenerFlutterApi = downloadListenerFlutterApi;
+ this.flutterApi = flutterApi;
}
@Override
public void create(Long instanceId) {
final DownloadListener downloadListener =
- downloadListenerCreator.createDownloadListener(instanceId, downloadListenerFlutterApi);
+ downloadListenerCreator.createDownloadListener(flutterApi);
instanceManager.addInstance(downloadListener, instanceId);
}
-
- @Override
- public void dispose(Long instanceId) {
- instanceManager.removeInstance(instanceId);
- }
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
index ba2b9b1..a342748 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Autogenerated from Pigeon (v1.0.7), do not edit directly.
+// Autogenerated from Pigeon (v1.0.9), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package io.flutter.plugins.webviewflutter;
@@ -1313,8 +1313,6 @@
public interface JavaScriptChannelHostApi {
void create(Long instanceId, String channelName);
- void dispose(Long instanceId);
-
/** The codec used by JavaScriptChannelHostApi. */
static MessageCodec<Object> getCodec() {
return JavaScriptChannelHostApiCodec.INSTANCE;
@@ -1354,31 +1352,6 @@
channel.setMessageHandler(null);
}
}
- {
- BasicMessageChannel<Object> channel =
- new BasicMessageChannel<>(
- binaryMessenger, "dev.flutter.pigeon.JavaScriptChannelHostApi.dispose", getCodec());
- if (api != null) {
- channel.setMessageHandler(
- (message, reply) -> {
- Map<String, Object> wrapped = new HashMap<>();
- try {
- ArrayList<Object> args = (ArrayList<Object>) message;
- Number instanceIdArg = (Number) args.get(0);
- if (instanceIdArg == null) {
- throw new NullPointerException("instanceIdArg unexpectedly null.");
- }
- api.dispose(instanceIdArg.longValue());
- wrapped.put("result", null);
- } catch (Error | RuntimeException exception) {
- wrapped.put("error", wrapError(exception));
- }
- reply.reply(wrapped);
- });
- } else {
- channel.setMessageHandler(null);
- }
- }
}
}
@@ -1405,6 +1378,19 @@
return JavaScriptChannelFlutterApiCodec.INSTANCE;
}
+ public void dispose(Long instanceIdArg, Reply<Void> callback) {
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose",
+ getCodec());
+ channel.send(
+ new ArrayList<Object>(Arrays.asList(instanceIdArg)),
+ channelReply -> {
+ callback.reply(null);
+ });
+ }
+
public void postMessage(Long instanceIdArg, String messageArg, Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
@@ -1429,8 +1415,6 @@
public interface WebViewClientHostApi {
void create(Long instanceId, Boolean shouldOverrideUrlLoading);
- void dispose(Long instanceId);
-
/** The codec used by WebViewClientHostApi. */
static MessageCodec<Object> getCodec() {
return WebViewClientHostApiCodec.INSTANCE;
@@ -1471,31 +1455,6 @@
channel.setMessageHandler(null);
}
}
- {
- BasicMessageChannel<Object> channel =
- new BasicMessageChannel<>(
- binaryMessenger, "dev.flutter.pigeon.WebViewClientHostApi.dispose", getCodec());
- if (api != null) {
- channel.setMessageHandler(
- (message, reply) -> {
- Map<String, Object> wrapped = new HashMap<>();
- try {
- ArrayList<Object> args = (ArrayList<Object>) message;
- Number instanceIdArg = (Number) args.get(0);
- if (instanceIdArg == null) {
- throw new NullPointerException("instanceIdArg unexpectedly null.");
- }
- api.dispose(instanceIdArg.longValue());
- wrapped.put("result", null);
- } catch (Error | RuntimeException exception) {
- wrapped.put("error", wrapError(exception));
- }
- reply.reply(wrapped);
- });
- } else {
- channel.setMessageHandler(null);
- }
- }
}
}
@@ -1513,9 +1472,6 @@
case (byte) 129:
return WebResourceRequestData.fromMap((Map<String, Object>) readValue(buffer));
- case (byte) 130:
- return WebResourceRequestData.fromMap((Map<String, Object>) readValue(buffer));
-
default:
return super.readValueOfType(type, buffer);
}
@@ -1529,9 +1485,6 @@
} else if (value instanceof WebResourceRequestData) {
stream.write(129);
writeValue(stream, ((WebResourceRequestData) value).toMap());
- } else if (value instanceof WebResourceRequestData) {
- stream.write(130);
- writeValue(stream, ((WebResourceRequestData) value).toMap());
} else {
super.writeValue(stream, value);
}
@@ -1554,6 +1507,17 @@
return WebViewClientFlutterApiCodec.INSTANCE;
}
+ public void dispose(Long instanceIdArg, Reply<Void> callback) {
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.dispose", getCodec());
+ channel.send(
+ new ArrayList<Object>(Arrays.asList(instanceIdArg)),
+ channelReply -> {
+ callback.reply(null);
+ });
+ }
+
public void onPageStarted(
Long instanceIdArg, Long webViewInstanceIdArg, String urlArg, Reply<Void> callback) {
BasicMessageChannel<Object> channel =
@@ -1666,8 +1630,6 @@
public interface DownloadListenerHostApi {
void create(Long instanceId);
- void dispose(Long instanceId);
-
/** The codec used by DownloadListenerHostApi. */
static MessageCodec<Object> getCodec() {
return DownloadListenerHostApiCodec.INSTANCE;
@@ -1703,31 +1665,6 @@
channel.setMessageHandler(null);
}
}
- {
- BasicMessageChannel<Object> channel =
- new BasicMessageChannel<>(
- binaryMessenger, "dev.flutter.pigeon.DownloadListenerHostApi.dispose", getCodec());
- if (api != null) {
- channel.setMessageHandler(
- (message, reply) -> {
- Map<String, Object> wrapped = new HashMap<>();
- try {
- ArrayList<Object> args = (ArrayList<Object>) message;
- Number instanceIdArg = (Number) args.get(0);
- if (instanceIdArg == null) {
- throw new NullPointerException("instanceIdArg unexpectedly null.");
- }
- api.dispose(instanceIdArg.longValue());
- wrapped.put("result", null);
- } catch (Error | RuntimeException exception) {
- wrapped.put("error", wrapError(exception));
- }
- reply.reply(wrapped);
- });
- } else {
- channel.setMessageHandler(null);
- }
- }
}
}
@@ -1754,6 +1691,17 @@
return DownloadListenerFlutterApiCodec.INSTANCE;
}
+ public void dispose(Long instanceIdArg, Reply<Void> callback) {
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger, "dev.flutter.pigeon.DownloadListenerFlutterApi.dispose", getCodec());
+ channel.send(
+ new ArrayList<Object>(Arrays.asList(instanceIdArg)),
+ channelReply -> {
+ callback.reply(null);
+ });
+ }
+
public void onDownloadStart(
Long instanceIdArg,
String urlArg,
@@ -1792,8 +1740,6 @@
public interface WebChromeClientHostApi {
void create(Long instanceId, Long webViewClientInstanceId);
- void dispose(Long instanceId);
-
/** The codec used by WebChromeClientHostApi. */
static MessageCodec<Object> getCodec() {
return WebChromeClientHostApiCodec.INSTANCE;
@@ -1833,31 +1779,6 @@
channel.setMessageHandler(null);
}
}
- {
- BasicMessageChannel<Object> channel =
- new BasicMessageChannel<>(
- binaryMessenger, "dev.flutter.pigeon.WebChromeClientHostApi.dispose", getCodec());
- if (api != null) {
- channel.setMessageHandler(
- (message, reply) -> {
- Map<String, Object> wrapped = new HashMap<>();
- try {
- ArrayList<Object> args = (ArrayList<Object>) message;
- Number instanceIdArg = (Number) args.get(0);
- if (instanceIdArg == null) {
- throw new NullPointerException("instanceIdArg unexpectedly null.");
- }
- api.dispose(instanceIdArg.longValue());
- wrapped.put("result", null);
- } catch (Error | RuntimeException exception) {
- wrapped.put("error", wrapError(exception));
- }
- reply.reply(wrapped);
- });
- } else {
- channel.setMessageHandler(null);
- }
- }
}
}
@@ -1884,6 +1805,17 @@
return WebChromeClientFlutterApiCodec.INSTANCE;
}
+ public void dispose(Long instanceIdArg, Reply<Void> callback) {
+ BasicMessageChannel<Object> channel =
+ new BasicMessageChannel<>(
+ binaryMessenger, "dev.flutter.pigeon.WebChromeClientFlutterApi.dispose", getCodec());
+ channel.send(
+ new ArrayList<Object>(Arrays.asList(instanceIdArg)),
+ channelReply -> {
+ callback.reply(null);
+ });
+ }
+
public void onProgressChanged(
Long instanceIdArg, Long webViewInstanceIdArg, Long progressArg, Reply<Void> callback) {
BasicMessageChannel<Object> channel =
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java
index bfa7d6f..a368baf 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java
@@ -8,32 +8,77 @@
import java.util.HashMap;
import java.util.Map;
-class InstanceManager {
+/**
+ * Maintains instances to intercommunicate with Dart objects.
+ *
+ * <p>When an instance is added with an instanceId, either can be used to retrieve the other.
+ */
+public class InstanceManager {
private final LongSparseArray<Object> instanceIdsToInstances = new LongSparseArray<>();
private final Map<Object, Long> instancesToInstanceIds = new HashMap<>();
- /** Add a new instance with instanceId. */
- void addInstance(Object instance, long instanceId) {
+ /**
+ * Add a new instance to the manager.
+ *
+ * <p>If an instance or instanceId has already been added, it will be replaced by the new values.
+ *
+ * @param instance the new object to be added
+ * @param instanceId unique id of the added object
+ */
+ public void addInstance(Object instance, long instanceId) {
instancesToInstanceIds.put(instance, instanceId);
instanceIdsToInstances.append(instanceId, instance);
}
- /** Remove the instance from the manager. */
- void removeInstance(long instanceId) {
+ /**
+ * Remove the instance with instanceId from the manager.
+ *
+ * @param instanceId the id of the instance to be removed
+ * @return the removed instance if the manager contains the instanceId, otherwise null
+ */
+ public Object removeInstanceWithId(long instanceId) {
final Object instance = instanceIdsToInstances.get(instanceId);
if (instance != null) {
instanceIdsToInstances.remove(instanceId);
instancesToInstanceIds.remove(instance);
}
+ return instance;
}
- /** Retrieve the Object paired with instanceId. */
- Object getInstance(long instanceId) {
+ /**
+ * Remove the instance from the manager.
+ *
+ * @param instance the instance to be removed
+ * @return the instanceId of the removed instance if the manager contains the value, otherwise
+ * null
+ */
+ public Long removeInstance(Object instance) {
+ final Long instanceId = instancesToInstanceIds.get(instance);
+ if (instanceId != null) {
+ instanceIdsToInstances.remove(instanceId);
+ instancesToInstanceIds.remove(instance);
+ }
+ return instanceId;
+ }
+
+ /**
+ * Retrieve the Object paired with instanceId.
+ *
+ * @param instanceId the instanceId of the desired instance
+ * @return the instance stored with the instanceId if the manager contains the value, otherwise
+ * null
+ */
+ public Object getInstance(long instanceId) {
return instanceIdsToInstances.get(instanceId);
}
- /** Retrieve the instanceId paired with instance. */
- Long getInstanceId(Object instance) {
+ /**
+ * Retrieve the instanceId paired with an instance.
+ *
+ * @param instance the value paired with the desired instanceId
+ * @return the instanceId paired with instance if the manager contains the value, otherwise null
+ */
+ public Long getInstanceId(Object instance) {
return instancesToInstanceIds.get(instance);
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java
index 2f987c0..96ae3ce 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java
@@ -7,6 +7,8 @@
import android.os.Handler;
import android.os.Looper;
import android.webkit.JavascriptInterface;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import io.flutter.plugin.common.MethodChannel;
import java.util.HashMap;
@@ -14,13 +16,16 @@
* Added as a JavaScript interface to the WebView for any JavaScript channel that the Dart code sets
* up.
*
- * <p>Exposes a single method named `postMessage` to JavaScript, which sends a message over a method
- * channel to the Dart code.
+ * <p>Exposes a single method named `postMessage` to JavaScript, which sends a message to the Dart
+ * code.
+ *
+ * <p>No messages are sent to Dart after {@link JavaScriptChannel#release} is called.
*/
-class JavaScriptChannel {
+public class JavaScriptChannel implements Releasable {
private final MethodChannel methodChannel;
- final String javaScriptChannelName;
private final Handler platformThreadHandler;
+ final String javaScriptChannelName;
+ @Nullable private JavaScriptChannelFlutterApiImpl flutterApi;
/**
* @param methodChannel the Flutter WebView method channel to which JS messages are sent
@@ -35,24 +40,58 @@
this.platformThreadHandler = platformThreadHandler;
}
+ /**
+ * Creates a {@link JavaScriptChannel} that passes arguments of callback methods to Dart.
+ *
+ * @param flutterApi the Flutter Api to which JS messages are sent
+ * @param channelName JavaScript channel the message was sent through
+ * @param platformThreadHandler handles making callbacks on the desired thread
+ */
+ public JavaScriptChannel(
+ @NonNull JavaScriptChannelFlutterApiImpl flutterApi,
+ String channelName,
+ Handler platformThreadHandler) {
+ this.flutterApi = flutterApi;
+ this.javaScriptChannelName = channelName;
+ this.platformThreadHandler = platformThreadHandler;
+ methodChannel = null;
+ }
+
// Suppressing unused warning as this is invoked from JavaScript.
@SuppressWarnings("unused")
@JavascriptInterface
public void postMessage(final String message) {
- Runnable postMessageRunnable =
- new Runnable() {
- @Override
- public void run() {
- HashMap<String, String> arguments = new HashMap<>();
- arguments.put("channel", javaScriptChannelName);
- arguments.put("message", message);
- methodChannel.invokeMethod("javascriptChannelMessage", arguments);
- }
- };
- if (platformThreadHandler.getLooper() == Looper.myLooper()) {
- postMessageRunnable.run();
- } else {
- platformThreadHandler.post(postMessageRunnable);
+ Runnable postMessageRunnable = null;
+
+ if (flutterApi != null) {
+ postMessageRunnable = () -> flutterApi.postMessage(this, message, reply -> {});
+ } else if (methodChannel != null) {
+ postMessageRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ HashMap<String, String> arguments = new HashMap<>();
+ arguments.put("channel", javaScriptChannelName);
+ arguments.put("message", message);
+ methodChannel.invokeMethod("javascriptChannelMessage", arguments);
+ }
+ };
}
+
+ if (postMessageRunnable != null) {
+ if (platformThreadHandler.getLooper() == Looper.myLooper()) {
+ postMessageRunnable.run();
+ } else {
+ platformThreadHandler.post(postMessageRunnable);
+ }
+ }
+ }
+
+ @Override
+ public void release() {
+ if (flutterApi != null) {
+ flutterApi.dispose(this, reply -> {});
+ }
+ flutterApi = null;
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java
new file mode 100644
index 0000000..120f66d
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java
@@ -0,0 +1,50 @@
+// 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 io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelFlutterApi;
+
+/**
+ * Flutter Api implementation for {@link JavaScriptChannel}.
+ *
+ * <p>Passes arguments of callbacks methods from a {@link JavaScriptChannel} to Dart.
+ */
+public class JavaScriptChannelFlutterApiImpl extends JavaScriptChannelFlutterApi {
+ private final InstanceManager instanceManager;
+
+ /**
+ * Creates a Flutter api that sends messages to Dart.
+ *
+ * @param binaryMessenger Handles sending messages to Dart.
+ * @param instanceManager Maintains instances stored to communicate with Dart objects.
+ */
+ public JavaScriptChannelFlutterApiImpl(
+ BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
+ super(binaryMessenger);
+ this.instanceManager = instanceManager;
+ }
+
+ /** Passes arguments from {@link JavaScriptChannel#postMessage} to Dart. */
+ public void postMessage(
+ JavaScriptChannel javaScriptChannel, String messageArg, Reply<Void> callback) {
+ super.postMessage(instanceManager.getInstanceId(javaScriptChannel), messageArg, callback);
+ }
+
+ /**
+ * Communicates to Dart that the reference to a {@link JavaScriptChannel} was removed.
+ *
+ * @param javaScriptChannel The instance whose reference will be removed.
+ * @param callback Reply callback with return value from Dart.
+ */
+ public void dispose(JavaScriptChannel javaScriptChannel, Reply<Void> callback) {
+ final Long instanceId = instanceManager.removeInstance(javaScriptChannel);
+ if (instanceId != null) {
+ dispose(instanceId, callback);
+ } else {
+ callback.reply(null);
+ }
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelHostApiImpl.java
index 2d42d95..ac67a81 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelHostApiImpl.java
@@ -5,44 +5,53 @@
package io.flutter.plugins.webviewflutter;
import android.os.Handler;
-import android.os.Looper;
-import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelFlutterApi;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelHostApi;
-class JavaScriptChannelHostApiImpl implements GeneratedAndroidWebView.JavaScriptChannelHostApi {
+/**
+ * Host api implementation for {@link JavaScriptChannel}.
+ *
+ * <p>Handles creating {@link JavaScriptChannel}s that intercommunicate with a paired Dart object.
+ */
+public class JavaScriptChannelHostApiImpl implements JavaScriptChannelHostApi {
private final InstanceManager instanceManager;
private final JavaScriptChannelCreator javaScriptChannelCreator;
- private final JavaScriptChannelFlutterApi javaScriptChannelFlutterApi;
+ private final JavaScriptChannelFlutterApiImpl flutterApi;
private final Handler platformThreadHandler;
- static class JavaScriptChannelCreator {
- JavaScriptChannel createJavaScriptChannel(
- Long instanceId,
- JavaScriptChannelFlutterApi javaScriptChannelFlutterApi,
+ /** Handles creating {@link JavaScriptChannel}s for a {@link JavaScriptChannelHostApiImpl}. */
+ public static class JavaScriptChannelCreator {
+ /**
+ * Creates a {@link JavaScriptChannel}.
+ *
+ * @param flutterApi handles sending messages to Dart
+ * @param channelName JavaScript channel the message should be sent through
+ * @param platformThreadHandler handles making callbacks on the desired thread
+ * @return the created {@link JavaScriptChannel}
+ */
+ public JavaScriptChannel createJavaScriptChannel(
+ JavaScriptChannelFlutterApiImpl flutterApi,
String channelName,
Handler platformThreadHandler) {
- return new JavaScriptChannel(null, channelName, platformThreadHandler) {
- @Override
- public void postMessage(String message) {
- final Runnable postMessageRunnable =
- () -> javaScriptChannelFlutterApi.postMessage(instanceId, message, reply -> {});
- if (platformThreadHandler.getLooper() == Looper.myLooper()) {
- postMessageRunnable.run();
- } else {
- platformThreadHandler.post(postMessageRunnable);
- }
- }
- };
+ return new JavaScriptChannel(flutterApi, channelName, platformThreadHandler);
}
}
- JavaScriptChannelHostApiImpl(
+ /**
+ * Creates a host API that handles creating {@link JavaScriptChannel}s.
+ *
+ * @param instanceManager maintains instances stored to communicate with Dart objects
+ * @param javaScriptChannelCreator handles creating {@link JavaScriptChannel}s
+ * @param flutterApi handles sending messages to Dart
+ * @param platformThreadHandler handles making callbacks on the desired thread
+ */
+ public JavaScriptChannelHostApiImpl(
InstanceManager instanceManager,
JavaScriptChannelCreator javaScriptChannelCreator,
- JavaScriptChannelFlutterApi javaScriptChannelFlutterApi,
+ JavaScriptChannelFlutterApiImpl flutterApi,
Handler platformThreadHandler) {
this.instanceManager = instanceManager;
this.javaScriptChannelCreator = javaScriptChannelCreator;
- this.javaScriptChannelFlutterApi = javaScriptChannelFlutterApi;
+ this.flutterApi = flutterApi;
this.platformThreadHandler = platformThreadHandler;
}
@@ -50,12 +59,7 @@
public void create(Long instanceId, String channelName) {
final JavaScriptChannel javaScriptChannel =
javaScriptChannelCreator.createJavaScriptChannel(
- instanceId, javaScriptChannelFlutterApi, channelName, platformThreadHandler);
+ flutterApi, channelName, platformThreadHandler);
instanceManager.addInstance(javaScriptChannel, instanceId);
}
-
- @Override
- public void dispose(Long instanceId) {
- instanceManager.removeInstance(instanceId);
- }
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/Releasable.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/Releasable.java
new file mode 100644
index 0000000..9c4ed76
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/Releasable.java
@@ -0,0 +1,14 @@
+// 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;
+
+/**
+ * Represents a resource, or a holder of resources, which may be released once they are no longer
+ * needed.
+ */
+interface Releasable {
+ /** Notify that that the reference to an object will be removed by a holder. */
+ void release();
+}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java
new file mode 100644
index 0000000..2ab9275
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java
@@ -0,0 +1,56 @@
+// 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.WebChromeClient;
+import android.webkit.WebView;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientFlutterApi;
+
+/**
+ * Flutter Api implementation for {@link WebChromeClient}.
+ *
+ * <p>Passes arguments of callbacks methods from a {@link WebChromeClient} to Dart.
+ */
+public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
+ private final InstanceManager instanceManager;
+
+ /**
+ * Creates a Flutter api that sends messages to Dart.
+ *
+ * @param binaryMessenger handles sending messages to Dart
+ * @param instanceManager maintains instances stored to communicate with Dart objects
+ */
+ public WebChromeClientFlutterApiImpl(
+ BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
+ super(binaryMessenger);
+ this.instanceManager = instanceManager;
+ }
+
+ /** Passes arguments from {@link WebChromeClient#onProgressChanged} to Dart. */
+ public void onProgressChanged(
+ WebChromeClient webChromeClient, WebView webView, Long progress, Reply<Void> callback) {
+ super.onProgressChanged(
+ instanceManager.getInstanceId(webChromeClient),
+ instanceManager.getInstanceId(webView),
+ progress,
+ callback);
+ }
+
+ /**
+ * Communicates to Dart that the reference to a {@link WebChromeClient}} was removed.
+ *
+ * @param webChromeClient the instance whose reference will be removed
+ * @param callback reply callback with return value from Dart
+ */
+ public void dispose(WebChromeClient webChromeClient, Reply<Void> callback) {
+ final Long instanceId = instanceManager.removeInstance(webChromeClient);
+ if (instanceId != null) {
+ dispose(instanceId, callback);
+ } else {
+ callback.reply(null);
+ }
+ }
+}
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 32f8fcb..f8bc512 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
@@ -11,68 +11,125 @@
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientFlutterApi;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi;
-class WebChromeClientHostApiImpl implements GeneratedAndroidWebView.WebChromeClientHostApi {
+/**
+ * Host api implementation for {@link WebChromeClient}.
+ *
+ * <p>Handles creating {@link WebChromeClient}s that intercommunicate with a paired Dart object.
+ */
+public class WebChromeClientHostApiImpl implements WebChromeClientHostApi {
private final InstanceManager instanceManager;
private final WebChromeClientCreator webChromeClientCreator;
- private final WebChromeClientFlutterApi webChromeClientFlutterApi;
+ private final WebChromeClientFlutterApiImpl flutterApi;
- static class WebChromeClientCreator {
- WebChromeClient createWebChromeClient(
- Long instanceId,
- InstanceManager instanceManager,
- WebViewClient webViewClient,
- WebChromeClientFlutterApi webChromeClientFlutterApi) {
- return new WebChromeClient() {
- // Verifies that a url opened by `Window.open` has a secure url.
- @Override
- public boolean onCreateWindow(
- final WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
- final WebViewClient newWindowWebViewClient =
- new WebViewClient() {
- @RequiresApi(api = Build.VERSION_CODES.N)
- @Override
- public boolean shouldOverrideUrlLoading(
- @NonNull WebView view, @NonNull WebResourceRequest request) {
- webViewClient.shouldOverrideUrlLoading(view, request);
- return true;
- }
+ /**
+ * Implementation of {@link WebChromeClient} that passes arguments of callback methods to Dart.
+ */
+ public static class WebChromeClientImpl extends WebChromeClient implements Releasable {
+ @Nullable private WebChromeClientFlutterApiImpl flutterApi;
+ private WebViewClient webViewClient;
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- webViewClient.shouldOverrideUrlLoading(view, url);
- return true;
- }
- };
+ /**
+ * Creates a {@link WebChromeClient} that passes arguments of callbacks methods to Dart.
+ *
+ * @param flutterApi handles sending messages to Dart
+ * @param webViewClient receives forwarded calls from {@link WebChromeClient#onCreateWindow}
+ */
+ public WebChromeClientImpl(
+ @NonNull WebChromeClientFlutterApiImpl flutterApi, WebViewClient webViewClient) {
+ this.flutterApi = flutterApi;
+ this.webViewClient = webViewClient;
+ }
- final WebView newWebView = new WebView(view.getContext());
- newWebView.setWebViewClient(newWindowWebViewClient);
+ // Verifies that a url opened by `Window.open` has a secure url.
+ @Override
+ public boolean onCreateWindow(
+ final WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
+ final WebViewClient newWindowWebViewClient =
+ new WebViewClient() {
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ @Override
+ public boolean shouldOverrideUrlLoading(
+ @NonNull WebView view, @NonNull WebResourceRequest request) {
+ webViewClient.shouldOverrideUrlLoading(view, request);
+ return true;
+ }
- final WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
- transport.setWebView(newWebView);
- resultMsg.sendToTarget();
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ webViewClient.shouldOverrideUrlLoading(view, url);
+ return true;
+ }
+ };
- return true;
- }
+ final WebView newWebView = new WebView(view.getContext());
+ newWebView.setWebViewClient(newWindowWebViewClient);
- @Override
- public void onProgressChanged(WebView view, int progress) {
- webChromeClientFlutterApi.onProgressChanged(
- instanceId, instanceManager.getInstanceId(view), (long) progress, reply -> {});
- }
- };
+ final WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
+ transport.setWebView(newWebView);
+ resultMsg.sendToTarget();
+
+ return true;
+ }
+
+ @Override
+ public void onProgressChanged(WebView view, int progress) {
+ if (flutterApi != null) {
+ flutterApi.onProgressChanged(this, view, (long) progress, reply -> {});
+ }
+ }
+
+ /**
+ * Set the {@link WebViewClient} that calls to {@link WebChromeClient#onCreateWindow} are passed
+ * to.
+ *
+ * @param webViewClient the forwarding {@link WebViewClient}
+ */
+ public void setWebViewClient(WebViewClient webViewClient) {
+ this.webViewClient = webViewClient;
+ }
+
+ @Override
+ public void release() {
+ if (flutterApi != null) {
+ flutterApi.dispose(this, reply -> {});
+ }
+ flutterApi = null;
}
}
- WebChromeClientHostApiImpl(
+ /** Handles creating {@link WebChromeClient}s for a {@link WebChromeClientHostApiImpl}. */
+ public static class WebChromeClientCreator {
+ /**
+ * Creates a {@link DownloadListenerHostApiImpl.DownloadListenerImpl}.
+ *
+ * @param flutterApi handles sending messages to Dart
+ * @param webViewClient receives forwarded calls from {@link WebChromeClient#onCreateWindow}
+ * @return the created {@link DownloadListenerHostApiImpl.DownloadListenerImpl}
+ */
+ public WebChromeClientImpl createWebChromeClient(
+ WebChromeClientFlutterApiImpl flutterApi, WebViewClient webViewClient) {
+ return new WebChromeClientImpl(flutterApi, webViewClient);
+ }
+ }
+
+ /**
+ * Creates a host API that handles creating {@link WebChromeClient}s.
+ *
+ * @param instanceManager maintains instances stored to communicate with Dart objects
+ * @param webChromeClientCreator handles creating {@link WebChromeClient}s
+ * @param flutterApi handles sending messages to Dart
+ */
+ public WebChromeClientHostApiImpl(
InstanceManager instanceManager,
WebChromeClientCreator webChromeClientCreator,
- WebChromeClientFlutterApi webChromeClientFlutterApi) {
+ WebChromeClientFlutterApiImpl flutterApi) {
this.instanceManager = instanceManager;
this.webChromeClientCreator = webChromeClientCreator;
- this.webChromeClientFlutterApi = webChromeClientFlutterApi;
+ this.flutterApi = flutterApi;
}
@Override
@@ -80,13 +137,7 @@
final WebViewClient webViewClient =
(WebViewClient) instanceManager.getInstance(webViewClientInstanceId);
final WebChromeClient webChromeClient =
- webChromeClientCreator.createWebChromeClient(
- instanceId, instanceManager, webViewClient, webChromeClientFlutterApi);
+ webChromeClientCreator.createWebChromeClient(flutterApi, webViewClient);
instanceManager.addInstance(webChromeClient, instanceId);
}
-
- @Override
- public void dispose(Long instanceId) {
- instanceManager.removeInstance(instanceId);
- }
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
index e70a867..239ef47 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
@@ -6,18 +6,38 @@
import android.webkit.WebSettings;
import android.webkit.WebView;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebSettingsHostApi;
-class WebSettingsHostApiImpl implements GeneratedAndroidWebView.WebSettingsHostApi {
+/**
+ * Host api implementation for {@link WebSettings}.
+ *
+ * <p>Handles creating {@link WebSettings}s that intercommunicate with a paired Dart object.
+ */
+public class WebSettingsHostApiImpl implements WebSettingsHostApi {
private final InstanceManager instanceManager;
private final WebSettingsCreator webSettingsCreator;
- static class WebSettingsCreator {
- WebSettings createWebSettings(WebView webView) {
+ /** Handles creating {@link WebSettings} for a {@link WebSettingsHostApiImpl}. */
+ public static class WebSettingsCreator {
+ /**
+ * Creates a {@link WebSettings}.
+ *
+ * @param webView the {@link WebView} which the settings affect
+ * @return the created {@link WebSettings}
+ */
+ public WebSettings createWebSettings(WebView webView) {
return webView.getSettings();
}
}
- WebSettingsHostApiImpl(InstanceManager instanceManager, WebSettingsCreator webSettingsCreator) {
+ /**
+ * Creates a host API that handles creating {@link WebSettings} and invoke its methods.
+ *
+ * @param instanceManager maintains instances stored to communicate with Dart objects
+ * @param webSettingsCreator handles creating {@link WebSettings}s
+ */
+ public WebSettingsHostApiImpl(
+ InstanceManager instanceManager, WebSettingsCreator webSettingsCreator) {
this.instanceManager = instanceManager;
this.webSettingsCreator = webSettingsCreator;
}
@@ -30,7 +50,7 @@
@Override
public void dispose(Long instanceId) {
- instanceManager.removeInstance(instanceId);
+ instanceManager.removeInstanceWithId(instanceId);
}
@Override
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java
new file mode 100644
index 0000000..9e462fa
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java
@@ -0,0 +1,198 @@
+// 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.os.Build;
+import android.webkit.WebResourceError;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import androidx.annotation.RequiresApi;
+import androidx.webkit.WebResourceErrorCompat;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientFlutterApi;
+
+/**
+ * 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 InstanceManager instanceManager;
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData(
+ WebResourceError error) {
+ final GeneratedAndroidWebView.WebResourceErrorData errorData =
+ new GeneratedAndroidWebView.WebResourceErrorData();
+ errorData.setErrorCode((long) error.getErrorCode());
+ errorData.setDescription(error.getDescription().toString());
+
+ return errorData;
+ }
+
+ @SuppressLint("RequiresFeature")
+ static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData(
+ WebResourceErrorCompat error) {
+ final GeneratedAndroidWebView.WebResourceErrorData errorData =
+ new GeneratedAndroidWebView.WebResourceErrorData();
+ errorData.setErrorCode((long) error.getErrorCode());
+ errorData.setDescription(error.getDescription().toString());
+
+ return errorData;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestData(
+ WebResourceRequest request) {
+ final GeneratedAndroidWebView.WebResourceRequestData requestData =
+ new GeneratedAndroidWebView.WebResourceRequestData();
+ requestData.setUrl(request.getUrl().toString());
+ requestData.setIsForMainFrame(request.isForMainFrame());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ requestData.setIsRedirect(request.isRedirect());
+ }
+ requestData.setHasGesture(request.hasGesture());
+ requestData.setMethod(request.getMethod());
+ requestData.setRequestHeaders(request.getRequestHeaders());
+
+ return requestData;
+ }
+
+ /**
+ * Creates a Flutter api that sends messages to Dart.
+ *
+ * @param binaryMessenger handles sending messages to Dart
+ * @param instanceManager maintains instances stored to communicate with Dart objects
+ */
+ public WebViewClientFlutterApiImpl(
+ BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
+ super(binaryMessenger);
+ this.instanceManager = instanceManager;
+ }
+
+ /** Passes arguments from {@link WebViewClient#onPageStarted} to Dart. */
+ public void onPageStarted(
+ WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
+ onPageStarted(
+ instanceManager.getInstanceId(webViewClient),
+ instanceManager.getInstanceId(webView),
+ urlArg,
+ callback);
+ }
+
+ /** Passes arguments from {@link WebViewClient#onPageFinished} to Dart. */
+ public void onPageFinished(
+ WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
+ onPageFinished(
+ instanceManager.getInstanceId(webViewClient),
+ instanceManager.getInstanceId(webView),
+ urlArg,
+ callback);
+ }
+
+ /**
+ * Passes arguments from {@link WebViewClient#onReceivedError(WebView, WebResourceRequest,
+ * WebResourceError)} to Dart.
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void onReceivedRequestError(
+ WebViewClient webViewClient,
+ WebView webView,
+ WebResourceRequest request,
+ WebResourceError error,
+ Reply<Void> callback) {
+ onReceivedRequestError(
+ instanceManager.getInstanceId(webViewClient),
+ instanceManager.getInstanceId(webView),
+ createWebResourceRequestData(request),
+ createWebResourceErrorData(error),
+ callback);
+ }
+
+ /**
+ * Passes arguments from {@link androidx.webkit.WebViewClientCompat#onReceivedError(WebView,
+ * WebResourceRequest, WebResourceError)} to Dart.
+ */
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ public void onReceivedRequestError(
+ WebViewClient webViewClient,
+ WebView webView,
+ WebResourceRequest request,
+ WebResourceErrorCompat error,
+ Reply<Void> callback) {
+ onReceivedRequestError(
+ instanceManager.getInstanceId(webViewClient),
+ instanceManager.getInstanceId(webView),
+ createWebResourceRequestData(request),
+ createWebResourceErrorData(error),
+ callback);
+ }
+
+ /**
+ * Passes arguments from {@link WebViewClient#onReceivedError(WebView, int, String, String)} to
+ * Dart.
+ */
+ public void onReceivedError(
+ WebViewClient webViewClient,
+ WebView webView,
+ Long errorCodeArg,
+ String descriptionArg,
+ String failingUrlArg,
+ Reply<Void> callback) {
+ onReceivedError(
+ instanceManager.getInstanceId(webViewClient),
+ instanceManager.getInstanceId(webView),
+ errorCodeArg,
+ descriptionArg,
+ failingUrlArg,
+ callback);
+ }
+
+ /**
+ * Passes arguments from {@link WebViewClient#shouldOverrideUrlLoading(WebView,
+ * WebResourceRequest)} to Dart.
+ */
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ public void requestLoading(
+ WebViewClient webViewClient,
+ WebView webView,
+ WebResourceRequest request,
+ Reply<Void> callback) {
+ requestLoading(
+ instanceManager.getInstanceId(webViewClient),
+ instanceManager.getInstanceId(webView),
+ createWebResourceRequestData(request),
+ callback);
+ }
+
+ /**
+ * Passes arguments from {@link WebViewClient#shouldOverrideUrlLoading(WebView, String)} to Dart.
+ */
+ public void urlLoading(
+ WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
+ urlLoading(
+ instanceManager.getInstanceId(webViewClient),
+ instanceManager.getInstanceId(webView),
+ urlArg,
+ callback);
+ }
+
+ /**
+ * Communicates to Dart that the reference to a {@link WebViewClient} was removed.
+ *
+ * @param webViewClient the instance whose reference will be removed
+ * @param callback reply callback with return value from Dart
+ */
+ public void dispose(WebViewClient webViewClient, Reply<Void> callback) {
+ final Long instanceId = instanceManager.removeInstance(webViewClient);
+ if (instanceId != null) {
+ dispose(instanceId, callback);
+ } else {
+ callback.reply(null);
+ }
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java
index 4d17eb1..6b659fa 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java
@@ -14,61 +14,200 @@
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebResourceErrorCompat;
import androidx.webkit.WebViewClientCompat;
-import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientFlutterApi;
-class WebViewClientHostApiImpl implements GeneratedAndroidWebView.WebViewClientHostApi {
+/**
+ * Host api implementation for {@link WebViewClient}.
+ *
+ * <p>Handles creating {@link WebViewClient}s that intercommunicate with a paired Dart object.
+ */
+public class WebViewClientHostApiImpl implements GeneratedAndroidWebView.WebViewClientHostApi {
private final InstanceManager instanceManager;
private final WebViewClientCreator webViewClientCreator;
- private final WebViewClientFlutterApi webViewClientFlutterApi;
+ private final WebViewClientFlutterApiImpl flutterApi;
- @RequiresApi(api = Build.VERSION_CODES.M)
- static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData(
- WebResourceError error) {
- final GeneratedAndroidWebView.WebResourceErrorData errorData =
- new GeneratedAndroidWebView.WebResourceErrorData();
- errorData.setErrorCode((long) error.getErrorCode());
- errorData.setDescription(error.getDescription().toString());
+ /**
+ * An interface implemented by a class that extends {@link WebViewClient} and {@link Releasable}.
+ */
+ public interface ReleasableWebViewClient extends Releasable {}
- return errorData;
- }
+ /** Implementation of {@link WebViewClient} that passes arguments of callback methods to Dart. */
+ @RequiresApi(Build.VERSION_CODES.N)
+ public static class WebViewClientImpl extends WebViewClient implements ReleasableWebViewClient {
+ @Nullable private WebViewClientFlutterApiImpl flutterApi;
+ private final boolean shouldOverrideUrlLoading;
- @SuppressLint("RequiresFeature")
- static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData(
- WebResourceErrorCompat error) {
- final GeneratedAndroidWebView.WebResourceErrorData errorData =
- new GeneratedAndroidWebView.WebResourceErrorData();
- errorData.setErrorCode((long) error.getErrorCode());
- errorData.setDescription(error.getDescription().toString());
-
- return errorData;
- }
-
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestData(
- WebResourceRequest request) {
- final GeneratedAndroidWebView.WebResourceRequestData requestData =
- new GeneratedAndroidWebView.WebResourceRequestData();
- requestData.setUrl(request.getUrl().toString());
- requestData.setIsForMainFrame(request.isForMainFrame());
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- requestData.setIsRedirect(request.isRedirect());
+ /**
+ * Creates a {@link WebViewClient} that passes arguments of callbacks methods to Dart.
+ *
+ * @param flutterApi handles sending messages to Dart
+ * @param shouldOverrideUrlLoading whether loading a url should be overridden
+ */
+ public WebViewClientImpl(
+ @NonNull WebViewClientFlutterApiImpl flutterApi, boolean shouldOverrideUrlLoading) {
+ this.shouldOverrideUrlLoading = shouldOverrideUrlLoading;
+ this.flutterApi = flutterApi;
}
- requestData.setHasGesture(request.hasGesture());
- requestData.setMethod(request.getMethod());
- requestData.setRequestHeaders(request.getRequestHeaders());
- return requestData;
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ if (flutterApi != null) {
+ flutterApi.onPageStarted(this, view, url, reply -> {});
+ }
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ if (flutterApi != null) {
+ flutterApi.onPageFinished(this, view, url, reply -> {});
+ }
+ }
+
+ @Override
+ public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
+ if (flutterApi != null) {
+ flutterApi.onReceivedRequestError(this, view, request, error, reply -> {});
+ }
+ }
+
+ @Override
+ public void onReceivedError(
+ WebView view, int errorCode, String description, String failingUrl) {
+ if (flutterApi != null) {
+ flutterApi.onReceivedError(
+ this, view, (long) errorCode, description, failingUrl, reply -> {});
+ }
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+ if (flutterApi != null) {
+ flutterApi.requestLoading(this, view, request, reply -> {});
+ }
+ return shouldOverrideUrlLoading;
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (flutterApi != null) {
+ flutterApi.urlLoading(this, view, url, reply -> {});
+ }
+ return shouldOverrideUrlLoading;
+ }
+
+ @Override
+ public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
+ // Deliberately empty. Occasionally the webview will mark events as having failed to be
+ // handled even though they were handled. We don't want to propagate those as they're not
+ // truly lost.
+ }
+
+ public void release() {
+ if (flutterApi != null) {
+ flutterApi.dispose(this, reply -> {});
+ }
+ flutterApi = null;
+ }
}
- static class WebViewClientCreator {
- WebViewClient createWebViewClient(
- Long instanceId,
- InstanceManager instanceManager,
- Boolean shouldOverrideUrlLoading,
- WebViewClientFlutterApi webViewClientFlutterApi) {
+ /**
+ * Implementation of {@link WebViewClientCompat} that passes arguments of callback methods to
+ * Dart.
+ */
+ public static class WebViewClientCompatImpl extends WebViewClientCompat
+ implements ReleasableWebViewClient {
+ private @Nullable WebViewClientFlutterApiImpl flutterApi;
+ private final boolean shouldOverrideUrlLoading;
+
+ public WebViewClientCompatImpl(
+ @NonNull WebViewClientFlutterApiImpl flutterApi, boolean shouldOverrideUrlLoading) {
+ this.shouldOverrideUrlLoading = shouldOverrideUrlLoading;
+ this.flutterApi = flutterApi;
+ }
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ if (flutterApi != null) {
+ flutterApi.onPageStarted(this, view, url, reply -> {});
+ }
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ if (flutterApi != null) {
+ flutterApi.onPageFinished(this, view, url, reply -> {});
+ }
+ }
+
+ // This method is only called when the WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR feature is
+ // enabled. The deprecated method is called when a device doesn't support this.
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ @SuppressLint("RequiresFeature")
+ @Override
+ public void onReceivedError(
+ @NonNull WebView view,
+ @NonNull WebResourceRequest request,
+ @NonNull WebResourceErrorCompat error) {
+ if (flutterApi != null) {
+ flutterApi.onReceivedRequestError(this, view, request, error, reply -> {});
+ }
+ }
+
+ @Override
+ public void onReceivedError(
+ WebView view, int errorCode, String description, String failingUrl) {
+ if (flutterApi != null) {
+ flutterApi.onReceivedError(
+ this, view, (long) errorCode, description, failingUrl, reply -> {});
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ @Override
+ public boolean shouldOverrideUrlLoading(
+ @NonNull WebView view, @NonNull WebResourceRequest request) {
+ if (flutterApi != null) {
+ flutterApi.requestLoading(this, view, request, reply -> {});
+ }
+ return shouldOverrideUrlLoading;
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (flutterApi != null) {
+ flutterApi.urlLoading(this, view, url, reply -> {});
+ }
+ return shouldOverrideUrlLoading;
+ }
+
+ @Override
+ public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
+ // Deliberately empty. Occasionally the webview will mark events as having failed to be
+ // handled even though they were handled. We don't want to propagate those as they're not
+ // truly lost.
+ }
+
+ public void release() {
+ if (flutterApi != null) {
+ flutterApi.dispose(this, reply -> {});
+ }
+ flutterApi = null;
+ }
+ }
+
+ /** Handles creating {@link WebViewClient}s for a {@link WebViewClientHostApiImpl}. */
+ public static class WebViewClientCreator {
+ /**
+ * Creates a {@link WebViewClient}.
+ *
+ * @param flutterApi handles sending messages to Dart
+ * @return the created {@link WebViewClient}
+ */
+ public WebViewClient createWebViewClient(
+ WebViewClientFlutterApiImpl flutterApi, boolean shouldOverrideUrlLoading) {
// WebViewClientCompat is used to get
// shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
// invoked by the webview on older Android devices, without it pages that use iframes will
@@ -78,162 +217,33 @@
// to bug https://bugs.chromium.org/p/chromium/issues/detail?id=925887. Also, see
// https://github.com/flutter/flutter/issues/29446.
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- return new WebViewClient() {
- @Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {
- webViewClientFlutterApi.onPageStarted(
- instanceId, instanceManager.getInstanceId(view), url, reply -> {});
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- webViewClientFlutterApi.onPageFinished(
- instanceId, instanceManager.getInstanceId(view), url, reply -> {});
- }
-
- @Override
- public void onReceivedError(
- WebView view, WebResourceRequest request, WebResourceError error) {
- webViewClientFlutterApi.onReceivedRequestError(
- instanceId,
- instanceManager.getInstanceId(view),
- createWebResourceRequestData(request),
- createWebResourceErrorData(error),
- reply -> {});
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public void onReceivedError(
- WebView view, int errorCode, String description, String failingUrl) {
- webViewClientFlutterApi.onReceivedError(
- instanceId,
- instanceManager.getInstanceId(view),
- (long) errorCode,
- description,
- failingUrl,
- reply -> {});
- }
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
- webViewClientFlutterApi.requestLoading(
- instanceId,
- instanceManager.getInstanceId(view),
- createWebResourceRequestData(request),
- reply -> {});
- return shouldOverrideUrlLoading;
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- webViewClientFlutterApi.urlLoading(
- instanceId, instanceManager.getInstanceId(view), url, reply -> {});
- return shouldOverrideUrlLoading;
- }
-
- @Override
- public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
- // Deliberately empty. Occasionally the webview will mark events as having failed to be
- // handled even though they were handled. We don't want to propagate those as they're not
- // truly lost.
- }
- };
+ return new WebViewClientImpl(flutterApi, shouldOverrideUrlLoading);
} else {
- return new WebViewClientCompat() {
- @Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {
- webViewClientFlutterApi.onPageStarted(
- instanceId, instanceManager.getInstanceId(view), url, reply -> {});
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- webViewClientFlutterApi.onPageFinished(
- instanceId, instanceManager.getInstanceId(view), url, reply -> {});
- }
-
- // This method is only called when the WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR feature is
- // enabled. The deprecated method is called when a device doesn't support this.
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
- @SuppressLint("RequiresFeature")
- @Override
- public void onReceivedError(
- @NonNull WebView view,
- @NonNull WebResourceRequest request,
- @NonNull WebResourceErrorCompat error) {
- webViewClientFlutterApi.onReceivedRequestError(
- instanceId,
- instanceManager.getInstanceId(view),
- createWebResourceRequestData(request),
- createWebResourceErrorData(error),
- reply -> {});
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public void onReceivedError(
- WebView view, int errorCode, String description, String failingUrl) {
- webViewClientFlutterApi.onReceivedError(
- instanceId,
- instanceManager.getInstanceId(view),
- (long) errorCode,
- description,
- failingUrl,
- reply -> {});
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- @Override
- public boolean shouldOverrideUrlLoading(
- @NonNull WebView view, @NonNull WebResourceRequest request) {
- webViewClientFlutterApi.requestLoading(
- instanceId,
- instanceManager.getInstanceId(view),
- createWebResourceRequestData(request),
- reply -> {});
- return shouldOverrideUrlLoading;
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- webViewClientFlutterApi.urlLoading(
- instanceId, instanceManager.getInstanceId(view), url, reply -> {});
- return shouldOverrideUrlLoading;
- }
-
- @Override
- public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
- // Deliberately empty. Occasionally the webview will mark events as having failed to be
- // handled even though they were handled. We don't want to propagate those as they're not
- // truly lost.
- }
- };
+ return new WebViewClientCompatImpl(flutterApi, shouldOverrideUrlLoading);
}
}
}
- WebViewClientHostApiImpl(
+ /**
+ * Creates a host API that handles creating {@link WebViewClient}s.
+ *
+ * @param instanceManager maintains instances stored to communicate with Dart objects
+ * @param webViewClientCreator handles creating {@link WebViewClient}s
+ * @param flutterApi handles sending messages to Dart
+ */
+ public WebViewClientHostApiImpl(
InstanceManager instanceManager,
WebViewClientCreator webViewClientCreator,
- WebViewClientFlutterApi webViewClientFlutterApi) {
+ WebViewClientFlutterApiImpl flutterApi) {
this.instanceManager = instanceManager;
this.webViewClientCreator = webViewClientCreator;
- this.webViewClientFlutterApi = webViewClientFlutterApi;
+ this.flutterApi = flutterApi;
}
@Override
public void create(Long instanceId, Boolean shouldOverrideUrlLoading) {
final WebViewClient webViewClient =
- webViewClientCreator.createWebViewClient(
- instanceId, instanceManager, shouldOverrideUrlLoading, webViewClientFlutterApi);
+ webViewClientCreator.createWebViewClient(flutterApi, shouldOverrideUrlLoading);
instanceManager.addInstance(webViewClient, instanceId);
}
-
- @Override
- public void dispose(Long instanceId) {
- instanceManager.removeInstance(instanceId);
- }
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java
index 35bdc60..2d96fc3 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java
@@ -4,36 +4,118 @@
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.platform.PlatformView;
+import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerImpl;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewHostApi;
+import io.flutter.plugins.webviewflutter.WebChromeClientHostApiImpl.WebChromeClientImpl;
+import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.ReleasableWebViewClient;
+import java.util.HashMap;
import java.util.Map;
-class WebViewHostApiImpl implements GeneratedAndroidWebView.WebViewHostApi {
+/**
+ * Host api implementation for {@link WebView}.
+ *
+ * <p>Handles creating {@link WebView}s that intercommunicate with a paired Dart object.
+ */
+public class WebViewHostApiImpl implements WebViewHostApi {
+ // TODO(bparrishMines): This can be removed once pigeon supports null values: https://github.com/flutter/flutter/issues/59118
+ // Workaround to represent null Strings since pigeon doesn't support null
+ // values.
+ private static final String nullStringIdentifier = "<null-value>";
+
private final InstanceManager instanceManager;
private final WebViewProxy webViewProxy;
private final Context context;
+ // Only used with WebView using virtual displays.
+ @Nullable private final View containerView;
- static class WebViewProxy {
- WebView createWebView(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
+ * @return the created {@link WebViewPlatformView}
+ */
+ public WebViewPlatformView createWebView(Context context) {
return new WebViewPlatformView(context);
}
- WebView createInputAwareWebView(Context context) {
- return new InputAwareWebViewPlatformView(context, null);
+ /**
+ * 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, @Nullable View containerView) {
+ return new InputAwareWebViewPlatformView(context, containerView);
}
- void setWebContentsDebuggingEnabled(boolean enabled) {
+ /**
+ * Forwards call to {@link WebView#setWebContentsDebuggingEnabled}.
+ *
+ * @param enabled whether debugging should be enabled
+ */
+ public void setWebContentsDebuggingEnabled(boolean enabled) {
WebView.setWebContentsDebuggingEnabled(enabled);
}
}
- private static class WebViewPlatformView extends WebView implements PlatformView {
+ private static class ReleasableValue<T extends Releasable> {
+ @Nullable private T value;
+
+ ReleasableValue() {}
+
+ ReleasableValue(@Nullable T value) {
+ this.value = value;
+ }
+
+ void set(@Nullable T newValue) {
+ release();
+ value = newValue;
+ }
+
+ @Nullable
+ T get() {
+ return value;
+ }
+
+ void release() {
+ if (value != null) {
+ value.release();
+ }
+ value = null;
+ }
+ }
+
+ /** Implementation of {@link WebView} that can be used as a Flutter {@link PlatformView}s. */
+ public static class WebViewPlatformView extends WebView implements PlatformView, Releasable {
+ private final ReleasableValue<WebViewClientHostApiImpl.ReleasableWebViewClient>
+ currentWebViewClient = new ReleasableValue<>();
+ private final ReleasableValue<DownloadListenerImpl> currentDownloadListener =
+ new ReleasableValue<>();
+ private final ReleasableValue<WebChromeClientImpl> currentWebChromeClient =
+ new ReleasableValue<>();
+ private final Map<String, ReleasableValue<JavaScriptChannel>> javaScriptInterfaces =
+ new HashMap<>();
+
+ /**
+ * Creates a {@link WebViewPlatformView}.
+ *
+ * @param context an Activity Context to access application assets. This value cannot be null.
+ */
public WebViewPlatformView(Context context) {
super(context);
}
@@ -47,11 +129,85 @@
public void dispose() {
destroy();
}
+
+ @Override
+ public void setWebViewClient(WebViewClient webViewClient) {
+ super.setWebViewClient(webViewClient);
+ currentWebViewClient.set((ReleasableWebViewClient) webViewClient);
+
+ final WebChromeClientImpl webChromeClient = currentWebChromeClient.get();
+ if (webChromeClient != null) {
+ ((WebChromeClientImpl) webChromeClient).setWebViewClient(webViewClient);
+ }
+ }
+
+ @Override
+ public void setDownloadListener(DownloadListener listener) {
+ super.setDownloadListener(listener);
+ currentDownloadListener.set((DownloadListenerImpl) listener);
+ }
+
+ @Override
+ public void setWebChromeClient(WebChromeClient client) {
+ super.setWebChromeClient(client);
+ currentWebChromeClient.set((WebChromeClientImpl) client);
+ }
+
+ @SuppressLint("JavascriptInterface")
+ @Override
+ public void addJavascriptInterface(Object object, String name) {
+ super.addJavascriptInterface(object, name);
+ if (object instanceof JavaScriptChannel) {
+ final ReleasableValue<JavaScriptChannel> javaScriptChannel = javaScriptInterfaces.get(name);
+ if (javaScriptChannel != null && javaScriptChannel.get() != object) {
+ javaScriptChannel.release();
+ }
+ javaScriptInterfaces.put(name, new ReleasableValue<>((JavaScriptChannel) object));
+ }
+ }
+
+ @Override
+ public void removeJavascriptInterface(@NonNull String name) {
+ super.removeJavascriptInterface(name);
+ final ReleasableValue<JavaScriptChannel> javaScriptChannel = javaScriptInterfaces.get(name);
+ javaScriptChannel.release();
+ javaScriptInterfaces.remove(name);
+ }
+
+ @Override
+ public void release() {
+ currentWebViewClient.release();
+ currentDownloadListener.release();
+ currentWebChromeClient.release();
+ for (ReleasableValue<JavaScriptChannel> channel : javaScriptInterfaces.values()) {
+ channel.release();
+ }
+ javaScriptInterfaces.clear();
+ }
}
- private static class InputAwareWebViewPlatformView extends InputAwareWebView
- implements PlatformView {
- InputAwareWebViewPlatformView(Context context, View containerView) {
+ /**
+ * Implementation of {@link InputAwareWebView} that can be used as a Flutter {@link
+ * PlatformView}s.
+ */
+ @SuppressLint("ViewConstructor")
+ public static class InputAwareWebViewPlatformView extends InputAwareWebView
+ implements PlatformView, Releasable {
+ private final ReleasableValue<WebViewClientHostApiImpl.ReleasableWebViewClient>
+ currentWebViewClient = new ReleasableValue<>();
+ private final ReleasableValue<DownloadListenerImpl> currentDownloadListener =
+ new ReleasableValue<>();
+ private final ReleasableValue<WebChromeClientImpl> currentWebChromeClient =
+ new ReleasableValue<>();
+ private final Map<String, ReleasableValue<JavaScriptChannel>> javaScriptInterfaces =
+ new HashMap<>();
+
+ /**
+ * Creates a {@link InputAwareWebViewPlatformView}.
+ *
+ * @param context an Activity Context to access application assets. This value cannot be null.
+ */
+ public InputAwareWebViewPlatformView(Context context, View containerView) {
super(context, containerView);
}
@@ -72,7 +228,7 @@
@Override
public void dispose() {
- dispose();
+ super.dispose();
destroy();
}
@@ -85,26 +241,104 @@
public void onInputConnectionUnlocked() {
unlockInputConnection();
}
+
+ @Override
+ public void setWebViewClient(WebViewClient webViewClient) {
+ super.setWebViewClient(webViewClient);
+ currentWebViewClient.set((ReleasableWebViewClient) webViewClient);
+
+ final WebChromeClientImpl webChromeClient = currentWebChromeClient.get();
+ if (webChromeClient != null) {
+ webChromeClient.setWebViewClient(webViewClient);
+ }
+ }
+
+ @Override
+ public void setDownloadListener(DownloadListener listener) {
+ super.setDownloadListener(listener);
+ currentDownloadListener.set((DownloadListenerImpl) listener);
+ }
+
+ @Override
+ public void setWebChromeClient(WebChromeClient client) {
+ super.setWebChromeClient(client);
+ currentWebChromeClient.set((WebChromeClientImpl) client);
+ }
+
+ @SuppressLint("JavascriptInterface")
+ @Override
+ public void addJavascriptInterface(Object object, String name) {
+ super.addJavascriptInterface(object, name);
+ if (object instanceof JavaScriptChannel) {
+ final ReleasableValue<JavaScriptChannel> javaScriptChannel = javaScriptInterfaces.get(name);
+ if (javaScriptChannel != null && javaScriptChannel.get() != object) {
+ javaScriptChannel.release();
+ }
+ javaScriptInterfaces.put(name, new ReleasableValue<>((JavaScriptChannel) object));
+ }
+ }
+
+ @Override
+ public void removeJavascriptInterface(@NonNull String name) {
+ super.removeJavascriptInterface(name);
+ final ReleasableValue<JavaScriptChannel> javaScriptChannel = javaScriptInterfaces.get(name);
+ javaScriptChannel.release();
+ javaScriptInterfaces.remove(name);
+ }
+
+ @Override
+ public void release() {
+ currentWebViewClient.release();
+ currentDownloadListener.release();
+ currentWebChromeClient.release();
+ for (ReleasableValue<JavaScriptChannel> channel : javaScriptInterfaces.values()) {
+ channel.release();
+ }
+ javaScriptInterfaces.clear();
+ }
}
- WebViewHostApiImpl(InstanceManager instanceManager, WebViewProxy webViewProxy, Context context) {
+ /**
+ * 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 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,
+ WebViewProxy webViewProxy,
+ Context context,
+ @Nullable View containerView) {
this.instanceManager = instanceManager;
this.webViewProxy = webViewProxy;
this.context = context;
+ this.containerView = containerView;
}
@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)
- : webViewProxy.createInputAwareWebView(context);
+ : webViewProxy.createInputAwareWebView(context, containerView);
+
+ displayListenerProxy.onPostWebViewInitialization(displayManager);
instanceManager.addInstance(webView, instanceId);
}
@Override
public void dispose(Long instanceId) {
- instanceManager.removeInstance(instanceId);
+ final WebView instance = (WebView) instanceManager.removeInstanceWithId(instanceId);
+ if (instance != null) {
+ ((Releasable) instance).release();
+ }
}
@Override
@@ -116,7 +350,8 @@
@Override
public String getUrl(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
- return webView.getUrl();
+ final String result = webView.getUrl();
+ return result != null ? result : nullStringIdentifier;
}
@Override
@@ -165,7 +400,8 @@
@Override
public String getTitle(Long instanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
- return webView.getTitle();
+ final String result = webView.getTitle();
+ return result != null ? result : nullStringIdentifier;
}
@Override
@@ -228,6 +464,6 @@
@Override
public void setWebChromeClient(Long instanceId, Long clientInstanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
- webView.setWebChromeClient((WebChromeClient) instanceManager.getInstance(instanceId));
+ webView.setWebChromeClient((WebChromeClient) instanceManager.getInstance(clientInstanceId));
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
index 5ba073c..2391193 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
@@ -6,11 +6,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.webkit.DownloadListener;
import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerCreator;
-import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerFlutterApi;
+import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerImpl;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -21,45 +23,49 @@
public class DownloadListenerTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
- @Mock public DownloadListenerFlutterApi mockFlutterApi;
+ @Mock public DownloadListenerFlutterApiImpl mockFlutterApi;
- InstanceManager testInstanceManager;
- DownloadListenerHostApiImpl testHostApiImpl;
- DownloadListener testDownloadListener;
+ InstanceManager instanceManager;
+ DownloadListenerHostApiImpl hostApiImpl;
+ DownloadListenerImpl downloadListener;
@Before
public void setUp() {
- testInstanceManager = new InstanceManager();
+ instanceManager = new InstanceManager();
final DownloadListenerCreator downloadListenerCreator =
new DownloadListenerCreator() {
@Override
- DownloadListener createDownloadListener(
- Long instanceId, DownloadListenerFlutterApi downloadListenerFlutterApi) {
- testDownloadListener =
- super.createDownloadListener(instanceId, downloadListenerFlutterApi);
- return testDownloadListener;
+ public DownloadListenerImpl createDownloadListener(
+ DownloadListenerFlutterApiImpl flutterApi) {
+ downloadListener = super.createDownloadListener(flutterApi);
+ return downloadListener;
}
};
- testHostApiImpl =
- new DownloadListenerHostApiImpl(
- testInstanceManager, downloadListenerCreator, mockFlutterApi);
- testHostApiImpl.create(0L);
+ hostApiImpl =
+ new DownloadListenerHostApiImpl(instanceManager, downloadListenerCreator, mockFlutterApi);
+ hostApiImpl.create(0L);
}
@Test
public void postMessage() {
- testDownloadListener.onDownloadStart(
+ downloadListener.onDownloadStart(
"https://www.google.com", "userAgent", "contentDisposition", "mimetype", 54);
verify(mockFlutterApi)
.onDownloadStart(
- eq(0L),
+ eq(downloadListener),
eq("https://www.google.com"),
eq("userAgent"),
eq("contentDisposition"),
eq("mimetype"),
eq(54L),
any());
+
+ reset(mockFlutterApi);
+ downloadListener.release();
+ downloadListener.onDownloadStart("", "", "", "", 23);
+ verify(mockFlutterApi, never())
+ .onDownloadStart((DownloadListener) any(), any(), any(), any(), any(), eq(23), any());
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
index 697ea0b..3de81da 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
@@ -6,10 +6,11 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.os.Handler;
-import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelFlutterApi;
import io.flutter.plugins.webviewflutter.JavaScriptChannelHostApiImpl.JavaScriptChannelCreator;
import org.junit.Before;
import org.junit.Rule;
@@ -21,40 +22,44 @@
public class JavaScriptChannelTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
- @Mock public GeneratedAndroidWebView.JavaScriptChannelFlutterApi mockFlutterApi;
+ @Mock public JavaScriptChannelFlutterApiImpl mockFlutterApi;
- InstanceManager testInstanceManager;
- JavaScriptChannelHostApiImpl testHostApiImpl;
- JavaScriptChannel testJavaScriptChannel;
+ InstanceManager instanceManager;
+ JavaScriptChannelHostApiImpl hostApiImpl;
+ JavaScriptChannel javaScriptChannel;
@Before
public void setUp() {
- testInstanceManager = new InstanceManager();
+ instanceManager = new InstanceManager();
final JavaScriptChannelCreator javaScriptChannelCreator =
new JavaScriptChannelCreator() {
@Override
- JavaScriptChannel createJavaScriptChannel(
- Long instanceId,
- JavaScriptChannelFlutterApi javaScriptChannelFlutterApi,
+ public JavaScriptChannel createJavaScriptChannel(
+ JavaScriptChannelFlutterApiImpl javaScriptChannelFlutterApi,
String channelName,
Handler platformThreadHandler) {
- testJavaScriptChannel =
+ javaScriptChannel =
super.createJavaScriptChannel(
- instanceId, javaScriptChannelFlutterApi, channelName, platformThreadHandler);
- return testJavaScriptChannel;
+ javaScriptChannelFlutterApi, channelName, platformThreadHandler);
+ return javaScriptChannel;
}
};
- testHostApiImpl =
+ hostApiImpl =
new JavaScriptChannelHostApiImpl(
- testInstanceManager, javaScriptChannelCreator, mockFlutterApi, new Handler());
- testHostApiImpl.create(0L, "aChannelName");
+ instanceManager, javaScriptChannelCreator, mockFlutterApi, new Handler());
+ hostApiImpl.create(0L, "aChannelName");
}
@Test
public void postMessage() {
- testJavaScriptChannel.postMessage("A message post.");
- verify(mockFlutterApi).postMessage(eq(0L), eq("A message post."), any());
+ javaScriptChannel.postMessage("A message post.");
+ verify(mockFlutterApi).postMessage(eq(javaScriptChannel), eq("A message post."), any());
+
+ reset(mockFlutterApi);
+ javaScriptChannel.release();
+ javaScriptChannel.postMessage("a message");
+ verify(mockFlutterApi, never()).postMessage((JavaScriptChannel) any(), any(), any());
}
}
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 5ab3ab1..d6593ab 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
@@ -6,13 +6,15 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientFlutterApi;
import io.flutter.plugins.webviewflutter.WebChromeClientHostApiImpl.WebChromeClientCreator;
+import io.flutter.plugins.webviewflutter.WebChromeClientHostApiImpl.WebChromeClientImpl;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -23,45 +25,45 @@
public class WebChromeClientTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
- @Mock public WebChromeClientFlutterApi mockFlutterApi;
+ @Mock public WebChromeClientFlutterApiImpl mockFlutterApi;
@Mock public WebView mockWebView;
@Mock public WebViewClient mockWebViewClient;
- InstanceManager testInstanceManager;
- WebChromeClientHostApiImpl testHostApiImpl;
- WebChromeClient testWebChromeClient;
+ InstanceManager instanceManager;
+ WebChromeClientHostApiImpl hostApiImpl;
+ WebChromeClientImpl webChromeClient;
@Before
public void setUp() {
- testInstanceManager = new InstanceManager();
- testInstanceManager.addInstance(mockWebView, 0L);
- testInstanceManager.addInstance(mockWebViewClient, 1L);
+ instanceManager = new InstanceManager();
+ instanceManager.addInstance(mockWebView, 0L);
+ instanceManager.addInstance(mockWebViewClient, 1L);
final WebChromeClientCreator webChromeClientCreator =
new WebChromeClientCreator() {
@Override
- WebChromeClient createWebChromeClient(
- Long instanceId,
- InstanceManager instanceManager,
- WebViewClient webViewClient,
- WebChromeClientFlutterApi webChromeClientFlutterApi) {
- testWebChromeClient =
- super.createWebChromeClient(
- instanceId, instanceManager, webViewClient, webChromeClientFlutterApi);
- return testWebChromeClient;
+ public WebChromeClientImpl createWebChromeClient(
+ WebChromeClientFlutterApiImpl flutterApi, WebViewClient webViewClient) {
+ webChromeClient = super.createWebChromeClient(flutterApi, webViewClient);
+ return webChromeClient;
}
};
- testHostApiImpl =
- new WebChromeClientHostApiImpl(testInstanceManager, webChromeClientCreator, mockFlutterApi);
- testHostApiImpl.create(2L, 1L);
+ hostApiImpl =
+ new WebChromeClientHostApiImpl(instanceManager, webChromeClientCreator, mockFlutterApi);
+ hostApiImpl.create(2L, 1L);
}
@Test
public void onProgressChanged() {
- testWebChromeClient.onProgressChanged(mockWebView, 23);
- verify(mockFlutterApi).onProgressChanged(eq(2L), eq(0L), eq(23L), any());
+ webChromeClient.onProgressChanged(mockWebView, 23);
+ verify(mockFlutterApi).onProgressChanged(eq(webChromeClient), eq(mockWebView), eq(23L), any());
+
+ reset(mockFlutterApi);
+ webChromeClient.release();
+ webChromeClient.onProgressChanged(mockWebView, 11);
+ verify(mockFlutterApi, never()).onProgressChanged((WebChromeClient) any(), any(), any(), any());
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
index f6d2054..62d2723 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
@@ -6,11 +6,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientFlutterApi;
+import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCompatImpl;
import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCreator;
import org.junit.Before;
import org.junit.Rule;
@@ -22,56 +24,76 @@
public class WebViewClientTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
- @Mock public WebViewClientFlutterApi mockFlutterApi;
+ @Mock public WebViewClientFlutterApiImpl mockFlutterApi;
@Mock public WebView mockWebView;
- InstanceManager testInstanceManager;
- WebViewClientHostApiImpl testHostApiImpl;
- WebViewClient testWebViewClient;
+ InstanceManager instanceManager;
+ WebViewClientHostApiImpl hostApiImpl;
+ WebViewClientCompatImpl webViewClient;
@Before
public void setUp() {
- testInstanceManager = new InstanceManager();
- testInstanceManager.addInstance(mockWebView, 0L);
+ instanceManager = new InstanceManager();
+ instanceManager.addInstance(mockWebView, 0L);
final WebViewClientCreator webViewClientCreator =
new WebViewClientCreator() {
@Override
- WebViewClient createWebViewClient(
- Long instanceId,
- InstanceManager instanceManager,
- Boolean shouldOverrideUrlLoading,
- WebViewClientFlutterApi webViewClientFlutterApi) {
- testWebViewClient =
- super.createWebViewClient(
- instanceId, instanceManager, shouldOverrideUrlLoading, webViewClientFlutterApi);
- return testWebViewClient;
+ public WebViewClient createWebViewClient(
+ WebViewClientFlutterApiImpl flutterApi, boolean shouldOverrideUrlLoading) {
+ webViewClient =
+ (WebViewClientCompatImpl)
+ super.createWebViewClient(flutterApi, shouldOverrideUrlLoading);
+ return webViewClient;
}
};
- testHostApiImpl =
- new WebViewClientHostApiImpl(testInstanceManager, webViewClientCreator, mockFlutterApi);
- testHostApiImpl.create(1L, true);
+ hostApiImpl =
+ new WebViewClientHostApiImpl(instanceManager, webViewClientCreator, mockFlutterApi);
+ hostApiImpl.create(1L, true);
}
@Test
public void onPageStarted() {
- testWebViewClient.onPageStarted(mockWebView, "https://www.google.com", null);
- verify(mockFlutterApi).onPageStarted(eq(1L), eq(0L), eq("https://www.google.com"), any());
+ webViewClient.onPageStarted(mockWebView, "https://www.google.com", null);
+ verify(mockFlutterApi)
+ .onPageStarted(eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), any());
+
+ reset(mockFlutterApi);
+ webViewClient.release();
+ webViewClient.onPageStarted(mockWebView, "", null);
+ verify(mockFlutterApi, never()).onPageStarted((WebViewClient) any(), any(), any(), any());
}
@Test
public void onReceivedError() {
- testWebViewClient.onReceivedError(mockWebView, 32, "description", "https://www.google.com");
+ webViewClient.onReceivedError(mockWebView, 32, "description", "https://www.google.com");
verify(mockFlutterApi)
.onReceivedError(
- eq(1L), eq(0L), eq(32L), eq("description"), eq("https://www.google.com"), any());
+ eq(webViewClient),
+ eq(mockWebView),
+ eq(32L),
+ eq("description"),
+ eq("https://www.google.com"),
+ any());
+
+ reset(mockFlutterApi);
+ webViewClient.release();
+ webViewClient.onReceivedError(mockWebView, 33, "", "");
+ verify(mockFlutterApi, never())
+ .onReceivedError((WebViewClient) any(), any(), any(), any(), any(), any());
}
@Test
public void urlLoading() {
- testWebViewClient.shouldOverrideUrlLoading(mockWebView, "https://www.google.com");
- verify(mockFlutterApi).urlLoading(eq(1L), eq(0L), eq("https://www.google.com"), any());
+ webViewClient.shouldOverrideUrlLoading(mockWebView, "https://www.google.com");
+ verify(mockFlutterApi)
+ .urlLoading(eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), any());
+
+ reset(mockFlutterApi);
+ webViewClient.release();
+ webViewClient.shouldOverrideUrlLoading(mockWebView, "");
+ verify(mockFlutterApi, never()).urlLoading((WebViewClient) any(), any(), any(), any());
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
index b914ce9..e506188 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
@@ -13,8 +13,13 @@
import android.content.Context;
import android.webkit.DownloadListener;
import android.webkit.ValueCallback;
-import android.webkit.WebView;
+import android.webkit.WebChromeClient;
import android.webkit.WebViewClient;
+import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerImpl;
+import io.flutter.plugins.webviewflutter.WebChromeClientHostApiImpl.WebChromeClientImpl;
+import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientImpl;
+import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.InputAwareWebViewPlatformView;
+import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.WebViewPlatformView;
import java.util.HashMap;
import org.junit.Before;
import org.junit.Rule;
@@ -27,7 +32,7 @@
public class WebViewTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
- @Mock public WebView mockWebView;
+ @Mock public WebViewPlatformView mockWebView;
@Mock WebViewHostApiImpl.WebViewProxy mockWebViewProxy;
@@ -40,45 +45,115 @@
public void setUp() {
testInstanceManager = new InstanceManager();
when(mockWebViewProxy.createWebView(mockContext)).thenReturn(mockWebView);
- testHostApiImpl = new WebViewHostApiImpl(testInstanceManager, mockWebViewProxy, mockContext);
+ testHostApiImpl =
+ new WebViewHostApiImpl(testInstanceManager, mockWebViewProxy, mockContext, null);
testHostApiImpl.create(0L, true);
}
@Test
- public void errorCodes() {
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_AUTHENTICATION),
- "authentication");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_BAD_URL), "badUrl");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_CONNECT), "connect");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE),
- "failedSslHandshake");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_FILE), "file");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_FILE_NOT_FOUND), "fileNotFound");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_HOST_LOOKUP), "hostLookup");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_IO), "io");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_PROXY_AUTHENTICATION),
- "proxyAuthentication");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_REDIRECT_LOOP), "redirectLoop");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_TIMEOUT), "timeout");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_TOO_MANY_REQUESTS),
- "tooManyRequests");
- assertEquals(FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_UNKNOWN), "unknown");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_UNSAFE_RESOURCE),
- "unsafeResource");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_UNSUPPORTED_AUTH_SCHEME),
- "unsupportedAuthScheme");
- assertEquals(
- FlutterWebViewClient.errorCodeToString(WebViewClient.ERROR_UNSUPPORTED_SCHEME),
- "unsupportedScheme");
+ public void releaseWebView() {
+ final WebViewPlatformView webView = new WebViewPlatformView(mockContext);
+
+ final WebViewClientImpl mockWebViewClient = mock(WebViewClientImpl.class);
+ final WebChromeClientImpl mockWebChromeClient = mock(WebChromeClientImpl.class);
+ final DownloadListenerImpl mockDownloadListener = mock(DownloadListenerImpl.class);
+ final JavaScriptChannel mockJavaScriptChannel = mock(JavaScriptChannel.class);
+
+ webView.setWebViewClient(mockWebViewClient);
+ webView.setWebChromeClient(mockWebChromeClient);
+ webView.setDownloadListener(mockDownloadListener);
+ webView.addJavascriptInterface(mockJavaScriptChannel, "jchannel");
+
+ webView.release();
+
+ verify(mockWebViewClient).release();
+ verify(mockWebChromeClient).release();
+ verify(mockDownloadListener).release();
+ verify(mockJavaScriptChannel).release();
+ }
+
+ @Test
+ public void releaseWebViewDependents() {
+ final WebViewPlatformView webView = new WebViewPlatformView(mockContext);
+
+ final WebViewClientImpl mockWebViewClient = mock(WebViewClientImpl.class);
+ final WebChromeClientImpl mockWebChromeClient = mock(WebChromeClientImpl.class);
+ final DownloadListenerImpl mockDownloadListener = mock(DownloadListenerImpl.class);
+ final JavaScriptChannel mockJavaScriptChannel = mock(JavaScriptChannel.class);
+ final JavaScriptChannel mockJavaScriptChannel2 = mock(JavaScriptChannel.class);
+
+ webView.setWebViewClient(mockWebViewClient);
+ webView.setWebChromeClient(mockWebChromeClient);
+ webView.setDownloadListener(mockDownloadListener);
+ webView.addJavascriptInterface(mockJavaScriptChannel, "jchannel");
+
+ // Release should be called on the object added above.
+ webView.addJavascriptInterface(mockJavaScriptChannel2, "jchannel");
+ verify(mockJavaScriptChannel).release();
+
+ webView.setWebViewClient(null);
+ webView.setWebChromeClient(null);
+ webView.setDownloadListener(null);
+ webView.removeJavascriptInterface("jchannel");
+
+ verify(mockWebViewClient).release();
+ verify(mockWebChromeClient).release();
+ verify(mockDownloadListener).release();
+ verify(mockJavaScriptChannel2).release();
+ }
+
+ @Test
+ public void releaseInputAwareWebView() {
+ final InputAwareWebViewPlatformView webView =
+ new InputAwareWebViewPlatformView(mockContext, null);
+
+ final WebViewClientImpl mockWebViewClient = mock(WebViewClientImpl.class);
+ final WebChromeClientImpl mockWebChromeClient = mock(WebChromeClientImpl.class);
+ final DownloadListenerImpl mockDownloadListener = mock(DownloadListenerImpl.class);
+ final JavaScriptChannel mockJavaScriptChannel = mock(JavaScriptChannel.class);
+
+ webView.setWebViewClient(mockWebViewClient);
+ webView.setWebChromeClient(mockWebChromeClient);
+ webView.setDownloadListener(mockDownloadListener);
+ webView.addJavascriptInterface(mockJavaScriptChannel, "jchannel");
+
+ webView.release();
+
+ verify(mockWebViewClient).release();
+ verify(mockWebChromeClient).release();
+ verify(mockDownloadListener).release();
+ verify(mockJavaScriptChannel).release();
+ }
+
+ @Test
+ public void releaseInputAwareWebViewDependents() {
+ final InputAwareWebViewPlatformView webView =
+ new InputAwareWebViewPlatformView(mockContext, null);
+
+ final WebViewClientImpl mockWebViewClient = mock(WebViewClientImpl.class);
+ final WebChromeClientImpl mockWebChromeClient = mock(WebChromeClientImpl.class);
+ final DownloadListenerImpl mockDownloadListener = mock(DownloadListenerImpl.class);
+ final JavaScriptChannel mockJavaScriptChannel = mock(JavaScriptChannel.class);
+ final JavaScriptChannel mockJavaScriptChannel2 = mock(JavaScriptChannel.class);
+
+ webView.setWebViewClient(mockWebViewClient);
+ webView.setWebChromeClient(mockWebChromeClient);
+ webView.setDownloadListener(mockDownloadListener);
+ webView.addJavascriptInterface(mockJavaScriptChannel, "jchannel");
+
+ // Release should be called on the object added above.
+ webView.addJavascriptInterface(mockJavaScriptChannel2, "jchannel");
+ verify(mockJavaScriptChannel).release();
+
+ webView.setWebViewClient(null);
+ webView.setWebChromeClient(null);
+ webView.setDownloadListener(null);
+ webView.removeJavascriptInterface("jchannel");
+
+ verify(mockWebViewClient).release();
+ verify(mockWebChromeClient).release();
+ verify(mockDownloadListener).release();
+ verify(mockJavaScriptChannel2).release();
}
@Test
@@ -195,7 +270,8 @@
@Test
public void addJavaScriptChannel() {
- final JavaScriptChannel javaScriptChannel = new JavaScriptChannel(null, "aName", null);
+ final JavaScriptChannel javaScriptChannel =
+ new JavaScriptChannel(mock(JavaScriptChannelFlutterApiImpl.class), "aName", null);
testInstanceManager.addInstance(javaScriptChannel, 1L);
testHostApiImpl.addJavaScriptChannel(0L, 1L);
@@ -204,7 +280,8 @@
@Test
public void removeJavaScriptChannel() {
- final JavaScriptChannel javaScriptChannel = new JavaScriptChannel(null, "aName", null);
+ final JavaScriptChannel javaScriptChannel =
+ new JavaScriptChannel(mock(JavaScriptChannelFlutterApiImpl.class), "aName", null);
testInstanceManager.addInstance(javaScriptChannel, 1L);
testHostApiImpl.removeJavaScriptChannel(0L, 1L);
@@ -219,4 +296,13 @@
testHostApiImpl.setDownloadListener(0L, 1L);
verify(mockWebView).setDownloadListener(mockDownloadListener);
}
+
+ @Test
+ public void setWebChromeClient() {
+ final WebChromeClient mockWebChromeClient = mock(WebChromeClient.class);
+ testInstanceManager.addInstance(mockWebChromeClient, 1L);
+
+ testHostApiImpl.setWebChromeClient(0L, 1L);
+ verify(mockWebView).setWebChromeClient(mockWebChromeClient);
+ }
}