שימוש בהתראות ב-Android

ה-codelab הזה הוא חלק מהקורס Advanced Android in Kotlin (פיתוח מתקדם ל-Android ב-Kotlin). כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר, אבל זה לא חובה. כל ה-codelab של הקורס מפורטים בדף הנחיתה של ה-codelab בנושא Android מתקדם ב-Kotlin.

מבוא

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

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

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

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

ב-codelab הזה תלמדו איך ליצור התראות באפליקציית Android ואיך להשתמש בהן.

מה שכדאי לדעת

חשוב שתכירו את:

  • איך יוצרים אפליקציות ל-Android ב-Kotlin. במיוחד, כדאי לעבוד עם Android SDK.
  • איך לתכנן אפליקציות באמצעות רכיבי הארכיטקטורה וקישור הנתונים.
  • הבנה בסיסית של BroadcastReceivers
  • הבנה בסיסית של AlarmManager

מה תלמדו

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

הפעולות שתבצעו:

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

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

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

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

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

משכפלים את המאגר מ-GitHub ועוברים להסתעפות starter.

$  git clone https://github.com/googlecodelabs/android-kotlin-notifications


לחלופין, אפשר להוריד את המאגר כקובץ ZIP, לבטל את הדחיסה שלו ולפתוח אותו ב-Android Studio.

הורדת קובץ Zip

  1. פותחים את האפליקציה ומריצים אותה ב-Android Studio.

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

  1. בודקים את קוד המקור. אפליקציית המתחילים מורכבת מפעילות אחת בשם MainActivity. יש שלוש חבילות משנה בשמות receiver, ‏ ui ו-util.

  • /receiver – החבילה receiver מכילה שני מקלטי שידורים בשמות AlarmReceiver ו-SnoozeReceiver. ‫AlarmReceiver מופעל על ידי AlarmManager כדי לשלוח את ההתראה כשהטיימר שהוגדר על ידי המשתמש מסתיים. ‫SnoozeReceiver מטפל בקליק של המשתמש כדי להשהות את ההתראה.
  • /ui – מכיל את EggTimerFragment שהוא חלק מממשק המשתמש של האפליקציה. EggTimerViewModel אחראי להפעלה ולביטול של הטיימר ולמשימות אחרות שקשורות למחזור החיים של האפליקציה.
  • /util – בחבילה הזו יש שני קבצים. ל-BindingUtils.kt יש מתאמי איגוד שמאפשרים איגוד נתונים בין ממשק המשתמש של האפליקציה לבין ViewModel. ל-NotificationUtils.kt יש שיטות הרחבה ב-NotificationManager.

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

שלב 1: יצירת התראה בסיסית

במשימה הזו, יוצרים התראה חדשה, מגדירים הודעה למשתמש ושולחים את ההתראה.

  1. פותחים את הכיתה NotificationUtils.kt ומאתרים את TODO: Step 1.1. תוכלו למצוא משימות תואמות ברשימת המשימות לביצוע ובקוד של האפליקציה.
  2. בודקים את הפונקציה sendNotification() שצוינה. תשתמשו בפונקציית התוסף הזו ב-NotificationManager כדי לשלוח התראות.
//NotificationUtils.kt
// TODO: Step 1.1 extension function to send messages (GIVEN)
/**
 * Builds and delivers a notification.
 *
 * @param messageBody, notification text.
 * @param context, activity context.
 */
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
  1. מקבלים מופע של כלי ליצירת התראות, מעבירים את הקשר של האפליקציה ואת מזהה הערוץ. מזהה הערוץ הוא ערך מחרוזת של הערוץ.

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

//NotificationUtils.kt
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
val builder = NotificationCompat.Builder(
        applicationContext,
        applicationContext.getString(R.string.egg_notification_channel_id)
)
  1. מגדירים את סמל ההתראה כך שייצג את האפליקציה, כותרת וטקסט התוכן של ההודעה שרוצים להציג למשתמש. ב-codelab יוצגו אפשרויות נוספות להתאמה אישית של ההתראה, אבל אלה הנתונים המינימליים שצריך להגדיר כדי לשלוח התראה.
//NotificationUtils.kt
   // TODO: Step 1.3 set title, text and icon to builder
   .setSmallIcon(R.drawable.cooked_egg)
   .setContentTitle(applicationContext.getString(R.string.notification_title))
   .setContentText(messageBody)
  1. לאחר מכן, צריך להתקשר אל notify() עם מזהה ייחודי של ההתראה ועם אובייקט Notification מהכלי ליצירת התראות.

המזהה הזה מייצג את מופע ההתראה הנוכחי ונדרש לעדכון או לביטול של ההתראה הזו. מכיוון שבכל זמן נתון תהיה לאפליקציה רק התראה פעילה אחת, אפשר להשתמש באותו מזהה לכל ההתראות. כבר יש קבוע למטרה הזו שנקרא NOTIFICATION_ID ב-NotificationUtils.kt. שימו לב שאפשר להתקשר ישירות אל notify() כי אתם מבצעים את השיחה מפונקציית הרחבה באותה כיתה.

//NotificationUtils.kt
   // TODO: Step 1.4 call notify to send the notification
    // Deliver the notification
    notify(NOTIFICATION_ID, builder.build())
  1. פותחים את ui/EggTimerViewModel.kt ומחפשים את הפונקציה startTimer(). הפונקציה הזו יוצרת שעון מעורר עם מרווח הזמן שנבחר כשהמשתמש מפעיל את שעון העצר.
  2. תפעילו התראה בפונקציה הזו כשהמשתמש יתחיל את הטיימר. כדי לקרוא לפונקציה sendNotification() שהטמעתם קודם, אתם צריכים מופע של NotificationManager. ‫NotificationManager הוא שירות מערכת שמספק את כל הפונקציות שזמינות ל-API של ההתראות, כולל פונקציית התוסף שהוספתם. בכל פעם שרוצים לשלוח, לבטל או לעדכן התראה, צריך לבקש מופע של NotificationManager מהמערכת. מתקשרים לפונקציה sendNotification()| עם הודעת ההתראה ועם ההקשר.
// EggTimerViewModel.kt
// TODO: Step 1.5 get an instance of NotificationManager 
// and call sendNotification

val notificationManager = ContextCompat.getSystemService(
    app, 
    NotificationManager::class.java
) as NotificationManager
                notificationManager.sendNotification(app.getString(R.string.timer_running), app)

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

  1. פותחים את logcat ומחפשים את "No Channel found". צריכה להופיע הודעת שגיאה שצירוף המקשים egg_channel לא קיים. בשלבים הבאים נסביר על Notification Channels ואיך לפתור את הבעיה הזו.

שלב 2: ערוצי התראות

החל מרמת API‏ 26, צריך להקצות את כל ההתראות לערוץ. אם לוחצים לחיצה ארוכה על סמל מרכז האפליקציות, בוחרים באפשרות 'פרטי האפליקציה' ואז בהתראות, מוצגת רשימה של ערוצי ההתראות שמשויכים לאפליקציה. בשלב הזה הרשימה ריקה כי האפליקציה לא יצרה ערוצים.

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

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

בשלב 1.1 השתמשתם ב-egg_notification_channel_id כערוץ ההתראות, ולכן עכשיו אתם צריכים ליצור ולהתאים אישית את הגדרות ההתראות ואת אופן הפעולה של הערוץ הזה.

  1. פותחים את EggTimerFragment.kt ומחפשים את הפונקציה createChannel().
  2. מעבירים את מזהה הערוץ הייחודי לקונסטרוקטור של NotificationChannel.
  3. מעבירים את השם של ערוץ ההתראות, שיופיע גם במסך ההגדרות של המשתמשים.
  4. כפרמטר האחרון, מעבירים את רמת החשיבות של ערוץ ההתראות. רמות החשיבות יוסברו בהמשך ה-codelab, אז בינתיים אפשר להשתמש ב-NotificationManager.IMPORTANCE_LOW.
  5. באובייקט notificationChannel מגדירים את enableLights לערך true. ההגדרה הזו תפעיל את האורות כשתופיע התראה.
  6. ב-notificationChannel object set lightColor, מגדירים את הצבע לאדום כדי להציג אור אדום כשמוצגת התראה.
  7. באובייקט notificationChannel מגדירים את enableVibration ל-true כדי להפעיל את הרטט.
  8. באובייקט notificationChannel מגדירים את תיאור הערוץ לערך ‘Time for breakfast'.
  9. כדי לקבל מופע של NotificationManager, מתקשרים אל getSystemService().
  10. קוראים לפונקציה createNotificationChannel() ב-NotificationManager ומעבירים את האובייקט notificationChannel שיצרתם בשלב הקודם.
//EggTimerFragment.kt
private fun createChannel(channelId: String, channelName: String) {
    // TODO: Step 1.6 START create a channel
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val notificationChannel = NotificationChannel(
            channelId,
            channelName,
            // TODO: Step 2.4 change importance
            NotificationManager.IMPORTANCE_LOW
        )
        // TODO: Step 2.6 disable badges for this channel

        notificationChannel.enableLights(true)
        notificationChannel.lightColor = Color.RED
        notificationChannel.enableVibration(true)
        notificationChannel.description = "Time for breakfast"

        val notificationManager = requireActivity().getSystemService(
            NotificationManager::class.java
        )
        notificationManager.createNotificationChannel(notificationChannel)
    }
    // TODO: Step 1.6 END create channel
}
  1. בשלב הבא, כדי ליצור ערוץ, צריך להפעיל את הפונקציה createChannel() שכתבתם (שלב 1.7). הפונקציה הזו מקבלת שני פרמטרים: מזהה הערוץ ושם הערוץ. צריך לחפש את מזהה הערוץ ואת שם הערוץ במשאבי המחרוזות שכבר מופיעים בפרויקט.
// EggTimerFragment.kt
    // TODO: Step 1.7 call createChannel
    createChannel(
          getString(R.string.egg_notification_channel_id),
          getString(R.string.egg_notification_channel_name)
    )
  1. צריך להעביר את מזהה הערוץ לבונה ההתראות. כבר עשיתם את זה בשלב 1.2. אם מגדירים ערך שגוי כמזהה הערוץ, ההתראה תיכשל. פותחים את NotificationUtils.kt כדי לוודא שמזהה הערוץ שהגדרתם קודם נכון.
// NotificationUtils.kt
val builder = NotificationCompat.Builder(
        applicationContext,
       // TODO: Step 1.8 verify the notification channel name
        applicationContext.getString(R.string.egg_notification_channel_id)
)
  1. מריצים את האפליקציה, ותראו שהיא שולחת התראה בכל פעם שמפעילים את הטיימר.
  2. מושכים את שורת הסטטוס ורואים שכותרת ההתראה, התוכן והסמל הם כמו שהגדרתם בשלבים הקודמים.
  3. כדי לוודא שהערוץ החדש נוצר, סוגרים את האפליקציה ומחפשים את סמל האפליקציה. לוחצים לחיצה ארוכה על סמל האפליקציה ובוחרים באפשרות פרטי האפליקציה.

  1. בוחרים באפשרות התראות מתוך רשימת ההגדרות. עכשיו אמור להופיע ערוץ חדש בשם Egg, ממש מתחת להגדרה הצגת התראות.

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

שלב 3: הוספת התראות לאפליקציה

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

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

האפליקציה שלך משתמשת ב-AlarmManager כדי להגדיר שעון מעורר. הקוד שקשור ל-AlarmManager כבר מופיע בקוד ההתחלתי ומשמש להצגת הודעת הטוסט. ‫AlarmManager עוקב אחרי בחירת הזמן הרצויה ויפעיל את הפונקציה onReceive() של AlarmReceiver.kt כשהזמן יסתיים. אם פותחים את AlarmReceiver.kt ועוברים אל onReceive(), אמורה להופיע ההודעה הקופצת שמוצגת בכל פעם שמגדירים טיימר לביצים.

  1. פותחים את AlarmReceiver.kt, מופע של NotificationManager, וקוראים לפונקציה sendNotification() עם הטקסט של ההודעה ופרמטרים של הקשר.
// AlarmReceiver.kt
   // TODO: Step 1.9 add call to sendNotification
   val notificationManager = ContextCompat.getSystemService(
       context, 
       NotificationManager::class.java
   ) as NotificationManager
             
   notificationManager.sendNotification(
       context.getText(R.string.eggs_ready).toString(), 
       context
   )
  1. אפשר להסיר את ההודעה הקופצת כי האפליקציה תשלח התראה כשהטיימר יסתיים.
// AlarmReceiver.kt
     // TODO: Step 1.10 [Optional] remove toast
//   Toast.makeText(
//       context, 
//       context.getText(R.string.eggs_ready),
//       Toast.LENGTH_SHORT
//   ).show()
  1. מפעילים את האפליקציה . תופיע התראה בכל פעם שתפעילו את הטיימר ובכל פעם שהטיימר יסתיים.

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

  1. פותחים את EggTimerFragment.kt ומסירים את קוד ההתראה של שלב 1.5.
// EggTimeViewModel.kt

// TODO: Step 1.5 get an instance of NotificationManager 
// and call sendNotification
// val notificationManager = ContextCompat.getSystemService(
//      app,
//      NotificationManager::class.java
// ) as NotificationManager
// notificationManager.sendNotification(app.getString(R.string.eggs_ready), app)
  1. מריצים את האפליקציה שוב.
  2. מגדירים טיימר, מעבירים אותו לרקע ומחכים שהזמן יסתיים. תוצג התראה. ההתראה הזו הרבה יותר שימושית.

שלב 4: הוספת כוונת רכישה לתוכן

  1. מריצים שוב את האפליקציה, אם היא לא פועלת כבר.
  2. לוחצים על ההתראה. לא קורה כלום.

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

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

  1. פותחים את NotificationUtils.kt ומאתרים את פונקציית התוסף sendNotification().
  2. יוצרים Intent עם applicationContext והפעילות שרוצים להפעיל, MainActivity::class.java.
// NotificationUtils.kt

fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
    // Create the content intent for the notification, which launches
    // this activity
   // TODO: Step 1.11 create intent
    val contentIntent = Intent(applicationContext, MainActivity::class.java)

יצרתם את ה-Intent, אבל ההתראה מוצגת מחוץ לאפליקציה. כדי ש-Intent יפעל מחוץ לאפליקציה, צריך ליצור PendingIntent חדש.

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

  1. יוצרים PendingIntent עם applicationContext,‏ NOTIFICATION_ID,‏ contentIntent שיצרתם בשלב הקודם והדגל PendingIntent. הדגל PendingIntent מציין את האפשרות ליצור PendingIntent חדש או להשתמש בקיים. צריך להגדיר את PendingIntent.FLAG_UPDATE_CURRENT כדגל, כי לא רוצים ליצור התראה חדשה אם יש התראה קיימת. כך תשנו את PendingIntent הנוכחי שמשויך לכוונת המשתמש שציינתם.
// NotificationUtils.kt
   // TODO: Step 1.12 create PendingIntent
    val contentPendingIntent = PendingIntent.getActivity(
        applicationContext, 
        NOTIFICATION_ID,
        contentIntent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )
  1. מעבירים את PendingIntent להתראה. כדי לעשות זאת, מתקשרים אל setContentIntent() ב-NotificationBuilder. מעכשיו, כשלוחצים על ההתראה, מופעלת הפעולה PendingIntent ונפתח MainActivity.
  2. צריך גם להגדיר את setAutoCancel() ל-true, כדי שכשהמשתמש יקיש על ההתראה, היא תיסגר כשהוא יועבר לאפליקציה.
// NotificationUtils.kt
    // TODO: Step 1.13 set content intent
    .setContentIntent(contentPendingIntent)
    .setAutoCancel(true)
  1. מריצים את האפליקציה שוב.
  2. מגדירים טיימר, מעבירים את האפליקציה לרקע ומחכים שההתראה תופיע.
  3. אחרי שרואים את ההתראה, לוחצים עליה על ידי משיכת שורת הסטטוס למטה, ורואים איך האפליקציה עוברת לחזית.

שלב 5: ביטול ההתראה

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

כדי לפתור את הבעיה, צריך לנקות את ההתראה הקודמת כשמתחילים טיימר חדש. מתחילים ביצירת פונקציית הרחבה נוספת ב-NotificationUtils.kt. ל-NotificationManager יש API לביטול כל ההתראות הפעילות שנקרא cancelAll().

  1. פתיחת NotificationsUtil.kt.
  2. מוסיפים פונקציית הרחבה ב-NotificationManager שמפעילה את cancelAll().
// NotificationUtils.kt

// TODO: Step 1.14 Cancel all notifications
/**
 * Cancels all notifications.
 *
 */
fun NotificationManager.cancelNotifications() {
    cancelAll()
}
  1. פותחים את EggTimerViewModel.kt ועוברים לפונקציה startTimer().
  2. בתוך startTimer(), מקבלים מופע של NotificationManager מהמערכת וקוראים ל-cancelNotifications().
//  EggTimerViewModel.kt
   //TODO Step 1.15 call cancel notification
    val notificationManager =
       ContextCompat.getSystemService(
            app,
            NotificationManager::class.java
        ) as NotificationManager
    notificationManager.cancelNotifications()       
  1. מפעילים את האפליקציה ומתחילים את הטיימר.
  2. אחרי שרואים את ההתראה, מפעילים שוב את הטיימר ורואים איך האפליקציה שלנו מוחקת אוטומטית את ההתראה הקודמת מסרגל המצב.

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

שלב 1: מגדירים את הסגנון של ההתראה

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

NotificationCompat מציע סגנונות מובנים ל:

  • BigTextStyle, שיכול להציג בלוק גדול של טקסט, למשל את התוכן של אימייל כשהוא מורחב.
  • BigPictureStyle, שבו מוצגות התראות בפורמט גדול שכוללות קובץ מצורף של תמונה גדולה.
  • InboxStyle, שבו מוצג תוכן טקסטואלי בסגנון שיחה.
  • MediaStyle, שבו מוצגים אמצעי בקרה להפעלת מדיה.
  • MessagingStyle, שבה מוצגות התראות בפורמט גדול שכוללות כמה הודעות בין מספר אנשים.

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

  1. פותחים את NotificationUtils.kt ומחפשים את הפונקציה sendNotification().
  2. מתחילים בטעינת תמונה מ-resources באמצעות BitmapFactory.
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
  1. יוצרים BigPictureStyle חדש ומגדירים את התמונה.
  2. מגדירים את bigLargeIcon() ל-null כדי שהסמל הגדול ייעלם כשההתראה מורחבת.
// NotificationUtils.kt

// TODO: Step 2.0 add style
val eggImage = BitmapFactory.decodeResource(
     applicationContext.resources, 
     R.drawable.cooked_egg
)
val bigPicStyle = NotificationCompat.BigPictureStyle()
        .bigPicture(eggImage)
        .bigLargeIcon(null)
  1. מגדירים את הסגנון עם setStyle() לערך bigPicStyle.
  2. מגדירים את הסמל הגדול עם setLargeIcon() ל-eggImage, כדי שהתמונה תוצג כסמל קטן יותר כשההתראה מכווצת.
// NotificationUtils.kt
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
  1. מפעילים את האפליקציה ומגדירים טיימר. כשההתראה מוצגת בפעם הראשונה, היא במצב מכווץ בחלונית ההזזה של ההתראות. אם מרחיבים את ההתראה, תמונה גדולה מוצגת באזור ההתראה המורחבת.

שלב 2: פעולות בהתראה

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

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

כדי להוסיף לחצן פעולה, מעבירים PendingIntent אל הפונקציה addAction() בכלי הבנייה. זה דומה להגדרת פעולת ברירת המחדל של ההקשה על ההתראה באמצעות קריאה ל-setContentIntent(), אבל במקום להפעיל פעילות, אפשר לבצע מגוון פעולות אחרות, למשל, להפעיל BroadcastReceiver שמבצע עבודה ברקע כדי שהפעולה לא תפריע לאפליקציה שכבר פתוחה.

ב-codelab הזה, כבר קיבלתם BoadcastReceiver בשם SnoozeReceiver. תשתמשו ב-SnoozeReceiver כדי לקבל את הקליק של המשתמש על פעולת ההתראה. בשלבים הבאים תוסיפו קוד כדי להשהות את ההתראה של שעון הביצים למשך 60 שניות כשהמשתמש ילחץ על לחצן ההשהיה. כשלוחצים על פעולת הדחייה, רכיב SnoozeReceiver מקבל כוונה ויוצר שעון מעורר חדש כדי לשלוח התראה חדשה אחרי 60 שניות.

  1. פתיחת SnoozeReceiver.kt. הכיתה הזו דומה לכיתה AlarmReceiver שבה השתמשת בעבר. בשלבים הבאים מוסיפים קוד שיפעיל את הפונקציה onReceive() של SnoozeReceiver. בקיצור, הקוד ב-SnoozeReceiver ייצור שעון מעורר חדש כדי לשלוח התראה חדשה דקה לאחר מכן. גוללים לחלק התחתון של הפונקציה onReceive, מקבלים מופע של notificationManager מהמערכת וקוראים לפונקציה cancelAll.
// SnoozeReceiver.kt
        val notificationManager = ContextCompat.getSystemService(
            context,
            NotificationManager::class.java
        ) as NotificationManager
        notificationManager.cancelAll()
  1. כדי להשתמש ב-SnoozeReceiver, פותחים את NotificationUtils.kt.
  2. יוצרים Intent snoozeIntent חדש בשביל SnoozeReceiver מיד אחרי הסגנון בפונקציה sendNotification().
  3. יוצרים PendingIntent על ידי קריאה לשיטה getBroadcast() ב-PendingIntent, שמצפה לפרמטרים שמפורטים בשלבים הבאים. המערכת תשתמש ב-PendingIntent כדי להגדיר שעון מעורר חדש שיציג התראה חדשה אחרי 60 שניות, אם המשתמש יקיש על לחצן הדחייה.
  4. הפרמטר הראשון הוא הקשר של האפליקציה שבה הפעילות PendingIntent צריכה להתחיל.
  5. הפרמטר השני הוא קוד הבקשה, שהוא קוד הבקשה של הכוונה הזו שממתינה. אם צריך לעדכן או לבטל את הכוונה התלויה ועומדת הזו, צריך להשתמש בקוד הזה כדי לגשת אליה.
  6. לאחר מכן מוסיפים את האובייקט snoozeIntent, שהוא ה-Intent של הפעילות שרוצים להפעיל.
  7. לבסוף, מוסיפים את ערך הדגל #FLAG_ONE_SHOT כי הכוונה תהיה בשימוש רק פעם אחת. הפעולה המהירה וההתראה ייעלמו אחרי הלחיצה הראשונה, ולכן אפשר להשתמש ב-Intent רק פעם אחת.
// NotificationUtils.kt

// TODO: Step 2.2 add snooze action
val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java)
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(
    applicationContext, 
    REQUEST_CODE, 
    snoozeIntent, 
    FLAGS
)
  1. לאחר מכן, קוראים לפונקציה addAction() ב-notificationBuilder. הפונקציה הזו מצפה לקבל סמל וטקסט לתיאור הפעולה למשתמש. צריך להוסיף גם את snoozeIntent. הכוונה הזו תשמש להפעלת boadcastReceiver הנכון כשלוחצים על הפעולה.
// NotificationUtils.kt
// TODO: Step 2.3 add snooze action
.addAction(
    R.drawable.egg_icon, 
    applicationContext.getString(R.string.snooze),
    snoozePendingIntent
)
  1. מריצים את אפליקציית שעון העצר כדי לבדוק את פעולת הדחייה.
  2. מפעילים את הטיימר ומעבירים את האפליקציה לרקע. כשהטיימר מגיע לאפס, מרחיבים את ההתראה ורואים שנוסף לה לחצן פעולה להשהיה, שמאפשר להשהות את טיימר הביצה למשך דקה נוספת.

שלב 3: חשיבות ההתראה

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

צריך לציין את רמת החשיבות בבונה NotificationChannel. הגדרתם במקור רמת חשיבות נמוכה לאפליקציית הטיימר לביצים. אפשר להשתמש באחת מתוך חמש רמות חשיבות, מ-IMPORTANCE_NONE(0) עד IMPORTANCE_HIGH(4). רמת החשיבות שאתם מקצים לערוץ חלה על כל ההודעות שאתם מפרסמים בו.

רמות החשיבות של הערוצים

רמת חשיבות שגלויות למשתמשים

חשיבות (Android מגרסה 8.0 ואילך)

עדיפות (Android מגרסה 7.1 ומטה)

משמיעה צליל ומופיעה כהתראה קופצת (מופיעה בחלק העליון של המסך)

IMPORTANCE_HIGH

PRIORITY_HIGH / PRIORITY_MAX

השמעת צליל

IMPORTANCE_DEFAULT

PRIORITY_DEFAULT

ללא קול

IMPORTANCE_LOW

PRIORITY_LOW

ללא צליל וההודעה לא מופיעה בשורת הסטטוס

IMPORTANCE_MIN

PRIORITY_MIN

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

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

  1. כדי לשנות את רמת החשיבות של ערוץ ההתראות של האפליקציה, פותחים את EggTimerFragment.kt ועוברים אל createChannel(). שינוי רמת החשיבות מ-IMPORTANCE_LOW ל-IMPORTANCE_HIGH.
// EggTimerFragment.kt
    val notificationChannel = NotificationChannel(
        channelId,
        channelName,
        // TODO: Step 2.4 change importance
        NotificationManager.IMPORTANCE_HIGH
    )

כדי לתמוך במכשירים עם Android 7.1 (רמת API‏ 25) ומטה, צריך גם לקרוא ל-setPriority() לכל התראה, באמצעות קבוע עדיפות מהמחלקה NotificationCompat.

  1. פותחים את NotificationUtils.kt ומוסיפים את הקוד הבא לאובייקט של כלי בניית ההתראות.
// NotificationUtils.kt
   .addAction(
       R.drawable.common_google_signin_btn_icon_dark,
       applicationContext.getString(R.string.snooze),
       snoozePendingIntent
    )
   // TODO: Step 2.5 set priority
    .setPriority(NotificationCompat.PRIORITY_HIGH)
  1. לפני שמפעילים את האפליקציה, לוחצים לחיצה ארוכה על סמל האפליקציה במכשיר או באמולטור ובוחרים באפשרות 'הסרת ההתקנה' כדי לנקות את הגדרות הערוץ הקודמות. אם לא תצליחו להסיר את האפליקציה, הגדרות העדיפות של הערוץ לא ישתנו, ולא יהיה שינוי בהתנהגות כשההתראה תפורסם.
  2. עכשיו מפעילים את האפליקציה שוב ומתחילים את הטיימר. הפעם, כשההתראה תועבר, אמור להופיע חלון קופץ בחלק העליון של המסך, בלי קשר לשאלה אם האפליקציה פועלת בחזית או ברקע.

שלב 4: תגיות של התראות

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

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

  1. מוסיפים setShowBadge(false) לקוד ליצירת הערוץ של טיימר הביצים כדי להשבית את התגים.
// EggTimerFragment.kt

    ).apply {
        // TODO: Step 2.6 disable badges for this channel
        setShowBadge(false)
    }
  1. מפעילים את האפליקציה שוב, מפעילים את הטיימר ומתבוננים בסמל האפליקציה. לא אמורים להופיע תגים בסמל האפליקציה.

קוד הפתרון נמצא בענף הראשי של הקוד שהורדתם.

קורס ב-Udacity:

מסמכי תיעוד למפתחי Android:

קישורים למדריכי Codelab נוספים בקורס הזה זמינים בדף הנחיתה של מדריכי Codelab בנושא Android מתקדם ב-Kotlin.