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 czas pracy baterii, więc podczas dodawania tej funkcji należy wziąć pod uwagę 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.
  • Wygaszacz: 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 wyświetlić nakładkę z treściami, takimi jak godzina. Jest to również 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 interaktywnymotoczenia.

Gdy zawsze aktywna aplikacja nadal wyświetla swój interfejs, gdy urządzenie jest w stanie niskiego zużycia energii Ambient, mówimy, że jest w trybie 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 wywoływanych przez nieaktywność użytkownika.

  • Limit czasu 1: stan interaktywny do stanu otoczenia: po okresie nieaktywności użytkownika urządzenie przechodzi w stan otoczenia.
  • Timeout 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 pierwszym przejściu systemu do stanu Ambient domyślne działanie zależy od wersji Wear OS i konfiguracji aplikacji:

  • Na Wear OS 5 i starszych 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.
  • Na Wear OS 6 i nowszych wersjach, 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 LocalAmbientModeManager do obserwowania przejść między stanami w Jetpack Compose na Wear OS. Ten stan jest reprezentowany przez węzeł „Ambiactive Mode” w tym schemacie blokowym.

Obserwowanie stanu otoczenia

Aby reagować na zdarzenia w trybie nieaktywnym w Jetpack Compose na Wear OS:

  1. Użyj LocalAmbientModeManager, aby obserwować zmiany stanu trybu otoczenia systemu i odpowiednio dostosowywać elementy kompozycyjne. Skonfiguruj komponent tak, aby przełączał się w tryb niskiego zużycia energii w stanie AmbientMode.Ambient i wracał do pełnego interaktywnego wyświetlania w stanie AmbientMode.Interactive.

    // In a production application, the AmbientModeManager should be instantiated and provided at
    // the highest level of the Compose hierarchy (typically in the host Activity's setContent
    // block) using a CompositionLocalProvider. This ensures proper lifecycle management and
    // broad accessibility.
    
    // For this self-contained demo, AmbientModeManager is created and provided locally:
    val activityAmbientModeManager = rememberAmbientModeManager()
    CompositionLocalProvider(LocalAmbientModeManager provides activityAmbientModeManager) {
        val ambientModeManager = LocalAmbientModeManager.current
        val ambientMode = ambientModeManager?.currentAmbientMode
    
        if (ambientModeManager != null) {
            ambientModeManager.AmbientTickEffect {
                // While device is in ambient mode, update properties every minute or so
                // ...
            }
        }
    
        // ...
            val ambientModeName =
                when (ambientMode) {
                    is AmbientMode.Interactive -> "Interactive"
                    is AmbientMode.Ambient -> "Ambient"
                    else -> "Unknown"
                }
    
            Text(text = "$ambientModeName Mode")
            // ...
    }

Deweloperzy korzystający z Jetpack Compose mogą używać LocalAmbientModeManager, co upraszcza implementację tego wzorca.

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 trybie nieaktywnym Wear OS. Wyświetla, jak wersja systemu operacyjnego urządzenia i konfiguracja aplikacji określają jeden z 3 wyników: rozmyta nakładka, globalny tryb Always-On Display 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 z trwającą aktywnością lub aktualizacją na żywo

Po pewnym czasie w stanie Ambient (Timeout 2) system zwykle wraca do tarczy zegara. 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 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.

Na zegarkach z Wear OS 7 lub nowszym 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 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ć modyfikatora keepScreenOn. Działa to jak blokada uśpienia, która utrzymuje urządzenie w stanie interaktywnym. Używaj tej funkcji z dużą ostrożnością, ponieważ ma ona duży wpływ na czas pracy 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 ich w błąd i ograniczając wizualny chaos, 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 dodatkowe 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 elementów marki lub obrazów tła.
  • Obsługa nieaktualnych danych dynamicznych
    • Wywołanie zwrotne AmbientTickEffect 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, zastąp te wartości na żywo 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 Twojej aplikacji zamiast widoku ustawień.
  • Obsługa wymagań dotyczących konkretnych urządzeń
    • W obiekcie AmbientMode zwróconym przez currentAmbientMode:
      • Jeśli isLowBitAmbientSupported ma wartość true, wyłącz wygładzanie, jeśli to możliwe.
      • Jeśli isBurnInProtectionRequired 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 treningu

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ść między stanami otoczenia i nie może być zastępowana przez tarczę zegarka.

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

  1. Użyj LocalAmbientModeManager, 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. Używaj interfejsu Ongoing Activity API (lub Aktualizacji na żywo na Wear OS 7 i nowszych) przez cały czas trwania treningu, aby zapobiec powrotowi systemu do tarczy zegarka.

Pełną implementację znajdziesz w przykładowej aplikacji do ćwiczeń opartej na Compose w GitHubie. Ten przykład pokazuje też, jak używać funkcji kompozycyjnej LocalAmbientModeManager, aby uprościć obsługę trybu otoczenia w Compose.