Cómo iniciar una actividad desde una notificación

Cuando inicies una actividad desde una notificación, debes preservar la experiencia de navegación esperada por el usuario. Cuando se presiona el botón Atrás, el usuario debe volver al flujo de trabajo normal de la app hasta la pantalla principal, y cuando se abre la pantalla Recientes, la actividad debe mostrarse como una tarea separada. Para conservar esta experiencia de navegación, inicia la actividad en una tarea nueva.

El enfoque básico para establecer el comportamiento de toque de tu notificación se describe en Crea una notificación básica. En esta página, se describe cómo configurar un PendingIntent para la acción de tu notificación, de modo que cree una tarea y pila de actividades nuevas. La forma de hacerlo depende del tipo de actividad que inicies:

  • Actividad normal: Es una actividad que existe como parte del flujo normal de la UX de tu app. Cuando el usuario llega a la actividad desde la notificación, la nueva tarea debe incluir una pila de actividades completa, lo que le permite al usuario presionar el botón Atrás para navegar hacia arriba en la jerarquía de la app.
  • Actividad especial: El usuario solo ve esta actividad si se inicia desde una notificación. En cierto sentido, esta actividad extiende la IU de la notificación, ya que proporciona información que es difícil de mostrar en la notificación en sí. Esta actividad no necesita una pila de actividades.

Cómo configurar un PendingIntent de actividad normal

Para iniciar una actividad normal desde tu notificación, configura PendingIntent con TaskStackBuilder de modo que cree una nueva pila de actividades pendientes de la siguiente manera.

Cómo definir la jerarquía de actividades de tu app

Define la jerarquía natural de tus actividades agregando el atributo android:parentActivityName a cada elemento <activity> en el archivo de manifiesto de tu app. Consulta el siguiente ejemplo:

<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<!-- MainActivity is the parent for ResultActivity. -->
<activity
    android:name=".ResultActivity"
    android:parentActivityName=".MainActivity" />
    ...
</activity>

Cómo compilar un PendingIntent con una pila de actividades

Para iniciar una actividad que incluya una pila de actividades anterior, crea una instancia de TaskStackBuilder y llama a addNextIntentWithParentStack(), pasándole el Intent de la actividad que deseas iniciar.

Siempre que definas la actividad principal para cada actividad como se describió anteriormente, puedes llamar a getPendingIntent() para recibir un PendingIntent que incluya toda la pila de actividades.

// Create an Intent for the activity you want to start.
val resultIntent = Intent(this, ResultActivity::class.java)
// Create the TaskStackBuilder.
val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(this).run {
    // Add the intent, which inflates the back stack.
    addNextIntentWithParentStack(resultIntent)
    // Get the PendingIntent containing the entire back stack.
    getPendingIntent(
        0,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
}

Si es necesario, puedes agregar argumentos a los objetos Intent de la pila llamando a TaskStackBuilder.editIntentAt(). A veces, esto es necesario para garantizar que una actividad en la pila de actividades secundarias muestre datos significativos cuando el usuario navega a ella.

Luego, puedes pasar PendingIntent a la notificación como de costumbre:

val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
    setContentIntent(resultPendingIntent)
    // ...
}
with(NotificationManagerCompat.from(this)) {
    notify(NOTIFICATION_ID, builder.build())
}

Cómo configurar un PendingIntent de actividad especial

Dado que una actividad especial que se inicia desde una notificación no necesita una pila de actividades anterior, puedes crear el PendingIntent llamando a getActivity(). Sin embargo, define las opciones de tareas adecuadas en el manifiesto.

  1. En tu manifiesto, agrega los siguientes atributos al elemento <activity>.

    android:taskAffinity=""
    En combinación con la marca FLAG_ACTIVITY_NEW_TASK que usas en el código, establece este atributo en blanco para asegurarte de que esta actividad no se incluya en la tarea predeterminada de la app. No se verán afectadas las tareas existentes que tengan la afinidad predeterminada de la app.
    android:excludeFromRecents="true"
    Excluye la nueva tarea de la pantalla Recientes para que el usuario no pueda volver a ella por accidente.

    Esto se muestra en el siguiente ejemplo:

    <activity
    android:name=".ResultActivity"
    android:launchMode="singleTask"
    android:taskAffinity=""
    android:excludeFromRecents="true">
    </activity>

  2. Crea y emite la notificación:

    1. Crea un Intent que inicie el Activity.
    2. Llama a setFlags() con las marcas FLAG_ACTIVITY_NEW_TASK y FLAG_ACTIVITY_CLEAR_TASK para establecer Activity de modo que se inicie en una tarea nueva y vacía.
    3. Crea un PendingIntent llamando a getActivity().

    Esto se muestra en el siguiente ejemplo:

    val notifyIntent = Intent(this, ResultActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }
    val notifyPendingIntent = PendingIntent.getActivity(
        this, 0, notifyIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

  3. Pasa PendingIntent a la notificación como de costumbre:

    val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
        setContentIntent(notifyPendingIntent)
        // ...
    }
    with(NotificationManagerCompat.from(this)) {
        notify(NOTIFICATION_ID, builder.build())
    }

Para obtener más información sobre las diferentes opciones de tareas y cómo funciona la pila de actividades, consulta Tareas y la pila de actividades.