Skip to content

Commit

Permalink
Implement JavaScript dialog feature on android
Browse files Browse the repository at this point in the history
  • Loading branch information
Eunchul Jeon committed Sep 20, 2023
1 parent 903a0ea commit 232e186
Show file tree
Hide file tree
Showing 16 changed files with 1,646 additions and 1,719 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.10.2

* Add support to show JavaScript dialog. Sees `PlatformWebViewController.setJavaScriptAlertDialogCallback`, `PlatformWebViewController.setJavaScriptConfirmDialogCallback` and `PlatformWebViewController.setJavaScriptTextInputDialogCallback`.

## 3.10.1

* Bumps androidx.annotation:annotation from 1.5.0 to 1.7.0.
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientFlutterApi;
Expand Down Expand Up @@ -149,6 +150,49 @@ public void onHideCustomView(
callback);
}

/**
* Sends a message to Dart to call `WebChromeClient.runJavaScriptDialog` on the Dart object
* representing `instance`.
*/

public void onHandleJavaScriptAlert(
@NonNull WebChromeClient instance,
@NonNull String message,
@NonNull WebChromeClientFlutterApi.Reply<Void> callback
) {
super.onHandleJavaScriptAlert(
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(instance)),
message,
callback
);
}

public void onHandleJavaScriptConfirm(
@NonNull WebChromeClient instance,
@NonNull String message,
@NonNull WebChromeClientFlutterApi.Reply<Boolean> callback
) {
super.onHandleJavaScriptConfirm(
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(instance)),
message,
callback
);
}

public void onHandleJavaScriptPrompt(
@NonNull WebChromeClient instance,
@NonNull String message,
@Nullable String defaultText,
@NonNull WebChromeClientFlutterApi.Reply<String> callback
) {
super.onHandleJavaScriptPrompt(
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(instance)),
message,
defaultText,
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 @@ -10,6 +10,8 @@
import android.os.Message;
import android.view.View;
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 @@ -41,6 +43,10 @@ public static class WebChromeClientImpl extends SecureWebChromeClient {
private final WebChromeClientFlutterApiImpl flutterApi;
private boolean returnValueForOnShowFileChooser = false;

private boolean hasJavaScriptAlertCallback = false;
private boolean hasJavaScriptConfirmCallback = false;
private boolean hasJavaScriptPromptCallback = false;

/**
* Creates a {@link WebChromeClient} that passes arguments of callbacks methods to Dart.
*
Expand Down Expand Up @@ -111,6 +117,64 @@ public void onPermissionRequest(@NonNull PermissionRequest request) {
public void setReturnValueForOnShowFileChooser(boolean value) {
returnValueForOnShowFileChooser = value;
}

public void setHasJavaScriptAlertCallback(boolean value) {
hasJavaScriptAlertCallback = value;
}

public void setHasJavaScriptConfirmCallback(boolean value) {
hasJavaScriptConfirmCallback = value;
}

public void setHasJavaScriptPromptCallback(boolean value) {
hasJavaScriptPromptCallback = value;
}


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

@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
if( hasJavaScriptConfirmCallback ) {
flutterApi.onHandleJavaScriptConfirm(this, 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( hasJavaScriptPromptCallback ) {
flutterApi.onHandleJavaScriptPrompt(this, 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 @@ -246,4 +310,20 @@ public void setSynchronousReturnValueForOnShowFileChooser(
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setReturnValueForOnShowFileChooser(value);
}

public void setHasJavaScriptAlertCallback(@NonNull Long instanceId, @NonNull Boolean value) {
final WebChromeClientImpl webChromeClient =
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setHasJavaScriptAlertCallback(value);
}
public void setHasJavaScriptConfirmCallback(@NonNull Long instanceId, @NonNull Boolean value) {
final WebChromeClientImpl webChromeClient =
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setHasJavaScriptConfirmCallback(value);
}
public void setHasJavaScriptPromptCallback(@NonNull Long instanceId, @NonNull Boolean value) {
final WebChromeClientImpl webChromeClient =
Objects.requireNonNull(instanceManager.getInstance(instanceId));
webChromeClient.setHasJavaScriptPromptCallback(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,25 @@ const String kTransparentBackgroundPage = '''
</html>
''';

const String kAlertTestPage = '''
<html>
<head>
<script type = "text/javascript">
function showAlert() {
alert ("This is an alert dialog box");
}
</script>
</head>
<body>
<p> Click the following button to see the effect </p>
<form>
<input type = "button" value = "Click me" onclick = "showAlert();" />
</form>
</body>
</html>
''';

class WebViewExample extends StatefulWidget {
const WebViewExample({super.key, this.cookieManager});

Expand Down Expand Up @@ -202,6 +221,7 @@ enum MenuOptions {
transparentBackground,
setCookie,
videoExample,
javaScriptAlert,
}

class SampleMenu extends StatelessWidget {
Expand Down Expand Up @@ -265,6 +285,9 @@ class SampleMenu extends StatelessWidget {
case MenuOptions.videoExample:
_onVideoExample(context);
break;
case MenuOptions.javaScriptAlert:
_onJavaScriptAlertExample(context);
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
Expand Down Expand Up @@ -325,6 +348,10 @@ class SampleMenu extends StatelessWidget {
value: MenuOptions.videoExample,
child: Text('Video example'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.javaScriptAlert,
child: Text('JavaScript Alert Example'),
),
],
);
}
Expand Down Expand Up @@ -473,6 +500,14 @@ class SampleMenu extends StatelessWidget {
return webViewController.loadHtmlString(kTransparentBackgroundPage);
}

Future<void> _onJavaScriptAlertExample(BuildContext context) {
webViewController.setOnJavaScriptAlertDialogCallback((message) async {
return _showAlert(context, message);
});

return webViewController.loadHtmlString(kAlertTestPage);
}

Widget _getCookieList(String cookies) {
if (cookies == '""') {
return Container();
Expand All @@ -497,6 +532,18 @@ class SampleMenu extends StatelessWidget {

return indexFile.path;
}

Future<void> _showAlert(BuildContext context, String message) async {
return showDialog(context: context, builder: (BuildContext ctx) {
return AlertDialog(content: Text(message),
actions: [
TextButton(onPressed: () {
Navigator.of(ctx).pop();
}, child: const Text('OK'))
],
);
});
}
}

class NavigationControls extends StatelessWidget {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
webview_flutter_platform_interface: ^2.4.0
webview_flutter_platform_interface:
path: ../../webview_flutter_platform_interface

dev_dependencies:
espresso: ^0.2.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class AndroidWebViewProxy {
android_webview.CustomViewCallback callback)?
onShowCustomView,
void Function(android_webview.WebChromeClient instance)? onHideCustomView,
Future<void> Function(String message)? onHandleJavaScriptAlert,
Future<bool> Function(String message)? onHandleJavaScriptConfirm,
Future<String> Function(String message, String defaultText)? onHandleJavaScriptPrompt,
}) createAndroidWebChromeClient;

/// Constructs a [android_webview.WebViewClient].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,12 @@ class WebChromeClient extends JavaObject {
this.onGeolocationPermissionsHidePrompt,
this.onShowCustomView,
this.onHideCustomView,
this.onHandleJavaScriptAlert,
this.onHandleJavaScriptConfirm,
this.onHandleJavaScriptPrompt,
this.hasJavaScriptAlertCallback,
this.hasJavaScriptConfirmCallback,
this.hasJavaScriptPromptCallback,
@visibleForTesting super.binaryMessenger,
@visibleForTesting super.instanceManager,
}) : super.detached() {
Expand All @@ -1068,6 +1074,12 @@ class WebChromeClient extends JavaObject {
this.onGeolocationPermissionsHidePrompt,
this.onShowCustomView,
this.onHideCustomView,
this.onHandleJavaScriptAlert,
this.onHandleJavaScriptConfirm,
this.onHandleJavaScriptPrompt,
this.hasJavaScriptAlertCallback,
this.hasJavaScriptConfirmCallback,
this.hasJavaScriptPromptCallback,
super.binaryMessenger,
super.instanceManager,
}) : super.detached();
Expand Down Expand Up @@ -1121,6 +1133,14 @@ class WebChromeClient extends JavaObject {
/// mode.
final HideCustomViewCallback? onHideCustomView;

final Future<void> Function(String message)? onHandleJavaScriptAlert;
final Future<bool> Function(String message)? onHandleJavaScriptConfirm;
final Future<String> Function(String message, String defaultValue)? onHandleJavaScriptPrompt;

final bool Function()? hasJavaScriptAlertCallback;
final bool Function()? hasJavaScriptConfirmCallback;
final bool Function()? hasJavaScriptPromptCallback;

/// Sets the required synchronous return value for the Java method,
/// `WebChromeClient.onShowFileChooser(...)`.
///
Expand Down Expand Up @@ -1150,6 +1170,25 @@ class WebChromeClient extends JavaObject {
);
}

Future<void> setHasJavaScriptAlertCallback(
bool value,
) {
return api.setHasJavaScriptAlertCallbackFromInstance(this, value);
}

Future<void> setHasJavaScriptConfirmCallback(
bool value,
) {
return api.setHasJavaScriptConfirmCallbackFromInstance(this, value);
}

Future<void> setHasJavaScriptPromptCallback(
bool value,
) {
return api.setHasJavaScriptPromptCallbackFromInstance(this, value);
}


@override
WebChromeClient copy() {
return WebChromeClient.detached(
Expand All @@ -1160,6 +1199,12 @@ class WebChromeClient extends JavaObject {
onGeolocationPermissionsHidePrompt: onGeolocationPermissionsHidePrompt,
onShowCustomView: onShowCustomView,
onHideCustomView: onHideCustomView,
onHandleJavaScriptAlert: onHandleJavaScriptAlert,
onHandleJavaScriptConfirm: onHandleJavaScriptConfirm,
onHandleJavaScriptPrompt: onHandleJavaScriptPrompt,
hasJavaScriptAlertCallback: hasJavaScriptAlertCallback,
hasJavaScriptConfirmCallback: hasJavaScriptConfirmCallback,
hasJavaScriptPromptCallback: hasJavaScriptPromptCallback,
binaryMessenger: _api.binaryMessenger,
instanceManager: _api.instanceManager,
);
Expand Down
Loading

0 comments on commit 232e186

Please sign in to comment.