Search code examples
androidservicewebviewandroid-alertdialog

Showing an AlertDialog from a Webview outside of an Activity


I've been working on a feature similar to Facebook's chat heads - a feature which needs to be active for our users even if they leave our app to go check email, etc. It needs to be able to create AlertDialogs via javascript - from a place that isn't impacted when the user switches Activities inside our app. It's OK for the feature to close/hide when the user leaves the app to do something else - however it must still be present when they come back.

Other than using a Service I haven't been able to think of another way to let users interact with us while they're off doing other things. When I use a service to create the webview via WindowManager the webview's JsDialogHelper detects that the context is not an activity and prevents it from showing AlertDialogs.

Any ideas would be most helpful around making this work. We need to allow AlertDialogs to pop up and be interactive for this feature to be helpful. We need the webview to stick around between activity transitions.

Is it possible to extend JsDialogHelper so I can get it to work with my code? Is there a better way to have a facebook chat heads like feature using webviews in an android app that I haven't thought of yet? I'm open to complete rewrites if it gets the user experience I'm looking for.


Solution

  • You can display the Dialog from the service, by setting the window type as TYPE_SYSTEM_ALERT. Remember to communicate back the action taken by the user using the JsResult instance passed to onJsAlert.

        // maintain reference to JsResult instance passed to onJsAlert, in order
        // communicate back the action taken by the user.
        private JsResult mResult; 
    
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                mResult = result;
                AlertDialog dialog = new AlertDialog.Builder(MyService.this)
                        .setTitle("Custom Dialog")
                        .setMessage("This is our custom dialog, not the one served by JsDialogHelper")
                        .setOnCancelListener(new CancelListener())
                        .setNegativeButton("Cancel", new CancelListener())
                        .setPositiveButton("Ok", new PositiveListener())
                        .create();
                dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                dialog.show();
    
                return true;
            }
        });
    
        private class CancelListener implements DialogInterface.OnCancelListener,
            DialogInterface.OnClickListener {
    
            @Override
            public void onCancel(DialogInterface dialogInterface) {
                mResult.cancel();
            }
    
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                mResult.cancel();
            }
        }
    
        private class PositiveListener implements DialogInterface.OnClickListener {
    
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                mResult.confirm();
            }
        }
    

    Add the required permissions to the manifest file:

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    

    Alternate solution: If the web application can be built specific to android webview, then you can create Javascript interfaces which will allow Javascript code to directly invoke the Android code which in turn displays the dialog for you. This avoids the JsDialogHelper route.

    1. Define a javascript interface:
    public class WebAppInterface {
        Context context;
    
        WebAppInterface(Context c) {
            context = c;
        }
    
        // Annotation required for api >= 17
        @JavascriptInterface
        public void showDialog() {
            AlertDialog dialog = new AlertDialog.Builder(context)
                    .setTitle("Custom Dialog")
                    .setMessage("This is our custom dialog, not the one served by JsDialogHelper")
                    .create();
            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            dialog.show();
        }
    }
    
    1. Bind the interface between Javascript and Android code, using addJavascriptInterface.

    webView.addJavascriptInterface(new WebAppInterface(this), "Android");

    1. Use the interface in Javascript code.
    <input type="button" value="Display dialog" onClick="displayDialog()" />
        <script>
            function displayDialog() {
                //alert("Javascript Dialog");
                Android.showDialog();
            }
        </script>
    </input>