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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

שימוש ב-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().

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

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

הצגת השעה עם טקסט בהתאם לסביבה

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

תרשים זרימה של התנהגות סביבתית

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

תרשים זרימה שממחיש את לוגיקת ההחלטה של מצב הרגישות לסביבה ב-Wear OS. ההגדרה הזו מראה איך גרסת מערכת ההפעלה של המכשיר וההגדרה של האפליקציה קובעות את אחת משלוש התוצאות: שכבת-על מטושטשת, מצב תצוגה תמיד פעילה (AOD) גלובלי או מצב Ambiactive שמנוהל על ידי האפליקציה.
איור 1.: תרשים זרימה שממחיש את לוגיקת ההחלטה של מצב הרגישות לסביבה ב-Wear OS.

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

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

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

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

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

לחלופין, ב-Wear OS 7 ואילך, אפשר להשתמש בעדכון בזמן אמת במקום בפעילות מתמשכת. לצורך תאימות לאחור, ממשיכים לתמוך בפעילות מתמשכת במכשירים עם Wear OS 6 ומטה.

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

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

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

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

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

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

  • הפחתת העומס החזותי והצגת עוצמה. ממשק משתמש נקי ומינימליסטי מסמן למשתמש שהאפליקציה במצב צריכת חשמל נמוכה, וחוסך משמעותית בסוללה על ידי הגבלת הפיקסלים הבהירים.
    • לפחות 85% מהמסך צריכים להיות שחורים.
    • להציג רק את המידע הכי חשוב ולהעביר את הפרטים המשניים לתצוגה האינטראקטיבית.
    • כדאי להשתמש בקווי מתאר לסמלים או ללחצנים גדולים במקום במילוי מלא.
    • אל תשתמשו בבלוקים גדולים של צבע אחיד ובמיתוג לא פונקציונלי או בתמונות רקע.
  • טיפול בנתונים דינמיים לא עדכניים
    • הקריאה החוזרת onUpdateAmbient() מופעלת רק מדי פעם – בדרך כלל פעם בדקה – כדי לחסוך בחשמל. בגלל המגבלה הזו, כל נתון שמשתנה בתדירות גבוהה – כמו שעון עצר, דופק או מרחק באימון – לא יהיה מעודכן בין העדכונים. כדי להימנע מהצגת מידע מטעה ושגוי, צריך להאזין לקריאה החוזרת 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. יוצרים פריסה חדשה עם צריכת חשמל נמוכה למצב הסביבה בהתאם לשיטות המומלצות.
  3. כדי שהמערכת לא תחזור למסך השעון, צריך להשתמש ב-Ongoing Activity API (או בעדכונים בזמן אמת ב-Wear OS 7 ואילך) למשך האימון.

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