אפליקציות שפועלות כל הזמן ומצב רגישות של המערכת

במדריך הזה מוסבר איך להגדיר את האפליקציה כך שתפעל תמיד, איך להגיב למעברים בין מצבי הפעלה ואיך לנהל את התנהגות האפליקציה כדי לספק חוויית משתמש טובה תוך חיסכון בסוללה.

הצגה קבועה של אפליקציה משפיעה באופן משמעותי על חיי הסוללה, ולכן חשוב לקחת בחשבון את ההשפעה על צריכת החשמל כשמוסיפים את התכונה הזו.

מושגים מרכזיים

כשמציגים אפליקציית Wear OS במסך מלא, היא נמצאת באחד משני מצבי צריכת חשמל:

  • אינטראקטיבי: מצב צריכת חשמל גבוהה שבו המסך בבהירות מלאה, ומאפשר אינטראקציה מלאה של המשתמש.
  • סביבה: מצב צריכת חשמל נמוכה שבו המסך מעומעם כדי לחסוך בחשמל. במצב הזה, ממשק המשתמש של האפליקציה עדיין תופס את כל המסך, אבל המערכת עשויה לשנות את המראה שלו על ידי טשטוש או הוספת שכבת תוכן כמו השעה. התכונה הזו נקראת גם תאורה קולנועית.

מערכת ההפעלה שולטת במעבר בין המצבים האלה.

אפליקציה שפועלת תמיד היא אפליקציה שמציגה תוכן במצבים האינטראקטיבי והסביבתי.

כשאפליקציה שפועלת תמיד ממשיכה להציג את ממשק המשתמש שלה בזמן שהמכשיר נמצא במצב סביבתי עם צריכת חשמל נמוכה, היא מוגדרת כפועלת במצב אמביאקטיבי.

מעברים במערכת והתנהגות ברירת מחדל

כשאפליקציה נמצאת בחזית, המערכת מנהלת את המעברים בין מצבי צריכת החשמל על סמך שני טיימאוטים שמופעלים בגלל חוסר פעילות של המשתמש.

  • זמן קצוב לתפוגה מספר 1: ממצב אינטראקטיבי למצב סביבתי: אחרי תקופה של חוסר פעילות מצד המשתמש, המכשיר עובר למצב סביבתי.
  • הזמן הקצוב לתפוגה מס' 2: חזרה לתצוגת השעון: אחרי תקופה נוספת של חוסר פעילות, המערכת עשויה להסתיר את האפליקציה הנוכחית ולהציג את תצוגת השעון.

מיד אחרי שהמערכת עוברת את המעבר הראשון למצב Ambient, התנהגות ברירת המחדל תלויה בגרסת Wear OS ובהגדרות האפליקציה:

  • ב-Wear OS 5 ובגרסאות קודמות, המערכת מציגה צילום מסך מטושטש של האפליקציה המושהית, עם השעה שמוצגת מעל.
  • ב-Wear OS בגרסה 6 ואילך, אם אפליקציה מטרגטת SDK בגרסה 36 ומעלה, היא נחשבת לאפליקציה שפועלת תמיד. התצוגה מעומעמת, אבל האפליקציה ממשיכה לפעול ונשארת גלויה. (יכול להיות שהעדכונים יתבצעו בתדירות נמוכה, למשל פעם בדקה).

התאמה אישית של ההתנהגות במצב אווירה

בכל הגרסאות של Wear OS, אפשר להתאים אישית את המראה או את ההתנהגות של האפליקציה במצב Ambient באמצעות AmbientLifecycleObserver כדי להאזין לקריאות חוזרות (callbacks) במעברים בין מצבים, בלי קשר להתנהגות המערכת שמוגדרת כברירת מחדל.

שימוש ב-AmbientLifecycleObserver

כדי להגיב לאירועים במצב תאורת אווירה, משתמשים במחלקה AmbientLifecycleObserver:

  1. מטמיעים את הממשק AmbientLifecycleObserver.AmbientLifecycleCallback. משתמשים בשיטה onEnterAmbient() כדי להתאים את ממשק המשתמש למצב צריכת חשמל נמוכה, ובשיטה onExitAmbient() כדי לשחזר אותו למצב תצוגה אינטראקטיבי מלא.

    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. יוצרים AmbientLifecycleObserver ורושמים אותו במחזור החיים של הפעילות או של הרכיב שאפשר להרכיב.

    private val ambientObserver = AmbientLifecycleObserver(activity, ambientCallback)
    
    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(ambientObserver)
    
        // ...
    }
    
  3. מתקשרים אל removeObserver() כדי להסיר את הצופה ב-onDestroy().

למפתחים שמשתמשים ב-Jetpack Compose, ספריית Horologist מספקת כלי עזר שימושי, ה-composable‏ AmbientAware, שמפשט את ההטמעה של התבנית הזו.

הצגת השעה בטקסט עם התאמה לתנאי התאורה

כחריג לדרישה להשתמש ב-Observer בהתאמה אישית, בווידג'ט TimeText ב-Wear OS 6 יש תמיכה במצב סביבה. העדכון מתבצע אוטומטית פעם בדקה כשהמכשיר במצב סביבתי, ללא צורך בקוד נוסף.

שליטה במשך הזמן שהמסך פועל

בקטעים הבאים מוסבר איך לקבוע לכמה זמן האפליקציה תישאר על המסך.

איך מונעים חזרה לתצוגת השעון עם פעילות מתמשכת

אחרי תקופה מסוימת במצב סביבתי (משך הזמן הקצוב לתפוגה מספר 2), המערכת בדרך כלל תחזור לתצוגת השעון. המשתמש יכול לקבוע את משך הזמן הקצוב לתפוגה בהגדרות המערכת. בתרחישי שימוש מסוימים, כמו מעקב של משתמש אחרי אימון, יכול להיות שאפליקציה תצטרך להישאר גלויה למשך זמן ארוך יותר.

ב-Wear OS 5 ומעלה, אפשר למנוע את זה באמצעות הטמעה של Ongoing Activity. אם האפליקציה מציגה מידע על משימה שוטפת של המשתמש, כמו אימון, אפשר להשתמש ב-Ongoing Activity API כדי שהאפליקציה תישאר גלויה עד שהמשימה תסתיים. אם משתמש חוזר באופן ידני לתצוגת השעון, האינדיקטור של הפעילות המתמשכת מאפשר לו לחזור לאפליקציה שלכם בלחיצה אחת.

כדי להטמיע את זה, כוונת המגע של ההתראה המתמשכת צריכה להפנות לפעילות התמידית שלכם, כמו שמוצג בקטע הקוד הבא:

private fun createNotification(): Notification {
    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)

    return notificationBuilder.build()
}

שמירת המסך במצב פעולה ומניעת מצב סביבתי

במקרים נדירים, יכול להיות שתצטרכו למנוע לגמרי מהמכשיר להיכנס למצב רגישות לסביבה. כלומר, כדי להימנע מפסק זמן מס' 1. כדי לעשות את זה, אפשר להשתמש בדגל החלון FLAG_KEEP_SCREEN_ON. הפונקציה הזו פועלת כנעילת השכמה, ושומרת את המכשיר במצב אינטראקטיבי. חשוב להשתמש באפשרות הזו בזהירות רבה כי היא משפיעה באופן משמעותי על חיי הסוללה.

המלצות לשימוש במצב אווירה

כדי לספק את חוויית המשתמש הטובה ביותר ולחסוך בצריכת החשמל במצב סביבה דינמית, צריך לפעול לפי הנחיות העיצוב האלה. ההמלצות האלה נותנות עדיפות לחוויית משתמש ברורה, על ידי מניעת מידע מטעה וצמצום העומס החזותי, ובו-זמנית מבצעות אופטימיזציה של עוצמת התצוגה.

  • הפחתת העומס החזותי והצגת עוצמת האות. ממשק משתמש נקי ומינימליסטי מסמן למשתמש שהאפליקציה במצב צריכת חשמל נמוכה, וחוסך משמעותית בסוללה על ידי הגבלת הפיקסלים הבהירים.
    • לפחות 85% מהמסך צריכים להיות שחורים.
    • להציג רק את המידע הכי חשוב, ולהעביר את הפרטים המשניים לתצוגה האינטראקטיבית.
    • במקום להשתמש במילוי אחיד, כדאי להשתמש בקווי מתאר לסמלים או ללחצנים גדולים.
    • אל תשתמשו בבלוקים גדולים של צבע אחיד ובמיתוג לא פונקציונלי או בתמונות רקע.
  • טיפול בנתונים דינמיים לא עדכניים
    • הקריאה החוזרת onUpdateAmbient() מופעלת רק מעת לעת – בדרך כלל פעם בדקה – כדי לחסוך בחשמל. בגלל המגבלה הזו, כל נתון שמשתנה בתדירות גבוהה – כמו שעון עצר, דופק או מרחק באימון – לא מעודכן בין העדכונים. כדי להימנע מהצגת מידע מטעה ושגוי, צריך להאזין לקריאה החוזרת (callback) של onEnterAmbient ולהחליף את הערכים בזמן אמת בתוכן placeholder סטטי, כמו --.
  • שמירה על פריסה עקבית
    • כדי ליצור מעבר חלק בין מצב אינטראקטיבי למצב סביבתי, חשוב להקפיד שהרכיבים יהיו באותו מיקום בשני המצבים.
    • השעה תמיד תוצג.
  • התאמה להקשר
    • אם המשתמש היה במסך הגדרות או במסך הגדרה כשהמכשיר נכנס למצב סביבתי, כדאי להציג מסך רלוונטי יותר מהאפליקציה במקום תצוגת ההגדרות.
  • טיפול בדרישות ספציפיות למכשיר
    • באובייקט AmbientDetails שמועבר אל onEnterAmbient():
      • אם הערך של deviceHasLowBitAmbient הוא true, משביתים את החלקת הקצוות (anti-aliasing) איפה שאפשר.
      • אם burnInProtectionRequired הוא true, כדאי להזיז מעט את רכיבי ממשק המשתמש מדי פעם ולהימנע מאזורים לבנים מלאים כדי למנוע שריפת פיקסלים במסך.

ניפוי באגים ובדיקות

הפקודות האלה של adb יכולות להיות שימושיות כשמפתחים או בודקים את התנהגות האפליקציה כשמפעילים במכשיר את מצב הסביבה:

# 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

דוגמה: אפליקציית אימונים

נניח שיש אפליקציה לאימונים שצריכה להציג למשתמש מדדים למשך כל סשן האימון. האפליקציה צריכה להישאר גלויה במהלך מעברים בין מצבי תצוגה פעילה, ולא להיות מוחלפת בתצוגת השעון.

כדי לעשות זאת, המפתח צריך לבצע את הפעולות הבאות:

  1. מטמיעים AmbientLifecycleObserver כדי לטפל בשינויים בממשק המשתמש בין המצבים אינטראקטיבי וסביבתי, כמו עמעום המסך והסרת נתונים לא חיוניים.
  2. יוצרים פריסה חדשה עם צריכת חשמל נמוכה למצב Ambient בהתאם לשיטות המומלצות.
  3. כדי למנוע מהמערכת לחזור לתצוגת השעון, כדאי להשתמש ב-Ongoing Activity API למשך האימון.

למידע על הטמעה מלאה, אפשר לעיין בדוגמה לתרגיל מבוסס-Compose ב-GitHub. בדוגמה הזו מוצג גם שימוש ב-AmbientAware composable מהספרייה Horologist כדי לפשט את הטיפול במצב סביבה ב-Compose.