Attività in corso

In Wear OS, l'accoppiamento di un'attività in corso con una notifica continua aggiunge la notifica ad altre piattaforme nell'interfaccia utente di Wear OS. In questo modo gli utenti possono rimanere più coinvolti con le attività di lunga durata.

Le notifiche in corso vengono generalmente utilizzate per indicare che una notifica include un'attività in background con cui l'utente ha interagito attivamente o che è in qualche modo in attesa e che quindi occupa il dispositivo.

Ad esempio, un utente di Wear OS potrebbe utilizzare un'app di allenamento per registrare una corsa da un'attività, quindi uscire dall'app per avviare altre attività. Quando l'utente esce dall'app di allenamento, l'app passa a una notifica continua associata ad alcune operazioni in background per mantenere l'utente informato durante la corsa. La notifica fornisce all'utente aggiornamenti e un modo semplice per riaccedere all'app.

Tuttavia, per visualizzare la notifica, l'utente deve scorrere nella barra delle notifiche sotto il quadrante e trovare la notifica corretta. Non è così comodo come su altre piattaforme.

Con l'API Onheading Activity, le notifiche continue di un'app possono mostrare informazioni su più utili piattaforme nuove e utili su Wear OS per mantenere alto l'interesse dell'utente.

Ad esempio, in questa app di allenamento, le informazioni possono essere visualizzate sul quadrante dell'orologio dell'utente come un'icona di corsa toccabile:

icona-corsa

Figura 1. Indicatore di attività.

Nella sezione Recenti di Avvio app globale sono elencate anche tutte le attività in corso:

avvio app

Figura 2. Avvio app globale.

Di seguito sono riportate alcune buone situazioni in cui utilizzare una notifica continua associata a un'attività in corso:

timer

Figura 3. Timer: viene eseguito il conto alla rovescia e termina quando il timer viene messo in pausa o interrotto.

mappa

Figura 4. Navigazione passo passo: annuncia le indicazioni stradali per raggiungere una destinazione. Termina quando l'utente raggiunge la destinazione o interrompe la navigazione.

musica

Figura 5. Contenuti multimediali: riproduce musica durante una sessione. Termina subito dopo che l'utente ha messo in pausa la sessione.

Wear crea automaticamente attività in corso per le app multimediali.

Consulta il codelab sulle attività in corso per un esempio approfondito sulla creazione di attività in corso per altri tipi di app.

Configurazione

Per iniziare a utilizzare l'API OnGoing Activity nella tua app, aggiungi le seguenti dipendenze al file build.gradle dell'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"
}

Avviare un'attività continua

Per iniziare, crea una notifica e poi un'attività in corso.

Creare una notifica continua

Un'attività in corso è strettamente correlata a una notifica continua. Collaborano per informare gli utenti di un'attività con cui l'utente è attivamente coinvolto o di un'attività che è in qualche modo in sospeso e che pertanto occupa il dispositivo.

Devi associare un'attività in corso a una notifica continua. Collegare la tua attività in corso a una notifica offre molti vantaggi, tra cui:

  • Le notifiche sono di riserva sui dispositivi che non supportano le attività in corso. La notifica è l'unica piattaforma mostrata quando l'app è in esecuzione in background.
  • Su Android 11 e versioni successive, Wear OS nasconde la notifica nell'area delle notifiche quando l'app è visibile come attività in corso su altre piattaforme.
  • L'implementazione corrente utilizza Notification come meccanismo di comunicazione.

Crea una notifica continua utilizzando Notification.Builder.setOnOn.

Avviare un'attività continua

Quando hai una notifica continua, crea un'attività continua come mostrato nell'esempio seguente. Controlla i commenti inclusi per comprendere il comportamento di ogni proprietà.

Kotlin

var notificationBuilder = 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)
        // Here, 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 notificationBuilder = 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)
        // Here, 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());

I passaggi seguenti richiamano la parte più importante dell'esempio precedente:

  1. Richiama .setOngoing(true) sul NotificationCompat.Builder e imposta eventuali campi facoltativi.

  2. Crea un elemento OngoingActivityStatus, o un'altra opzione di stato, come descritto nella sezione seguente, per rappresentare il testo.

  3. Crea un OngoingActivity e imposta un ID notifica.

  4. Chiama apply() su OngoingActivity con il contesto.

  5. Chiama notificationManager.notify() e trasmetti lo stesso ID notifica impostato nell'attività in corso per collegarli.

Stato

Puoi usare Status per mostrare all'utente lo stato attuale e attivo di OngoingActivity su nuove piattaforme, ad esempio nella sezione Recenti di Avvio app. Per utilizzare la funzionalità, utilizza la sottoclasse Status.Builder.

Nella maggior parte dei casi, devi solo aggiungere un modello che rappresenti il testo da visualizzare nella sezione Recenti di Avvio applicazioni.

Puoi quindi personalizzare l'aspetto del testo con gli intervalli utilizzando il metodo addTemplate() e specificando eventuali parti dinamiche del testo come Status.Part.

L'esempio seguente mostra come far apparire in rosso la parola "tempo". L'esempio utilizza un Status.StopwatchPart per rappresentare un cronometro nella sezione Recenti di Avvio app.

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();

Per fare riferimento a una parte del modello, utilizza il nome racchiuso da #. Per produrre # nell'output, utilizza ## nel modello.

L'esempio precedente utilizza HTMLCompat per generare un CharSequence da passare al modello, che è più semplice rispetto alla definizione manuale di un oggetto Spannable.

Personalizzazioni aggiuntive

Oltre il giorno Status, puoi personalizzare le attività in corso o le notifiche nei seguenti modi. Tuttavia, queste personalizzazioni potrebbero non essere utilizzate, a seconda dell'implementazione dell'OEM.

Notifica in corso

  • La categoria impostata determina la priorità dell'attività in corso.
    • CATEGORY_CALL: una chiamata vocale o una videochiamata in arrivo o una richiesta di comunicazione sincrona simile
    • CATEGORY_NAVIGATION: una mappa o una navigazione passo passo
    • CATEGORY_TRANSPORT: controllo del trasporto multimediale per la riproduzione
    • CATEGORY_ALARM: una sveglia o un timer
    • CATEGORY_WORKOUT: un esercizio (nuova categoria)
    • CATEGORY_LOCATION_SHARING: condivisione temporanea della posizione (nuova categoria)
    • CATEGORY_STOPWATCH: cronometro (nuova categoria)

Attività in corso

  • Icona animata: un vettore bianco e nero, preferibilmente con uno sfondo trasparente. Viene visualizzato sul quadrante in modalità attiva. Se non viene fornita l'icona animata, viene utilizzata l'icona di notifica predefinita. (l'icona di notifica predefinita è diversa per ogni applicazione).

  • Icona statica: un'icona vettoriale con sfondo trasparente. Vengono visualizzati sul quadrante in modalità Ambient. Se l'icona animata non è impostata, viene utilizzata l'icona statica sul quadrante in modalità attiva. Se non viene specificata, viene utilizzata l'icona di notifica. Se nessuna delle due è impostata, viene lanciata un'eccezione. Avvio applicazioni continua a utilizzare l'icona dell'app.

  • OndingActivityStatus: testo normale o un Chronometer. Vengono visualizzate nella sezione Recenti di Avvio applicazioni. Se non viene fornito, viene utilizzato il "testo contestuale" della notifica.

  • Touch Intent:un PendingIntent utilizzato per tornare all'app se l'utente tocca l'icona dell'attività in corso. Viene visualizzata sul quadrante o nell'elemento in Avvio app. Può essere diverso dall'intent originale utilizzato per avviare l'app. Se non viene specificato, viene usato l'intent di contenuti della notifica. Se nessuno dei due viene impostato, viene generata un'eccezione.

  • LocusId: ID che assegna la scorciatoia Avvio app a cui corrisponde l'attività in corso. Viene visualizzato in Avvio applicazioni nella sezione Recenti mentre l'attività è in corso. Se non viene fornito, Avvio app nasconde tutti gli elementi delle app nella sezione Recenti dello stesso pacchetto e mostra solo l'attività in corso.

  • ID attività in corso: ID utilizzato per distinguere le chiamate a fromExistingOngoingActivity() quando un'applicazione ha più di un'attività in corso.

Aggiornare un'attività in corso

Nella maggior parte dei casi, gli sviluppatori creano una nuova notifica continua e una nuova attività in corso quando devono aggiornare i dati sullo schermo. Tuttavia, l'API OnGoing Activity offre anche metodi helper per aggiornare un OngoingActivity se vuoi conservare un'istanza anziché ricrearla.

Se l'app è in esecuzione in background, può inviare aggiornamenti all'API OnGoing Activity. Tuttavia, non farlo troppo spesso, poiché il metodo di aggiornamento ignora le chiamate troppo vicine tra loro. Alcuni aggiornamenti al minuto sono ragionevoli.

Per aggiornare l'attività in corso e la notifica pubblicata, utilizza l'oggetto creato in precedenza e chiama update(), come mostrato nell'esempio seguente:

Kotlin

ongoingActivity.update(context, newStatus)

Java

ongoingActivity.update(context, newStatus);

Per praticità, esiste un metodo statico per creare un'attività continua.

Kotlin

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

Java

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

Interrompere un'attività in corso

Quando l'app è in esecuzione come attività in corso, deve solo annullare la notifica in corso.

Puoi anche scegliere di annullare la notifica o l'attività in corso quando sei in primo piano e poi ricrearle quando torni in background, ma questa operazione non è obbligatoria.

Mettere in pausa un'attività in corso

Se per l'app è presente un'azione di interruzione esplicita, continua l'attività in corso dopo averla riattivata. Per un'app senza un'azione di interruzione esplicita, termina l'attività quando è in pausa.

Best practice

Ricorda quanto segue quando lavori con l'API Onheading Activity:

  • Chiama il numero ongoingActivity.apply(context) prima di chiamare notificationManager.notify(...).
  • Imposta un'icona statica per l'Attività in corso, in modo esplicito o come riserva tramite la notifica. In caso contrario, riceverai un IllegalArgumentException.

  • Utilizza icone vettoriali in bianco e nero con sfondi trasparenti.

  • Imposta un intent touch per l'attività in corso, in modo esplicito o come riserva utilizzando la notifica. In caso contrario, riceverai un IllegalArgumentException.

  • Per NotificationCompat, usa la libreria AndroidX Core core:1.5.0-alpha05+, che include LocusIdCompat e le nuove categorie relative ad allenamenti, cronometri e condivisione della posizione.

  • Se nel file manifest della tua app sono state dichiarate più attività MAIN LAUNCHER, pubblica una scorciatoia dinamica e associala all'attività in corso utilizzando LocusId.

Pubblica notifiche multimediali durante la riproduzione di contenuti multimediali su dispositivi Wear OS

Se i contenuti multimediali sono in riproduzione su un dispositivo Wear OS, pubblica una notifica relativa ai contenuti multimediali. Ciò consente al sistema di creare l'attività in corso corrispondente.

Se utilizzi Media3, la notifica viene pubblicata automaticamente. Se crei la notifica manualmente, deve utilizzare MediaStyleNotificationHelper.MediaStyle e la notifica MediaSession corrispondente dovrebbe essere completata la sua attività di sessione.