3

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.

Matthew Runo
  • 1,387
  • 3
  • 20
  • 43
  • Have the service start an activity in your application that when started displays a dialog based on the provided MESSAGE_KEY. – petey Sep 29 '14 at 15:38
  • That would work, but WebView complains that it cannot open an alertdialog because the context it was created with is not an Activity. Should I be overriding a different method other than onJsAlert like above? – Matthew Runo Sep 29 '14 at 16:10
  • You're giving up a lot of reputation for something you could have done a bit more research on – But I'm Not A Wrapper Class Sep 29 '14 at 20:08
  • Trust me, I did a lot of research on this. User Experience reasons seem to dictate this webview must go in something not attached to the activity lifecycle - but the webview also relies on javascript alert dialogs, which is where the problem comes up. This is mostly a hope against hope that there's a way out. A way I can display a webview outside the activity lifecycle and still have javascript alerts work. – Matthew Runo Oct 01 '14 at 14:12
  • @MatthewRuno Please take a look at my answer here: [Android Facebook lock screen notification](http://stackoverflow.com/a/25160309/2558882). You'll be using a service - so `something not attached to the activity lifecycle`. `So, for example, while this feature is active, the user must be able to navigate around our app to do other things` - sure, you can keep the view around for as long as you need it. `...but the service we're using requires it to be a webview` - ok - instead of displaying the alertdialog, use the service and add the view using WM. But, I'm not a 100% sure if this will help. – Vikram Oct 03 '14 at 05:00

3 Answers3

4

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>
Manish Mulimani
  • 17,535
  • 2
  • 41
  • 60
  • When I try this, I get the following in the logs, and no AlertDialog.. W/JsDialogHelper﹕ Cannot create a dialog, the WebView context is not an Activity – Matthew Runo Sep 30 '14 at 21:41
  • https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/webkit/JsDialogHelper.java Line 89 is where the error is being thrown. – Matthew Runo Sep 30 '14 at 21:44
  • @MatthewRuno Can you put breakpoint or logs in `onJsAlert` method to see whether that method is getting called. If you see the [CallbackProxy](http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3.1_r1/android/webkit/CallbackProxy.java/#564) implementation, `showDialog` in `JsDialogHelper` need to be called only if `onJsAlert` returns false. The default implementation of `onJsAlert` returns false, so I doubt whether the overridden method is ever called. – Manish Mulimani Oct 01 '14 at 02:06
  • @MatthewRuno BTW is the web application you have built is specific for android webview? If yes, you can think of using Javascript interface as explained [here](http://developer.android.com/guide/webapps/webview.html#UsingJavaScript) – Manish Mulimani Oct 01 '14 at 02:23
  • This web app is not built specifically for Android, however I can have changes made. I may need to go that route to remove the AlertDialog dependency. I really appreciate the help! I'll check the breakpoint shortly once I get into work. – Matthew Runo Oct 01 '14 at 14:07
  • @MatthewRuno Did the set breakpoint trigger in onJsAlert method? – Manish Mulimani Oct 06 '14 at 10:39
  • It did not, but I think you have the best answer. This webview isn't designed for Android or iOS app integration in mind, and we're really just hacking it in. You're right - the proper way to do this is via the Javascript interface you outlined. I'll have to file some tickets against the people who own this feature to create that so I can use it in a future update. – Matthew Runo Oct 06 '14 at 15:27
2

You can't display an AlertDialog outside of an Activity.

However, you could build a custom View which looks like the dialog you want to show, and display it in the window via WindowManager.

For example:

WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT, 
                WindowManager.LayoutParams.WRAP_CONTENT
                WindowManager.LayoutParams.TYPE_PHONE,
                0,
                PixelFormat.TRANSLUCENT);

params.gravity = Gravity.CENTER;

windowManager.addView(yourView, params);

The code above displays your custom view in the center of the screen, on top of everything else. You could inflate the view via LayoutInflater.

You also need to add the permission in the manifest in order to display the view.

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Alessandro Roaro
  • 4,665
  • 6
  • 29
  • 48
  • How would I trigger this view from the webview, instead of it trying to create a javascript alertdialog? – Matthew Runo Sep 29 '14 at 16:10
  • I don't get why do you need a WebView. You can treat the custom view you add via WindowManager just like you would with a normal view. – Alessandro Roaro Sep 29 '14 at 22:52
  • I would LOVE to be able to do this without a webview, but the service we're using requires it to be a webview =\ – Matthew Runo Sep 30 '14 at 21:42
  • Have you tried adding the WebView using the method I described above? – Alessandro Roaro Oct 01 '14 at 08:00
  • Yes, and that is actually how it's being displayed currently. The problem with it being activity based is that the webview resets/is removed when the user leaves the activity. So, for example, while this feature is active, the user must be able to navigate around our app to do other things. That's why I went the service route instead - the activity lifecycle doesn't affect it. – Matthew Runo Oct 01 '14 at 14:03
1

I suppose you can create a JavaScript Interface for two way communication between your activity and WebView. I have not tried the solution myself, but have stumbled before on that. The SO Question posted here might help you on that.

I suppose your WebView will reside in an activity too, you can use the Activity's context to pop the AlertDialog. You just need a method in the activity which you'd like to call in the webview (I'd just pass the Activity object in the addJavascriptInterface method).

Another way would be to use a Service and initiate a new Activity which implements Theme.dialog in it to be used as AlertDialog.

Let me know if it doesn't solve your problem.

Community
  • 1
  • 1
0xC0DED00D
  • 19,522
  • 20
  • 117
  • 184