Aktywność w toku

W Wear OS sparowanie trwającej aktywności z powiadomieniem o trwającej aktywności spowoduje dodanie tego powiadomienia na kolejnych platformach w interfejsie Wear OS. Dzięki temu użytkownicy mogą być bardziej zaangażowani w długotrwałe działania.

Powiadomienia ciągłe są zwykle używane do informowania, że powiadomienie ma zadanie w tle, z którym użytkownik jest w jakiś sposób aktywnie zaangażowany lub oczekuje na zatwierdzenie i dlatego zajmuje urządzenie.

Na przykład użytkownik Wear OS może użyć aplikacji do treningu, aby nagrać bieg podczas aktywności, a następnie wyjść z tej aplikacji, aby rozpocząć inne zadanie. Gdy użytkownik opuści aplikację treningową, aplikacja przejdzie do ciągłego powiadomienia powiązanego z jakimiś działaniami w tle, aby przekazać mu informacje o biegu. Zawiera ono informacje o aktualizacjach i prosty sposób powrotu do aplikacji.

Aby je wyświetlić, użytkownik musi przesunąć palcem do panelu powiadomień pod tarczą zegarka i znaleźć właściwe powiadomienie. Nie jest to tak wygodne jak na innych platformach.

Interfejs Ongoing Activity API umożliwia wyświetlanie informacji w wielu nowych, wygodnych miejscach na Wear OS w celu podtrzymania zaangażowania użytkownika.

Na przykład w tej aplikacji do treningu informacje mogą pojawiać się na tarczy zegarka użytkownika w postaci dotknięcia ikony biegania:

ikona do biegania

Rysunek 1. Wskaźnik aktywności.

Sekcja Ostatnie w globalnym menu z aplikacjami zawiera też listę trwających działań:

launcher

Rysunek 2. Globalny program uruchamiający.

Oto dobre sytuacje, w których można korzystać z powiadomień o trwającej aktywności:

minutnik

Rysunek 3. Minutnik: aktywnie odlicza czas przestoju i kończy, gdy licznik czasu zostanie wstrzymany lub zatrzymany.

mapa

Rysunek 4. Nawigacja zakręt po zakręcie: podaje wskazówki dojazdu do miejsca docelowego. Kończy się, gdy użytkownik dotrze do miejsca docelowego lub zatrzyma nawigację.

muzyka

Rysunek 5. Multimedia:odtwarza muzykę w trakcie sesji. Kończy się natychmiast po wstrzymaniu sesji przez użytkownika.

Wear automatycznie tworzy trwające działania w aplikacjach do multimediów.

Dokładny przykład tworzenia bieżących działań w innych rodzajach aplikacji znajdziesz w ćwiczeniach z programowania w ramach trwającej aktywności.

Skonfiguruj

Aby zacząć korzystać z interfejsu Ongoing Activity API w swojej aplikacji, dodaj te zależności do pliku build.gradle aplikacji:

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"
}

Rozpoczynanie trwającego działania

Zacznij od utworzenia powiadomienia o trwającej aktywności, a następnie trwającej aktywności.

Tworzenie trwającego powiadomienia

Trwające działanie jest ściśle powiązane z trwającym powiadomieniem. Współpracują one, aby informować użytkowników o zadaniu, którym użytkownik jest aktywnie zaangażowany, lub o zadaniu, które jest w jakiś sposób oczekujące i dlatego zajmuje urządzenie.

Musisz powiązać trwającą aktywność z trwającym powiadomieniem. Połączenie trwającej aktywności z powiadomieniem ma wiele zalet, w tym:

  • Powiadomienia to dane zastępcze na urządzeniach, które nie obsługują bieżących aktywności. Jest to jedyny obszar widoczny dla aplikacji, gdy działa ona w tle.
  • Na Androidzie 11 i nowszych Wear OS ukrywa powiadomienie w obszarze powiadomień, gdy aplikacja jest widoczna jako trwająca aktywność na dodatkowych platformach.
  • W obecnej implementacji wykorzystuje ona sama Notification jako mechanizm komunikacji.

Utwórz aktywne powiadomienie za pomocą funkcji powiadomienie.Builder.setOngoing.

Rozpoczynanie trwającego działania

Po otrzymaniu powiadomienia utwórz trwające działanie w sposób pokazany w przykładzie poniżej. Zapoznaj się z dołączonymi komentarzami, aby dowiedzieć się więcej o działaniu poszczególnych usług.

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

Poniższe kroki wskazują najważniejszą część poprzedniego przykładu:

  1. Wywołaj .setOngoing(true) w NotificationCompat.Builder i ustaw dowolne opcjonalne pola.

  2. Utwórz element OngoingActivityStatus lub inną opcję stanu zgodnie z opisem w następnej sekcji, aby reprezentować tekst.

  3. Utwórz OngoingActivity i ustaw identyfikator powiadomienia.

  4. Zadzwoń do firmy apply() pod numer OngoingActivity i podaj kontekst.

  5. Zadzwoń pod numer notificationManager.notify() i przekaż ten sam identyfikator powiadomienia, który jest ustawiony w trwającej aktywności, aby je powiązać.

Stan

Użyj opcji Status, aby pokazać użytkownikowi bieżący stan opublikowanej wersji OngoingActivity na nowych platformach, takich jak Ostatnie w Menu z aplikacjami. Aby korzystać z tej funkcji, użyj podklasy Status.Builder.

W większości przypadków wystarczy dodać szablon reprezentujący tekst widoczny w sekcji Ostatnie w Menu z aplikacjami.

Następnie za pomocą metody addTemplate() możesz dostosować sposób wyświetlania tekstu za pomocą spanów i wskazać dowolne dynamiczne części tekstu jako Status.Part.

Poniższy przykład pokazuje, jak sprawić, aby słowo „czas” było zaznaczone na czerwono. W tym przykładzie użyto symbolu Status.StopwatchPart, który oznacza stoper w sekcji Ostatnie w Menu z aplikacjami.

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

Aby odwołać się do części szablonu, użyj nazwy otoczonej tagiem #. Aby w danych wyjściowych podać #, użyj w szablonie elementu ##.

W poprzednim przykładzie użyto metody HTMLCompat do wygenerowania CharSequence, które przekaże je do szablonu. Jest to łatwiejsze niż ręczne zdefiniowanie obiektu Spannable.

Dodatkowe dostosowania

Oprócz Status możesz dostosowywać bieżącą aktywność lub powiadomienia na poniższe sposoby. Jednak w zależności od wdrożenia producenta OEM te modyfikacje mogą nie zostać użyte.

Bieżące powiadomienie

  • Ustawiona kategoria określa priorytet trwającego działania.
    • CATEGORY_CALL: przychodzące połączenie głosowe lub wideo albo podobne synchroniczne żądanie komunikacji.
    • CATEGORY_NAVIGATION: mapa lub szczegółowa nawigacja
    • CATEGORY_TRANSPORT: sterowanie przenoszeniem multimediów na potrzeby odtwarzania
    • CATEGORY_ALARM: alarm lub minutnik
    • CATEGORY_WORKOUT: trening (nowa kategoria)
    • CATEGORY_LOCATION_SHARING: tymczasowe udostępnianie lokalizacji (nowa kategoria)
    • CATEGORY_STOPWATCH: stoper (nowa kategoria)

Bieżące działania

  • Animowana ikona:czarno-biały wektor, najlepiej z przezroczystym tłem. Wyświetla się na tarczy zegarka w trybie aktywnym. Jeśli animowana ikona nie jest dostępna, używana jest domyślna ikona powiadomienia. (Domyślna ikona powiadomień jest inna dla każdej aplikacji).

  • Ikona statyczna: ikona wektorowa z przezroczystym tłem. Wyświetla na tarczy zegarka w trybie nieaktywnym. Jeśli nie ustawisz animowanej ikony, na tarczy zegarka w trybie aktywnym będzie wyświetlana ikona statyczna. Jeśli ta wartość nie jest podana, używana jest ikona powiadomienia. Jeśli nie ustawisz żadnej, zgłoszony jest wyjątek. W menu z aplikacjami nadal jest wyświetlana ikona aplikacji.

  • OngoingActivityStatus: zwykły tekst lub Chronometer. Wyświetla się w sekcji Ostatnie w menu z aplikacjami. Jeśli nie zostanie podany, używany jest tekst kontekstu w powiadomieniu.

  • Touch Intent: PendingIntent umożliwia powrót do aplikacji, gdy użytkownik kliknie ikonę trwającej aktywności. Wyświetla się na tarczy zegarka lub w programie uruchamiającym. Może ona różnić się od pierwotnej intencji użytej do uruchomienia aplikacji. Jeśli nie zostanie podany, zostanie użyta intencja dotycząca treści powiadomienia. Jeśli żadna z tych opcji nie jest skonfigurowana, zgłaszany jest wyjątek.

  • LocusId: identyfikator, który przypisuje skrót do menu z aplikacjami, któremu odpowiada bieżące działanie. Wyświetla się w programie uruchamiającym w sekcji Ostatnie w trakcie działania. Jeśli tego nie zrobisz, Menu z aplikacjami w sekcji Ostatnie ukryje wszystkie elementy aplikacji w tym samym pakiecie i będzie wyświetlać tylko trwającą aktywność.

  • Identyfikator trwającej aktywności:identyfikator używany do jednoznacznego identyfikowania wywołań do fromExistingOngoingActivity(), gdy aplikacja ma więcej niż 1 trwającą aktywność.

Aktualizowanie trwającej aktywności

W większości przypadków, gdy muszą zaktualizować dane na ekranie, deweloperzy tworzą nowe powiadomienia i trwającą aktywność. Interfejs Ongoing Activity API oferuje też metody pomocnicze do aktualizowania OngoingActivity, jeśli chcesz zachować instancję, a nie ją odtworzyć.

Jeśli aplikacja działa w tle, może wysyłać aktualizacje do interfejsu Ongoing Activity API. Nie rób tego jednak zbyt często, ponieważ metoda aktualizacji ignoruje wywołania, które znajdują się zbyt blisko siebie. Kilka aktualizacji na minutę to rozsądna zmiana.

Aby zaktualizować trwającą aktywność i opublikowane powiadomienie, użyj obiektu utworzonego przed i wywołaj update(), jak pokazano w tym przykładzie:

Kotlin

ongoingActivity.update(context, newStatus)

Java

ongoingActivity.update(context, newStatus);

Dla wygody dostępna jest statyczna metoda tworzenia ciągłych działań.

Kotlin

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

Java

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

Zatrzymywanie trwającej aktywności

Gdy aplikacja zakończy działanie jako trwające działanie, wystarczy anulować trwające powiadomienie.

Możesz też anulować powiadomienie lub trwające działanie na pierwszym planie, a potem odtworzyć je po powrocie do tła, ale nie jest to wymagane.

Wstrzymywanie trwającej aktywności

Jeśli aplikacja ma wyraźną opcję zatrzymania, kontynuuj trwającą aktywność po jej wznowieniu. W przypadku aplikacji bez wyraźnego zatrzymania zakończ działanie, gdy jest wstrzymana.

Sprawdzone metody

Podczas pracy z interfejsem Ongoing Activity API pamiętaj o tych kwestiach:

  • Zadzwoń pod numer ongoingActivity.apply(context), zanim zadzwonisz notificationManager.notify(...).
  • Ustaw statyczną ikonę bieżącej aktywności – wyraźnie lub jako ikonę zastępczą w powiadomieniu. Jeśli nie, otrzymasz IllegalArgumentException.

  • Używaj czarno-białych ikon wektorowych z przezroczystym tłem.

  • Ustaw intencję dotyku dla trwającej aktywności – wyraźnie lub jako metodę zastępczą za pomocą powiadomienia. Jeśli nie, otrzymasz IllegalArgumentException.

  • W przypadku aplikacji NotificationCompat skorzystaj z biblioteki Core AndroidX core:1.5.0-alpha05+, która zawiera LocusIdCompat i nowe kategorie treningów, stoper i udostępniania lokalizacji.

  • Jeśli Twoja aplikacja ma zadeklarowaną w pliku manifestu więcej niż jedną aktywność MAIN LAUNCHER, opublikuj skrót dynamiczny i powiąż go z bieżącą aktywnością za pomocą usługi LocusId.

Publikuj powiadomienia o multimediach podczas odtwarzania multimediów na urządzeniach z Wear OS

Jeśli treści multimedialne są odtwarzane na urządzeniu z Wear OS, opublikuj powiadomienie o multimediach. Dzięki temu system będzie mógł utworzyć odpowiednią aktywność.

Jeśli korzystasz z Media3, powiadomienie zostanie opublikowane automatycznie. Jeśli powiadomienie tworzysz ręcznie, powinno ono korzystać z elementu MediaStyleNotificationHelper.MediaStyle, a odpowiadający mu MediaSession powinien zawierać wypełnioną aktywność w sesji.