diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 37abf3cf2b1b..9821c2c74e2e 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.2.0 + +* Adds support for handling file selection. See `AndroidWebViewController.setOnShowFileSelector`. +* Updates pigeon dev dependency to `4.2.14`. + ## 3.1.3 * Fixes crash when the Java `InstanceManager` was used after plugin was removed from the engine. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserParamsFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserParamsFlutterApiImpl.java new file mode 100644 index 000000000000..679785949697 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserParamsFlutterApiImpl.java @@ -0,0 +1,74 @@ +// 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.os.Build; +import android.webkit.WebChromeClient; +import androidx.annotation.RequiresApi; +import io.flutter.plugin.common.BinaryMessenger; +import java.util.Arrays; + +/** + * Flutter Api implementation for {@link android.webkit.WebChromeClient.FileChooserParams}. + * + *

Passes arguments of callbacks methods from a {@link + * android.webkit.WebChromeClient.FileChooserParams} to Dart. + */ +@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) +public class FileChooserParamsFlutterApiImpl + extends GeneratedAndroidWebView.FileChooserParamsFlutterApi { + 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 FileChooserParamsFlutterApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + super(binaryMessenger); + this.instanceManager = instanceManager; + } + + private static GeneratedAndroidWebView.FileChooserModeEnumData toFileChooserEnumData(int mode) { + final GeneratedAndroidWebView.FileChooserModeEnumData.Builder builder = + new GeneratedAndroidWebView.FileChooserModeEnumData.Builder(); + + switch (mode) { + case WebChromeClient.FileChooserParams.MODE_OPEN: + builder.setValue(GeneratedAndroidWebView.FileChooserMode.OPEN); + break; + case WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE: + builder.setValue(GeneratedAndroidWebView.FileChooserMode.OPEN_MULTIPLE); + break; + case WebChromeClient.FileChooserParams.MODE_SAVE: + builder.setValue(GeneratedAndroidWebView.FileChooserMode.SAVE); + break; + default: + throw new IllegalArgumentException(String.format("Unsupported FileChooserMode: %d", mode)); + } + + return builder.build(); + } + + /** + * Stores the FileChooserParams instance and notifies Dart to create a new FileChooserParams + * instance that is attached to this one. + * + * @return the instanceId of the stored instance + */ + public long create(WebChromeClient.FileChooserParams instance, Reply callback) { + final long instanceId = instanceManager.addHostCreatedInstance(instance); + create( + instanceId, + instance.isCaptureEnabled(), + Arrays.asList(instance.getAcceptTypes()), + toFileChooserEnumData(instance.getMode()), + instance.getFilenameHint(), + callback); + return 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 15c80cc0a907..425f6c1415bd 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 @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v4.2.3), do not edit directly. +// Autogenerated from Pigeon (v4.2.14), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.webviewflutter; @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,6 +25,90 @@ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) public class GeneratedAndroidWebView { + /** + * Mode of how to select files for a file chooser. + * + *

See + * https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams. + */ + public enum FileChooserMode { + /** + * Open single file and requires that the file exists before allowing the user to pick it. + * + *

See + * https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_OPEN. + */ + OPEN(0), + /** + * Similar to [open] but allows multiple files to be selected. + * + *

See + * https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_OPEN_MULTIPLE. + */ + OPEN_MULTIPLE(1), + /** + * Allows picking a nonexistent file and saving it. + * + *

See + * https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_SAVE. + */ + SAVE(2); + + private final int index; + + private FileChooserMode(final int index) { + this.index = index; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static class FileChooserModeEnumData { + private @NonNull FileChooserMode value; + + public @NonNull FileChooserMode getValue() { + return value; + } + + public void setValue(@NonNull FileChooserMode setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"value\" is null."); + } + this.value = setterArg; + } + + /** Constructor is private to enforce null safety; use Builder. */ + private FileChooserModeEnumData() {} + + public static final class Builder { + private @Nullable FileChooserMode value; + + public @NonNull Builder setValue(@NonNull FileChooserMode setterArg) { + this.value = setterArg; + return this; + } + + public @NonNull FileChooserModeEnumData build() { + FileChooserModeEnumData pigeonReturn = new FileChooserModeEnumData(); + pigeonReturn.setValue(value); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(value == null ? null : value.index); + return toListResult; + } + + static @NonNull FileChooserModeEnumData fromList(@NonNull ArrayList list) { + FileChooserModeEnumData pigeonResult = new FileChooserModeEnumData(); + Object value = list.get(0); + pigeonResult.setValue(value == null ? null : FileChooserMode.values()[(int) value]); + return pigeonResult; + } + } + /** Generated class from Pigeon that represents data sent in messages. */ public static class WebResourceRequestData { private @NonNull String url; @@ -162,30 +245,30 @@ public static final class Builder { } @NonNull - Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("url", url); - toMapResult.put("isForMainFrame", isForMainFrame); - toMapResult.put("isRedirect", isRedirect); - toMapResult.put("hasGesture", hasGesture); - toMapResult.put("method", method); - toMapResult.put("requestHeaders", requestHeaders); - return toMapResult; - } - - static @NonNull WebResourceRequestData fromMap(@NonNull Map map) { + ArrayList toList() { + ArrayList toListResult = new ArrayList(6); + toListResult.add(url); + toListResult.add(isForMainFrame); + toListResult.add(isRedirect); + toListResult.add(hasGesture); + toListResult.add(method); + toListResult.add(requestHeaders); + return toListResult; + } + + static @NonNull WebResourceRequestData fromList(@NonNull ArrayList list) { WebResourceRequestData pigeonResult = new WebResourceRequestData(); - Object url = map.get("url"); + Object url = list.get(0); pigeonResult.setUrl((String) url); - Object isForMainFrame = map.get("isForMainFrame"); + Object isForMainFrame = list.get(1); pigeonResult.setIsForMainFrame((Boolean) isForMainFrame); - Object isRedirect = map.get("isRedirect"); + Object isRedirect = list.get(2); pigeonResult.setIsRedirect((Boolean) isRedirect); - Object hasGesture = map.get("hasGesture"); + Object hasGesture = list.get(3); pigeonResult.setHasGesture((Boolean) hasGesture); - Object method = map.get("method"); + Object method = list.get(4); pigeonResult.setMethod((String) method); - Object requestHeaders = map.get("requestHeaders"); + Object requestHeaders = list.get(5); pigeonResult.setRequestHeaders((Map) requestHeaders); return pigeonResult; } @@ -246,21 +329,21 @@ public static final class Builder { } @NonNull - Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("errorCode", errorCode); - toMapResult.put("description", description); - return toMapResult; + ArrayList toList() { + ArrayList toListResult = new ArrayList(2); + toListResult.add(errorCode); + toListResult.add(description); + return toListResult; } - static @NonNull WebResourceErrorData fromMap(@NonNull Map map) { + static @NonNull WebResourceErrorData fromList(@NonNull ArrayList list) { WebResourceErrorData pigeonResult = new WebResourceErrorData(); - Object errorCode = map.get("errorCode"); + Object errorCode = list.get(0); pigeonResult.setErrorCode( (errorCode == null) ? null : ((errorCode instanceof Integer) ? (Integer) errorCode : (Long) errorCode)); - Object description = map.get("description"); + Object description = list.get(1); pigeonResult.setDescription((String) description); return pigeonResult; } @@ -321,18 +404,18 @@ public static final class Builder { } @NonNull - Map toMap() { - Map toMapResult = new HashMap<>(); - toMapResult.put("x", x); - toMapResult.put("y", y); - return toMapResult; + ArrayList toList() { + ArrayList toListResult = new ArrayList(2); + toListResult.add(x); + toListResult.add(y); + return toListResult; } - static @NonNull WebViewPoint fromMap(@NonNull Map map) { + static @NonNull WebViewPoint fromList(@NonNull ArrayList list) { WebViewPoint pigeonResult = new WebViewPoint(); - Object x = map.get("x"); + Object x = list.get(0); pigeonResult.setX((x == null) ? null : ((x instanceof Integer) ? (Integer) x : (Long) x)); - Object y = map.get("y"); + Object y = list.get(1); pigeonResult.setY((y == null) ? null : ((y instanceof Integer) ? (Integer) y : (Long) y)); return pigeonResult; } @@ -370,7 +453,7 @@ static void setup(BinaryMessenger binaryMessenger, JavaObjectHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -379,9 +462,10 @@ static void setup(BinaryMessenger binaryMessenger, JavaObjectHostApi api) { throw new NullPointerException("identifierArg unexpectedly null."); } api.dispose((identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -448,25 +532,25 @@ static void setup(BinaryMessenger binaryMessenger, CookieManagerHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { Result resultCallback = new Result() { public void success(Boolean result) { - wrapped.put("result", result); + wrapped.add(0, result); reply.reply(wrapped); } public void error(Throwable error) { - wrapped.put("error", wrapError(error)); - reply.reply(wrapped); + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); } }; api.clearCookies(resultCallback); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - reply.reply(wrapped); + ArrayList wrappedError = wrapError(exception); + reply.reply(wrappedError); } }); } else { @@ -480,7 +564,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -493,9 +577,10 @@ public void error(Throwable error) { throw new NullPointerException("valueArg unexpectedly null."); } api.setCookie(urlArg, valueArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -515,7 +600,7 @@ private WebViewHostApiCodec() {} protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { case (byte) 128: - return WebViewPoint.fromMap((Map) readValue(buffer)); + return WebViewPoint.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); @@ -526,7 +611,7 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { if (value instanceof WebViewPoint) { stream.write(128); - writeValue(stream, ((WebViewPoint) value).toMap()); + writeValue(stream, ((WebViewPoint) value).toList()); } else { super.writeValue(stream, value); } @@ -620,7 +705,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -635,9 +720,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { api.create( (instanceIdArg == null) ? null : instanceIdArg.longValue(), useHybridCompositionArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -652,7 +738,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -671,9 +757,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { dataArg, mimeTypeArg, encodingArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -690,7 +777,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -713,9 +800,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { mimeTypeArg, encodingArg, historyUrlArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -730,7 +818,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -750,9 +838,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { (instanceIdArg == null) ? null : instanceIdArg.longValue(), urlArg, headersArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -767,7 +856,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -785,9 +874,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { } api.postUrl( (instanceIdArg == null) ? null : instanceIdArg.longValue(), urlArg, dataArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -802,7 +892,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -812,9 +902,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { } String output = api.getUrl((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -829,7 +920,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -839,9 +930,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { } Boolean output = api.canGoBack((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -856,7 +948,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -866,9 +958,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { } Boolean output = api.canGoForward((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -883,7 +976,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -892,9 +985,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } api.goBack((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -909,7 +1003,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -918,9 +1012,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } api.goForward((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -935,7 +1030,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -944,9 +1039,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } api.reload((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -961,7 +1057,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -976,9 +1072,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { api.clearCache( (instanceIdArg == null) ? null : instanceIdArg.longValue(), includeDiskFilesArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -995,7 +1092,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1010,13 +1107,13 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Result resultCallback = new Result() { public void success(String result) { - wrapped.put("result", result); + wrapped.add(0, result); reply.reply(wrapped); } public void error(Throwable error) { - wrapped.put("error", wrapError(error)); - reply.reply(wrapped); + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); } }; @@ -1025,8 +1122,8 @@ public void error(Throwable error) { javascriptStringArg, resultCallback); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - reply.reply(wrapped); + ArrayList wrappedError = wrapError(exception); + reply.reply(wrappedError); } }); } else { @@ -1040,7 +1137,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1050,9 +1147,10 @@ public void error(Throwable error) { } String output = api.getTitle((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1067,7 +1165,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1087,9 +1185,10 @@ public void error(Throwable error) { (instanceIdArg == null) ? null : instanceIdArg.longValue(), (xArg == null) ? null : xArg.longValue(), (yArg == null) ? null : yArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1104,7 +1203,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1124,9 +1223,10 @@ public void error(Throwable error) { (instanceIdArg == null) ? null : instanceIdArg.longValue(), (xArg == null) ? null : xArg.longValue(), (yArg == null) ? null : yArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1141,7 +1241,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1151,9 +1251,10 @@ public void error(Throwable error) { } Long output = api.getScrollX((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1168,7 +1269,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1178,9 +1279,10 @@ public void error(Throwable error) { } Long output = api.getScrollY((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1195,7 +1297,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1206,9 +1308,10 @@ public void error(Throwable error) { WebViewPoint output = api.getScrollPosition( (instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1225,7 +1328,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1234,9 +1337,10 @@ public void error(Throwable error) { throw new NullPointerException("enabledArg unexpectedly null."); } api.setWebContentsDebuggingEnabled(enabledArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1251,7 +1355,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1268,9 +1372,10 @@ public void error(Throwable error) { (webViewClientInstanceIdArg == null) ? null : webViewClientInstanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1287,7 +1392,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1305,9 +1410,10 @@ public void error(Throwable error) { (javaScriptChannelInstanceIdArg == null) ? null : javaScriptChannelInstanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1324,7 +1430,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1342,9 +1448,10 @@ public void error(Throwable error) { (javaScriptChannelInstanceIdArg == null) ? null : javaScriptChannelInstanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1361,7 +1468,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1373,9 +1480,10 @@ public void error(Throwable error) { api.setDownloadListener( (instanceIdArg == null) ? null : instanceIdArg.longValue(), (listenerInstanceIdArg == null) ? null : listenerInstanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1392,7 +1500,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1404,9 +1512,10 @@ public void error(Throwable error) { api.setWebChromeClient( (instanceIdArg == null) ? null : instanceIdArg.longValue(), (clientInstanceIdArg == null) ? null : clientInstanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1423,7 +1532,7 @@ public void error(Throwable error) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1438,9 +1547,10 @@ public void error(Throwable error) { api.setBackgroundColor( (instanceIdArg == null) ? null : instanceIdArg.longValue(), (colorArg == null) ? null : colorArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1493,7 +1603,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1508,9 +1618,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { api.create( (instanceIdArg == null) ? null : instanceIdArg.longValue(), (webViewInstanceIdArg == null) ? null : webViewInstanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1527,7 +1638,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1541,9 +1652,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setDomStorageEnabled( (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1560,7 +1672,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1574,9 +1686,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setJavaScriptCanOpenWindowsAutomatically( (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1593,7 +1706,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1607,9 +1720,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setSupportMultipleWindows( (instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1626,7 +1740,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1640,9 +1754,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setJavaScriptEnabled( (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1659,7 +1774,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1671,9 +1786,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { api.setUserAgentString( (instanceIdArg == null) ? null : instanceIdArg.longValue(), userAgentStringArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1690,7 +1806,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1704,9 +1820,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setMediaPlaybackRequiresUserGesture( (instanceIdArg == null) ? null : instanceIdArg.longValue(), requireArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1723,7 +1840,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1737,9 +1854,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setSupportZoom( (instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1756,7 +1874,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1770,9 +1888,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setLoadWithOverviewMode( (instanceIdArg == null) ? null : instanceIdArg.longValue(), overviewArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1789,7 +1908,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1803,9 +1922,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setUseWideViewPort( (instanceIdArg == null) ? null : instanceIdArg.longValue(), useArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1822,7 +1942,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1836,9 +1956,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setDisplayZoomControls( (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1855,7 +1976,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1869,9 +1990,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setBuiltInZoomControls( (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1888,7 +2010,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1902,9 +2024,10 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } api.setAllowFileAccess( (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -1934,7 +2057,7 @@ static void setup(BinaryMessenger binaryMessenger, JavaScriptChannelHostApi api) if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -1948,9 +2071,10 @@ static void setup(BinaryMessenger binaryMessenger, JavaScriptChannelHostApi api) } api.create( (instanceIdArg == null) ? null : instanceIdArg.longValue(), channelNameArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2013,7 +2137,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -2022,9 +2146,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2041,7 +2166,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -2055,9 +2180,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { } api.setSynchronousReturnValueForShouldOverrideUrlLoading( (instanceIdArg == null) ? null : instanceIdArg.longValue(), valueArg); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2077,10 +2203,10 @@ private WebViewClientFlutterApiCodec() {} protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { case (byte) 128: - return WebResourceErrorData.fromMap((Map) readValue(buffer)); + return WebResourceErrorData.fromList((ArrayList) readValue(buffer)); case (byte) 129: - return WebResourceRequestData.fromMap((Map) readValue(buffer)); + return WebResourceRequestData.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); @@ -2091,10 +2217,10 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { if (value instanceof WebResourceErrorData) { stream.write(128); - writeValue(stream, ((WebResourceErrorData) value).toMap()); + writeValue(stream, ((WebResourceErrorData) value).toList()); } else if (value instanceof WebResourceRequestData) { stream.write(129); - writeValue(stream, ((WebResourceRequestData) value).toMap()); + writeValue(stream, ((WebResourceRequestData) value).toList()); } else { super.writeValue(stream, value); } @@ -2247,7 +2373,7 @@ static void setup(BinaryMessenger binaryMessenger, DownloadListenerHostApi api) if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -2256,9 +2382,10 @@ static void setup(BinaryMessenger binaryMessenger, DownloadListenerHostApi api) throw new NullPointerException("instanceIdArg unexpectedly null."); } api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2315,6 +2442,9 @@ public void onDownloadStart( public interface WebChromeClientHostApi { void create(@NonNull Long instanceId); + void setSynchronousReturnValueForOnShowFileChooser( + @NonNull Long instanceId, @NonNull Boolean value); + /** The codec used by WebChromeClientHostApi. */ static MessageCodec getCodec() { return new StandardMessageCodec(); @@ -2331,7 +2461,7 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -2340,9 +2470,44 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.WebChromeClientHostApi.setSynchronousReturnValueForOnShowFileChooser", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + Boolean valueArg = (Boolean) args.get(1); + if (valueArg == null) { + throw new NullPointerException("valueArg unexpectedly null."); + } + api.setSynchronousReturnValueForOnShowFileChooser( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), valueArg); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2376,7 +2541,7 @@ static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi ap if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -2385,9 +2550,10 @@ static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi ap throw new NullPointerException("pathArg unexpectedly null."); } List output = api.list(pathArg); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2404,7 +2570,7 @@ static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi ap if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -2413,9 +2579,10 @@ static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi ap throw new NullPointerException("nameArg unexpectedly null."); } String output = api.getAssetFilePathByName(nameArg); - wrapped.put("result", output); + wrapped.add(0, output); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2457,6 +2624,26 @@ public void onProgressChanged( callback.reply(null); }); } + + public void onShowFileChooser( + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull Long paramsInstanceIdArg, + Reply> callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser", + getCodec()); + channel.send( + new ArrayList( + Arrays.asList(instanceIdArg, webViewInstanceIdArg, paramsInstanceIdArg)), + channelReply -> { + @SuppressWarnings("ConstantConditions") + List output = (List) channelReply; + callback.reply(output); + }); + } } /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebStorageHostApi { @@ -2479,7 +2666,7 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -2488,9 +2675,10 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2505,7 +2693,7 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - Map wrapped = new HashMap<>(); + ArrayList wrapped = new ArrayList<>(); try { ArrayList args = (ArrayList) message; assert args != null; @@ -2514,9 +2702,10 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } api.deleteAllData((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); + wrapped.add(0, null); } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; } reply.reply(wrapped); }); @@ -2527,14 +2716,84 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { } } + private static class FileChooserParamsFlutterApiCodec extends StandardMessageCodec { + public static final FileChooserParamsFlutterApiCodec INSTANCE = + new FileChooserParamsFlutterApiCodec(); + + private FileChooserParamsFlutterApiCodec() {} + + @Override + protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return FileChooserModeEnumData.fromList((ArrayList) readValue(buffer)); + + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { + if (value instanceof FileChooserModeEnumData) { + stream.write(128); + writeValue(stream, ((FileChooserModeEnumData) value).toList()); + } else { + super.writeValue(stream, value); + } + } + } + + /** + * Handles callbacks methods for the native Java FileChooserParams class. + * + *

See + * https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams. + * + *

Generated class from Pigeon that represents Flutter messages that can be called from Java. + */ + public static class FileChooserParamsFlutterApi { + private final BinaryMessenger binaryMessenger; + + public FileChooserParamsFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + /** The codec used by FileChooserParamsFlutterApi. */ + static MessageCodec getCodec() { + return FileChooserParamsFlutterApiCodec.INSTANCE; + } + + public void create( + @NonNull Long instanceIdArg, + @NonNull Boolean isCaptureEnabledArg, + @NonNull List acceptTypesArg, + @NonNull FileChooserModeEnumData modeArg, + @Nullable String filenameHintArg, + Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.FileChooserParamsFlutterApi.create", getCodec()); + channel.send( + new ArrayList( + Arrays.asList( + instanceIdArg, isCaptureEnabledArg, acceptTypesArg, modeArg, filenameHintArg)), + channelReply -> { + callback.reply(null); + }); + } + } + @NonNull - private static Map wrapError(@NonNull Throwable exception) { - Map errorMap = new HashMap<>(); - errorMap.put("message", exception.toString()); - errorMap.put("code", exception.getClass().getSimpleName()); - errorMap.put( - "details", + private static ArrayList wrapError(@NonNull Throwable exception) { + ArrayList errorList = new ArrayList<>(3); + errorList.add(exception.toString()); + errorList.add(exception.getClass().getSimpleName()); + errorList.add( "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); - return errorMap; + return errorList; } } 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 index cf263e2a6d34..92f0e41905cc 100644 --- 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 @@ -4,10 +4,14 @@ package io.flutter.plugins.webviewflutter; +import android.os.Build; import android.webkit.WebChromeClient; import android.webkit.WebView; +import androidx.annotation.RequiresApi; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientFlutterApi; +import java.util.List; +import java.util.Objects; /** * Flutter Api implementation for {@link WebChromeClient}. @@ -15,6 +19,7 @@ *

Passes arguments of callbacks methods from a {@link WebChromeClient} to Dart. */ public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi { + private final BinaryMessenger binaryMessenger; private final InstanceManager instanceManager; /** @@ -26,6 +31,7 @@ public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi { public WebChromeClientFlutterApiImpl( BinaryMessenger binaryMessenger, InstanceManager instanceManager) { super(binaryMessenger); + this.binaryMessenger = binaryMessenger; this.instanceManager = instanceManager; } @@ -40,6 +46,27 @@ public void onProgressChanged( getIdentifierForClient(webChromeClient), webViewIdentifier, progress, callback); } + /** Passes arguments from {@link WebChromeClient#onShowFileChooser} to Dart. */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public void onShowFileChooser( + WebChromeClient webChromeClient, + WebView webView, + WebChromeClient.FileChooserParams fileChooserParams, + Reply> callback) { + Long paramsInstanceId = instanceManager.getIdentifierForStrongReference(fileChooserParams); + if (paramsInstanceId == null) { + final FileChooserParamsFlutterApiImpl flutterApi = + new FileChooserParamsFlutterApiImpl(binaryMessenger, instanceManager); + paramsInstanceId = flutterApi.create(fileChooserParams, reply -> {}); + } + + onShowFileChooser( + Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webChromeClient)), + Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView)), + paramsInstanceId, + callback); + } + private long getIdentifierForClient(WebChromeClient webChromeClient) { final Long identifier = instanceManager.getIdentifierForStrongReference(webChromeClient); if (identifier == 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 3fa4a2f9c298..a5825c0133ec 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java @@ -4,8 +4,10 @@ package io.flutter.plugins.webviewflutter; +import android.net.Uri; import android.os.Build; import android.os.Message; +import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebView; @@ -15,6 +17,7 @@ import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi; +import java.util.Objects; /** * Host api implementation for {@link WebChromeClient}. @@ -31,6 +34,7 @@ public class WebChromeClientHostApiImpl implements WebChromeClientHostApi { */ public static class WebChromeClientImpl extends SecureWebChromeClient { private final WebChromeClientFlutterApiImpl flutterApi; + private boolean returnValueForOnShowFileChooser = false; /** * Creates a {@link WebChromeClient} that passes arguments of callbacks methods to Dart. @@ -45,6 +49,36 @@ public WebChromeClientImpl(@NonNull WebChromeClientFlutterApiImpl flutterApi) { public void onProgressChanged(WebView view, int progress) { flutterApi.onProgressChanged(this, view, (long) progress, reply -> {}); } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean onShowFileChooser( + WebView webView, + ValueCallback filePathCallback, + FileChooserParams fileChooserParams) { + final boolean currentReturnValueForOnShowFileChooser = returnValueForOnShowFileChooser; + flutterApi.onShowFileChooser( + this, + webView, + fileChooserParams, + reply -> { + // The returned list of file paths can only be passed to `filePathCallback` if the + // `onShowFileChooser` method returned true. + if (currentReturnValueForOnShowFileChooser) { + final Uri[] filePaths = new Uri[reply.size()]; + for (int i = 0; i < reply.size(); i++) { + filePaths[i] = Uri.parse(reply.get(i)); + } + filePathCallback.onReceiveValue(filePaths); + } + }); + return currentReturnValueForOnShowFileChooser; + } + + /** Sets return value for {@link #onShowFileChooser}. */ + public void setReturnValueForOnShowFileChooser(boolean value) { + returnValueForOnShowFileChooser = value; + } } /** @@ -163,4 +197,12 @@ public void create(Long instanceId) { webChromeClientCreator.createWebChromeClient(flutterApi); instanceManager.addDartCreatedInstance(webChromeClient, instanceId); } + + @Override + public void setSynchronousReturnValueForOnShowFileChooser( + @NonNull Long instanceId, @NonNull Boolean value) { + final WebChromeClientImpl webChromeClient = + Objects.requireNonNull(instanceManager.getInstance(instanceId)); + webChromeClient.setReturnValueForOnShowFileChooser(value); + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FileChooserParamsTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FileChooserParamsTest.java new file mode 100644 index 000000000000..3172ea4330c8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FileChooserParamsTest.java @@ -0,0 +1,74 @@ +// 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 static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.webkit.WebChromeClient.FileChooserParams; +import io.flutter.plugin.common.BinaryMessenger; +import java.util.Arrays; +import java.util.Objects; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class FileChooserParamsTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public FileChooserParams mockFileChooserParams; + + @Mock public BinaryMessenger mockBinaryMessenger; + + InstanceManager instanceManager; + + @Before + public void setUp() { + instanceManager = InstanceManager.open(identifier -> {}); + } + + @After + public void tearDown() { + instanceManager.close(); + } + + @Test + public void flutterApiCreate() { + final FileChooserParamsFlutterApiImpl spyFlutterApi = + spy(new FileChooserParamsFlutterApiImpl(mockBinaryMessenger, instanceManager)); + + when(mockFileChooserParams.isCaptureEnabled()).thenReturn(true); + when(mockFileChooserParams.getAcceptTypes()).thenReturn(new String[] {"my", "list"}); + when(mockFileChooserParams.getMode()).thenReturn(FileChooserParams.MODE_OPEN_MULTIPLE); + when(mockFileChooserParams.getFilenameHint()).thenReturn("filenameHint"); + spyFlutterApi.create(mockFileChooserParams, reply -> {}); + + final long identifier = + Objects.requireNonNull( + instanceManager.getIdentifierForStrongReference(mockFileChooserParams)); + final ArgumentCaptor modeCaptor = + ArgumentCaptor.forClass(GeneratedAndroidWebView.FileChooserModeEnumData.class); + + verify(spyFlutterApi) + .create( + eq(identifier), + eq(true), + eq(Arrays.asList("my", "list")), + modeCaptor.capture(), + eq("filenameHint"), + any()); + assertEquals( + modeCaptor.getValue().getValue(), GeneratedAndroidWebView.FileChooserMode.OPEN_MULTIPLE); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart index 180175a22a0a..cbec6b767952 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart @@ -1557,7 +1557,7 @@ class CopyableObjectWithCallback with Copyable { class ClassWithCallbackClass { ClassWithCallbackClass() { callbackClass = CopyableObjectWithCallback( - withWeakRefenceTo( + withWeakReferenceTo( this, (WeakReference weakReference) { return () { diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index f6a55b1f2795..3f62053d0ac3 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -1181,7 +1181,7 @@ class CopyableObjectWithCallback with Copyable { class ClassWithCallbackClass { ClassWithCallbackClass() { callbackClass = CopyableObjectWithCallback( - withWeakRefenceTo( + withWeakReferenceTo( this, (WeakReference weakReference) { return () { diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_navigation_delegate.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_navigation_delegate.dart deleted file mode 100644 index 51c62764fde4..000000000000 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_navigation_delegate.dart +++ /dev/null @@ -1,318 +0,0 @@ -// 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. - -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; - -import 'android_proxy.dart'; -import 'android_webview.dart' as android_webview; - -/// Signature for the `loadRequest` callback responsible for loading the [url] -/// after a navigation request has been approved. -typedef LoadRequestCallback = Future Function(LoadRequestParams params); - -/// Error returned in `WebView.onWebResourceError` when a web resource loading error has occurred. -@immutable -class AndroidWebResourceError extends WebResourceError { - /// Creates a new [AndroidWebResourceError]. - AndroidWebResourceError._({ - required super.errorCode, - required super.description, - super.isForMainFrame, - this.failingUrl, - }) : super( - errorType: _errorCodeToErrorType(errorCode), - ); - - /// Gets the URL for which the failing resource request was made. - final String? failingUrl; - - static WebResourceErrorType? _errorCodeToErrorType(int errorCode) { - switch (errorCode) { - case android_webview.WebViewClient.errorAuthentication: - return WebResourceErrorType.authentication; - case android_webview.WebViewClient.errorBadUrl: - return WebResourceErrorType.badUrl; - case android_webview.WebViewClient.errorConnect: - return WebResourceErrorType.connect; - case android_webview.WebViewClient.errorFailedSslHandshake: - return WebResourceErrorType.failedSslHandshake; - case android_webview.WebViewClient.errorFile: - return WebResourceErrorType.file; - case android_webview.WebViewClient.errorFileNotFound: - return WebResourceErrorType.fileNotFound; - case android_webview.WebViewClient.errorHostLookup: - return WebResourceErrorType.hostLookup; - case android_webview.WebViewClient.errorIO: - return WebResourceErrorType.io; - case android_webview.WebViewClient.errorProxyAuthentication: - return WebResourceErrorType.proxyAuthentication; - case android_webview.WebViewClient.errorRedirectLoop: - return WebResourceErrorType.redirectLoop; - case android_webview.WebViewClient.errorTimeout: - return WebResourceErrorType.timeout; - case android_webview.WebViewClient.errorTooManyRequests: - return WebResourceErrorType.tooManyRequests; - case android_webview.WebViewClient.errorUnknown: - return WebResourceErrorType.unknown; - case android_webview.WebViewClient.errorUnsafeResource: - return WebResourceErrorType.unsafeResource; - case android_webview.WebViewClient.errorUnsupportedAuthScheme: - return WebResourceErrorType.unsupportedAuthScheme; - case android_webview.WebViewClient.errorUnsupportedScheme: - return WebResourceErrorType.unsupportedScheme; - } - - throw ArgumentError( - 'Could not find a WebResourceErrorType for errorCode: $errorCode', - ); - } -} - -/// Object specifying creation parameters for creating a [AndroidNavigationDelegate]. -/// -/// When adding additional fields make sure they can be null or have a default -/// value to avoid breaking changes. See [PlatformNavigationDelegateCreationParams] for -/// more information. -@immutable -class AndroidNavigationDelegateCreationParams - extends PlatformNavigationDelegateCreationParams { - /// Creates a new [AndroidNavigationDelegateCreationParams] instance. - const AndroidNavigationDelegateCreationParams._({ - @visibleForTesting this.androidWebViewProxy = const AndroidWebViewProxy(), - }) : super(); - - /// Creates a [AndroidNavigationDelegateCreationParams] instance based on [PlatformNavigationDelegateCreationParams]. - factory AndroidNavigationDelegateCreationParams.fromPlatformNavigationDelegateCreationParams( - // Recommended placeholder to prevent being broken by platform interface. - // ignore: avoid_unused_constructor_parameters - PlatformNavigationDelegateCreationParams params, { - @visibleForTesting - AndroidWebViewProxy androidWebViewProxy = const AndroidWebViewProxy(), - }) { - return AndroidNavigationDelegateCreationParams._( - androidWebViewProxy: androidWebViewProxy, - ); - } - - /// Handles constructing objects and calling static methods for the Android WebView - /// native library. - @visibleForTesting - final AndroidWebViewProxy androidWebViewProxy; -} - -/// A place to register callback methods responsible to handle navigation events -/// triggered by the [android_webview.WebView]. -class AndroidNavigationDelegate extends PlatformNavigationDelegate { - /// Creates a new [AndroidNavigationkDelegate]. - AndroidNavigationDelegate(PlatformNavigationDelegateCreationParams params) - : super.implementation(params is AndroidNavigationDelegateCreationParams - ? params - : AndroidNavigationDelegateCreationParams - .fromPlatformNavigationDelegateCreationParams(params)) { - final WeakReference weakThis = - WeakReference(this); - - _webChromeClient = (this.params as AndroidNavigationDelegateCreationParams) - .androidWebViewProxy - .createAndroidWebChromeClient( - onProgressChanged: (android_webview.WebView webView, int progress) { - if (weakThis.target?._onProgress != null) { - weakThis.target!._onProgress!(progress); - } - }); - - _webViewClient = (this.params as AndroidNavigationDelegateCreationParams) - .androidWebViewProxy - .createAndroidWebViewClient( - onPageFinished: (android_webview.WebView webView, String url) { - if (weakThis.target?._onPageFinished != null) { - weakThis.target!._onPageFinished!(url); - } - }, - onPageStarted: (android_webview.WebView webView, String url) { - if (weakThis.target?._onPageStarted != null) { - weakThis.target!._onPageStarted!(url); - } - }, - onReceivedRequestError: ( - android_webview.WebView webView, - android_webview.WebResourceRequest request, - android_webview.WebResourceError error, - ) { - if (weakThis.target?._onWebResourceError != null) { - weakThis.target!._onWebResourceError!(AndroidWebResourceError._( - errorCode: error.errorCode, - description: error.description, - failingUrl: request.url, - isForMainFrame: request.isForMainFrame, - )); - } - }, - onReceivedError: ( - android_webview.WebView webView, - int errorCode, - String description, - String failingUrl, - ) { - if (weakThis.target?._onWebResourceError != null) { - weakThis.target!._onWebResourceError!(AndroidWebResourceError._( - errorCode: errorCode, - description: description, - failingUrl: failingUrl, - isForMainFrame: true, - )); - } - }, - requestLoading: ( - android_webview.WebView webView, - android_webview.WebResourceRequest request, - ) { - if (weakThis.target != null) { - weakThis.target!._handleNavigation( - request.url, - headers: request.requestHeaders, - isForMainFrame: request.isForMainFrame, - ); - } - }, - urlLoading: ( - android_webview.WebView webView, - String url, - ) { - if (weakThis.target != null) { - weakThis.target!._handleNavigation(url, isForMainFrame: true); - } - }, - ); - - _downloadListener = (this.params as AndroidNavigationDelegateCreationParams) - .androidWebViewProxy - .createDownloadListener( - onDownloadStart: ( - String url, - String userAgent, - String contentDisposition, - String mimetype, - int contentLength, - ) { - if (weakThis.target != null) { - weakThis.target?._handleNavigation(url, isForMainFrame: true); - } - }, - ); - } - - late final android_webview.WebChromeClient _webChromeClient; - - /// Gets the native [android_webview.WebChromeClient] that is bridged by this [AndroidNavigationDelegate]. - /// - /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setWebChromeClient`. - android_webview.WebChromeClient get androidWebChromeClient => - _webChromeClient; - - late final android_webview.WebViewClient _webViewClient; - - /// Gets the native [android_webview.WebViewClient] that is bridged by this [AndroidNavigationDelegate]. - /// - /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setWebViewClient`. - android_webview.WebViewClient get androidWebViewClient => _webViewClient; - - late final android_webview.DownloadListener _downloadListener; - - /// Gets the native [android_webview.DownloadListener] that is bridged by this [AndroidNavigationDelegate]. - /// - /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setDownloadListener`. - android_webview.DownloadListener get androidDownloadListener => - _downloadListener; - - PageEventCallback? _onPageFinished; - PageEventCallback? _onPageStarted; - ProgressCallback? _onProgress; - WebResourceErrorCallback? _onWebResourceError; - NavigationRequestCallback? _onNavigationRequest; - LoadRequestCallback? _onLoadRequest; - - void _handleNavigation( - String url, { - required bool isForMainFrame, - Map headers = const {}, - }) { - final LoadRequestCallback? onLoadRequest = _onLoadRequest; - final NavigationRequestCallback? onNavigationRequest = _onNavigationRequest; - - if (onNavigationRequest == null || onLoadRequest == null) { - return; - } - - final FutureOr returnValue = onNavigationRequest( - NavigationRequest( - url: url, - isMainFrame: isForMainFrame, - ), - ); - - if (returnValue is NavigationDecision && - returnValue == NavigationDecision.navigate) { - onLoadRequest(LoadRequestParams( - uri: Uri.parse(url), - headers: headers, - )); - } else if (returnValue is Future) { - returnValue.then((NavigationDecision shouldLoadUrl) { - if (shouldLoadUrl == NavigationDecision.navigate) { - onLoadRequest(LoadRequestParams( - uri: Uri.parse(url), - headers: headers, - )); - } - }); - } - } - - /// Invoked when loading the url after a navigation request is approved. - Future setOnLoadRequest( - LoadRequestCallback onLoadRequest, - ) async { - _onLoadRequest = onLoadRequest; - } - - @override - Future setOnNavigationRequest( - NavigationRequestCallback onNavigationRequest, - ) async { - _onNavigationRequest = onNavigationRequest; - _webViewClient.setSynchronousReturnValueForShouldOverrideUrlLoading(true); - } - - @override - Future setOnPageStarted( - PageEventCallback onPageStarted, - ) async { - _onPageStarted = onPageStarted; - } - - @override - Future setOnPageFinished( - PageEventCallback onPageFinished, - ) async { - _onPageFinished = onPageFinished; - } - - @override - Future setOnProgress( - ProgressCallback onProgress, - ) async { - _onProgress = onProgress; - } - - @override - Future setOnWebResourceError( - WebResourceErrorCallback onWebResourceError, - ) async { - _onWebResourceError = onWebResourceError; - } -} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart index db247ee41d1c..9437e9dd3eb4 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart @@ -37,6 +37,11 @@ class AndroidWebViewProxy { final android_webview.WebChromeClient Function({ void Function(android_webview.WebView webView, int progress)? onProgressChanged, + Future> Function( + android_webview.WebView webView, + android_webview.FileChooserParams params, + )? + onShowFileChooser, }) createAndroidWebChromeClient; /// Constructs a [android_webview.WebViewClient]. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index 66f93dde1679..f7d536ce3972 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -20,6 +20,8 @@ import 'android_webview.pigeon.dart'; import 'android_webview_api_impls.dart'; import 'instance_manager.dart'; +export 'android_webview_api_impls.dart' show FileChooserMode; + /// Root of the Java class hierarchy. /// /// See https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html. @@ -871,7 +873,8 @@ class DownloadListener extends JavaObject { /// Handles JavaScript dialogs, favicons, titles, and the progress for [WebView]. class WebChromeClient extends JavaObject { /// Constructs a [WebChromeClient]. - WebChromeClient({this.onProgressChanged}) : super.detached() { + WebChromeClient({this.onProgressChanged, this.onShowFileChooser}) + : super.detached() { AndroidWebViewFlutterApis.instance.ensureSetUp(); api.createFromInstance(this); } @@ -881,7 +884,10 @@ class WebChromeClient extends JavaObject { /// /// This should only be used by subclasses created by this library or to /// create copies. - WebChromeClient.detached({this.onProgressChanged}) : super.detached(); + WebChromeClient.detached({ + this.onProgressChanged, + this.onShowFileChooser, + }) : super.detached(); /// Pigeon Host Api implementation for [WebChromeClient]. @visibleForTesting @@ -890,9 +896,95 @@ class WebChromeClient extends JavaObject { /// Notify the host application that a file should be downloaded. final void Function(WebView webView, int progress)? onProgressChanged; + /// Indicates the client should show a file chooser. + /// + /// To handle the request for a file chooser with this callback, passing true + /// to [setSynchronousReturnValueForOnShowFileChooser] is required. Otherwise, + /// the returned list of strings will be ignored and the client will use the + /// default handling of a file chooser request. + /// + /// Only invoked on Android versions 21+. + final Future> Function( + WebView webView, + FileChooserParams params, + )? onShowFileChooser; + + /// Sets the required synchronous return value for the Java method, + /// `WebChromeClient.onShowFileChooser(...)`. + /// + /// The Java method, `WebChromeClient.onShowFileChooser(...)`, requires + /// a boolean to be returned and this method sets the returned value for all + /// calls to the Java method. + /// + /// Setting this to true indicates that all file chooser requests should be + /// handled by [onShowFileChooser] and the returned list of Strings will be + /// returned to the WebView. Otherwise, the client will use the default + /// handling and the returned value in [onShowFileChooser] will be ignored. + /// + /// Requires [onShowFileChooser] to be nonnull. + /// + /// Defaults to false. + Future setSynchronousReturnValueForOnShowFileChooser( + bool value, + ) { + if (value && onShowFileChooser != null) { + throw StateError( + 'Setting this to true requires `onShowFileChooser` to be nonnull.', + ); + } + return api.setSynchronousReturnValueForOnShowFileChooserFromInstance( + this, + value, + ); + } + @override WebChromeClient copy() { - return WebChromeClient.detached(onProgressChanged: onProgressChanged); + return WebChromeClient.detached( + onProgressChanged: onProgressChanged, + onShowFileChooser: onShowFileChooser, + ); + } +} + +/// Parameters received when a [WebChromeClient] should show a file chooser. +/// +/// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams. +class FileChooserParams extends JavaObject { + /// Constructs a [FileChooserParams] without creating the associated Java + /// object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + FileChooserParams.detached({ + required this.isCaptureEnabled, + required this.acceptTypes, + required this.filenameHint, + required this.mode, + super.binaryMessenger, + super.instanceManager, + }) : super.detached(); + + /// Preference for a live media captured value (e.g. Camera, Microphone). + final bool isCaptureEnabled; + + /// A list of acceptable MIME types. + final List acceptTypes; + + /// The file name of a default selection if specified, or null. + final String? filenameHint; + + /// Mode of how to select files for a file chooser. + final FileChooserMode mode; + + @override + FileChooserParams copy() { + return FileChooserParams.detached( + isCaptureEnabled: isCaptureEnabled, + acceptTypes: acceptTypes, + filenameHint: filenameHint, + mode: mode, + ); } } diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 5bdab16d6720..d3c306a10238 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v4.2.3), do not edit directly. +// Autogenerated from Pigeon (v4.2.14), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import import 'dart:async'; @@ -10,6 +10,48 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +/// Mode of how to select files for a file chooser. +/// +/// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams. +enum FileChooserMode { + /// Open single file and requires that the file exists before allowing the + /// user to pick it. + /// + /// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_OPEN. + open, + + /// Similar to [open] but allows multiple files to be selected. + /// + /// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_OPEN_MULTIPLE. + openMultiple, + + /// Allows picking a nonexistent file and saving it. + /// + /// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_SAVE. + save, +} + +class FileChooserModeEnumData { + FileChooserModeEnumData({ + required this.value, + }); + + FileChooserMode value; + + Object encode() { + return [ + value.index, + ]; + } + + static FileChooserModeEnumData decode(Object result) { + result as List; + return FileChooserModeEnumData( + value: FileChooserMode.values[result[0]! as int], + ); + } +} + class WebResourceRequestData { WebResourceRequestData({ required this.url, @@ -21,33 +63,38 @@ class WebResourceRequestData { }); String url; + bool isForMainFrame; + bool? isRedirect; + bool hasGesture; + String method; + Map requestHeaders; Object encode() { - final Map pigeonMap = {}; - pigeonMap['url'] = url; - pigeonMap['isForMainFrame'] = isForMainFrame; - pigeonMap['isRedirect'] = isRedirect; - pigeonMap['hasGesture'] = hasGesture; - pigeonMap['method'] = method; - pigeonMap['requestHeaders'] = requestHeaders; - return pigeonMap; - } - - static WebResourceRequestData decode(Object message) { - final Map pigeonMap = message as Map; + return [ + url, + isForMainFrame, + isRedirect, + hasGesture, + method, + requestHeaders, + ]; + } + + static WebResourceRequestData decode(Object result) { + result as List; return WebResourceRequestData( - url: pigeonMap['url']! as String, - isForMainFrame: pigeonMap['isForMainFrame']! as bool, - isRedirect: pigeonMap['isRedirect'] as bool?, - hasGesture: pigeonMap['hasGesture']! as bool, - method: pigeonMap['method']! as String, - requestHeaders: (pigeonMap['requestHeaders'] as Map?)! - .cast(), + url: result[0]! as String, + isForMainFrame: result[1]! as bool, + isRedirect: result[2] as bool?, + hasGesture: result[3]! as bool, + method: result[4]! as String, + requestHeaders: + (result[5] as Map?)!.cast(), ); } } @@ -59,20 +106,21 @@ class WebResourceErrorData { }); int errorCode; + String description; Object encode() { - final Map pigeonMap = {}; - pigeonMap['errorCode'] = errorCode; - pigeonMap['description'] = description; - return pigeonMap; + return [ + errorCode, + description, + ]; } - static WebResourceErrorData decode(Object message) { - final Map pigeonMap = message as Map; + static WebResourceErrorData decode(Object result) { + result as List; return WebResourceErrorData( - errorCode: pigeonMap['errorCode']! as int, - description: pigeonMap['description']! as String, + errorCode: result[0]! as int, + description: result[1]! as String, ); } } @@ -84,20 +132,21 @@ class WebViewPoint { }); int x; + int y; Object encode() { - final Map pigeonMap = {}; - pigeonMap['x'] = x; - pigeonMap['y'] = y; - return pigeonMap; + return [ + x, + y, + ]; } - static WebViewPoint decode(Object message) { - final Map pigeonMap = message as Map; + static WebViewPoint decode(Object result) { + result as List; return WebViewPoint( - x: pigeonMap['x']! as int, - y: pigeonMap['y']! as int, + x: result[0]! as int, + y: result[1]! as int, ); } } @@ -121,20 +170,18 @@ class JavaObjectHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_identifier]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_identifier]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -149,6 +196,7 @@ abstract class JavaObjectFlutterApi { static const MessageCodec codec = StandardMessageCodec(); void dispose(int identifier); + static void setup(JavaObjectFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -187,28 +235,25 @@ class CookieManagerHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.CookieManagerHostApi.clearCookies', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send(null) as Map?; - if (replyMap == null) { + final List? replyList = await channel.send(null) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); - } else if (replyMap['result'] == null) { + } else if (replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyMap['result'] as bool?)!; + return (replyList[0] as bool?)!; } } @@ -216,20 +261,18 @@ class CookieManagerHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.CookieManagerHostApi.setCookie', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_url, arg_value]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_url, arg_value]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -275,21 +318,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.create', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = + final List? replyList = await channel.send([arg_instanceId, arg_useHybridComposition]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -301,21 +342,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel.send( + final List? replyList = await channel.send( [arg_instanceId, arg_data, arg_mimeType, arg_encoding]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -332,26 +371,24 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel.send([ + final List? replyList = await channel.send([ arg_instanceId, arg_baseUrl, arg_data, arg_mimeType, arg_encoding, arg_historyUrl - ]) as Map?; - if (replyMap == null) { + ]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -363,21 +400,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = + final List? replyList = await channel.send([arg_instanceId, arg_url, arg_headers]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -389,21 +424,18 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId, arg_url, arg_data]) - as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_url, arg_data]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -414,23 +446,21 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { - return (replyMap['result'] as String?); + return (replyList[0] as String?); } } @@ -438,28 +468,26 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.canGoBack', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); - } else if (replyMap['result'] == null) { + } else if (replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyMap['result'] as bool?)!; + return (replyList[0] as bool?)!; } } @@ -467,28 +495,26 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.canGoForward', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); - } else if (replyMap['result'] == null) { + } else if (replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyMap['result'] as bool?)!; + return (replyList[0] as bool?)!; } } @@ -496,20 +522,18 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.goBack', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -520,20 +544,18 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.goForward', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -544,20 +566,18 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.reload', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -568,21 +588,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.clearCache', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = + final List? replyList = await channel.send([arg_instanceId, arg_includeDiskFiles]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -594,24 +612,22 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.evaluateJavascript', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = + final List? replyList = await channel.send([arg_instanceId, arg_javascriptString]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { - return (replyMap['result'] as String?); + return (replyList[0] as String?); } } @@ -619,23 +635,21 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getTitle', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { - return (replyMap['result'] as String?); + return (replyList[0] as String?); } } @@ -643,21 +657,18 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.scrollTo', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId, arg_x, arg_y]) - as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_x, arg_y]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -668,21 +679,18 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.scrollBy', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId, arg_x, arg_y]) - as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_x, arg_y]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -693,28 +701,26 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getScrollX', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); - } else if (replyMap['result'] == null) { + } else if (replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyMap['result'] as int?)!; + return (replyList[0] as int?)!; } } @@ -722,28 +728,26 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getScrollY', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); - } else if (replyMap['result'] == null) { + } else if (replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyMap['result'] as int?)!; + return (replyList[0] as int?)!; } } @@ -751,28 +755,26 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); - } else if (replyMap['result'] == null) { + } else if (replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyMap['result'] as WebViewPoint?)!; + return (replyList[0] as WebViewPoint?)!; } } @@ -781,20 +783,18 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_enabled]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_enabled]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -806,21 +806,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setWebViewClient', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel + final List? replyList = await channel .send([arg_instanceId, arg_webViewClientInstanceId]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -832,21 +830,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel + final List? replyList = await channel .send([arg_instanceId, arg_javaScriptChannelInstanceId]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -858,21 +854,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel + final List? replyList = await channel .send([arg_instanceId, arg_javaScriptChannelInstanceId]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -884,21 +878,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setDownloadListener', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = + final List? replyList = await channel.send([arg_instanceId, arg_listenerInstanceId]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -910,21 +902,19 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setWebChromeClient', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = + final List? replyList = await channel.send([arg_instanceId, arg_clientInstanceId]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -935,20 +925,18 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_color]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_color]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -970,21 +958,19 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.create', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = + final List? replyList = await channel.send([arg_instanceId, arg_webViewInstanceId]) - as Map?; - if (replyMap == null) { + as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -995,20 +981,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_flag]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1021,20 +1005,18 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_flag]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1047,20 +1029,18 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_support]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_support]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1071,20 +1051,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_flag]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1096,21 +1074,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId, arg_userAgentString]) - as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_userAgentString]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1123,20 +1098,18 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_require]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_require]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1147,20 +1120,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_support]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_support]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1172,21 +1143,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId, arg_overview]) - as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_overview]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1197,20 +1165,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_use]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_use]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1222,20 +1188,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_enabled]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1247,20 +1211,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_enabled]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1271,20 +1233,18 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_enabled]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1306,21 +1266,18 @@ class JavaScriptChannelHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.JavaScriptChannelHostApi.create', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId, arg_channelName]) - as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_channelName]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1332,6 +1289,7 @@ abstract class JavaScriptChannelFlutterApi { static const MessageCodec codec = StandardMessageCodec(); void postMessage(int instanceId, String message); + static void setup(JavaScriptChannelFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1373,20 +1331,18 @@ class WebViewClientHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewClientHostApi.create', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1399,20 +1355,18 @@ class WebViewClientHostApi { 'dev.flutter.pigeon.WebViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_value]) as Map?; - if (replyMap == null) { + final List? replyList = await channel + .send([arg_instanceId, arg_value]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1454,14 +1408,20 @@ abstract class WebViewClientFlutterApi { static const MessageCodec codec = _WebViewClientFlutterApiCodec(); void onPageStarted(int instanceId, int webViewInstanceId, String url); + void onPageFinished(int instanceId, int webViewInstanceId, String url); + void onReceivedRequestError(int instanceId, int webViewInstanceId, WebResourceRequestData request, WebResourceErrorData error); + void onReceivedError(int instanceId, int webViewInstanceId, int errorCode, String description, String failingUrl); + void requestLoading( int instanceId, int webViewInstanceId, WebResourceRequestData request); + void urlLoading(int instanceId, int webViewInstanceId, String url); + static void setup(WebViewClientFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1647,20 +1607,18 @@ class DownloadListenerHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.DownloadListenerHostApi.create', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1673,6 +1631,7 @@ abstract class DownloadListenerFlutterApi { void onDownloadStart(int instanceId, String url, String userAgent, String contentDisposition, String mimetype, int contentLength); + static void setup(DownloadListenerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1728,20 +1687,42 @@ class WebChromeClientHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebChromeClientHostApi.create', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + Future setSynchronousReturnValueForOnShowFileChooser( + int arg_instanceId, bool arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebChromeClientHostApi.setSynchronousReturnValueForOnShowFileChooser', + codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel + .send([arg_instanceId, arg_value]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1763,28 +1744,26 @@ class FlutterAssetManagerHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_path]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_path]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); - } else if (replyMap['result'] == null) { + } else if (replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyMap['result'] as List?)!.cast(); + return (replyList[0] as List?)!.cast(); } } @@ -1793,28 +1772,26 @@ class FlutterAssetManagerHostApi { 'dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_name]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_name]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); - } else if (replyMap['result'] == null) { + } else if (replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyMap['result'] as String?)!; + return (replyList[0] as String?)!; } } } @@ -1823,6 +1800,10 @@ abstract class WebChromeClientFlutterApi { static const MessageCodec codec = StandardMessageCodec(); void onProgressChanged(int instanceId, int webViewInstanceId, int progress); + + Future> onShowFileChooser( + int instanceId, int webViewInstanceId, int paramsInstanceId); + static void setup(WebChromeClientFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1852,6 +1833,33 @@ abstract class WebChromeClientFlutterApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser was null, expected non-null int.'); + final int? arg_webViewInstanceId = (args[1] as int?); + assert(arg_webViewInstanceId != null, + 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser was null, expected non-null int.'); + final int? arg_paramsInstanceId = (args[2] as int?); + assert(arg_paramsInstanceId != null, + 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onShowFileChooser was null, expected non-null int.'); + final List output = await api.onShowFileChooser( + arg_instanceId!, arg_webViewInstanceId!, arg_paramsInstanceId!); + return output; + }); + } + } } } @@ -1869,20 +1877,18 @@ class WebStorageHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebStorageHostApi.create', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; @@ -1893,23 +1899,92 @@ class WebStorageHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { + final List? replyList = + await channel.send([arg_instanceId]) as List?; + if (replyList == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + } else if (replyList.length > 1) { throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], ); } else { return; } } } + +class _FileChooserParamsFlutterApiCodec extends StandardMessageCodec { + const _FileChooserParamsFlutterApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is FileChooserModeEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return FileChooserModeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +/// Handles callbacks methods for the native Java FileChooserParams class. +/// +/// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams. +abstract class FileChooserParamsFlutterApi { + static const MessageCodec codec = + _FileChooserParamsFlutterApiCodec(); + + void create(int instanceId, bool isCaptureEnabled, List acceptTypes, + FileChooserModeEnumData mode, String? filenameHint); + + static void setup(FileChooserParamsFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileChooserParamsFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FileChooserParamsFlutterApi.create was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.FileChooserParamsFlutterApi.create was null, expected non-null int.'); + final bool? arg_isCaptureEnabled = (args[1] as bool?); + assert(arg_isCaptureEnabled != null, + 'Argument for dev.flutter.pigeon.FileChooserParamsFlutterApi.create was null, expected non-null bool.'); + final List? arg_acceptTypes = + (args[2] as List?)?.cast(); + assert(arg_acceptTypes != null, + 'Argument for dev.flutter.pigeon.FileChooserParamsFlutterApi.create was null, expected non-null List.'); + final FileChooserModeEnumData? arg_mode = + (args[3] as FileChooserModeEnumData?); + assert(arg_mode != null, + 'Argument for dev.flutter.pigeon.FileChooserParamsFlutterApi.create was null, expected non-null FileChooserModeEnumData.'); + final String? arg_filenameHint = (args[4] as String?); + api.create(arg_instanceId!, arg_isCaptureEnabled!, arg_acceptTypes!, + arg_mode!, arg_filenameHint); + return; + }); + } + } + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index 8aa5f7d9dab4..023f3c4ac421 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -13,6 +13,8 @@ import 'android_webview.dart'; import 'android_webview.pigeon.dart'; import 'instance_manager.dart'; +export 'android_webview.pigeon.dart' show FileChooserMode; + /// Converts [WebResourceRequestData] to [WebResourceRequest] WebResourceRequest _toWebResourceRequest(WebResourceRequestData data) { return WebResourceRequest( @@ -42,6 +44,7 @@ class AndroidWebViewFlutterApis { WebViewClientFlutterApiImpl? webViewClientFlutterApi, WebChromeClientFlutterApiImpl? webChromeClientFlutterApi, JavaScriptChannelFlutterApiImpl? javaScriptChannelFlutterApi, + FileChooserParamsFlutterApiImpl? fileChooserParamsFlutterApi, }) { this.javaObjectFlutterApi = javaObjectFlutterApi ?? JavaObjectFlutterApiImpl(); @@ -53,6 +56,8 @@ class AndroidWebViewFlutterApis { webChromeClientFlutterApi ?? WebChromeClientFlutterApiImpl(); this.javaScriptChannelFlutterApi = javaScriptChannelFlutterApi ?? JavaScriptChannelFlutterApiImpl(); + this.fileChooserParamsFlutterApi = + fileChooserParamsFlutterApi ?? FileChooserParamsFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -77,6 +82,9 @@ class AndroidWebViewFlutterApis { /// Flutter Api for [JavaScriptChannel]. late final JavaScriptChannelFlutterApiImpl javaScriptChannelFlutterApi; + /// Flutter Api for [FileChooserParams]. + late final FileChooserParamsFlutterApiImpl fileChooserParamsFlutterApi; + /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { @@ -85,6 +93,7 @@ class AndroidWebViewFlutterApis { WebViewClientFlutterApi.setup(webViewClientFlutterApi); WebChromeClientFlutterApi.setup(webChromeClientFlutterApi); JavaScriptChannelFlutterApi.setup(javaScriptChannelFlutterApi); + FileChooserParamsFlutterApi.setup(fileChooserParamsFlutterApi); _haveBeenSetUp = true; } } @@ -781,6 +790,17 @@ class WebChromeClientHostApiImpl extends WebChromeClientHostApi { return create(identifier); } } + + /// Helper method to convert instances ids to objects. + Future setSynchronousReturnValueForOnShowFileChooserFromInstance( + WebChromeClient instance, + bool value, + ) { + return setSynchronousReturnValueForOnShowFileChooser( + instanceManager.getIdentifier(instance)!, + value, + ); + } } /// Flutter api implementation for [DownloadListener]. @@ -810,6 +830,26 @@ class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi { instance.onProgressChanged!(webViewInstance!, progress); } } + + @override + Future> onShowFileChooser( + int instanceId, + int webViewInstanceId, + int paramsInstanceId, + ) { + final WebChromeClient instance = + instanceManager.getInstanceWithWeakReference(instanceId)!; + if (instance.onShowFileChooser != null) { + return instance.onShowFileChooser!( + instanceManager.getInstanceWithWeakReference(webViewInstanceId)! + as WebView, + instanceManager.getInstanceWithWeakReference(paramsInstanceId)! + as FileChooserParams, + ); + } + + return Future>.value(const []); + } } /// Host api implementation for [WebStorage]. @@ -836,3 +876,32 @@ class WebStorageHostApiImpl extends WebStorageHostApi { return deleteAllData(instanceManager.getIdentifier(instance)!); } } + +/// Flutter api implementation for [FileChooserParams]. +class FileChooserParamsFlutterApiImpl extends FileChooserParamsFlutterApi { + /// Constructs a [FileChooserParamsFlutterApiImpl]. + FileChooserParamsFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Maintains instances stored to communicate with java objects. + final InstanceManager instanceManager; + + @override + void create( + int instanceId, + bool isCaptureEnabled, + List acceptTypes, + FileChooserModeEnumData mode, + String? filenameHint, + ) { + instanceManager.addHostCreatedInstance( + FileChooserParams.detached( + isCaptureEnabled: isCaptureEnabled, + acceptTypes: acceptTypes.cast(), + mode: mode.value, + filenameHint: filenameHint, + ), + instanceId, + ); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart index 2e32705a83ea..f6e54b86d8a0 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:async'; + // TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) // ignore: unnecessary_import import 'dart:typed_data'; @@ -11,10 +15,8 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'android_navigation_delegate.dart'; import 'android_proxy.dart'; import 'android_webview.dart' as android_webview; -import 'android_webview.dart'; import 'instance_manager.dart'; import 'platform_views_service_proxy.dart'; import 'weak_reference_utils.dart'; @@ -76,6 +78,8 @@ class AndroidWebViewController extends PlatformWebViewController { _webView.settings.setUseWideViewPort(true); _webView.settings.setDisplayZoomControls(false); _webView.settings.setBuiltInZoomControls(true); + + _webView.setWebChromeClient(_webChromeClient); } AndroidWebViewControllerCreationParams get _androidWebViewParams => @@ -91,6 +95,30 @@ class AndroidWebViewController extends PlatformWebViewController { useHybridComposition: true, ); + late final android_webview.WebChromeClient _webChromeClient = + withWeakReferenceTo(this, + (WeakReference weakReference) { + return _androidWebViewParams.androidWebViewProxy + .createAndroidWebChromeClient( + onProgressChanged: (android_webview.WebView webView, int progress) { + if (weakReference.target?._currentNavigationDelegate._onProgress != + null) { + weakReference + .target!._currentNavigationDelegate._onProgress!(progress); + } + }, + onShowFileChooser: (android_webview.WebView webView, + android_webview.FileChooserParams params) async { + if (weakReference.target?._onShowFileSelectorCallback != null) { + return weakReference.target!._onShowFileSelectorCallback!( + FileSelectorParams._fromFileChooserParams(params), + ); + } + return []; + }, + ); + }); + /// The native [android_webview.FlutterAssetManager] allows managing assets. late final android_webview.FlutterAssetManager _flutterAssetManager = _androidWebViewParams.androidWebViewProxy.createFlutterAssetManager(); @@ -103,6 +131,9 @@ class AndroidWebViewController extends PlatformWebViewController { // ignore: unused_field late AndroidNavigationDelegate _currentNavigationDelegate; + Future> Function(FileSelectorParams)? + _onShowFileSelectorCallback; + /// Whether to enable the platform's webview content debugging tools. /// /// Defaults to false. @@ -213,7 +244,6 @@ class AndroidWebViewController extends PlatformWebViewController { _currentNavigationDelegate = handler; handler.setOnLoadRequest(loadRequest); _webView.setWebViewClient(handler.androidWebViewClient); - _webView.setWebChromeClient(handler.androidWebChromeClient); _webView.setDownloadListener(handler.androidDownloadListener); } @@ -309,6 +339,79 @@ class AndroidWebViewController extends PlatformWebViewController { Future setMediaPlaybackRequiresUserGesture(bool require) { return _webView.settings.setMediaPlaybackRequiresUserGesture(require); } + + /// Sets the callback that is invoked when the client should show a file + /// selector. + Future setOnShowFileSelector( + Future> Function(FileSelectorParams params)? + onShowFileSelector, + ) { + _onShowFileSelectorCallback = onShowFileSelector; + return _webChromeClient.setSynchronousReturnValueForOnShowFileChooser( + onShowFileSelector != null, + ); + } +} + +/// Mode of how to select files for a file chooser. +enum FileSelectorMode { + /// Open single file and requires that the file exists before allowing the + /// user to pick it. + open, + + /// Similar to [open] but allows multiple files to be selected. + openMultiple, + + /// Allows picking a nonexistent file and saving it. + save, +} + +/// Parameters received when the `WebView` should show a file selector. +@immutable +class FileSelectorParams { + /// Constructs a [FileSelectorParams]. + const FileSelectorParams({ + required this.isCaptureEnabled, + required this.acceptTypes, + this.filenameHint, + required this.mode, + }); + + factory FileSelectorParams._fromFileChooserParams( + android_webview.FileChooserParams params, + ) { + final FileSelectorMode mode; + switch (params.mode) { + case android_webview.FileChooserMode.open: + mode = FileSelectorMode.open; + break; + case android_webview.FileChooserMode.openMultiple: + mode = FileSelectorMode.openMultiple; + break; + case android_webview.FileChooserMode.save: + mode = FileSelectorMode.save; + break; + } + + return FileSelectorParams( + isCaptureEnabled: params.isCaptureEnabled, + acceptTypes: params.acceptTypes, + mode: mode, + filenameHint: params.filenameHint, + ); + } + + /// Preference for a live media captured value (e.g. Camera, Microphone). + final bool isCaptureEnabled; + + /// A list of acceptable MIME types. + final List acceptTypes; + + /// The file name of a default selection if specified, or null. + final String? filenameHint; + + /// Mode of how to select files for a file selector. + final FileSelectorMode mode; } /// An implementation of [JavaScriptChannelParams] with the Android WebView API. @@ -325,7 +428,7 @@ class AndroidJavaScriptChannelParams extends JavaScriptChannelParams { }) : assert(name.isNotEmpty), _javaScriptChannel = webViewProxy.createJavaScriptChannel( name, - postMessage: withWeakRefenceTo( + postMessage: withWeakReferenceTo( onMessageReceived, (WeakReference weakReference) { return ( @@ -374,7 +477,8 @@ class AndroidWebViewWidgetCreationParams @visibleForTesting InstanceManager? instanceManager, @visibleForTesting this.platformViewsServiceProxy = const PlatformViewsServiceProxy(), - }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + }) : instanceManager = + instanceManager ?? android_webview.JavaObject.globalInstanceManager; /// Constructs a [WebKitWebViewWidgetCreationParams] using a /// [PlatformWebViewWidgetCreationParams]. @@ -488,3 +592,308 @@ class AndroidWebViewWidget extends PlatformWebViewWidget { } } } + +/// Signature for the `loadRequest` callback responsible for loading the [url] +/// after a navigation request has been approved. +typedef LoadRequestCallback = Future Function(LoadRequestParams params); + +/// Error returned in `WebView.onWebResourceError` when a web resource loading error has occurred. +@immutable +class AndroidWebResourceError extends WebResourceError { + /// Creates a new [AndroidWebResourceError]. + AndroidWebResourceError._({ + required super.errorCode, + required super.description, + super.isForMainFrame, + this.failingUrl, + }) : super( + errorType: _errorCodeToErrorType(errorCode), + ); + + /// Gets the URL for which the failing resource request was made. + final String? failingUrl; + + static WebResourceErrorType? _errorCodeToErrorType(int errorCode) { + switch (errorCode) { + case android_webview.WebViewClient.errorAuthentication: + return WebResourceErrorType.authentication; + case android_webview.WebViewClient.errorBadUrl: + return WebResourceErrorType.badUrl; + case android_webview.WebViewClient.errorConnect: + return WebResourceErrorType.connect; + case android_webview.WebViewClient.errorFailedSslHandshake: + return WebResourceErrorType.failedSslHandshake; + case android_webview.WebViewClient.errorFile: + return WebResourceErrorType.file; + case android_webview.WebViewClient.errorFileNotFound: + return WebResourceErrorType.fileNotFound; + case android_webview.WebViewClient.errorHostLookup: + return WebResourceErrorType.hostLookup; + case android_webview.WebViewClient.errorIO: + return WebResourceErrorType.io; + case android_webview.WebViewClient.errorProxyAuthentication: + return WebResourceErrorType.proxyAuthentication; + case android_webview.WebViewClient.errorRedirectLoop: + return WebResourceErrorType.redirectLoop; + case android_webview.WebViewClient.errorTimeout: + return WebResourceErrorType.timeout; + case android_webview.WebViewClient.errorTooManyRequests: + return WebResourceErrorType.tooManyRequests; + case android_webview.WebViewClient.errorUnknown: + return WebResourceErrorType.unknown; + case android_webview.WebViewClient.errorUnsafeResource: + return WebResourceErrorType.unsafeResource; + case android_webview.WebViewClient.errorUnsupportedAuthScheme: + return WebResourceErrorType.unsupportedAuthScheme; + case android_webview.WebViewClient.errorUnsupportedScheme: + return WebResourceErrorType.unsupportedScheme; + } + + throw ArgumentError( + 'Could not find a WebResourceErrorType for errorCode: $errorCode', + ); + } +} + +/// Object specifying creation parameters for creating a [AndroidNavigationDelegate]. +/// +/// When adding additional fields make sure they can be null or have a default +/// value to avoid breaking changes. See [PlatformNavigationDelegateCreationParams] for +/// more information. +@immutable +class AndroidNavigationDelegateCreationParams + extends PlatformNavigationDelegateCreationParams { + /// Creates a new [AndroidNavigationDelegateCreationParams] instance. + const AndroidNavigationDelegateCreationParams._({ + @visibleForTesting this.androidWebViewProxy = const AndroidWebViewProxy(), + }) : super(); + + /// Creates a [AndroidNavigationDelegateCreationParams] instance based on [PlatformNavigationDelegateCreationParams]. + factory AndroidNavigationDelegateCreationParams.fromPlatformNavigationDelegateCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformNavigationDelegateCreationParams params, { + @visibleForTesting + AndroidWebViewProxy androidWebViewProxy = const AndroidWebViewProxy(), + }) { + return AndroidNavigationDelegateCreationParams._( + androidWebViewProxy: androidWebViewProxy, + ); + } + + /// Handles constructing objects and calling static methods for the Android WebView + /// native library. + @visibleForTesting + final AndroidWebViewProxy androidWebViewProxy; +} + +/// A place to register callback methods responsible to handle navigation events +/// triggered by the [android_webview.WebView]. +class AndroidNavigationDelegate extends PlatformNavigationDelegate { + /// Creates a new [AndroidNavigationDelegate]. + AndroidNavigationDelegate(PlatformNavigationDelegateCreationParams params) + : super.implementation(params is AndroidNavigationDelegateCreationParams + ? params + : AndroidNavigationDelegateCreationParams + .fromPlatformNavigationDelegateCreationParams(params)) { + final WeakReference weakThis = + WeakReference(this); + + _webViewClient = (this.params as AndroidNavigationDelegateCreationParams) + .androidWebViewProxy + .createAndroidWebViewClient( + onPageFinished: (android_webview.WebView webView, String url) { + if (weakThis.target?._onPageFinished != null) { + weakThis.target!._onPageFinished!(url); + } + }, + onPageStarted: (android_webview.WebView webView, String url) { + if (weakThis.target?._onPageStarted != null) { + weakThis.target!._onPageStarted!(url); + } + }, + onReceivedRequestError: ( + android_webview.WebView webView, + android_webview.WebResourceRequest request, + android_webview.WebResourceError error, + ) { + if (weakThis.target?._onWebResourceError != null) { + weakThis.target!._onWebResourceError!(AndroidWebResourceError._( + errorCode: error.errorCode, + description: error.description, + failingUrl: request.url, + isForMainFrame: request.isForMainFrame, + )); + } + }, + onReceivedError: ( + android_webview.WebView webView, + int errorCode, + String description, + String failingUrl, + ) { + if (weakThis.target?._onWebResourceError != null) { + weakThis.target!._onWebResourceError!(AndroidWebResourceError._( + errorCode: errorCode, + description: description, + failingUrl: failingUrl, + isForMainFrame: true, + )); + } + }, + requestLoading: ( + android_webview.WebView webView, + android_webview.WebResourceRequest request, + ) { + if (weakThis.target != null) { + weakThis.target!._handleNavigation( + request.url, + headers: request.requestHeaders, + isForMainFrame: request.isForMainFrame, + ); + } + }, + urlLoading: ( + android_webview.WebView webView, + String url, + ) { + if (weakThis.target != null) { + weakThis.target!._handleNavigation(url, isForMainFrame: true); + } + }, + ); + + _downloadListener = (this.params as AndroidNavigationDelegateCreationParams) + .androidWebViewProxy + .createDownloadListener( + onDownloadStart: ( + String url, + String userAgent, + String contentDisposition, + String mimetype, + int contentLength, + ) { + if (weakThis.target != null) { + weakThis.target?._handleNavigation(url, isForMainFrame: true); + } + }, + ); + } + + AndroidNavigationDelegateCreationParams get _androidParams => + params as AndroidNavigationDelegateCreationParams; + + late final android_webview.WebChromeClient _webChromeClient = + _androidParams.androidWebViewProxy.createAndroidWebChromeClient(); + + /// Gets the native [android_webview.WebChromeClient] that is bridged by this [AndroidNavigationDelegate]. + /// + /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setWebChromeClient`. + @Deprecated( + 'This value is not used by `AndroidWebViewController` and has no effect on the `WebView`.', + ) + android_webview.WebChromeClient get androidWebChromeClient => + _webChromeClient; + + late final android_webview.WebViewClient _webViewClient; + + /// Gets the native [android_webview.WebViewClient] that is bridged by this [AndroidNavigationDelegate]. + /// + /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setWebViewClient`. + android_webview.WebViewClient get androidWebViewClient => _webViewClient; + + late final android_webview.DownloadListener _downloadListener; + + /// Gets the native [android_webview.DownloadListener] that is bridged by this [AndroidNavigationDelegate]. + /// + /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setDownloadListener`. + android_webview.DownloadListener get androidDownloadListener => + _downloadListener; + + PageEventCallback? _onPageFinished; + PageEventCallback? _onPageStarted; + ProgressCallback? _onProgress; + WebResourceErrorCallback? _onWebResourceError; + NavigationRequestCallback? _onNavigationRequest; + LoadRequestCallback? _onLoadRequest; + + void _handleNavigation( + String url, { + required bool isForMainFrame, + Map headers = const {}, + }) { + final LoadRequestCallback? onLoadRequest = _onLoadRequest; + final NavigationRequestCallback? onNavigationRequest = _onNavigationRequest; + + if (onNavigationRequest == null || onLoadRequest == null) { + return; + } + + final FutureOr returnValue = onNavigationRequest( + NavigationRequest( + url: url, + isMainFrame: isForMainFrame, + ), + ); + + if (returnValue is NavigationDecision && + returnValue == NavigationDecision.navigate) { + onLoadRequest(LoadRequestParams( + uri: Uri.parse(url), + headers: headers, + )); + } else if (returnValue is Future) { + returnValue.then((NavigationDecision shouldLoadUrl) { + if (shouldLoadUrl == NavigationDecision.navigate) { + onLoadRequest(LoadRequestParams( + uri: Uri.parse(url), + headers: headers, + )); + } + }); + } + } + + /// Invoked when loading the url after a navigation request is approved. + Future setOnLoadRequest( + LoadRequestCallback onLoadRequest, + ) async { + _onLoadRequest = onLoadRequest; + } + + @override + Future setOnNavigationRequest( + NavigationRequestCallback onNavigationRequest, + ) async { + _onNavigationRequest = onNavigationRequest; + _webViewClient.setSynchronousReturnValueForShouldOverrideUrlLoading(true); + } + + @override + Future setOnPageStarted( + PageEventCallback onPageStarted, + ) async { + _onPageStarted = onPageStarted; + } + + @override + Future setOnPageFinished( + PageEventCallback onPageFinished, + ) async { + _onPageFinished = onPageFinished; + } + + @override + Future setOnProgress( + ProgressCallback onProgress, + ) async { + _onProgress = onProgress; + } + + @override + Future setOnWebResourceError( + WebResourceErrorCallback onWebResourceError, + ) async { + _onWebResourceError = onWebResourceError; + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_platform.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_platform.dart index 24581ebd24dc..7997f69d7eba 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_platform.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_platform.dart @@ -4,7 +4,6 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'android_navigation_delegate.dart'; import 'android_webview_controller.dart'; import 'android_webview_cookie_manager.dart'; diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_widget.dart index fc1028e7af15..7e4ae9e6f9d1 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_widget.dart @@ -134,7 +134,7 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { final Map _javaScriptChannels = {}; - late final android_webview.WebViewClient _webViewClient = withWeakRefenceTo( + late final android_webview.WebViewClient _webViewClient = withWeakReferenceTo( this, (WeakReference weakReference) { return webViewProxy.createWebViewClient( onPageStarted: (_, String url) { @@ -213,7 +213,7 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { @visibleForTesting late final android_webview.DownloadListener downloadListener = android_webview.DownloadListener( - onDownloadStart: withWeakRefenceTo( + onDownloadStart: withWeakReferenceTo( this, (WeakReference weakReference) { return ( @@ -236,7 +236,7 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { @visibleForTesting late final android_webview.WebChromeClient webChromeClient = android_webview.WebChromeClient( - onProgressChanged: withWeakRefenceTo( + onProgressChanged: withWeakReferenceTo( this, (WeakReference weakReference) { return (_, int progress) { @@ -574,7 +574,7 @@ class WebViewAndroidJavaScriptChannel super.channelName, this.javascriptChannelRegistry, ) : super( - postMessage: withWeakRefenceTo( + postMessage: withWeakReferenceTo( javascriptChannelRegistry, (WeakReference weakReference) { return (String message) { diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/weak_reference_utils.dart b/packages/webview_flutter/webview_flutter_android/lib/src/weak_reference_utils.dart index ad0c9ebf4f5c..fd3e3f0dc273 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/weak_reference_utils.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/weak_reference_utils.dart @@ -25,7 +25,7 @@ /// ), /// ); /// ``` -S withWeakRefenceTo( +S withWeakReferenceTo( T reference, S Function(WeakReference weakReference) onCreate, ) { diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_flutter_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_flutter_android.dart index faab71549995..95f835ed8a1d 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_flutter_android.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_flutter_android.dart @@ -4,7 +4,6 @@ library webview_flutter_android; -export 'src/android_navigation_delegate.dart'; export 'src/android_webview_controller.dart'; export 'src/android_webview_cookie_manager.dart'; export 'src/android_webview_platform.dart'; diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index d3adac8ee4c4..29d3181f0fbf 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -26,6 +26,34 @@ import 'package:pigeon/pigeon.dart'; ), ), ) + +/// Mode of how to select files for a file chooser. +/// +/// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams. +enum FileChooserMode { + /// Open single file and requires that the file exists before allowing the + /// user to pick it. + /// + /// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_OPEN. + open, + + /// Similar to [open] but allows multiple files to be selected. + /// + /// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_OPEN_MULTIPLE. + openMultiple, + + /// Allows picking a nonexistent file and saving it. + /// + /// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams#MODE_SAVE. + save, +} + +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 +class FileChooserModeEnumData { + late FileChooserMode value; +} + class WebResourceRequestData { WebResourceRequestData( this.url, @@ -262,6 +290,11 @@ abstract class DownloadListenerFlutterApi { @HostApi(dartHostTestHandler: 'TestWebChromeClientHostApi') abstract class WebChromeClientHostApi { void create(int instanceId); + + void setSynchronousReturnValueForOnShowFileChooser( + int instanceId, + bool value, + ); } @HostApi(dartHostTestHandler: 'TestAssetManagerHostApi') @@ -274,6 +307,13 @@ abstract class FlutterAssetManagerHostApi { @FlutterApi() abstract class WebChromeClientFlutterApi { void onProgressChanged(int instanceId, int webViewInstanceId, int progress); + + @async + List onShowFileChooser( + int instanceId, + int webViewInstanceId, + int paramsInstanceId, + ); } @HostApi(dartHostTestHandler: 'TestWebStorageHostApi') @@ -282,3 +322,17 @@ abstract class WebStorageHostApi { void deleteAllData(int instanceId); } + +/// Handles callbacks methods for the native Java FileChooserParams class. +/// +/// See https://developer.android.com/reference/android/webkit/WebChromeClient.FileChooserParams. +@FlutterApi() +abstract class FileChooserParamsFlutterApi { + void create( + int instanceId, + bool isCaptureEnabled, + List acceptTypes, + FileChooserModeEnumData mode, + String? filenameHint, + ); +} diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 123a7c918870..b0088e22f403 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.1.3 +version: 3.2.0 environment: sdk: ">=2.17.0 <3.0.0" @@ -29,4 +29,4 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.3.2 - pigeon: ^4.2.3 + pigeon: ^4.2.14 diff --git a/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart index 26d4e686d389..dac7c69a84f3 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart @@ -346,22 +346,6 @@ void main() { isTrue); }); - test('onProgress', () { - final AndroidNavigationDelegate androidNavigationDelegate = - AndroidNavigationDelegate(_buildCreationParams()); - - late final int callbackProgress; - androidNavigationDelegate - .setOnProgress((int progress) => callbackProgress = progress); - - CapturingWebChromeClient.lastCreatedDelegate.onProgressChanged!( - android_webview.WebView.detached(), - 42, - ); - - expect(callbackProgress, 42); - }); - test( 'onLoadRequest from onDownloadStart should not be called when navigationRequestCallback is not specified', () { @@ -495,6 +479,7 @@ class CapturingWebViewClient extends android_webview.WebViewClient { class CapturingWebChromeClient extends android_webview.WebChromeClient { CapturingWebChromeClient({ super.onProgressChanged, + super.onShowFileChooser, }) : super.detached() { lastCreatedDelegate = this; } diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart index 0e7842ab467d..80fa1924487c 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart @@ -19,6 +19,7 @@ import 'package:webview_flutter_android/src/platform_views_service_proxy.dart'; import 'package:webview_flutter_android/webview_flutter_android.dart'; import 'package:webview_flutter_platform_interface/src/webview_platform.dart'; +import 'android_navigation_delegate_test.dart'; import 'android_webview_controller_test.mocks.dart'; @GenerateNiceMocks(>[ @@ -44,7 +45,16 @@ void main() { AndroidWebViewController createControllerWithMocks({ android_webview.FlutterAssetManager? mockFlutterAssetManager, android_webview.JavaScriptChannel? mockJavaScriptChannel, - android_webview.WebChromeClient? mockWebChromeClient, + android_webview.WebChromeClient Function({ + void Function(android_webview.WebView webView, int progress)? + onProgressChanged, + Future> Function( + android_webview.WebView webView, + android_webview.FileChooserParams params, + )? + onShowFileChooser, + })? + createWebChromeClient, android_webview.WebView? mockWebView, android_webview.WebViewClient? mockWebViewClient, android_webview.WebStorage? mockWebStorage, @@ -57,10 +67,17 @@ void main() { AndroidWebViewControllerCreationParams( androidWebStorage: mockWebStorage ?? MockWebStorage(), androidWebViewProxy: AndroidWebViewProxy( - createAndroidWebChromeClient: ( - {void Function(android_webview.WebView, int)? - onProgressChanged}) => - mockWebChromeClient ?? MockWebChromeClient(), + createAndroidWebChromeClient: createWebChromeClient ?? + ({ + void Function(android_webview.WebView, int)? + onProgressChanged, + Future> Function( + android_webview.WebView webView, + android_webview.FileChooserParams params, + )? + onShowFileChooser, + }) => + MockWebChromeClient(), createAndroidWebView: ({required bool useHybridComposition}) => nonNullMockWebView, createAndroidWebViewClient: ({ @@ -486,10 +503,88 @@ void main() { await controller.setPlatformNavigationDelegate(mockNavigationDelegate); - verifyInOrder([ - mockWebView.setWebViewClient(mockWebViewClient), - mockWebView.setWebChromeClient(mockWebChromeClient), - ]); + verify(mockWebView.setWebViewClient(mockWebViewClient)); + verifyNever(mockWebView.setWebChromeClient(mockWebChromeClient)); + }); + + test('onProgress', () { + final AndroidNavigationDelegate androidNavigationDelegate = + AndroidNavigationDelegate( + AndroidNavigationDelegateCreationParams + .fromPlatformNavigationDelegateCreationParams( + const PlatformNavigationDelegateCreationParams(), + androidWebViewProxy: const AndroidWebViewProxy( + createAndroidWebViewClient: android_webview.WebViewClient.detached, + createAndroidWebChromeClient: + android_webview.WebChromeClient.detached, + createDownloadListener: android_webview.DownloadListener.detached, + ), + ), + ); + + late final int callbackProgress; + androidNavigationDelegate + .setOnProgress((int progress) => callbackProgress = progress); + + final AndroidWebViewController controller = createControllerWithMocks( + createWebChromeClient: CapturingWebChromeClient.new, + ); + controller.setPlatformNavigationDelegate(androidNavigationDelegate); + + CapturingWebChromeClient.lastCreatedDelegate.onProgressChanged!( + android_webview.WebView.detached(), + 42, + ); + + expect(callbackProgress, 42); + }); + + test('setOnShowFileSelector', () async { + late final Future> Function( + android_webview.WebView webView, + android_webview.FileChooserParams params, + ) onShowFileChooserCallback; + final MockWebChromeClient mockWebChromeClient = MockWebChromeClient(); + final AndroidWebViewController controller = createControllerWithMocks( + createWebChromeClient: ({ + dynamic onProgressChanged, + Future> Function( + android_webview.WebView webView, + android_webview.FileChooserParams params, + )? + onShowFileChooser, + }) { + onShowFileChooserCallback = onShowFileChooser!; + return mockWebChromeClient; + }, + ); + + late final FileSelectorParams fileSelectorParams; + await controller.setOnShowFileSelector( + (FileSelectorParams params) async { + fileSelectorParams = params; + return []; + }, + ); + + verify( + mockWebChromeClient.setSynchronousReturnValueForOnShowFileChooser(true), + ); + + onShowFileChooserCallback( + android_webview.WebView.detached(), + android_webview.FileChooserParams.detached( + isCaptureEnabled: false, + acceptTypes: ['png'], + filenameHint: 'filenameHint', + mode: android_webview.FileChooserMode.open, + ), + ); + + expect(fileSelectorParams.isCaptureEnabled, isFalse); + expect(fileSelectorParams.acceptTypes, ['png']); + expect(fileSelectorParams.filenameHint, 'filenameHint'); + expect(fileSelectorParams.mode, FileSelectorMode.open); }); test('runJavaScript', () async { diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart index 643f5ac964b1..01885caff54c 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart @@ -4,20 +4,18 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i9; -import 'dart:typed_data' as _i15; +import 'dart:typed_data' as _i14; import 'dart:ui' as _i4; -import 'package:flutter/foundation.dart' as _i12; -import 'package:flutter/gestures.dart' as _i13; -import 'package:flutter/material.dart' as _i14; +import 'package:flutter/foundation.dart' as _i11; +import 'package:flutter/gestures.dart' as _i12; +import 'package:flutter/material.dart' as _i13; import 'package:flutter/services.dart' as _i7; import 'package:mockito/mockito.dart' as _i1; -import 'package:webview_flutter_android/src/android_navigation_delegate.dart' - as _i8; -import 'package:webview_flutter_android/src/android_proxy.dart' as _i11; +import 'package:webview_flutter_android/src/android_proxy.dart' as _i10; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; import 'package:webview_flutter_android/src/android_webview_controller.dart' - as _i10; + as _i8; import 'package:webview_flutter_android/src/instance_manager.dart' as _i5; import 'package:webview_flutter_android/src/platform_views_service_proxy.dart' as _i6; @@ -349,7 +347,7 @@ class MockAndroidNavigationDelegate extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockAndroidWebViewController extends _i1.Mock - implements _i10.AndroidWebViewController { + implements _i8.AndroidWebViewController { @override _i3.PlatformWebViewControllerCreationParams get params => (super.noSuchMethod( Invocation.getter(#params), @@ -649,13 +647,25 @@ class MockAndroidWebViewController extends _i1.Mock returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override + _i9.Future setOnShowFileSelector( + _i9.Future> Function(_i8.FileSelectorParams)? + onShowFileSelectorCallback) => + (super.noSuchMethod( + Invocation.method( + #setOnShowFileSelector, + [onShowFileSelectorCallback], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); } /// A class which mocks [AndroidWebViewProxy]. /// /// See the documentation for Mockito's code generation for more information. class MockAndroidWebViewProxy extends _i1.Mock - implements _i11.AndroidWebViewProxy { + implements _i10.AndroidWebViewProxy { @override _i2.WebView Function({required bool useHybridComposition}) get createAndroidWebView => (super.noSuchMethod( @@ -672,40 +682,63 @@ class MockAndroidWebViewProxy extends _i1.Mock ), ) as _i2.WebView Function({required bool useHybridComposition})); @override - _i2.WebChromeClient Function( - {void Function( - _i2.WebView, - int, - )? - onProgressChanged}) get createAndroidWebChromeClient => - (super.noSuchMethod( + _i2.WebChromeClient Function({ + void Function( + _i2.WebView, + int, + )? + onProgressChanged, + _i9.Future> Function( + _i2.WebView, + _i2.FileChooserParams, + )? + onShowFileChooser, + }) get createAndroidWebChromeClient => (super.noSuchMethod( Invocation.getter(#createAndroidWebChromeClient), - returnValue: ( - {void Function( - _i2.WebView, - int, - )? - onProgressChanged}) => + returnValue: ({ + void Function( + _i2.WebView, + int, + )? + onProgressChanged, + _i9.Future> Function( + _i2.WebView, + _i2.FileChooserParams, + )? + onShowFileChooser, + }) => _FakeWebChromeClient_0( this, Invocation.getter(#createAndroidWebChromeClient), ), - returnValueForMissingStub: ( - {void Function( - _i2.WebView, - int, - )? - onProgressChanged}) => + returnValueForMissingStub: ({ + void Function( + _i2.WebView, + int, + )? + onProgressChanged, + _i9.Future> Function( + _i2.WebView, + _i2.FileChooserParams, + )? + onShowFileChooser, + }) => _FakeWebChromeClient_0( this, Invocation.getter(#createAndroidWebChromeClient), ), - ) as _i2.WebChromeClient Function( - {void Function( - _i2.WebView, - int, - )? - onProgressChanged})); + ) as _i2.WebChromeClient Function({ + void Function( + _i2.WebView, + int, + )? + onProgressChanged, + _i9.Future> Function( + _i2.WebView, + _i2.FileChooserParams, + )? + onShowFileChooser, + })); @override _i2.WebViewClient Function({ void Function( @@ -958,7 +991,7 @@ class MockAndroidWebViewProxy extends _i1.Mock /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockAndroidWebViewWidgetCreationParams extends _i1.Mock - implements _i10.AndroidWebViewWidgetCreationParams { + implements _i8.AndroidWebViewWidgetCreationParams { @override _i5.InstanceManager get instanceManager => (super.noSuchMethod( Invocation.getter(#instanceManager), @@ -1009,13 +1042,13 @@ class MockAndroidWebViewWidgetCreationParams extends _i1.Mock returnValueForMissingStub: _i4.TextDirection.rtl, ) as _i4.TextDirection); @override - Set<_i12.Factory<_i13.OneSequenceGestureRecognizer>> get gestureRecognizers => + Set<_i11.Factory<_i12.OneSequenceGestureRecognizer>> get gestureRecognizers => (super.noSuchMethod( Invocation.getter(#gestureRecognizers), - returnValue: <_i12.Factory<_i13.OneSequenceGestureRecognizer>>{}, + returnValue: <_i11.Factory<_i12.OneSequenceGestureRecognizer>>{}, returnValueForMissingStub: < - _i12.Factory<_i13.OneSequenceGestureRecognizer>>{}, - ) as Set<_i12.Factory<_i13.OneSequenceGestureRecognizer>>); + _i11.Factory<_i12.OneSequenceGestureRecognizer>>{}, + ) as Set<_i11.Factory<_i12.OneSequenceGestureRecognizer>>); } /// A class which mocks [ExpensiveAndroidViewController]. @@ -1162,7 +1195,7 @@ class MockExpensiveAndroidViewController extends _i1.Mock returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future dispatchPointerEvent(_i14.PointerEvent? event) => + _i9.Future dispatchPointerEvent(_i13.PointerEvent? event) => (super.noSuchMethod( Invocation.method( #dispatchPointerEvent, @@ -1514,7 +1547,7 @@ class MockSurfaceAndroidViewController extends _i1.Mock returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future dispatchPointerEvent(_i14.PointerEvent? event) => + _i9.Future dispatchPointerEvent(_i13.PointerEvent? event) => (super.noSuchMethod( Invocation.method( #dispatchPointerEvent, @@ -1548,6 +1581,16 @@ class MockSurfaceAndroidViewController extends _i1.Mock /// See the documentation for Mockito's code generation for more information. class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { @override + _i9.Future setSynchronousReturnValueForOnShowFileChooser(bool? value) => + (super.noSuchMethod( + Invocation.method( + #setSynchronousReturnValueForOnShowFileChooser, + [value], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + @override _i2.WebChromeClient copy() => (super.noSuchMethod( Invocation.method( #copy, @@ -1793,7 +1836,7 @@ class MockWebView extends _i1.Mock implements _i2.WebView { @override _i9.Future postUrl( String? url, - _i15.Uint8List? data, + _i14.Uint8List? data, ) => (super.noSuchMethod( Invocation.method( diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index 4e972fe7b98d..bd01494cfb4d 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -831,10 +831,68 @@ void main() { expect(result, containsAllInOrder([mockWebView, 76])); }); + test('onShowFileChooser', () async { + late final List result; + when(mockWebChromeClient.onShowFileChooser).thenReturn( + (WebView webView, FileChooserParams params) { + result = [webView, params]; + return Future>.value(['fileOne', 'fileTwo']); + }, + ); + + final FileChooserParams params = FileChooserParams.detached( + isCaptureEnabled: false, + acceptTypes: [], + filenameHint: 'filenameHint', + mode: FileChooserMode.open, + ); + + instanceManager.addHostCreatedInstance(params, 3); + + await expectLater( + flutterApi.onShowFileChooser( + mockWebChromeClientInstanceId, + mockWebViewInstanceId, + 3, + ), + completion(['fileOne', 'fileTwo']), + ); + expect(result[0], mockWebView); + expect(result[1], params); + }); + test('copy', () { expect(WebChromeClient.detached().copy(), isA()); }); }); + + group('FileChooserParams', () { + test('FlutterApi create', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final FileChooserParamsFlutterApiImpl flutterApi = + FileChooserParamsFlutterApiImpl( + instanceManager: instanceManager, + ); + + flutterApi.create( + 0, + false, + const ['my', 'list'], + FileChooserModeEnumData(value: FileChooserMode.openMultiple), + 'filenameHint', + ); + + final FileChooserParams instance = instanceManager + .getInstanceWithWeakReference(0)! as FileChooserParams; + expect(instance.isCaptureEnabled, false); + expect(instance.acceptTypes, const ['my', 'list']); + expect(instance.mode, FileChooserMode.openMultiple); + expect(instance.filenameHint, 'filenameHint'); + }); + }); }); group('CookieManager', () { diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index 81cdc9ef545f..e3a2f108d8ec 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -304,6 +304,21 @@ class MockTestWebChromeClientHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); + @override + void setSynchronousReturnValueForOnShowFileChooser( + int? instanceId, + bool? value, + ) => + super.noSuchMethod( + Invocation.method( + #setSynchronousReturnValueForOnShowFileChooser, + [ + instanceId, + value, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestWebSettingsHostApi]. @@ -952,6 +967,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { _i1.throwOnMissingStub(this); } + @override + _i5.Future setSynchronousReturnValueForOnShowFileChooser(bool? value) => + (super.noSuchMethod( + Invocation.method( + #setSynchronousReturnValueForOnShowFileChooser, + [value], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i2.WebChromeClient copy() => (super.noSuchMethod( Invocation.method( diff --git a/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart index 1f16c29aa953..03489ce5c1e0 100644 --- a/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart @@ -763,6 +763,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { _i1.throwOnMissingStub(this); } + @override + _i5.Future setSynchronousReturnValueForOnShowFileChooser(bool? value) => + (super.noSuchMethod( + Invocation.method( + #setSynchronousReturnValueForOnShowFileChooser, + [value], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i2.WebChromeClient copy() => (super.noSuchMethod( Invocation.method( diff --git a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart index bcfb453e85c4..b5df3c1354d0 100644 --- a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v4.2.3), do not edit directly. +// Autogenerated from Pigeon (v4.2.14), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import // ignore_for_file: avoid_relative_lib_imports @@ -22,6 +22,7 @@ abstract class TestJavaObjectHostApi { static const MessageCodec codec = StandardMessageCodec(); void dispose(int identifier); + static void setup(TestJavaObjectHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -39,7 +40,7 @@ abstract class TestJavaObjectHostApi { assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null, expected non-null int.'); api.dispose(arg_identifier!); - return {}; + return []; }); } } @@ -74,33 +75,59 @@ abstract class TestWebViewHostApi { static const MessageCodec codec = _TestWebViewHostApiCodec(); void create(int instanceId, bool useHybridComposition); + void loadData( int instanceId, String data, String? mimeType, String? encoding); + void loadDataWithBaseUrl(int instanceId, String? baseUrl, String data, String? mimeType, String? encoding, String? historyUrl); + void loadUrl(int instanceId, String url, Map headers); + void postUrl(int instanceId, String url, Uint8List data); + String? getUrl(int instanceId); + bool canGoBack(int instanceId); + bool canGoForward(int instanceId); + void goBack(int instanceId); + void goForward(int instanceId); + void reload(int instanceId); + void clearCache(int instanceId, bool includeDiskFiles); + Future evaluateJavascript(int instanceId, String javascriptString); + String? getTitle(int instanceId); + void scrollTo(int instanceId, int x, int y); + void scrollBy(int instanceId, int x, int y); + int getScrollX(int instanceId); + int getScrollY(int instanceId); + WebViewPoint getScrollPosition(int instanceId); + void setWebContentsDebuggingEnabled(bool enabled); + void setWebViewClient(int instanceId, int webViewClientInstanceId); + void addJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); + void removeJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); + void setDownloadListener(int instanceId, int? listenerInstanceId); + void setWebChromeClient(int instanceId, int? clientInstanceId); + void setBackgroundColor(int instanceId, int color); + static void setup(TestWebViewHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -121,7 +148,7 @@ abstract class TestWebViewHostApi { assert(arg_useHybridComposition != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.create was null, expected non-null bool.'); api.create(arg_instanceId!, arg_useHybridComposition!); - return {}; + return []; }); } } @@ -145,7 +172,7 @@ abstract class TestWebViewHostApi { final String? arg_mimeType = (args[2] as String?); final String? arg_encoding = (args[3] as String?); api.loadData(arg_instanceId!, arg_data!, arg_mimeType, arg_encoding); - return {}; + return []; }); } } @@ -172,7 +199,7 @@ abstract class TestWebViewHostApi { final String? arg_historyUrl = (args[5] as String?); api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl, arg_data!, arg_mimeType, arg_encoding, arg_historyUrl); - return {}; + return []; }); } } @@ -198,7 +225,7 @@ abstract class TestWebViewHostApi { assert(arg_headers != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null Map.'); api.loadUrl(arg_instanceId!, arg_url!, arg_headers!); - return {}; + return []; }); } } @@ -223,7 +250,7 @@ abstract class TestWebViewHostApi { assert(arg_data != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null Uint8List.'); api.postUrl(arg_instanceId!, arg_url!, arg_data!); - return {}; + return []; }); } } @@ -242,7 +269,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null, expected non-null int.'); final String? output = api.getUrl(arg_instanceId!); - return {'result': output}; + return [output]; }); } } @@ -261,7 +288,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoBack was null, expected non-null int.'); final bool output = api.canGoBack(arg_instanceId!); - return {'result': output}; + return [output]; }); } } @@ -280,7 +307,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoForward was null, expected non-null int.'); final bool output = api.canGoForward(arg_instanceId!); - return {'result': output}; + return [output]; }); } } @@ -299,7 +326,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goBack was null, expected non-null int.'); api.goBack(arg_instanceId!); - return {}; + return []; }); } } @@ -318,7 +345,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goForward was null, expected non-null int.'); api.goForward(arg_instanceId!); - return {}; + return []; }); } } @@ -337,7 +364,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.reload was null, expected non-null int.'); api.reload(arg_instanceId!); - return {}; + return []; }); } } @@ -359,7 +386,7 @@ abstract class TestWebViewHostApi { assert(arg_includeDiskFiles != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null, expected non-null bool.'); api.clearCache(arg_instanceId!, arg_includeDiskFiles!); - return {}; + return []; }); } } @@ -382,7 +409,7 @@ abstract class TestWebViewHostApi { 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null String.'); final String? output = await api.evaluateJavascript( arg_instanceId!, arg_javascriptString!); - return {'result': output}; + return [output]; }); } } @@ -401,7 +428,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null, expected non-null int.'); final String? output = api.getTitle(arg_instanceId!); - return {'result': output}; + return [output]; }); } } @@ -426,7 +453,7 @@ abstract class TestWebViewHostApi { assert(arg_y != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); api.scrollTo(arg_instanceId!, arg_x!, arg_y!); - return {}; + return []; }); } } @@ -451,7 +478,7 @@ abstract class TestWebViewHostApi { assert(arg_y != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); api.scrollBy(arg_instanceId!, arg_x!, arg_y!); - return {}; + return []; }); } } @@ -470,7 +497,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollX was null, expected non-null int.'); final int output = api.getScrollX(arg_instanceId!); - return {'result': output}; + return [output]; }); } } @@ -489,7 +516,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollY was null, expected non-null int.'); final int output = api.getScrollY(arg_instanceId!); - return {'result': output}; + return [output]; }); } } @@ -508,7 +535,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollPosition was null, expected non-null int.'); final WebViewPoint output = api.getScrollPosition(arg_instanceId!); - return {'result': output}; + return [output]; }); } } @@ -528,7 +555,7 @@ abstract class TestWebViewHostApi { assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null, expected non-null bool.'); api.setWebContentsDebuggingEnabled(arg_enabled!); - return {}; + return []; }); } } @@ -550,7 +577,7 @@ abstract class TestWebViewHostApi { assert(arg_webViewClientInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null, expected non-null int.'); api.setWebViewClient(arg_instanceId!, arg_webViewClientInstanceId!); - return {}; + return []; }); } } @@ -573,7 +600,7 @@ abstract class TestWebViewHostApi { 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.'); api.addJavaScriptChannel( arg_instanceId!, arg_javaScriptChannelInstanceId!); - return {}; + return []; }); } } @@ -596,7 +623,7 @@ abstract class TestWebViewHostApi { 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.'); api.removeJavaScriptChannel( arg_instanceId!, arg_javaScriptChannelInstanceId!); - return {}; + return []; }); } } @@ -616,7 +643,7 @@ abstract class TestWebViewHostApi { 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.'); final int? arg_listenerInstanceId = (args[1] as int?); api.setDownloadListener(arg_instanceId!, arg_listenerInstanceId); - return {}; + return []; }); } } @@ -636,7 +663,7 @@ abstract class TestWebViewHostApi { 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); final int? arg_clientInstanceId = (args[1] as int?); api.setWebChromeClient(arg_instanceId!, arg_clientInstanceId); - return {}; + return []; }); } } @@ -658,7 +685,7 @@ abstract class TestWebViewHostApi { assert(arg_color != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor was null, expected non-null int.'); api.setBackgroundColor(arg_instanceId!, arg_color!); - return {}; + return []; }); } } @@ -669,18 +696,31 @@ abstract class TestWebSettingsHostApi { static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId, int webViewInstanceId); + void setDomStorageEnabled(int instanceId, bool flag); + void setJavaScriptCanOpenWindowsAutomatically(int instanceId, bool flag); + void setSupportMultipleWindows(int instanceId, bool support); + void setJavaScriptEnabled(int instanceId, bool flag); + void setUserAgentString(int instanceId, String? userAgentString); + void setMediaPlaybackRequiresUserGesture(int instanceId, bool require); + void setSupportZoom(int instanceId, bool support); + void setLoadWithOverviewMode(int instanceId, bool overview); + void setUseWideViewPort(int instanceId, bool use); + void setDisplayZoomControls(int instanceId, bool enabled); + void setBuiltInZoomControls(int instanceId, bool enabled); + void setAllowFileAccess(int instanceId, bool enabled); + static void setup(TestWebSettingsHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -701,7 +741,7 @@ abstract class TestWebSettingsHostApi { assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!, arg_webViewInstanceId!); - return {}; + return []; }); } } @@ -723,7 +763,7 @@ abstract class TestWebSettingsHostApi { assert(arg_flag != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null bool.'); api.setDomStorageEnabled(arg_instanceId!, arg_flag!); - return {}; + return []; }); } } @@ -747,7 +787,7 @@ abstract class TestWebSettingsHostApi { 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null bool.'); api.setJavaScriptCanOpenWindowsAutomatically( arg_instanceId!, arg_flag!); - return {}; + return []; }); } } @@ -770,7 +810,7 @@ abstract class TestWebSettingsHostApi { assert(arg_support != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null bool.'); api.setSupportMultipleWindows(arg_instanceId!, arg_support!); - return {}; + return []; }); } } @@ -792,7 +832,7 @@ abstract class TestWebSettingsHostApi { assert(arg_flag != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null bool.'); api.setJavaScriptEnabled(arg_instanceId!, arg_flag!); - return {}; + return []; }); } } @@ -812,7 +852,7 @@ abstract class TestWebSettingsHostApi { 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null int.'); final String? arg_userAgentString = (args[1] as String?); api.setUserAgentString(arg_instanceId!, arg_userAgentString); - return {}; + return []; }); } } @@ -836,7 +876,7 @@ abstract class TestWebSettingsHostApi { 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null bool.'); api.setMediaPlaybackRequiresUserGesture( arg_instanceId!, arg_require!); - return {}; + return []; }); } } @@ -858,7 +898,7 @@ abstract class TestWebSettingsHostApi { assert(arg_support != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null, expected non-null bool.'); api.setSupportZoom(arg_instanceId!, arg_support!); - return {}; + return []; }); } } @@ -881,7 +921,7 @@ abstract class TestWebSettingsHostApi { assert(arg_overview != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null bool.'); api.setLoadWithOverviewMode(arg_instanceId!, arg_overview!); - return {}; + return []; }); } } @@ -903,7 +943,7 @@ abstract class TestWebSettingsHostApi { assert(arg_use != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null, expected non-null bool.'); api.setUseWideViewPort(arg_instanceId!, arg_use!); - return {}; + return []; }); } } @@ -925,7 +965,7 @@ abstract class TestWebSettingsHostApi { assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null bool.'); api.setDisplayZoomControls(arg_instanceId!, arg_enabled!); - return {}; + return []; }); } } @@ -947,7 +987,7 @@ abstract class TestWebSettingsHostApi { assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null bool.'); api.setBuiltInZoomControls(arg_instanceId!, arg_enabled!); - return {}; + return []; }); } } @@ -969,7 +1009,7 @@ abstract class TestWebSettingsHostApi { assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null, expected non-null bool.'); api.setAllowFileAccess(arg_instanceId!, arg_enabled!); - return {}; + return []; }); } } @@ -980,6 +1020,7 @@ abstract class TestJavaScriptChannelHostApi { static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId, String channelName); + static void setup(TestJavaScriptChannelHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1000,7 +1041,7 @@ abstract class TestJavaScriptChannelHostApi { assert(arg_channelName != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null, expected non-null String.'); api.create(arg_instanceId!, arg_channelName!); - return {}; + return []; }); } } @@ -1011,8 +1052,10 @@ abstract class TestWebViewClientHostApi { static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId); + void setSynchronousReturnValueForShouldOverrideUrlLoading( int instanceId, bool value); + static void setup(TestWebViewClientHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1030,7 +1073,7 @@ abstract class TestWebViewClientHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!); - return {}; + return []; }); } } @@ -1054,7 +1097,7 @@ abstract class TestWebViewClientHostApi { 'Argument for dev.flutter.pigeon.WebViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading was null, expected non-null bool.'); api.setSynchronousReturnValueForShouldOverrideUrlLoading( arg_instanceId!, arg_value!); - return {}; + return []; }); } } @@ -1065,6 +1108,7 @@ abstract class TestDownloadListenerHostApi { static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId); + static void setup(TestDownloadListenerHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1082,7 +1126,7 @@ abstract class TestDownloadListenerHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.DownloadListenerHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!); - return {}; + return []; }); } } @@ -1093,6 +1137,10 @@ abstract class TestWebChromeClientHostApi { static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId); + + void setSynchronousReturnValueForOnShowFileChooser( + int instanceId, bool value); + static void setup(TestWebChromeClientHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1110,7 +1158,31 @@ abstract class TestWebChromeClientHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!); - return {}; + return []; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebChromeClientHostApi.setSynchronousReturnValueForOnShowFileChooser', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.setSynchronousReturnValueForOnShowFileChooser was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.setSynchronousReturnValueForOnShowFileChooser was null, expected non-null int.'); + final bool? arg_value = (args[1] as bool?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.setSynchronousReturnValueForOnShowFileChooser was null, expected non-null bool.'); + api.setSynchronousReturnValueForOnShowFileChooser( + arg_instanceId!, arg_value!); + return []; }); } } @@ -1121,7 +1193,9 @@ abstract class TestAssetManagerHostApi { static const MessageCodec codec = StandardMessageCodec(); List list(String path); + String getAssetFilePathByName(String name); + static void setup(TestAssetManagerHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1139,7 +1213,7 @@ abstract class TestAssetManagerHostApi { assert(arg_path != null, 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.list was null, expected non-null String.'); final List output = api.list(arg_path!); - return {'result': output}; + return [output]; }); } } @@ -1159,7 +1233,7 @@ abstract class TestAssetManagerHostApi { assert(arg_name != null, 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName was null, expected non-null String.'); final String output = api.getAssetFilePathByName(arg_name!); - return {'result': output}; + return [output]; }); } } @@ -1170,7 +1244,9 @@ abstract class TestWebStorageHostApi { static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId); + void deleteAllData(int instanceId); + static void setup(TestWebStorageHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1188,7 +1264,7 @@ abstract class TestWebStorageHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebStorageHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!); - return {}; + return []; }); } } @@ -1207,7 +1283,7 @@ abstract class TestWebStorageHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebStorageHostApi.deleteAllData was null, expected non-null int.'); api.deleteAllData(arg_instanceId!); - return {}; + return []; }); } }