Show dialog activity over another app from background

Chris Middleton picture Chris Middleton · Sep 13, 2016 · Viewed 8.3k times · Source

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.

Answer

Chris Middleton picture Chris Middleton · Sep 25, 2016

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

  • The dialog activity must have a translucent theme, to allow the other application to show up behind it (see @android:style/Theme.Translucent.NoTitleBar)
  • The dialog activity must not fill the screen, for the same reason (see Window.setLayout)
  • The dialog activity must be part of a separate task from the base activity, so that it when it is shown above the other application, it does not pull the base activity above the other application as well (see FLAG_ACTIVITY_NEW_TASK)
  • The dialog activity must be brought to the front, so that it is still shown if the activity it is being launched from is running in the background (see FLAG_ACTIVITY_REORDER_TO_FRONT)
  • The dialog activity must show a dialog somehow, e.g. by creating a class which extends the Dialog class directly

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.