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

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

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

<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

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

<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.

Default theme

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

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();

Holo theme

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:

Dialog on top of a basic 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():

Finishing the activity

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.