Actividad en curso

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

En Wear OS, cuando se vincula una actividad en curso con una notificación en curso, se agrega esa notificación a las plataformas adicionales dentro de la interfaz de usuario de Wear OS, lo que permite a los usuarios mantener una mayor participación con actividades prolongadas.

Por lo general, las notificaciones en curso se usan para indicar que una notificación tiene una tarea en segundo plano con la que el usuario interactúa de manera activa o está pendiente de alguna manera y, por lo tanto, ocupa el dispositivo.

Por ejemplo, un usuario de Wear OS puede usar una app de entrenamiento para registrar un ejercicio, como correr, a partir de una actividad y, luego, salir de esa app a fin de iniciar otra tarea. Cuando el usuario salga de la app de entrenamiento, con frecuencia, la app hará una transición a una notificación en curso que está vinculada a algunas tareas en segundo plano (por ejemplo, servicios o administradores de alarmas) para mantener informado al usuario sobre el ejercicio. La notificación le proporciona al usuario actualizaciones y una manera fácil de volver a la app.

Sin embargo, para ver la notificación, el usuario tiene que deslizar el dedo en la bandeja de notificaciones que se encuentra debajo de la cara de reloj y buscar la correcta. No es tan conveniente como otras plataformas.

Con la API de Ongoing Activity, la notificación en curso de una app puede mostrar información a varias plataformas nuevas y prácticas en Wear OS para mantener la interacción del usuario.

Por ejemplo, en el caso de la app de entrenamiento, la información puede aparecer en la cara de reloj del usuario como un ícono de una persona corriendo que se puede presionar:

running-icon

Figura 1: Indicador de actividad

La sección Recientes del selector global de aplicaciones también enumera las actividades en curso:

selector

Figura 2: Selector global

Las siguientes son buenas situaciones para usar una notificación en curso vinculada a una actividad en curso:

temporizador

Figura 3: Temporizador: Realiza una cuenta regresiva de forma activa y finaliza cuando se pausa o detiene.

mapa

Figura 4: Navegación paso a paso Anuncia las instrucciones para llegar a un destino. Finaliza cuando el usuario llega a la ubicación deseada o detiene la navegación.

música

Figura 5: Contenido multimedia: Reproduce música durante una sesión. Finaliza inmediatamente después de que el usuario pausa la sesión.

Wear crea actividades en curso automáticamente para las apps de música. Consulta el codelab de actividades en curso en GitHub para obtener un ejemplo detallado de creación de actividades en curso para otros tipos de apps.

Configuración

Para comenzar a usar la API de Ongoing Activity en tu app, agrega las siguientes dependencias al archivo build.gradle de tu app:

dependencies {
  implementation "androidx.wear:wear-ongoing:1.0.0"
  // Includes LocusIdCompat and new Notification categories for Ongoing Activity.
  implementation "androidx.core:core:1.6.0"
}

Inicia una actividad en curso

Comienza con una actividad en curso.

Notificación en curso

Como se mencionó antes, las actividades en curso están estrechamente relacionadas con las notificaciones en curso.

Ambas funcionan en conjunto para informar a los usuarios sobre una tarea con la que pueden interactuar de manera activa, o bien que está pendiente de alguna manera y, por lo tanto, ocupa el dispositivo.

Debes vincular una actividad en curso con una notificación en curso.

La vinculación de tu actividad en curso a una notificación genera muchos beneficios, entre los que se incluyen los siguientes:

  • Las notificaciones son el resguardo de los dispositivos que no admiten actividades en curso. La notificación es la única plataforma que mostrará tu app mientras se ejecuta en segundo plano.
  • En Android 11 y versiones posteriores, Wear OS oculta la notificación en la bandeja de notificaciones cuando la app se puede ver como actividad en curso en plataformas adicionales.
  • La implementación actual usa el mismo elemento Notification como mecanismo de comunicación.

Actividad en curso

Es simple iniciar una actividad en curso una vez que tengas una notificación en curso.

La siguiente muestra de código incluye comentarios para ayudarte a comprender qué significa cada propiedad:

Kotlin

var builder = NotificationCompat.Builder(this, CHANNEL_ID)
      …
      .setSmallIcon(..)
      .setOngoing(true)

val ongoingActivityStatus = Status.Builder()
    // Sets the text used across various surfaces.
    .addTemplate(mainText)
    .build()

val ongoingActivity =
    OngoingActivity.Builder(
        applicationContext, NOTIFICATION_ID, notificationBuilder
    )
        // Sets the animated icon that will appear on the watch face in
        // active mode.
        // If it isn't set, the watch face will use the static icon in
        // active mode.
        .setAnimatedIcon(R.drawable.ic_walk)
        // Sets the icon that will appear on the watch face in ambient mode.
        // Falls back to Notification's smallIcon if not set.
        // If neither is set, an Exception is thrown.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap/touch event, so users can re-enter your app from the
        // other surfaces.
        // Falls back to Notification's contentIntent if not set.
        // If neither is set, an Exception is thrown.
        .setTouchIntent(activityPendingIntent)
        // In our case, sets the text used for the Ongoing Activity (more
        // options are available for timers and stopwatches).
        .setStatus(ongoingActivityStatus)
        .build()

ongoingActivity.apply(applicationContext)

notificationManager.notify(NOTIFICATION_ID, builder.build())

Java

NotificationCompat.Builder builder = NotificationCompat.Builder(this, CHANNEL_ID)
      …
      .setSmallIcon(..)
      .setOngoing(true);

OngoingActivityStatus ongoingActivityStatus = OngoingActivityStatus.Builder()
    // Sets the text used across various surfaces.
    .addTemplate(mainText)
    .build();

OngoingActivity ongoingActivity =
    OngoingActivity.Builder(
        applicationContext, NOTIFICATION_ID, notificationBuilder
    )
        // Sets the animated icon that will appear on the watch face in
        // active mode.
        // If it isn't set, the watch face will use the static icon in
        // active mode.
        .setAnimatedIcon(R.drawable.ic_walk)
        // Sets the icon that will appear on the watch face in ambient mode.
        // Falls back to Notification's smallIcon if not set.
        // If neither is set, an Exception is thrown.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap/touch event, so users can re-enter your app from the
        // other surfaces.
        // Falls back to Notification's contentIntent if not set.
        // If neither is set, an Exception is thrown.
        .setTouchIntent(activityPendingIntent)
        // In our case, sets the text used for the Ongoing Activity (more
        // options are available for timers and stopwatches).
        .setStatus(ongoingActivityStatus)
        .build();

ongoingActivity.apply(applicationContext);

notificationManager.notify(NOTIFICATION_ID, builder.build());

En los siguientes pasos, se indica la parte más importante del ejemplo anterior:

  1. Llama a .setOngoing(true) en NotificationCompat.Builder y configura los campos opcionales.

  2. Crea un objeto OngoingActivityStatus para representar el texto (en la siguiente sección, se describen otras opciones de estado).

  3. Crea un objeto OngoingActivity y configura un ID de notificación (obligatorio).

  4. Llama a apply() en OngoingActivity con el contexto.

  5. Llama a notificationManager.notify() y pasa el mismo ID de notificación que la actividad en curso para vincularlos.

Estado

El objeto Status permite que los desarrolladores le muestren el estado actual y en vivo de OngoingActivity al usuario en plataformas nuevas, como la sección Recientes del selector. Para usar la función, utiliza Status.Builder.

En la mayoría de los casos, los desarrolladores solo necesitan agregar una plantilla que represente el texto que desean que aparezca en la sección Recientes del selector de aplicaciones.

Los desarrolladores pueden personalizar la forma en que aparece el texto con Intervalos a través del método addTemplate() y la especificación de cualquier parte dinámica del texto como un Status.Part.

En el siguiente ejemplo, se muestra la forma para hacer que la palabra "tiempo" aparezca en rojo. En el ejemplo, se usa un objeto Status.TimerPart, que nos permite representar un temporizador o un objeto Status.StopwatchPart, para representar un cronómetro en la sección Recientes del selector de aplicaciones.

Kotlin

val htmlStatus =
        "<p>The <font color=\"red\">time</font> on your current #type# is #time#.</p>"

val statusTemplate =
        Html.fromHtml(
                htmlStatus,
                Html.FROM_HTML_MODE_COMPACT
        )

// Creates a 5 minute timer.
// Note the use of SystemClock.elapsedRealtime(), not System.currentTimeMillis()
val runStartTime = SystemClock.elapsedRealtime() + TimeUnit.MINUTES.toMillis(5)

val status = new Status.Builder()
   .addTemplate(statusTemplate)
   .addPart("type", Status.TextPart("run"))
   .addPart("time", Status.StopwatchPart(runStartTime)
   .build()

Java

String htmlStatus =
        "<p>The <font color=\"red\">time</font> on your current #type# is #time#.</p>";

Spanned statusTemplate =
        Html.fromHtml(
                htmlStatus,
                Html.FROM_HTML_MODE_COMPACT
        );

// Creates a 5 minute timer.
// Note the use of SystemClock.elapsedRealtime(), not System.currentTimeMillis()
Long runStartTime = SystemClock.elapsedRealtime() + TimeUnit.MINUTES.toMillis(5);

Status status = new Status.Builder()
   .addTemplate(statusTemplate)
   .addPart("type", new Status.TextPart("run"))
   .addPart("time", new Status.StopwatchPart(runStartTime)
   .build();

Para hacer referencia a una parte de la plantilla, utiliza el nombre que aparece entre "#". A fin de producir "#" en el resultado, usa "##" en la plantilla.

En el ejemplo anterior, se usa HTMLCompat a fin de generar un objeto CharSequence para pasar a la plantilla, lo que es más fácil que definir un objeto Spannable de forma manual.

Personalizaciones adicionales

Además de Status, los desarrolladores pueden personalizar su actividad en curso o sus notificaciones de las siguientes maneras. Estas personalizaciones se pueden usar o no, según la implementación del OEM.

Notificación en curso

  • El conjunto de category determina la prioridad de la actividad en curso.
    • CATEGORY_CALL: Una llamada entrante (voz o video) o una solicitud de comunicación sincrónica similar
    • CATEGORY_NAVIGATION: Un mapa o navegación paso a paso
    • CATEGORY_TRANSPORT: Control de transporte de contenido multimedia para la reproducción
    • CATEGORY_ALARM: Una alarma o un temporizador
    • CATEGORY_WORKOUT: Un entrenamiento (nuevo)
    • CATEGORY_LOCATION_SHARING: Compartir ubicación temporalmente (nuevo)
    • CATEGORY_STOPWATCH: Cronómetro (nuevo) \

Actividad en curso

  • Ícono animado: Un vector en blanco y negro, preferentemente con un fondo transparente. Se muestra en la cara de reloj durante el modo activo. Si no se proporciona, se usa el ícono de notificación predeterminado.

  • Ícono estático: Un ícono vectorial con fondo transparente. Se muestra en la cara de reloj en el modo ambiente. Si no se configura el ícono animado, se usa el ícono estático en la cara de reloj para el modo activo. Si no se proporciona, se usa el ícono de notificación. Si no se establece ninguno, se arroja una excepción (el ícono que se usa en el selector de aplicaciones seguirá usando el de la app).

  • OngoingActivityStatus: Texto sin formato o un cronómetro. Se muestra en la sección Recientes del selector de aplicaciones. Si no se proporciona, se usa la notificación "texto de contexto".

  • Intent táctil: Se usa un PendingIntent para volver a la app si el usuario presiona el ícono de la actividad en curso. Se muestra en la cara de reloj o en el elemento del selector. Puede ser diferente del intent original que se usó para iniciar la app. Si no se proporciona, se usa el intent de contenido de la notificación; si no se establece ninguno, se arroja una excepción. \

  • LocusId: Un ID que asigna el acceso directo del selector al que corresponde la actividad en curso. Se muestra en el selector en la sección Recientes mientras la actividad está en curso. Si no se proporciona, el selector ocultará todos los elementos de la app de la sección Recientes del mismo paquete y solo mostrará la actividad en curso. \

  • ID de actividad en curso: Un ID que se usa para desambiguar las llamadas a fromExistingOngoingActivityfromExistingOngoingActivity() cuando una aplicación tiene más de una actividad en curso.

Actualiza una actividad en curso

En la mayoría de los casos, los desarrolladores crearán una nueva notificación en curso y una nueva actividad en curso cuando necesiten actualizar los datos en la pantalla. Sin embargo, la API de Ongoing Activity también ofrece métodos auxiliares para actualizar OngoingActivity si deseas conservar una instancia en lugar de volver a crearla.

Si la app se ejecuta en segundo plano, puede enviar actualizaciones a la API de Ongoing Activity, pero es poco frecuente. Es posible que el método de actualización ignore llamadas demasiado cercanas entre sí. Algunas actualizaciones por minuto son razonables.

Para actualizar la actividad en curso y la notificación publicada, usa el objeto que creaste antes y llama a update(), como se muestra en el siguiente ejemplo:

Kotlin

ongoingActivity.update(context, newStatus)

Java

ongoingActivity.update(context, newStatus);

Por comodidad, existe un método estático para crear una actividad en curso.

Kotlin

OngoingActivity.recoverOngoingActivity(context)
               .update(context, newStatus)

Java

OngoingActivity.recoverOngoingActivity(context)
               .update(context, newStatus);

Detén una actividad en curso

Cuando la app termina de ejecutarse como una actividad en curso, solo necesita cancelar la notificación en curso.

Depende de la app si desea cancelar la notificación o la actividad en curso cuando pasa a primer plano y, luego, recrearlas cuando vuelven a segundo plano.

Pausa una actividad en curso

Si tu app tiene una acción explícita de detener, continúa con la actividad en curso después de reanudarla. Sin embargo, las apps sin una acción explícita de detener deben finalizar la actividad cuando se pausa.

Prácticas recomendadas

Recuerda lo siguiente cuando trabajes con la API de Ongoing Activity:

  • Siempre llama a ongoingActivity.apply(context) antes de llamar a notificationManager.notify(...).
  • Siempre configura un ícono estático para tu actividad en curso, ya sea de forma explícita o como resguardo a través de la notificación. De lo contrario obtendrás un elemento IllegalArgumentException.

  • Los íconos deben ser vectores en blanco y negro con un fondo transparente.

  • Siempre configura un intent táctil para tu actividad en curso, ya sea de forma explícita o como resguardo a través de la notificación. De lo contrario, obtendrás un elemento IllegalArgumentException.

  • Para NotificationCompat, usa la biblioteca principal de androidx core:1.5.0-alpha05+, que incluye LocusIdCompat y nuevas categorías (entrenamiento, cronómetro o compartir ubicación).

  • Si la app tiene más de una actividad MAIN LAUNCHER declarada en el manifiesto, publica unacceso directo dinámico y asócialo a tu actividad en curso mediante LocusId.