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 przejścia między stanami zasilania i jak zarządzać zachowaniem aplikacji, aby zapewnić użytkownikom wygodę i oszczędzać baterię.

Ciągłe wyświetlanie aplikacji ma znaczący wpływ na żywotność baterii, dlatego podczas dodawania tej funkcji należy wziąć 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 wysokiego zużycia energii, w którym ekran jest w pełni jasny, co umożliwia pełną interakcję z użytkownikiem.
  • Nieaktywny: 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, rozmywając go lub nakładając na niego treści, takie jak godzina. Ten stan jest też nazywany trybem nieaktywnym.

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

Aplikacja zawsze włączona to aplikacja, która wyświetla treści zarówno w stanie interaktywnym, jak i nieaktywnym.

Gdy aplikacja zawsze włączona nadal wyświetla swój interfejs, gdy urządzenie jest w stanie nieaktywnym o niskim zużyciu energii, mówi się, że jest w trybie ambiactive.

Przejścia systemowe i domyślne zachowanie

Gdy aplikacja jest na pierwszym planie, system zarządza przejściami między stanami zasilania na podstawie 2 limitów czasu aktywowanych przez brak aktywności użytkownika.

  • Limit czasu nr 1: przejście ze stanu interaktywnego do nieaktywnego: po pewnym czasie braku aktywności użytkownika urządzenie przechodzi w stan nieaktywny.
  • Limit czasu nr 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 do stanu nieaktywnego domyślne zachowanie zależy od wersji Wear OS i konfiguracji aplikacji:

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

Dostosowywanie zachowania w stanie nieaktywnym

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

Używanie AmbientLifecycleObserver

Aby reagować na zdarzenia trybu nieaktywnego, użyj klasy AmbientLifecycleObserver:

  1. Zaimplementuj AmbientLifecycleObserver.AmbientLifecycleCallback interfejs. Użyj metody onEnterAmbient() , aby dostosować interfejs do stanu niskiego zużycia energii, oraz onExitAmbient() , aby przywrócić go do pełnego interaktywnego wyświetlania.

    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 elementu kompozycyjnego.

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

  3. Wywołaj removeObserver() , aby usunąć obserwatora w onDestroy().

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

W przypadku programistów korzystających z Jetpack Compose biblioteka Horologist udostępnia przydatne narzędzie – element kompozycyjny AmbientAware , który upraszcza implementację tego wzorca.

TimeText z obsługą trybu nieaktywnego

Wyjątkiem od wymagania niestandardowego obserwatora jest to, że w Wear OS 6 TimeText widżet obsługuje tryb nieaktywny. Gdy urządzenie jest w stanie nieaktywnym , automatycznie aktualizuje się raz na minutę bez dodatkowego kodu.

Schemat blokowy zachowania w trybie nieaktywnym

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

Schemat ilustrujący logikę decyzji dotyczącą 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ę podejmowania decyzji w trybie nieaktywnym w Wear OS.

Kontrolowanie czasu włączenia ekranu

W tych sekcjach opisujemy, jak zarządzać czasem wyświetlania aplikacji na ekranie.

Zapobieganie powrotowi do tarczy zegarka za pomocą trwającej aktywności lub aktualizacji na żywo

Po pewnym czasie w stanie nieaktywnym (limit czasu nr 2) system zwykle wraca do tarczy zegarka. Użytkownik może skonfigurować czas trwania limitu czasu w ustawieniach systemu. W niektórych przypadkach, np. gdy użytkownik śledzi trening, aplikacja może potrzebować dłuższego czasu wyświetlania.

W Wear OS 5 i nowszych wersjach możesz temu zapobiec, implementując trwającą aktywność. 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.

W Wear OS 7 i nowszych wersjach możesz też użyć aktualizacji na żywo zamiast trwającej aktywności. Aby zachować zgodność wsteczną, nadal obsługuj trwającą aktywność na urządzeniach z Wear OS 6 lub starszym.

Aby to zaimplementować, intencja dotknięcia trwającego 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()

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

W rzadkich przypadkach może być konieczne całkowite uniemożliwienie urządzeniu przejścia w stan nieaktywny. Oznacza to, że należy uniknąć limitu czasu nr 1. Aby to zrobić, możesz użyć flagi okna FLAG_KEEP_SCREEN_ON. Działa ona jako blokada uśpienia, utrzymując urządzenie w stanie interaktywnym. Używaj jej z dużą ostrożnością, ponieważ ma ona poważny wpływ na żywotność baterii.

Zalecenia dotyczące trybu nieaktywnego

Aby zapewnić użytkownikom jak najlepsze wrażenia i oszczędzać energię w trybie nieaktywnym, postępuj zgodnie z tymi wytycznymi dotyczącymi projektowania. Zalecenia te mają na celu zapewnienie przejrzystości dla użytkownika przez zapobieganie wprowadzającym w błąd informacjom i zmniejszanie wizualnego bałaganu, a jednocześnie optymalizowanie zużycia energii przez wyświetlacz.

  • Zmniejsz wizualny bałagan i zużycie energii przez wyświetlacz. Czysty, minimalistyczny interfejs sygnalizuje użytkownikowi, że aplikacja jest w stanie niskiego zużycia energii, i oszczędza znaczną ilość energii, ograniczając liczbę jasnych pikseli.
    • Co najmniej 85% ekranu powinno być czarne.
    • Wyświetlaj tylko najważniejsze informacje, a szczegóły dodatkowe przenieś na wyświetlacz interaktywny.
    • W przypadku dużych ikon lub przycisków używaj konturów zamiast pełnych wypełnień.
    • Unikaj dużych bloków jednolitego koloru oraz niefunkcjonalnych obrazów marki lub tła.
  • Obsługuj nieaktualne dane dynamiczne.
    • Wywołanie zwrotne onUpdateAmbient() jest wywoływane tylko okresowo – zwykle raz na minutę – aby oszczędzać energię. Z powodu tego ograniczenia wszystkie dane, które często się zmieniają – np. stoper, tętno lub 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 na żywo statyczną treścią zastępczą, np. --.
  • Zachowaj spójny układ.
    • Aby zapewnić płynne przejście, utrzymuj elementy w tej samej pozycji w trybie interaktywnym i nieaktywnym.
    • Zawsze wyświetlaj godzinę.
  • Uwzględniaj kontekst.
    • Jeśli w momencie przejścia urządzenia w tryb nieaktywny użytkownik był na ekranie ustawień lub konfiguracji, rozważ wyświetlenie bardziej odpowiedniego ekranu z aplikacji zamiast widoku ustawień.
  • Obsługuj wymagania dotyczące urządzenia.
    • W obiekcie AmbientDetails przekazywanym do onEnterAmbient():
      • Jeśli deviceHasLowBitAmbient ma wartość true, wyłącz wygładzanie tam, gdzie 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

Podczas tworzenia lub testowania zachowania aplikacji, gdy urządzenie jest w trybie nieaktywnym, mogą się przydać te adb polecenia:

# 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 treningu

Rozważmy aplikację do treningu, która musi wyświetlać użytkownikowi dane przez cały czas trwania sesji ćwiczeń. Aplikacja musi pozostać widoczna podczas przejść w stan nieaktywny i nie może zostać zastąpiona przez tarczę zegarka.

Aby to osiągnąć, programista powinien wykonać te czynności:

  1. Zaimplementuj AmbientLifecycleObserver , aby obsługiwać zmiany interfejsu między stanami interaktywnym i nieaktywnym , takie jak przyciemnianie ekranu i usuwanie nieistotnych danych.
  2. Utwórz nowy układ o niskim zużyciu energii dla stanu nieaktywnego , który jest zgodny ze sprawdzonymi metodami.
  3. Używaj interfejsu Ongoing Activity API (lub aktualizacji na żywo w Wear OS 7 i nowszych wersjach) przez cały czas trwania treningu, aby uniemożliwić systemowi powrót do tarczy zegarka.

Pełną implementację znajdziesz w przykładzie ćwiczenia opartego na Compose w GitHubie. Ten przykład pokazuje też, jak używać elementu kompozycyjnego AmbientAware z biblioteki Horologist, aby uprościć obsługę trybu nieaktywnego w Compose.