androidandroid-activityandroid-dialogandroid-task

Show dialog activity over another app from background


Say you have an app A which opens up another app B (e.g. a map), which is not controlled by you (i.e. it's a preexisting app). So now app A is in the background. Suppose an event occurs and A wants to show a floating dialog over app B's UI (while leaving app B's activity visible behind it). Is this possible?

(The usual answer to this would be to display a notification, but this is not a mass market app, and we are trying to get the user's attention very directly.)

Currently, I was trying to do something like this:

// This code runs in a class other than app A's main activity,
// and the "activity" variable used here is a reference to that activity.
Intent intent = new Intent(activity, NotificationDialogActivity.class);
// EDIT: I'm not exactly sure whether NEW_TASK helps here or not
// so I removed it, but the REORDER_TO_FRONT would ideally cause
// app A's dialog activity to be moved to the front of the back stack?
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
// The "msg" variable here is just some data being passed to the dialog activity
// I included it here only so it is clear that there is a purpose.
intent.putExtra(NotificationDialogActivity.EXTRA_MSG, msg);
activity.startActivity(intent);

from within app A (the one in the background).

But what happens when I do that is that the dialog gets inserted between the original app A activity and the app B activity on the back stack.


Solution

  • In order to have a dialog activity shown over another application, a few things must be done:

    In the code that starts the dialog activity:

    Intent intent = new Intent(baseActivity, DialogActivity.class);
    // NEW_TASK allows the new dialog activity to be separate from the existing activity.
    // REORDER_TO_FRONT causes the dialog activity to be moved to the front,
    // if it's already running.
    // Without the NEW_TASK flag, the existing "base" activity
    // would be moved to the front as well.
    intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.putExtra(DialogActivity.EXTRA_SOME_PARAM, someParamValue);
    // The activity must be started from the application context.
    // I'm not sure why exactly.
    baseActivity.getApplicationContext().startActivity(intent);
    

    In the above, baseActivity is a reference to the main activity of the application.

    It may help to give the dialog activity a launchMode of singleInstance, ensuring that it never accumulates other activities in its task, but this may be unnecessary. The @android:style/Theme.Translucent.NoTitleBar theme allows the activity underneath it to show through.

    <activity
        android:name=".DialogActivity"
        android:launchMode="singleInstance"
        android:theme="@android:style/Theme.Translucent.NoTitleBar">
    </activity>
    

    For the dialog activity itself, it may be necessary to adjust its window to ensure that it doesn't fill the whole screen:

     getWindow().setLayout(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.WRAP_CONTENT
    );
    

    Likewise, in the dialog activity's layout XML, it may also be necessary:

    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    

    For the dialog itself, you can do a lot of things, but one solution is to extend the Dialog class:

    class DialogActivity extends Dialog { ... }
    

    To show the dialog from the activity, just create a new instance of the Dialog and call its show() method.