enoent

Android: display a Dialog from an AppWidget

Issue

When you want to display a dialog, you don’t only need a context, you need an activity context. From an activity, displaying a dialog is pretty straightforward:

Display a dialog from an activity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new AlertDialog.Builder(MyActivity.this)
    .setTitle("Dialog title")
    .setMessage("Dialog message")
    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            // Handle a positive answer
        }
    })
    .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            // Handle a negative answer
        }
    })
    .setIcon(R.drawable.ic_dialog_alert)
    .show();

Okay, that’s a pretty usual code sample. But what about displaying it from an app-widget?

Display a Dialog from an AppWidget

What is needed to display a Dialog? An Activity. So let’s open an Activity, which will open the Dialog. When you update your AppWidget, via a RemoteView:

Open an activity from an AppWidget
1
2
3
4
5
6
7
8
9
10
11
12
Intent intent = new Intent(getApplicationContext(), MyActivity.class);

// Old activities shouldn't be in the history stack
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
    0,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT);

// Link the PendingIntent to a Button
rv.setOnClickPendingIntent(R.id.btn_dialog, pendingIntent);

When the button btn_dialog is pressed, the activity MyActivity is launched. Let’s say we have the AlertDialogBuilder code from the first sample in MyActivity.onCreate(), we have a dialog displayed from an app-widget. But there’s an issue: we don’t want the activity to be visible.

Hide the proxy activity

The activity must be displayed. But what about making it fully transparent? That’s an easy, two-steps task. First, in the manifest, remove any decoration and hide this activity from history:

Remove Activity decorations
1
2
3
4
5
<activity
    android:name=".activities.MyActivity"
    android:noHistory="true"
    android:theme="@android:style/Theme.Translucent.NoTitleBar"
    />

Then in the activity itself, set a transparent background:

Set a transparent background
1
2
3
4
5
6
7
8
public class MyActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setBackgroundDrawable(new ColorDrawable(0));

        // Dialog creation goes here
    }
}

At this point, there are two issues. First, if the dialog is displayed from the app-widget, it will appear in the recent apps list. That’s probably not wanted. There’s again a simple solution. In the manifest:

Hide the Activity from recent apps
1
2
3
4
5
6
<activity
    android:name=".activities.MyActivity"
    android:noHistory="true"
    android:excludeFromRecents="true"
    android:theme="@android:style/Theme.Translucent.NoTitleBar"
    />

The second issue is more visible. The theme Theme.Translucent.NoTitleBar refers to a pre-ICS theme, hence the Gingerbread-looking dialog.

To use the Holo theme on 3.0+ devices, the dialog construction code has to be tweaked a little:

Applying a theme to the dialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Context context;
// For a custom theme:
context = new ContextThemeWrapper(MyActivity.this, R.style.dialog);

// For the Holo one on 3.0+ devices, fallback on 1.x/2.x devices:
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    context = new ContextThemeWrapper(MyActivity.this, android.R.style.Theme_Holo);
} else {
    context = new ContextThemeWrapper(MyActivity.this, android.R.style.Theme_Dialog);
}

new AlertDialog.Builder(context)
    .setTitle("Dialog title")
    .setMessage("Dialog message")
    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            // Handle a positive answer
        }
    })
    .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            // Handle a negative answer
        }
    })
    .setIcon(R.drawable.ic_dialog_alert)
    .show();

If multiple dialogs are needed, the activity could be reused by adding parameters to the intent, and display the needed dialog accordingly. You can also call this activity like any other from your other activities, and share the dialog creation code. Here’s an example of a generic dialog activity, called from a button on another activity:

Last point: even if the activity is invisible, it still needs to be closed when the dialog is hidden. Don’t forget to call Activity.finish() when the dialogs are dismissed. Starting with API 17, you can use a DialogInterface.OnDismissListener():

Applying a theme to the dialog
1
2
3
4
5
6
7
8
9
new AlertDialog.Builder(context)
    .setOnDismissListener(new DialogInterface.OnDismissListener() {
        @Override
        public void onDismiss(DialogInterface dialogInterface) {
            finish();
        }
    })
    // …
    .show()

You can find a full sample code on GitHub.

Comments