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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

גילוי מצב הסביבה

כדי להגיב לאירועים במצב רגיש לסביבה ב-Jetpack Compose ב-Wear OS:

  1. אפשר להשתמש ב-LocalAmbientModeManager כדי לעקוב אחרי שינויים בסטטוס של מצב הסביבה במערכת, ולהתאים את רכיבי ה-Composable בהתאם. מגדירים את הרכיב כך שיעבור למצב צריכת חשמל נמוכה במצב AmbientMode.Ambient, ויחזור לתצוגה אינטראקטיבית מלאה במצב 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")
            // ...
    }

מפתחים שמשתמשים ב-Jetpack פיתוח נייטיב יכולים להשתמש ב-LocalAmbientModeManager, שמפשט את ההטמעה של התבנית הזו.

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

בתרשים הזרימה הבא מוצג איך המערכת קובעת את התנהגות הסביבה על סמך גרסת 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. כדי לעשות את זה, אפשר להשתמש במילת המפתח לשינוי keepScreenOn. הפונקציה הזו פועלת כחסימת מצב שינה, ושומרת את המכשיר במצב אינטראקטיבי. חשוב להשתמש באפשרות הזו בזהירות רבה כי היא משפיעה באופן משמעותי על חיי הסוללה.

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

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

  • הפחתת העומס החזותי והצגת עוצמה. ממשק משתמש נקי ומינימליסטי מסמן למשתמש שהאפליקציה במצב צריכת חשמל נמוכה, וחוסך משמעותית בסוללה על ידי הגבלת הפיקסלים הבהירים.
    • לפחות 85% מהמסך צריכים להיות שחורים.
    • להציג רק את המידע הכי חשוב ולהעביר את הפרטים המשניים לתצוגה האינטראקטיבית.
    • כדאי להשתמש בקווי מתאר לסמלים או ללחצנים גדולים במקום במילוי מלא.
    • אל תשתמשו בבלוקים גדולים של צבע אחיד ובמיתוג לא פונקציונלי או בתמונות רקע.
  • טיפול בנתונים דינמיים לא עדכניים
    • הקריאה החוזרת AmbientTickEffect מופעלת רק מדי פעם – בדרך כלל פעם בדקה – כדי לחסוך בחשמל. בגלל המגבלה הזו, כל נתון שמשתנה בתדירות גבוהה – כמו שעון עצר, דופק או מרחק באימון – לא יהיה מעודכן בין העדכונים. כדי להימנע מהצגת מידע מטעה ושגוי, צריך להחליף את הערכים הדינמיים האלה בתוכן סטטי של placeholder, כמו --.
  • שמירה על פריסה עקבית
    • כדי ליצור מעבר חלק בין המצבים אינטראקטיבי וסביבתי, חשוב להקפיד שהרכיבים יישארו באותו המיקום.
    • הצגת השעה באופן קבוע.
  • התאמה להקשר
    • אם המשתמש היה במסך הגדרות או במסך הגדרה כשהמכשיר נכנס למצב סביבתי, כדאי להציג מסך רלוונטי יותר מהאפליקציה במקום תצוגת ההגדרות.
  • טיפול בדרישות ספציפיות למכשיר
    • באובייקט AmbientMode שמוחזר מ-currentAmbientMode:
      • אם הערך של isLowBitAmbientSupported הוא true, משביתים את מניעת הקצוות המחודדים (anti-aliasing) איפה שאפשר.
      • אם isBurnInProtectionRequired הוא 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. משתמשים ב-LocalAmbientModeManager כדי לטפל בשינויים בממשק המשתמש בין מצבי אינטראקטיבי לסביבתי, כמו עמעום המסך והסרה של נתונים לא חיוניים.
  2. יוצרים פריסה חדשה עם צריכת חשמל נמוכה למצב Ambient בהתאם לשיטות המומלצות.
  3. כדי שהמערכת לא תחזור לתצוגת השעון, צריך להשתמש ב-Ongoing Activity API (או בעדכונים בזמן אמת ב-Wear OS 7 ואילך) למשך האימון.

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