פעילויות מתמשכות

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

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

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

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

running-icon

איור 1. אינדיקטור של פעילות.

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

מרכז האפליקציות

איור 2. מרכז האפליקציות הגלובלי.

אלה מצבים שבהם כדאי להשתמש בהתראה מתמשכת שקשורה לפעילות מתמשכת:

טיימר

איור 3. טיימר: ספירה לאחור של הזמן, והוא מסתיים כשמשהים או מפסיקים אותו.

מפה

איור 4. מסלול מפורט: הכרזה על הוראות הגעה ליעד. השיתוף מסתיים כשהמשתמש מגיע ליעד או מפסיק את הניווט.

מוזיקה

איור 5. מדיה: השמעת מוזיקה לאורך כל הסשן. הסשן מסתיים מיד אחרי שהמשתמש משהה אותו.

ב-Wear נוצרות פעילויות שוטפות באופן אוטומטי לאפליקציות מדיה.

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

הגדרה

כדי להתחיל להשתמש ב-Ongoing Activity API באפליקציה, מוסיפים את התלות הבאה לקובץ build.gradle של האפליקציה:

dependencies {
  implementation "androidx.wear:wear-ongoing:1.1.0"
  implementation "androidx.core:core:1.17.0"
}

יצירת פעילות מתמשכת

התהליך כולל שלושה שלבים:

  1. יוצרים NotificationCompat.Builder רגיל ומגדירים אותו כמתמשך.
  2. יוצרים ומגדירים אובייקט OngoingActivity ומעבירים אליו את ה-builder של ההתראה.
  3. מחילים את הפעילות המתמשכת על כלי בניית ההתראות ומפרסמים את ההתראה שנוצרת.

יצירה והגדרה של ההתראה

קודם כול יוצרים NotificationCompat.Builder. השלב העיקרי הוא לקרוא ל-setOngoing(true) כדי לסמן אותה כהתראה פעילה. בשלב הזה אפשר גם להגדיר מאפיינים אחרים של ההתראה, כמו הסמל הקטן והקטגוריה.

// Create a PendingIntent to pass to the notification builder
val pendingIntent =
    PendingIntent.getActivity(
        this,
        0,
        Intent(this, AlwaysOnActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        },
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
    )

val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("Always On Service")
    .setContentText("Service is running in background")
    .setSmallIcon(R.drawable.animated_walk)
    // Category helps the system prioritize the ongoing activity
    .setCategory(NotificationCompat.CATEGORY_WORKOUT)
    .setContentIntent(pendingIntent)
    .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
    .setOngoing(true) // Important!

יצירת OngoingActivity

לאחר מכן, יוצרים מופע של OngoingActivity באמצעות הכלי ליצירה שלו. הפונקציה OngoingActivity.Builder דורשת Context, מזהה התראה ו-NotificationCompat.Builder שיצרתם בשלב הקודם.

מגדירים את מאפייני המפתח שיוצגו בממשק המשתמש החדש:

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

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // Sets the icon that appears on the watch face in active mode.
        .setAnimatedIcon(R.drawable.animated_walk)
        // Sets the icon that appears on the watch face in ambient mode.
        .setStaticIcon(R.drawable.ic_walk)
        // Sets the tap target to bring the user back to the app.
        .setTouchIntent(pendingIntent)
        .build()

החלת ההגדרה על ההתראה ועל הפוסט

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

// This call modifies notificationBuilder to include the ongoing activity data.
ongoingActivity.apply(applicationContext)

// Post the notification.
startForeground(NOTIFICATION_ID, notificationBuilder.build())

הוספת טקסט סטטוס דינמי למרכז האפליקציות

הקוד שלמעלה מוסיף את הסמל שאפשר להקיש עליו לתצוגת השעון. כדי לספק עדכונים עשירים יותר בזמן אמת בקטע האחרונים במרכז האפליקציות, צריך ליצור אובייקט Status ולצרף אותו ל-OngoingActivity. אם לא מציינים Status מותאם אישית, המערכת משתמשת כברירת מחדל בטקסט התוכן של ההתראה (מוגדר באמצעות setContentText()). כדי להציג טקסט דינמי, צריך להשתמש ב-Status.Builder. אפשר להגדיר מחרוזת תבנית עם placeholders ולספק Status.Part אובייקטים כדי למלא את ה-placeholders האלה. הערך Status.Part יכול להיות דינמי, כמו שעון עצר או טיימר.

בדוגמה הבאה מוצג אופן יצירת סטטוס שמציג את הטקסט 'הפעלה למשך [שעון עצר]':

// Define a template with placeholders for the activity type and the timer.
val statusTemplate = "#type# for #time#"

// Set the start time for a stopwatch.
// Use SystemClock.elapsedRealtime() for time-based parts.
val runStartTime = SystemClock.elapsedRealtime()

val ongoingActivityStatus = Status.Builder()
    // Sets the template string.
    .addTemplate(statusTemplate)
    // Fills the #type# placeholder with a static text part.
    .addPart("type", Status.TextPart("Run"))
    // Fills the #time# placeholder with a stopwatch part.
    .addPart("time", Status.StopwatchPart(runStartTime))
    .build()

לבסוף, כדי לקשר את Status אל OngoingActivity, צריך להתקשר אל setStatus() ב-OngoingActivity.Builder.

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // Add the status to the OngoingActivity.
        .setStatus(ongoingActivityStatus)
        .build()

התאמות אישיות נוספות

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

התראה פעילה

  • הקטגוריה שמוגדרת קובעת את סדר העדיפויות של הפעילות המתמשכת.
    • CATEGORY_CALL: שיחת וידאו או שיחה קולית נכנסת או בקשה דומה לתקשורת סינכרונית
    • CATEGORY_NAVIGATION: מפה או מסלול מפורט
    • CATEGORY_TRANSPORT: שליטה בהעברת מדיה להפעלה
    • CATEGORY_ALARM: שעון מעורר או טיימר
    • CATEGORY_WORKOUT: אימון
    • CATEGORY_LOCATION_SHARING: קטגוריה של שיתוף מיקום זמני
    • CATEGORY_STOPWATCH: שעון עצר

פעילות שוטפת

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

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

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

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

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

  • Ongoing Activity ID: מזהה שמשמש להבחנה בין קריאות ל-fromExistingOngoingActivity() כשיש לאפליקציה יותר מפעילות אחת מתמשכת.

עדכון פעילות מתמשכת

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

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

כדי לעדכן את הפעילות המתמשכת ואת ההתראה שפורסמה, משתמשים באובייקט שיצרתם קודם ומפעילים את update(), כמו בדוגמה הבאה:

ongoingActivity.update(context, newStatus)

לנוחותכם, יש שיטה סטטית ליצירת פעילות מתמשכת.

OngoingActivity.recoverOngoingActivity(context)
               .update(context, newStatus)

איך מפסיקים פעילות מתמשכת

כשהאפליקציה מסיימת לפעול כפעילות מתמשכת, היא צריכה רק לבטל את ההתראה המתמשכת.

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

איך משהים פעילויות מתמשכות

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

שיטות מומלצות

כשעובדים עם Ongoing Activity API, חשוב לזכור את הדברים הבאים:

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

  • שימוש בסמלי וקטור בשחור-לבן עם רקע שקוף.

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

  • אם באפליקציה שלכם מוצהרת יותר מפעילות אחת MAIN LAUNCHER במניפסט, צריך לפרסם קיצור דרך דינמי ולשייך אותו לפעילות המתמשכת באמצעות LocusId.

פרסום התראות על מדיה כשמפעילים מדיה במכשירי Wear OS

אם תוכן מדיה מופעל במכשיר Wear OS, מפרסמים התראה על מדיה. כך המערכת יכולה ליצור את הפעילות המתמשכת המתאימה.

אם אתם משתמשים ב-Media3, ההתראה מתפרסמת באופן אוטומטי. אם יוצרים את ההתראה באופן ידני, צריך להשתמש ב-MediaStyleNotificationHelper.MediaStyle, וב-MediaSession המתאים צריך להיות מאוכלס session activity.