Los dispositivos Wear OS se suelen usar para experiencias de larga duración, como el seguimiento de un entrenamiento. Esto presenta un desafío para la experiencia del usuario: si un usuario comienza una tarea y luego navega a la cara de reloj, ¿cómo vuelve? Volver a la app con el selector puede ser difícil, en especial cuando estás en movimiento, lo que genera una fricción innecesaria.
La solución es vincular una notificación continua con un OngoingActivity
.
Esto permite que el dispositivo muestre información sobre la actividad de larga duración en toda la interfaz de usuario, lo que habilita funciones como el ícono que se puede presionar en la parte inferior de la cara del reloj. Esto mantiene a los usuarios al tanto de la tarea en segundo plano y les proporciona una forma de volver a la app con un solo toque.
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:
Figura 1: Indicador de actividad.
Las notificaciones en curso también muestran información en la sección Recientes del selector global de aplicaciones. Esto proporciona otro lugar conveniente para que los usuarios vean el estado de su tarea y vuelvan a interactuar con la app:
Figura 2: Selector global.
Los siguientes son escenarios aptos para usar una notificación continua vinculada a una actividad en curso:
Figura 3: Temporizador: Realiza una cuenta regresiva de forma activa y finaliza cuando se pausa o detiene.
Figura 4: Navegación paso a paso: Anuncia las instrucciones para llegar a un destino. Finaliza cuando el usuario llega al destino o detiene la navegación.
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 contenido multimedia.
Consulta el codelab de Ongoing Activity para ver un ejemplo detallado de cómo crear 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.1.0"
implementation "androidx.core:core:1.17.0"
}
Crea una actividad en curso
El proceso consta de tres pasos:
- Crea un
NotificationCompat.Builder
estándar y configúralo como recurrente. - Crea y configura un objeto
OngoingActivity
y pásale el compilador de notificaciones. - Aplica la actividad en curso al compilador de notificaciones y publica la notificación resultante.
Crea y configura la notificación
Para comenzar, crea un NotificationCompat.Builder
. El paso clave es llamar a setOngoing(true)
para marcarla como una notificación en curso. También puedes establecer otras propiedades de notificación en esta etapa, como el ícono pequeño y la categoría.
// Create a PendingIntent to pass to the notification builder val pendingIntent = PendingIntent.getActivity( this, 0, Intent(this, AlwaysOnActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_SINGLE_TOP }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("Always On Service") .setContentText("Service is running in background") .setSmallIcon(R.drawable.animated_walk) // Category helps the system prioritize the ongoing activity .setCategory(NotificationCompat.CATEGORY_WORKOUT) .setContentIntent(pendingIntent) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setOngoing(true) // Important!
Crea el objeto OngoingActivity
A continuación, crea una instancia de OngoingActivity
con su compilador. El objeto OngoingActivity.Builder
requiere un objeto Context
, un ID de notificación y el objeto NotificationCompat.Builder
que creaste en el paso anterior.
Configura las propiedades clave que se mostrarán en las nuevas plataformas de la IU:
- Íconos animados y estáticos: Proporciona íconos que se muestran en la cara de reloj en los modos activo y ambiente.
- Intent táctil: Es un
PendingIntent
que lleva al usuario de vuelta a tu app cuando presiona el ícono de la actividad en curso. Puedes volver a usar el objetopendingIndent
que creaste en el paso anterior.
val ongoingActivity = OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder) // Sets the icon that appears on the watch face in active mode. .setAnimatedIcon(R.drawable.animated_walk) // Sets the icon that appears on the watch face in ambient mode. .setStaticIcon(R.drawable.ic_walk) // Sets the tap target to bring the user back to the app. .setTouchIntent(pendingIntent) .build()
Aplicar a la notificación y a la publicación
El paso final es vincular el OngoingActivity
con la notificación y, luego, publicarla. El método ongoingActivity.apply()
modifica el compilador de notificaciones original y agrega los datos necesarios para que el sistema pueda mostrarlo en las superficies adicionales. Después de aplicarlo, puedes compilar y publicar la notificación como de costumbre.
// This call modifies notificationBuilder to include the ongoing activity data. ongoingActivity.apply(applicationContext) // Post the notification. startForeground(NOTIFICATION_ID, notificationBuilder.build())
Agrega texto de estado dinámico al selector
El código anterior agrega el ícono que se puede presionar a la cara de reloj. Para proporcionar actualizaciones aún más enriquecidas y en tiempo real en la sección Recientes del selector, crea un objeto Status
y adjúntalo a tu OngoingActivity
. Si no proporcionas un Status
personalizado, el sistema usará de forma predeterminada el texto del contenido de la notificación (establecido con setContentText()
).
Para mostrar texto dinámico, usa un Status.Builder
. Puedes definir una cadena de plantilla con marcadores de posición y proporcionar objetos Status.Part
para completar esos marcadores. El Status.Part
puede ser dinámico, como un cronómetro o un temporizador .
En el siguiente ejemplo, se muestra cómo crear un estado que muestre "Ejecutar durante [un cronómetro]":
// Define a template with placeholders for the activity type and the timer. val statusTemplate = "#type# for #time#" // Set the start time for a stopwatch. // Use SystemClock.elapsedRealtime() for time-based parts. val runStartTime = SystemClock.elapsedRealtime() val ongoingActivityStatus = Status.Builder() // Sets the template string. .addTemplate(statusTemplate) // Fills the #type# placeholder with a static text part. .addPart("type", Status.TextPart("Run")) // Fills the #time# placeholder with a stopwatch part. .addPart("time", Status.StopwatchPart(runStartTime)) .build()
Por último, vincula este Status
a tu OngoingActivity
llamando a setStatus()
en el OngoingActivity.Builder
.
val ongoingActivity = OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder) // ... // Add the status to the OngoingActivity. .setStatus(ongoingActivityStatus) .build()
Personalizaciones adicionales
Más allá de Status
, puedes personalizar tu actividad en curso o tus notificaciones de las siguientes maneras. Sin embargo, es posible que estas personalizaciones no se usen, según la implementación del OEM.
Notificación continua
- El conjunto de category determina la prioridad de la actividad en curso.
CATEGORY_CALL
: Una llamada de voz o video entrante, o una solicitud de comunicación síncrona similarCATEGORY_NAVIGATION
: Un mapa o navegación paso a pasoCATEGORY_TRANSPORT
: Control de transporte de contenido multimedia para la reproducciónCATEGORY_ALARM
: Una alarma o un temporizadorCATEGORY_WORKOUT
: Un entrenamientoCATEGORY_LOCATION_SHARING
: Uso compartido temporal de ubicación (nueva categoría)CATEGORY_STOPWATCH
: cronómetro
Actividad en curso
Ícono animado: Un vector en blanco y negro, preferentemente con un fondo transparente. Se muestra en la cara de reloj en el modo ambiente. Si no se proporciona, se usa el ícono de notificación predeterminado. El ícono de notificación predeterminado es diferente para cada aplicación.
Í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 en 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 selector de aplicaciones todavía utiliza el ícono de la app).
OngoingActivityStatus: Texto sin formato o un
Chronometer
. 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
: Es 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 oculta todos los elementos de la app de la sección Recientes del mismo paquete y solo muestra la actividad en curso.ID de actividad en curso: Es un ID que se usa para desambiguar las llamadas a
fromExistingOngoingActivity()
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 crean una nueva notificación continua y una nueva actividad en curso cuando necesitan actualizar los datos en la pantalla. Sin embargo, la API de Ongoing Activity también ofrece métodos auxiliares para actualizar un OngoingActivity
si quieres 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. Sin embargo, no hagas esto con demasiada frecuencia, ya que el método de actualización ignora las llamadas que están demasiado cerca 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:
ongoingActivity.update(context, newStatus)
Por comodidad, existe un método estático para crear una actividad en curso.
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 continua.
También puedes cancelar la notificación o la actividad en curso cuando pase a primer plano y, luego, volver a crearlas cuando vuelvan a segundo plano, aunque esto no es obligatorio.
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. En el caso de las apps sin una acción de detención explícita, finaliza la actividad cuando esté en pausa.
Prácticas recomendadas
Recuerda lo siguiente cuando trabajes con la API de Ongoing Activity:
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
.Usa íconos vectoriales en blanco y negro con fondos transparentes.
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
.Si la app tiene más de una actividad
MAIN LAUNCHER
declarada en el manifiesto, publica un acceso directo dinámico y asócialo a tu actividad en curso conLocusId
.
Publica notificaciones de contenido multimedia cuando se reproduce contenido multimedia en dispositivos Wear OS
Si se reproduce contenido multimedia en un dispositivo Wear OS, publica una notificación multimedia. Esto le permite al sistema crear la actividad en curso correspondiente.
Si usas Media3, la notificación se publica automáticamente. Si creas tu notificación manualmente, debes usar MediaStyleNotificationHelper.MediaStyle
, y el MediaSession
correspondiente debe tener su actividad de la sesión propagada.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Cómo crear una notificación {:#notification}
- Nuevas maneras de atraer usuarios de Wear OS con la API de Ongoing Activity
- Cómo crear una notificación expandible {:#expandable-notification}