Skip to content

Commit

Permalink
[webview_flutter_android] Add javascript panel interface for android (#…
Browse files Browse the repository at this point in the history
…5796)

* There are cases where Web calls System Popup with javascript on webview_flutter
* Android has a interface on WebChromeClient
   * https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult)

* Related issue: flutter/flutter#30358 (comment)
* Related Interface PR: #5670
* The PR that contains all changes can be found at #4704
  • Loading branch information
jsharp83 authored Jan 24, 2024
1 parent dced730 commit 5aa3d35
Show file tree
Hide file tree
Showing 20 changed files with 1,358 additions and 4 deletions.
4 changes: 4 additions & 0 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.14.0

* Adds support to show JavaScript dialog. See `AndroidWebViewController.setOnJavaScriptAlertDialog`, `AndroidWebViewController.setOnJavaScriptConfirmDialog` and `AndroidWebViewController.setOnJavaScriptTextInputDialog`.

## 3.13.2

* Fixes new lint warnings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2643,6 +2643,12 @@ void setSynchronousReturnValueForOnShowFileChooser(
void setSynchronousReturnValueForOnConsoleMessage(
@NonNull Long instanceId, @NonNull Boolean value);

void setSynchronousReturnValueForOnJsAlert(@NonNull Long instanceId, @NonNull Boolean value);

void setSynchronousReturnValueForOnJsConfirm(@NonNull Long instanceId, @NonNull Boolean value);

void setSynchronousReturnValueForOnJsPrompt(@NonNull Long instanceId, @NonNull Boolean value);

/** The codec used by WebChromeClientHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
Expand Down Expand Up @@ -2732,6 +2738,87 @@ static void setup(
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnJsAlert",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number instanceIdArg = (Number) args.get(0);
Boolean valueArg = (Boolean) args.get(1);
try {
api.setSynchronousReturnValueForOnJsAlert(
(instanceIdArg == null) ? null : instanceIdArg.longValue(), valueArg);
wrapped.add(0, null);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnJsConfirm",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number instanceIdArg = (Number) args.get(0);
Boolean valueArg = (Boolean) args.get(1);
try {
api.setSynchronousReturnValueForOnJsConfirm(
(instanceIdArg == null) ? null : instanceIdArg.longValue(), valueArg);
wrapped.add(0, null);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebChromeClientHostApi.setSynchronousReturnValueForOnJsPrompt",
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<Object>();
ArrayList<Object> args = (ArrayList<Object>) message;
Number instanceIdArg = (Number) args.get(0);
Boolean valueArg = (Boolean) args.get(1);
try {
api.setSynchronousReturnValueForOnJsPrompt(
(instanceIdArg == null) ? null : instanceIdArg.longValue(), valueArg);
wrapped.add(0, null);
} catch (Throwable exception) {
ArrayList<Object> wrappedError = wrapError(exception);
wrapped = wrappedError;
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
Expand Down Expand Up @@ -2967,6 +3054,60 @@ public void onConsoleMessage(
new ArrayList<Object>(Arrays.asList(instanceIdArg, messageArg)),
channelReply -> callback.reply(null));
}

public void onJsAlert(
@NonNull Long instanceIdArg,
@NonNull String urlArg,
@NonNull String messageArg,
@NonNull Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebChromeClientFlutterApi.onJsAlert",
getCodec());
channel.send(
new ArrayList<Object>(Arrays.asList(instanceIdArg, urlArg, messageArg)),
channelReply -> callback.reply(null));
}

public void onJsConfirm(
@NonNull Long instanceIdArg,
@NonNull String urlArg,
@NonNull String messageArg,
@NonNull Reply<Boolean> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebChromeClientFlutterApi.onJsConfirm",
getCodec());
channel.send(
new ArrayList<Object>(Arrays.asList(instanceIdArg, urlArg, messageArg)),
channelReply -> {
@SuppressWarnings("ConstantConditions")
Boolean output = (Boolean) channelReply;
callback.reply(output);
});
}

public void onJsPrompt(
@NonNull Long instanceIdArg,
@NonNull String urlArg,
@NonNull String messageArg,
@NonNull String defaultValueArg,
@NonNull Reply<String> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebChromeClientFlutterApi.onJsPrompt",
getCodec());
channel.send(
new ArrayList<Object>(Arrays.asList(instanceIdArg, urlArg, messageArg, defaultValueArg)),
channelReply -> {
@SuppressWarnings("ConstantConditions")
String output = (String) channelReply;
callback.reply(output);
});
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface WebStorageHostApi {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,56 @@ public void onConsoleMessage(
callback);
}

/**
* Sends a message to Dart to call `WebChromeClient.onJsAlert` on the Dart object representing
* `instance`.
*/
public void onJsAlert(
@NonNull WebChromeClient instance,
@NonNull String url,
@NonNull String message,
@NonNull WebChromeClientFlutterApi.Reply<Void> callback) {
super.onJsAlert(
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(instance)),
url,
message,
callback);
}

/**
* Sends a message to Dart to call `WebChromeClient.onJsConfirm` on the Dart object representing
* `instance`.
*/
public void onJsConfirm(
@NonNull WebChromeClient instance,
@NonNull String url,
@NonNull String message,
@NonNull WebChromeClientFlutterApi.Reply<Boolean> callback) {
super.onJsConfirm(
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(instance)),
url,
message,
callback);
}

/**
* Sends a message to Dart to call `WebChromeClient.onJsPrompt` on the Dart object representing
* `instance`.
*/
public void onJsPrompt(
@NonNull WebChromeClient instance,
@NonNull String url,
@NonNull String message,
@NonNull String defaultValue,
@NonNull WebChromeClientFlutterApi.Reply<String> callback) {
super.onJsPrompt(
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(instance)),
url,
message,
defaultValue,
callback);
}

private long getIdentifierForClient(WebChromeClient webChromeClient) {
final Long identifier = instanceManager.getIdentifierForStrongReference(webChromeClient);
if (identifier == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
Expand Down Expand Up @@ -43,6 +45,10 @@ public static class WebChromeClientImpl extends SecureWebChromeClient {
private boolean returnValueForOnShowFileChooser = false;
private boolean returnValueForOnConsoleMessage = false;

private boolean returnValueForOnJsAlert = false;
private boolean returnValueForOnJsConfirm = false;
private boolean returnValueForOnJsPrompt = false;

/**
* Creates a {@link WebChromeClient} that passes arguments of callbacks methods to Dart.
*
Expand Down Expand Up @@ -124,6 +130,77 @@ public void setReturnValueForOnShowFileChooser(boolean value) {
public void setReturnValueForOnConsoleMessage(boolean value) {
returnValueForOnConsoleMessage = value;
}

public void setReturnValueForOnJsAlert(boolean value) {
returnValueForOnJsAlert = value;
}

public void setReturnValueForOnJsConfirm(boolean value) {
returnValueForOnJsConfirm = value;
}

public void setReturnValueForOnJsPrompt(boolean value) {
returnValueForOnJsPrompt = value;
}

@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
if (returnValueForOnJsAlert) {
flutterApi.onJsAlert(
this,
url,
message,
reply -> {
result.confirm();
});
return true;
} else {
return false;
}
}

@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
if (returnValueForOnJsConfirm) {
flutterApi.onJsConfirm(
this,
url,
message,
reply -> {
if (reply) {
result.confirm();
} else {
result.cancel();
}
});
return true;
} else {
return false;
}
}

@Override
public boolean onJsPrompt(
WebView view, String url, String message, String defaultValue, JsPromptResult result) {
if (returnValueForOnJsPrompt) {
flutterApi.onJsPrompt(
this,
url,
message,
defaultValue,
reply -> {
@Nullable String inputMessage = reply;
if (inputMessage != null) {
result.confirm(inputMessage);
} else {
result.cancel();
}
});
return true;
} else {
return false;
}
}
}

/**
Expand Down Expand Up @@ -267,4 +344,28 @@ public void setSynchronousReturnValueForOnConsoleMessage(
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setReturnValueForOnConsoleMessage(value);
}

@Override
public void setSynchronousReturnValueForOnJsAlert(
@NonNull Long instanceId, @NonNull Boolean value) {
final WebChromeClientImpl webChromeClient =
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setReturnValueForOnJsAlert(value);
}

@Override
public void setSynchronousReturnValueForOnJsConfirm(
@NonNull Long instanceId, @NonNull Boolean value) {
final WebChromeClientImpl webChromeClient =
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setReturnValueForOnJsConfirm(value);
}

@Override
public void setSynchronousReturnValueForOnJsPrompt(
@NonNull Long instanceId, @NonNull Boolean value) {
final WebChromeClientImpl webChromeClient =
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setReturnValueForOnJsPrompt(value);
}
}
Loading

0 comments on commit 5aa3d35

Please sign in to comment.