Zawsze włączone aplikacje i tryb nieaktywny systemu

Z tego przewodnika dowiesz się, jak sprawić, aby aplikacja była zawsze włączona, jak reagować na zmiany stanu zasilania i jak zarządzać działaniem aplikacji, aby zapewnić użytkownikom komfort korzystania z niej przy jednoczesnym oszczędzaniu baterii.

Ciągłe wyświetlanie aplikacji ma znaczący wpływ na żywotność baterii, więc dodając tę funkcję, weź pod uwagę jej wpływ na zużycie energii.

Kluczowe pojęcia

Gdy aplikacja na Wear OS jest wyświetlana na pełnym ekranie, może być w jednym z 2 stanów zasilania:

  • Interaktywny: stan o wysokim poborze mocy, w którym ekran jest w pełni jasny i umożliwia pełną interakcję z użytkownikiem.
  • Otoczenie: stan niskiego zużycia energii, w którym wyświetlacz przyciemnia się, aby oszczędzać energię. W tym stanie interfejs aplikacji nadal zajmuje cały ekran, ale system może zmienić jego wygląd, np. rozmyć go lub nałożyć na niego treści, takie jak godzina. Jest to tzw. oświetlenie kinowe.

System operacyjny kontroluje przejście między tymi stanami.

Aplikacja zawsze włączona to aplikacja, która wyświetla treści w trybach interaktywnymwygaszacza.

Gdy zawsze aktywna aplikacja nadal wyświetla swój interfejs, gdy urządzenie jest w stanie Ambient o niskim poborze mocy, jest to określane jako tryb ambiactive.

Przejścia systemowe i domyślne zachowanie

Gdy aplikacja działa na pierwszym planie, system zarządza przejściami między stanami zasilania na podstawie 2 limitów czasu aktywowanych przez nieaktywność użytkownika.

  • Limit czasu 1. Stan interaktywny do stanu otoczenia: po okresie nieaktywności użytkownika urządzenie przechodzi w stan otoczenia.
  • Limit czasu 2: powrót do tarczy zegarka: po kolejnym okresie braku aktywności system może ukryć bieżącą aplikację i wyświetlić tarczę zegarka.

Bezpośrednio po przejściu systemu w stan Ambient domyślne działanie zależy od wersji Wear OS i konfiguracji aplikacji:

  • Na Wear OS 5 i starszych wersjach system wyświetla rozmyty zrzut ekranu wstrzymanej aplikacji z nałożonym na niego czasem. Ten stan jest reprezentowany przez węzeł „AOD Lite” na tym schemacie blokowym.
  • W przypadku Wear OS 6 i nowszych wersji, jeśli aplikacja jest kierowana na pakiet SDK 36 lub nowszy, jest uznawana za zawsze włączoną. Wyświetlacz jest przyciemniony, ale aplikacja nadal działa i jest widoczna. (Aktualizacje mogą być wykonywane nawet raz na minutę). Ten stan jest reprezentowany przez węzeł „Global AOD” na tym schemacie blokowym.

Dostosowywanie działania w trybie nieaktywnym

Niezależnie od domyślnego działania systemu we wszystkich wersjach Wear OS możesz dostosować wygląd lub działanie aplikacji w stanie Ambient, używając AmbientLifecycleObserver do nasłuchiwania wywołań zwrotnych podczas przejść między stanami. Ten stan jest reprezentowany przez węzeł „Tryb ambiaktywny” na poniższym schemacie blokowym.

Używanie AmbientLifecycleObserver

Aby reagować na zdarzenia w trybie otoczenia, użyj klasy AmbientLifecycleObserver:

  1. Zaimplementuj interfejs AmbientLifecycleObserver.AmbientLifecycleCallback. Użyj metody onEnterAmbient(), aby dostosować interfejs do stanu niskiego zużycia energii, a metody onExitAmbient(), aby przywrócić pełną interaktywność wyświetlacza.

    val ambientCallback = object : AmbientLifecycleObserver.AmbientLifecycleCallback {
        override fun onEnterAmbient(ambientDetails: AmbientLifecycleObserver.AmbientDetails) {
            // ... Called when moving from interactive mode into ambient mode.
            // Adjust UI for low-power state: dim colors, hide non-essential elements.
        }
    
        override fun onExitAmbient() {
            // ... Called when leaving ambient mode, back into interactive mode.
            // Restore full UI.
        }
    
        override fun onUpdateAmbient() {
            // ... Called by the system periodically (typically once per minute)
            // to allow the app to update its display while in ambient mode.
        }
    }

  2. Utwórz AmbientLifecycleObserver i zarejestruj go w cyklu życia aktywności lub komponentu.

    private val ambientObserver = AmbientLifecycleObserver(activity, ambientCallback)
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(ambientObserver)
    
        // ...
    }

  3. Zadzwoń pod numer removeObserver(), aby usunąć obserwatora w onDestroy().

    override fun onDestroy() {
        super.onDestroy()
        lifecycle.removeObserver(ambientObserver)
    
        // ...
    }

Deweloperom korzystającym z Jetpack Compose biblioteka Horologist udostępnia przydatne narzędzie, czyli komponent AmbientAware, który upraszcza implementację tego wzorca.

Tekst czasu z uwzględnieniem otoczenia

W przypadku Wear OS 6 widget TimeText jest dostosowany do trybu otoczenia, co stanowi wyjątek od wymagania niestandardowego obserwatora. Gdy urządzenie jest w stanie Ambient, automatycznie aktualizuje się raz na minutę bez konieczności dodawania dodatkowego kodu.

Schemat blokowy działania w trybie otoczenia

Poniższy schemat blokowy pokazuje, jak system określa zachowanie w trybie otoczenia na podstawie wersji Wear OS na urządzeniu, targetSdkVersion aplikacji i tego, czy implementuje ona AmbientLifecycleCallback.

Schemat blokowy ilustrujący logikę decyzji w przypadku trybu nieaktywnego Wear OS. Pokazuje, jak wersja systemu operacyjnego urządzenia i konfiguracja aplikacji określają jeden z 3 wyników: rozmyta nakładka, globalny tryb zawsze aktywnego wyświetlacza lub tryb Ambiactive zarządzany przez aplikację.
Rysunek 1. Schemat blokowy ilustrujący logikę decyzji w trybie nieaktywnym Wear OS.

Sterowanie czasem włączenia ekranu

W sekcjach poniżej znajdziesz informacje o tym, jak zarządzać czasem wyświetlania aplikacji na ekranie.

Zapobieganie powrotowi do tarczy zegarka w przypadku trwającej aktywności

Po pewnym czasie w stanie Ambient (Timeout 2) system zwykle wraca do tarczy zegarka. Użytkownik może skonfigurować czas oczekiwania w ustawieniach systemu. W niektórych przypadkach, np. gdy użytkownik śledzi trening, aplikacja może potrzebować dłuższego czasu widoczności.

W przypadku Wear OS 5 i nowszych możesz temu zapobiec, wdrażając OngoingActivity. Jeśli aplikacja wyświetla informacje o trwającym zadaniu użytkownika, np. o sesji treningowej, możesz użyć interfejsu Ongoing Activity API, aby aplikacja była widoczna do czasu zakończenia zadania. Jeśli użytkownik ręcznie wróci do tarczy zegarka, wskaźnik trwającej aktywności umożliwi mu powrót do aplikacji jednym kliknięciem.

Aby to zrobić, intencja dotknięcia trwałego powiadomienia musi wskazywać aktywność zawsze włączoną, jak pokazano w tym fragmencie kodu:

val activityIntent =
    Intent(this, AlwaysOnActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
    }

val pendingIntent =
    PendingIntent.getActivity(
        this,
        0,
        activityIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
    )

val notificationBuilder =
    NotificationCompat.Builder(this, CHANNEL_ID)
        // ...
        // ...
        .setOngoing(true)

// ...

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // ...
        .setTouchIntent(pendingIntent)
        .build()

ongoingActivity.apply(applicationContext)

val notification = notificationBuilder.build()

Pozostawianie włączonego ekranu i zapobieganie przejściu w stan nieaktywny

W rzadkich przypadkach może być konieczne całkowite uniemożliwienie przejścia urządzenia w stan nieaktywny. Chodzi o uniknięcie przekroczenia limitu czasu nr 1. Możesz do tego użyć flagi okna FLAG_KEEP_SCREEN_ON. Działa to jak blokada wybudzania, która utrzymuje urządzenie w stanie Interaktywny. Używaj tej funkcji z dużą ostrożnością, ponieważ ma ona duży wpływ na żywotność baterii.

Zalecenia dotyczące trybu nieaktywnego

Aby zapewnić użytkownikom jak najlepsze wrażenia i oszczędzać energię w trybie Ambient, postępuj zgodnie z tymi wytycznymi dotyczącymi projektowania. Te rekomendacje stawiają na pierwszym miejscu wygodę użytkowników, zapobiegając wprowadzaniu w błąd i ograniczając nadmiar elementów wizualnych, a jednocześnie optymalizując moc wyświetlania.

  • Zmniejsz wizualny bałagan i zwiększ moc wyświetlania. Czysty, minimalistyczny interfejs informuje użytkownika, że aplikacja jest w stanie niskiego zużycia energii, i znacznie oszczędza baterię, ograniczając jasne piksele.
    • Co najmniej 85% ekranu musi być czarne.
    • Wyświetlaj tylko najważniejsze informacje, a szczegóły drugorzędne przenieś na wyświetlacz interaktywny.
    • W przypadku dużych ikon lub przycisków używaj obramowań zamiast pełnych wypełnień.
    • Unikaj dużych bloków jednolitego koloru oraz niefunkcjonalnych obrazów z logo lub tłem.
  • Obsługa nieaktualnych danych dynamicznych
    • Wywołanie zwrotne onUpdateAmbient() jest wywoływane tylko okresowo – zwykle raz na minutę – aby oszczędzać energię. Z tego powodu wszelkie dane, które często się zmieniają, np. stoper, tętno czy dystans treningu, stają się nieaktualne między aktualizacjami. Aby uniknąć wyświetlania wprowadzających w błąd i nieprawidłowych informacji, nasłuchuj wywołania zwrotnego onEnterAmbient i zastąp te wartości dynamiczne statyczną treścią zastępczą, np. --.
  • Zachowaj spójny układ
    • Zachowaj elementy w tej samej pozycji w trybach InteraktywnymOtoczenia, aby zapewnić płynne przejście.
    • Zawsze pokazuj czas.
  • Uwzględniaj kontekst
    • Jeśli w momencie przejścia urządzenia w tryb otoczenia użytkownik był na ekranie ustawień lub konfiguracji, rozważ wyświetlenie bardziej odpowiedniego ekranu z aplikacji zamiast widoku ustawień.
  • Obsługa wymagań dotyczących konkretnych urządzeń
    • W obiekcie AmbientDetails przekazanym do metody onEnterAmbient():
      • Jeśli deviceHasLowBitAmbient ma wartość true, wyłącz wygładzanie, jeśli to możliwe.
      • Jeśli burnInProtectionRequired ma wartość true, okresowo przesuwaj nieznacznie elementy interfejsu i unikaj jednolitych białych obszarów, aby zapobiec wypaleniu ekranu.

Debugowanie i testowanie

Te polecenia adb mogą być przydatne podczas programowania lub testowania działania aplikacji, gdy urządzenie jest w trybie otoczenia:

# put device in ambient mode if the always on display is enabled in settings
# (and not disabled by other settings, such as theatre mode)
$ adb shell input keyevent KEYCODE_SLEEP

# put device in interactive mode
$ adb shell input keyevent KEYCODE_WAKEUP

Przykład: aplikacja do ćwiczeń

Weźmy pod uwagę aplikację do ćwiczeń, która musi wyświetlać użytkownikowi dane przez cały czas trwania sesji treningowej. Aplikacja musi być widoczna podczas przejść do stanu Ambient i nie może być zastępowana przez tarczę zegara.

Aby to zrobić, deweloper powinien wykonać te czynności:

  1. Zaimplementuj AmbientLifecycleObserver, aby obsługiwać zmiany interfejsu między stanami interaktywnymotoczenia, takie jak przyciemnianie ekranu i usuwanie nieistotnych danych.
  2. Utwórz nowy układ o niskim poborze mocy dla stanu Ambient, który jest zgodny ze sprawdzonymi metodami.
  3. Podczas treningu używaj interfejsu Ongoing Activity API, aby zapobiec powrotowi systemu do tarczy zegarka.

Pełną implementację znajdziesz w przykładowej aplikacji do ćwiczeń opartej na Compose w serwisie GitHub. Ten przykład pokazuje też, jak używać komponentu AmbientAware z biblioteki Horologist, aby uprościć obsługę trybu otoczenia w Compose.