Cómo descartar la notificación después de hacer clic en la acción

142

Desde el nivel 16 de API (Jelly Bean), existe la posibilidad de agregar acciones a una notificación con

builder.addAction(iconId, title, intent);

Pero cuando agrego una acción a una notificación y la acción se presiona, la notificación no se descartará. Cuando se hace clic en la notificación, se puede descartar con

notification.flags = Notification.FLAG_AUTO_CANCEL;

o

builder.setAutoCancel(true);

Pero obviamente, esto no tiene nada que ver con las acciones asociadas a la notificación.

¿Alguna pista? ¿O todavía no es parte de la API? No encontré nada.

endowzoner
fuente

Respuestas:

154

Cuando llamaste a Notificar en el administrador de notificaciones, le diste una identificación, esa es la identificación única que puedes usar para acceder a ella más tarde (esto es desde el administrador de notificaciones:

notify(int id, Notification notification)

Para cancelar, debe llamar a:

cancel(int id)

con el mismo id. Entonces, básicamente, ¿necesita hacer un seguimiento de la identificación o posiblemente colocar la identificación en un Paquete que agregue a la Intención dentro del PendingIntent?

Kaediil
fuente
25
Gracias, eso resolvió mi problema. Sin embargo, sigo pensando que es un poco demasiado complicado. En lugar de simplemente proporcionar una API para descartar automáticamente la notificación cuando se presiona una acción, debe trabajar con la intención y pasar la identificación de notificación para lograr lo mismo.
endowzoner
2
Si crees que esto es complicado, no busques actualizar una notificación (no pierdas el rastro de esa identificación) ni compruebes si se muestra o no (la API no la rastrea, debes hacerlo) ...: P
Travis
2
@Daksh: Básicamente, agrega la etiqueta de notificación y la identificación a su intención, que comienza cuando se presiona su acción. Con esa información adicional, puede verificar en la actividad de inicio si se inició mediante una acción de notificación.
endowzoner
55
Ejemplo de código de onCreate (): Bundle extras = getIntent (). GetExtras (); if (extras! = null) {String tag = extras.getString (NotificationReceiver.INTENT_EXTRA_NOTIFICATION_TAG); int id = extras.getInt (NotificationReceiver.INTENT_EXTRA_NOTIFICATION_ID); if (NotificationReceiver.NOTIFICATION_ID == id && NotificationReceiver.NOTIFICATION_TAG.equals (etiqueta)) {// la actividad se ha iniciado mediante una acción de notificación, descartar // notificación NotificationManager manager = (NotificationManager) getSystemService (Service.NOTIFICATION_SERVICE); manager.cancel (etiqueta, id); }}
endowzoner
1
En la nueva API, tiene que notificar (etiqueta de cadena, id int, notificación de notificación) y cancelar correspondientemente (etiqueta de cadena, id int)
Malachiasz
64

Descubrí que esto es un problema al usar la notificación de visualización de Lollipop. Ver pautas de diseño . Aquí está el código completo (ish) para implementar.

Hasta ahora, tener un botón de 'Descartar' era menos importante, pero ahora está más en tu cara.

notificación de aviso

Construyendo la Notificación

int notificationId = new Random().nextInt(); // just use a counter in some util class...
PendingIntent dismissIntent = NotificationActivity.getDismissIntent(notificationId, context);

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(NotificationCompat.PRIORITY_MAX) //HIGH, MAX, FULL_SCREEN and setDefaults(Notification.DEFAULT_ALL) will make it a Heads Up Display Style
        .setDefaults(Notification.DEFAULT_ALL) // also requires VIBRATE permission
        .setSmallIcon(R.drawable.ic_action_refresh) // Required!
        .setContentTitle("Message from test")
        .setContentText("message")
        .setAutoCancel(true)
        .addAction(R.drawable.ic_action_cancel, "Dismiss", dismissIntent)
        .addAction(R.drawable.ic_action_boom, "Action!", someOtherPendingIntent);

// Gets an instance of the NotificationManager service
NotificationManager notifyMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

// Builds the notification and issues it.
notifyMgr.notify(notificationId, builder.build());

NotificaciónActividad

public class NotificationActivity extends Activity {

    public static final String NOTIFICATION_ID = "NOTIFICATION_ID";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.cancel(getIntent().getIntExtra(NOTIFICATION_ID, -1));
        finish(); // since finish() is called in onCreate(), onDestroy() will be called immediately
    }

    public static PendingIntent getDismissIntent(int notificationId, Context context) {
        Intent intent = new Intent(context, NotificationActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        intent.putExtra(NOTIFICATION_ID, notificationId);
        PendingIntent dismissIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        return dismissIntent;
    }

}

AndroidManifest.xml (atributos necesarios para evitar que SystemUI se enfoque en una pila de reserva)

<activity
    android:name=".NotificationActivity"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>
aaronvargas
fuente
Tengo varias notificaciones de una aplicación y las notificaciones se configuran como notificaciones continuas. Quiero borrar la notificación cuando la adición se realiza en una notificación particular
Prasad
3
¿No sería más efectivo usar un BroadcastReceiver aquí para descartar la notificación? Aquí hay un buen ejemplo que muestra la implementación, pero puede reducirse aún más: stackoverflow.com/a/19745745/793150
alice.harrison
1
Las soluciones funcionan, excepto que los extras establecidos se envían con la intención. No se reenvían a onCreate. Una forma es usar variables estáticas. ¿Alguien sabe por qué no se reenvían los intentos adicionales?
Baschi
¿Por qué getDismissIntent solo funciona cuando se coloca en NotificationActivity? La intención de descartar no funciona si el código de creación PendingIntent se coloca en la clase de generador de notificaciones. Acabo de pasar 2 horas en este tema y no puedo entender por qué la intención pendiente DEBE crearse en la actividad. ¿Alguien puede explicar por qué este es el caso?
Ray Li
getDismissIntent () es una función "auxiliar" estática que crea la intención correcta para comunicarse con NotificationActivity. Como tal, estos generalmente se incluyen con la Actividad. Pero no veo por qué esta función estática no se pudo colocar en la clase de generador de notificaciones, siempre y cuando tenga cuidado de configurar el NOTIFICATION_ID y el contexto correctamente.
Mike
17

Descubrí que cuando usas los botones de acción en notificaciones expandidas, tienes que escribir código adicional y estás más limitado.

Debe cancelar manualmente su notificación cuando el usuario hace clic en un botón de acción. La notificación solo se cancela automáticamente para la acción predeterminada.

Además, si inicia un receptor de difusión desde el botón, el cajón de notificaciones no se cierra.

Terminé creando una nueva NotificationActivity para abordar estos problemas. Esta actividad intermedia sin ninguna IU cancela la notificación y luego comienza la actividad que realmente quería comenzar desde la notificación.

He publicado un código de muestra en una publicación relacionada Al hacer clic en Acciones de notificación de Android no se cierra el cajón de notificaciones .

Vicki
fuente
2
Lástima que todavía no hayan incluido esto en la API ... Es bastante hackeo hacerlo así. Pero sigue siendo la única forma, especialmente si no tiene ningún control sobre la intención del destino, como ver una URL.
Bogdan Zurac
Gracias por la información, estás en lo correcto. Sin embargo, usaría un servicio intents en lugar de una actividad intermediaria
Tim
7

Puede siempre cancel()el Notificationde lo que está siendo invocado por la acción (por ejemplo, en el onCreate()de la actividad ligada a la PendingIntentque usted suministra a addAction()).

CommonsWare
fuente
2
Pero, ¿cómo obtengo acceso a la notificación en la actividad que se ha llamado?
endowzoner el
@FleshWound: cancel()toma la identificación del Notification, que usaste cuando llamaste notify(). No necesitas el Notificationobjeto.
CommonsWare
@CommonsWare cancel (id) ha dejado de funcionar si setGroup está configurado y hay una notificación de resumen de grupo. En este caso, la cancelación no hace nada por alguna razón. Sin el resumen del grupo, cancelar funciona bien
Kushan
si mi intención pendiente es ACTION_VIEWy el tipo es image/jpeg(compartir una imagen con otra aplicación), ¿cómo se supone que se activará esa cancelación? ¡IMO Android debería cancelarse automáticamente, estoy desconcertado de por qué Android no solo se encarga de eso?
Alguien en algún lugar
@SomeoneSomewhere: "entonces, ¿cómo se supone que se activará esa cancelación?" No puede. Si bien no hay nada que le impida apuntar a una aplicación de terceros Notificationrelacionada PendingIntent, no es así como fue diseñada para funcionar, por lo que se encontrará con problemas como este. "IMO Android debería cancelarse automáticamente". Pude ver que ofrecía una bandera para eso en la acción, pero no debería ser una cosa de todo el tiempo. Si lo fuera, omitir una pista en una notificación de reproductor de música cerraría la notificación.
CommonsWare
7

En mi opinión, usar a BroadcastReceiveres una forma más limpia de cancelar una Notificación:

En AndroidManifest.xml:

<receiver 
    android:name=.NotificationCancelReceiver" >
    <intent-filter android:priority="999" >
         <action android:name="com.example.cancel" />
    </intent-filter>
</receiver>

En el archivo java:

Intent cancel = new Intent("com.example.cancel");
PendingIntent cancelP = PendingIntent.getBroadcast(context, 0, cancel, PendingIntent.FLAG_CANCEL_CURRENT);

NotificationCompat.Action actions[] = new NotificationCompat.Action[1];

NotificationCancelReceiver

public class NotificationCancelReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //Cancel your ongoing Notification
    };
}
Himanshu Khandelwal
fuente
1
Eso es lo que hago, pero ¿cómo se obtiene el ID de notificación (en este ejemplo, 0) del método onReceive? No está en la intención, ya que no se le ha agregado. Intenté agregarlo como extra pero parece que la identificación de notificación real no es la que estaba agregando como extra en la actividad de creación ...: - /
Marco Zanetti
Realmente me gusta este enfoque de usar servicios de difusión en lugar de actividades, es un enfoque mucho más ligero en mi humilde opinión.
Christophe Moine
Pero tuve que usar <intent.setAction (Integer.toString (notifyId));> además para poder descartar cualquiera de las notificaciones mostradas.
Christophe Moine
1
@MarcoZanetti necesita generar una identificación de notificación que pase al intento pendiente y también al método de notificación al enviar la notificación. Si hace eso, cuando el usuario haga clic en la acción para cancelar, llamará al receptor de transmisión y luego podrá obtener la identificación de notificación de los extras.
Ray Hunter
@ChristopheMoine puede poner la identificación a través intent.putExtra()y obtenerlaBroadcastReceiver
Vadim Kotov
5

En las nuevas API no te olvides de TAG:

notify(String tag, int id, Notification notification)

y correspondientemente

cancel(String tag, int id) 

en vez de:

cancel(int id)

https://developer.android.com/reference/android/app/NotificationManager

Malaquiasz
fuente
¡Usted tenía razón! aunque la cancel()función tiene 2 implementaciones; uno con TAG y otro sin. Pero tenemos que proporcionar a TAG. Aquí está la cancelfunción de los documentos public void cancel(@Nullable String tag, int id). Última comprobación en Android Q
sud007
1

Solo pon esta línea:

 builder.setAutoCancel(true);

Y el código completo es:

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setSmallIcon(android.R.drawable.ic_dialog_alert);
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.co.in/"));
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
    builder.setContentIntent(pendingIntent);
    builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.misti_ic));
    builder.setContentTitle("Notifications Title");
    builder.setContentText("Your notification content here.");
    builder.setSubText("Tap to view the website.");
    Toast.makeText(getApplicationContext(), "The notification has been created!!", Toast.LENGTH_LONG).show();

    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    builder.setAutoCancel(true);
    // Will display the notification in the notification bar
    notificationManager.notify(1, builder.build());
Hanisha
fuente
AutoCancel parece no tener ningún efecto cuando se dirige a Android 9 (funcionó bien cuando se apunta a Android 8.1)
Alix
la distinción aquí es con la acción
Alguien en algún lugar
0

Deberá ejecutar el siguiente código después de que se haya disparado su intención de eliminar la notificación.

NotificationManagerCompat.from(this).cancel(null, notificationId);

Nota: NotificationId es la misma identificación que se pasó para ejecutar su notificación

Houssin Boulla
fuente
-3

builder.setAutoCancel (verdadero);

Probado en Android 9 también.

rudakovsky
fuente