184 lines
5.8 KiB
Markdown
184 lines
5.8 KiB
Markdown
+++
|
|
template = "article.html"
|
|
title = "Android: display a Dialog from an AppWidget"
|
|
date = 2014-04-06T22:27:19+02:00
|
|
description = "A workaround for displaying dialogs from Android AppWidgets, which normally can't show dialogs due to context limitations."
|
|
|
|
[taxonomies]
|
|
tags = ["android"]
|
|
+++
|
|
|
|
## 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*
|
|
|
|
```java
|
|
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?
|
|
|
|
<!--more-->
|
|
|
|
## 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*
|
|
|
|
```java
|
|
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*
|
|
|
|
```xml
|
|
<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*
|
|
|
|
```java
|
|
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*
|
|
|
|
```xml
|
|
<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.
|
|
|
|
{{ img(src="/images/articles/android-display-a-dialog-from-an-appwidget/1-dialog-gb-theme.png", caption="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*
|
|
|
|
```java
|
|
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();
|
|
```
|
|
|
|
{{ img(src="/images/articles/android-display-a-dialog-from-an-appwidget/2-dialog-holo-theme.png", caption="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:
|
|
|
|
{{ img(src="/images/articles/android-display-a-dialog-from-an-appwidget/3-in-app-shared-dialog.png", caption="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*
|
|
|
|
```java
|
|
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](https://github.com/Kernald/android-dialog-activity-sample).
|