Aktywne aktywności

Urządzenia z Wear OS są często używane do długotrwałych aktywności, takich jak śledzenie treningu. Stwarza to problem z interfejsem użytkownika: jeśli użytkownik rozpocznie zadanie, a następnie przejdzie do tarczy zegarka, jak może wrócić do zadania? Powrót do aplikacji za pomocą programu uruchamiającego może być trudny, zwłaszcza w ruchu, co powoduje niepotrzebne utrudnienia.

Rozwiązaniem jest połączenie trwałego powiadomienia z OngoingActivity. Dzięki temu urządzenie może wyświetlać informacje o długotrwałej aktywności w interfejsie użytkownika, co umożliwia korzystanie z funkcji takich jak ikona, w którą można kliknąć u dołu tarczy zegarka. Dzięki temu użytkownicy będą wiedzieć, że zadanie jest wykonywane w tle, i będą mogli jednym kliknięciem wrócić do aplikacji.

Na przykład w tej aplikacji do ćwiczeń informacje mogą być wyświetlane na tarczy zegarka użytkownika jako ikona biegania, w którą można kliknąć:

ikona biegania

Rysunek 1. Wskaźnik aktywności.

Bieżące powiadomienie wyświetla też informacje w sekcji Ostatnie globalnego selektora aplikacji. Dzięki temu użytkownicy mają kolejne wygodne miejsce, w którym mogą sprawdzić stan zadania i ponownie zaangażować się w aplikację:

launcher

Rysunek 2. Globalny program uruchamiający.

Oto przykłady sytuacji, w których warto używać ciągłego powiadomienia powiązanego z ciągłą aktywnością:

licznik czasu

Rysunek 3. Minutnik: odlicza czas i zatrzymuje się, gdy zostanie wstrzymany lub wyłączony.

mapa

Rysunek 4. Szczegółowa nawigacja: 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ę przez całą sesję. Kończy się natychmiast po wstrzymaniu sesji przez użytkownika.

Wear automatycznie tworzy ciągłe aktywności w przypadku aplikacji multimedialnych.

Szczegółowy przykład tworzenia trwających aktywności w przypadku innych rodzajów aplikacji znajdziesz w ćwiczeniach z programowania dotyczących trwającej aktywności.

Konfiguracja

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

dependencies {
  implementation "androidx.wear:wear-ongoing:1.1.0"
  implementation "androidx.core:core:1.17.0"
}

Tworzenie działania ciągłego

Proces ten obejmuje 3 etapy:

  1. Utwórz standardowy NotificationCompat.Builder i skonfiguruj go jako trwający.
  2. Utwórz i skonfiguruj obiekt OngoingActivity, przekazując do niego narzędzie do tworzenia powiadomień.
  3. Zastosuj trwającą aktywność w kreatorze powiadomień i opublikuj wynikowe powiadomienie.

Tworzenie i konfigurowanie powiadomienia

Zacznij od utworzenia NotificationCompat.Builder. Kluczowym krokiem jest wywołanie setOngoing(true), aby oznaczyć je jako bieżące powiadomienie. Na tym etapie możesz też ustawić inne właściwości powiadomienia, takie jak mała ikona i kategoria.

// 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!

Tworzenie obiektu OngoingActivity

Następnie utwórz instancję OngoingActivity za pomocą jej kreatora. Funkcja OngoingActivity.Builder wymaga Context, identyfikatora powiadomienia i NotificationCompat.Builder utworzonego w poprzednim kroku.

Skonfiguruj kluczowe właściwości, które będą wyświetlane w nowych miejscach docelowych reklam:

  • Animowane i statyczne ikony: udostępnij ikony, które będą wyświetlane na tarczy zegarka w trybie aktywnym i wygaszonym.
  • Intencja dotknięcia: PendingIntent, która po kliknięciu ikony trwającej aktywności przenosi użytkownika z powrotem do Twojej aplikacji. Możesz ponownie użyć pendingIndent utworzonego w poprzednim kroku.

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

Zastosuj do powiadomienia i posta

Ostatnim krokiem jest połączenie OngoingActivity z powiadomieniem, a następnie opublikowanie go. Metoda ongoingActivity.apply() modyfikuje oryginalny konstruktor powiadomień, dodając niezbędne dane, aby system mógł wyświetlać powiadomienia na dodatkowych platformach. Po zastosowaniu tego ustawienia możesz utworzyć i opublikować powiadomienie w zwykły sposób.

// This call modifies notificationBuilder to include the ongoing activity data.
ongoingActivity.apply(applicationContext)

// Post the notification.
startForeground(NOTIFICATION_ID, notificationBuilder.build())

Dodawanie dynamicznego tekstu stanu do programu uruchamiającego

Powyższy kod dodaje ikonę, którą można kliknąć, do tarczy zegarka. Aby dostarczać jeszcze bardziej szczegółowe aktualizacje w czasie rzeczywistym w sekcji Ostatnie w launcherze, utwórz obiekt Status i dołącz go do OngoingActivity . Jeśli nie podasz niestandardowego Status, system domyślnie użyje tekstu powiadomienia (ustawionego za pomocą setContentText()).

Aby wyświetlić tekst dynamiczny, użyj Status.Builder. Możesz zdefiniować ciąg znaków szablonu z symbolami zastępczymi i podać Status.Part obiektów, aby wypełnić te symbole. Status.Part może być dynamiczny, np. stoper lub minutnik.

Poniższy przykład pokazuje, jak utworzyć stan, który wyświetla „Bieganie przez [stoper]”:

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

Na koniec połącz ten Status z urządzeniem OngoingActivity, dzwoniąc pod numer setStatus() na OngoingActivity.Builder.

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // Add the status to the OngoingActivity.
        .setStatus(ongoingActivityStatus)
        .build()

Dodatkowe dostosowania

Oprócz Status możesz dostosować bieżącą aktywność lub powiadomienia w następujący sposób: Jednak w zależności od implementacji OEM te dostosowania mogą nie być używane.

Stałe powiadomienie

  • Ustawiona kategoria określa priorytet trwającej aktywności.
    • CATEGORY_CALL: przychodzące połączenie głosowe lub wideo albo podobna prośba o komunikację synchroniczną;
    • CATEGORY_NAVIGATION: mapę lub szczegółowe wskazówki nawigacyjne.
    • CATEGORY_TRANSPORT: sterowanie odtwarzaniem multimediów;
    • CATEGORY_ALARM: alarm lub minutnik.
    • CATEGORY_WORKOUT: trening
    • CATEGORY_LOCATION_SHARING: tymczasowe udostępnianie lokalizacji kategoria)
    • CATEGORY_STOPWATCH: stoper

Ongoing Activity

  • Animowana ikona: czarno-biała grafika wektorowa, najlepiej na przezroczystym tle. Wyświetla się na tarczy zegarka w trybie aktywnym. Jeśli nie podasz animowanej ikony, użyjemy domyślnej ikony powiadomienia. Domyślna ikona powiadomienia różni się w zależności od aplikacji.

  • Statyczna ikona: ikona wektorowa z przezroczystym tłem. Wyświetla się na tarczy zegarka w trybie nieaktywnym. Jeśli animowana ikona nie jest ustawiona, w trybie aktywnym na tarczy zegarka jest używana ikona statyczna. Jeśli nie podasz tej wartości, użyta zostanie ikona powiadomienia. Jeśli nie jest ustawiona żadna z tych wartości, zgłaszany jest wyjątek. (Program uruchamiający aplikacje nadal używa ikony aplikacji).

  • OngoingActivityStatus: zwykły tekst lub Chronometer. Wyświetla się w sekcji Ostatnio używane w menu z aplikacjami. Jeśli nie zostanie podany, używany jest „tekst kontekstowy” powiadomienia.

  • Touch Intent:PendingIntent używany do powrotu do aplikacji, gdy użytkownik kliknie ikonę trwającej aktywności. Wyświetla się na tarczy zegarka lub w elemencie programu uruchamiającego. Może się różnić od pierwotnego zamiaru użytego do uruchomienia aplikacji. Jeśli nie zostanie podany, używany jest zamiar treści powiadomienia. Jeśli nie ustawisz żadnej z tych wartości, zostanie zgłoszony wyjątek.

  • LocusId: identyfikator przypisujący skrót launchera, do którego odnosi się trwająca aktywność. Wyświetla się w launcherze w sekcji Ostatnie podczas trwania aktywności. Jeśli nie podasz tej wartości, program uruchamiający ukryje wszystkie elementy aplikacji w sekcji Ostatnie z tego samego pakietu i wyświetli tylko trwającą aktywność.

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

Aktualizowanie trwającej aktywności

W większości przypadków deweloperzy tworzą nowe powiadomienie ciągłe i nową aktywność ciągłą, gdy chcą zaktualizować dane na ekranie. Interfejs Ongoing Activity API udostępnia też metody pomocnicze do aktualizowania obiektu OngoingActivity, jeśli chcesz zachować instancję zamiast tworzyć ją od nowa.

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 są zbyt blisko siebie. Kilka aktualizacji na minutę to rozsądna liczba.

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

ongoingActivity.update(context, newStatus)

Dla wygody użytkowników istnieje statyczna metoda tworzenia trwającej aktywności.

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

Zatrzymywanie trwającej aktywności

Gdy aplikacja zakończy działanie jako trwająca aktywność, wystarczy, że anuluje trwające powiadomienie.

Możesz też anulować powiadomienie lub trwającą aktywność, gdy przechodzi na pierwszy plan, a potem odtworzyć je, gdy wraca w tle, ale nie jest to wymagane.

Wstrzymywanie trwającej aktywności

Jeśli aplikacja ma wyraźne działanie zatrzymania, po wznowieniu kontynuuj bieżącą aktywność. W przypadku aplikacji bez wyraźnego działania zatrzymującego zakończ aktywność, gdy zostanie wstrzymana.

Sprawdzone metody

Podczas korzystania z interfejsu Ongoing Activity API pamiętaj o tych kwestiach:

  • Ustaw statyczną ikonę dla trwającej aktywności, jawnie lub jako rezerwę za pomocą powiadomienia. Jeśli nie, otrzymasz IllegalArgumentException.

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

  • Ustaw zamiar dotknięcia dla bieżącej aktywności, jawnie lub jako rezerwowy za pomocą powiadomienia. Jeśli nie, otrzymasz IllegalArgumentException.

  • Jeśli w pliku manifestu aplikacji zadeklarowano więcej niż 1 aktywność MAIN LAUNCHER, opublikuj dynamiczny skrót i powiąż go z bieżącą aktywnością za pomocą LocusId.

Publikowanie powiadomień o multimediach podczas odtwarzania multimediów na urządzeniach z Wear OS

Jeśli na urządzeniu z Wear OS są odtwarzane multimedia, opublikuj powiadomienie o multimediach. Dzięki temu system może utworzyć odpowiednią aktywność ciągłą.

Jeśli używasz Media3, powiadomienie jest publikowane automatycznie. Jeśli tworzysz powiadomienie ręcznie, powinno ono korzystać z elementu MediaStyleNotificationHelper.MediaStyle, a odpowiedni element MediaSession powinien zawierać wypełnione pole session activity (aktywność w sesji).