Codelab ל-Activity Recognition Transition API

1. מבוא

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

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

Activity Recognition Transition API פותר את הבעיות האלה על ידי מתן ממשק API פשוט שמבצע את כל העיבוד בשבילכם ומספר לכם רק את מה שחשוב לכם: מתי הפעילות של המשתמש השתנתה. האפליקציה פשוט נרשמת למעבר בפעילויות שמעניינות אתכם, ו-API מעדכן אתכם על השינויים

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

בסדנת הקוד הזו תלמדו איך להשתמש ב-Activity Recognition Transition API כדי לקבוע מתי משתמש מתחיל או מפסיק פעילות כמו הליכה או ריצה.

דרישות מוקדמות

היכרות עם פיתוח Android והיכרות מסוימת עם קריאות חזרה (callbacks).

מה תלמדו

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

מה צריך להכין

  • Android Studio Bumblebee
  • מכשיר Android או אמולטור

2. תחילת העבודה

שכפול של המאגר של פרויקט ה-starter

כדי לעזור לכם להתחיל לעבוד כמה שיותר מהר, הכנו פרויקט להתחלה שתוכלו להמשיך ממנו. אם כבר התקנתם את git, תוכלו פשוט להריץ את הפקודה הבאה. (אפשר לבדוק זאת על ידי הקלדה של git --version במסוף או בשורת הפקודה, ולוודא שהיא פועלת כראוי).

 git clone https://github.com/android/codelab-activity_transitionapi

אם אין לכם git, תוכלו לקבל את הפרויקט כקובץ zip:

ייבוא הפרויקט

פותחים את Android Studio, בוחרים באפשרות 'פתיחת פרויקט קיים ב-Android Studio' במסך הפתיחה ופותחים את ספריית הפרויקט.

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

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

d2363db913d8e5ad.png

יש שני סמלי תיקיות (base ו-complete). כל אחד מהם נקרא 'מודול'.

לתשומת ליבכם: יכול להיות שיחלפו כמה שניות עד ש-Android Studio יתעדכן את הפרויקט ברקע בפעם הראשונה. במהלך התהליך יופיע סמל ספינר בשורת הסטטוס שבתחתית Android Studio:

c9f23d5336be3cfe.png

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

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

הסבר על פרויקט ההתחלה

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

אפשר להשתמש במודול complete כדי לבדוק את העבודה שלכם, או להיעזר בו אם נתקלתם בבעיות.

סקירה כללית של הרכיבים העיקריים:

  • MainActivity: מכיל את כל הקוד הנדרש לזיהוי פעילות.

הגדרת המהדר

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

הפעלת פרויקט ההתחלה

נריץ את האפליקציה.

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

a640a291ffaf62ad.png

  • האפליקציה אמורה להופיע בהמשך:

f58d4bb92ee77f41.png

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

סיכום

בשלב הזה למדתם על:

  • הגדרה כללית של codelab.
  • העקרונות הבסיסיים של האפליקציה שלנו.
  • איך לפרוס את האפליקציה.

3. בדיקת הספרייה והוספת הרשאה למניפסט

כדי להשתמש ב-Transition API באפליקציה, צריך להצהיר על תלות ב-API של זיהוי המיקום והפעילות של Google ולציין את ההרשאה com.google.android.gms.permission.ACTIVITY_RECOGNITION במניפסט של האפליקציה.

  1. מחפשים את השורה TODO: Review play services library required for activity recognition בקובץ build.gradle. בשלב הזה (שלב 1) לא נדרשת פעולה כלשהי, רק בודקים את התלות המוצגת שאנחנו דורשים. הוא אמור להיראות כך:
    // TODO: Review play services library required for activity recognition.
    implementation 'com.google.android.gms:play-services-location:19.0.1'
  1. במסגרת המודול base, מחפשים את הטקסט TODO: Add both activity recognition permissions to the manifest בקטע AndroidManifest.xml ומוסיפים את הקוד שבהמשך לרכיב <manifest>.
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

הקוד אמור להיראות כך:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.example.myapp">
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

  ...
</manifest>

כפי שאפשר לראות מהתגובות, צריך להוסיף הרשאה שנייה ל-Android 10. הדבר נדרש להרשאת זמן הריצה שנוספה בגרסה 29 של ה-API.

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

הרצת האפליקציה

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

4. בדיקה או בקשה של הרשאות בסביבת זמן הריצה ב-Android

אנחנו נותנים תמיכה בהרשאות בגרסה 28 ומטה של ה-API, אבל אנחנו צריכים לתמוך בהרשאות בסביבת זמן הריצה בגרסה 29 ומעלה:

  • ב-MainActivity.java, נבדוק אם המשתמש משתמש ב-Android מגרסה 10 (29) ואילך, ואם כן, נבדוק את ההרשאות לזיהוי פעילות.
  • אם ההרשאות לא יאושרו, נשלח את המשתמש למסך הפתיחה (PermissionRationalActivity.java) שבו נסביר למה נדרשת לאפליקציה ההרשאה, ונאפשר לו לאשר אותה.

בדיקת הקוד של גרסת Android

במודול base, מחפשים את TODO: Review check for devices with Android 10 (29+) בקטע MainActivity.java. קטע הקוד הזה אמור להופיע.

הערה: אין פעולה שצריך לבצע בקטע הזה.

// TODO: Review check for devices with Android 10 (29+).
private boolean runningQOrLater =
    android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q;

כפי שצוין קודם, צריך לקבל אישור להרשאת זמן הריצה android.permission.ACTIVITY_RECOGNITION ב-Android מגרסה 10 ואילך. אנחנו משתמשים בבדיקת הפונקציה הפשוטה הזו כדי להחליט אם אנחנו צריכים לבדוק את ההרשאות בזמן הריצה.

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

במודול base, מחפשים את TODO: Review permission check for 29+ ב-MainActivity.java. קטע הקוד הזה אמור להופיע.

הערה: אין פעולה שצריך לבצע בקטע הזה.

// TODO: Review permission check for 29+.
if (runningQOrLater) {

   return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
           this,
           Manifest.permission.ACTIVITY_RECOGNITION
   );
} else {
   return true;
}

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

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

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

במודול base, מחפשים את TODO: Enable/Disable activity tracking and ask for permissions if needed ב-MainActivity.java. מוסיפים את הקוד הבא אחרי התגובה.

// TODO: Enable/Disable activity tracking and ask for permissions if needed.
if (activityRecognitionPermissionApproved()) {

   if (activityTrackingEnabled) {
      disableActivityTransitions();

   } else {
      enableActivityTransitions();
   }

} else {  
   // Request permission and start activity for result. If the permission is approved, we
   // want to make sure we start activity recognition tracking.
   Intent startIntent = new Intent(this, PermissionRationalActivity.class);
   startActivityForResult(startIntent, 0);

}

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

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

בדיקת הקוד של בקשת ההרשאה

במסגרת המודול base, מחפשים את TODO: Review permission request for activity recognition בקטע PermissionRationalActivity.java. קטע הקוד הזה אמור להופיע.

הערה: אין פעולה שצריך לבצע בקטע הזה.

// TODO: Review permission request for activity recognition.
ActivityCompat.requestPermissions(
             this,
             new String[]{Manifest.permission.ACTIVITY_RECOGNITION},
             PERMISSION_REQUEST_ACTIVITY_RECOGNITION)

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

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

אפשר לעיין בקובץ כדי לקבל מידע נוסף.

5. רישום/ביטול רישום של מקלט למעברים בין פעילויות

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

יצירת BroadcastReceiver למעבר

במודול base, מחפשים את TODO: Create a BroadcastReceiver to listen for activity transitions ב-MainActivity.java. מדביקים את קטע הקוד שלמטה.

// TODO: Create a BroadcastReceiver to listen for activity transitions.
// The receiver listens for the PendingIntent above that is triggered by the system when an
// activity transition occurs.
mTransitionsReceiver = new TransitionsReceiver();

רישום BroadcastReceiver למעבר

במודול base, מחפשים את הטקסט TODO: Register a BroadcastReceiver to listen for activity transitions ב-MainActivity.java. (הוא נמצא ב-onStart()). מדביקים את קטע הקוד שלמטה.

// TODO: Register a BroadcastReceiver to listen for activity transitions.
registerReceiver(mTransitionsReceiver, new IntentFilter(TRANSITIONS_RECEIVER_ACTION));

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

ביטול הרישום של BroadcastReceiver

במודול base, מחפשים את האפשרות Unregister activity transition receiver when user leaves the app (ביטול הרישום של מקלט המעבר לפעילות כשהמשתמש יוצא מהאפליקציה) ב-MainActivity.java. (הוא נמצא ב-onStop()).מדביקים את קטע הקוד שלמטה.

// TODO: Unregister activity transition receiver when user leaves the app.
unregisterReceiver(mTransitionsReceiver);

מומלץ לבטל את הרישום של מקלט כשה-Activity מושבת.

6. הגדרת מעברים בין פעילויות ובקשה לעדכונים

כדי להתחיל לקבל עדכונים על מעברים בין פעילויות, צריך להטמיע את הרכיבים הבאים:

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

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

  1. סוג פעילות, שמיוצג על ידי הכיתה DetectedActivity. Transition API תומך בפעילויות הבאות:
  1. סוג המעבר, שמיוצג על ידי הכיתה ActivityTransition. סוגי המעברים הם:

במודול base, מחפשים את TODO: Add activity transitions to track ב-MainActivity.java. מוסיפים את הקוד הבא אחרי התגובה.

// TODO: Add activity transitions to track.
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.WALKING)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.WALKING)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.STILL)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.STILL)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
        .build());

הקוד הזה מוסיף לרשימה שהיתה ריקה קודם את המעברים שאנחנו רוצים לעקוב אחריהם.

יצירת PendingIntent

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

במודול base, מחפשים את TODO: Initialize PendingIntent that will be triggered when a activity transition occurs ב-MainActivity.java. מוסיפים את הקוד הבא אחרי התגובה.

// TODO: Initialize PendingIntent that will be triggered when a activity transition occurs.
Intent intent = new Intent(TRANSITIONS_RECEIVER_ACTION);
mActivityTransitionsPendingIntent =
        PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0);

עכשיו יש לנו PendingIntent שאפשר להפעיל כשאחד מאירועי ActivityTransition מתרחש.

יצירת ActivityTransitionRequest ובקשת עדכונים

כדי ליצור אובייקט ActivityTransitionRequest, מעבירים את רשימת ה-ActivityTransitions לכיתה ActivityTransitionRequest.

במודול base, מחפשים את Create request and listen for activity changes ב-MainActivity.java. מוסיפים את הקוד הבא אחרי התגובה.

// TODO: Create request and listen for activity changes.
ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);

// Register for Transitions Updates.
Task<Void> task =
        ActivityRecognition.getClient(this)
                .requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);


task.addOnSuccessListener(
        new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
                activityTrackingEnabled = true;
                printToScreen("Transitions Api was successfully registered.");

            }
        });
task.addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions Api could NOT be registered: " + e);
                Log.e(TAG, "Transitions Api could NOT be registered: " + e);

            }
        });

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

ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);

בשלב הבא, אנחנו נרשמים לעדכונים של מעברי פעילות על ידי העברת המופע של ActivityTransitionRequest ואובייקט PendingIntent שיצרנו בשלב האחרון ל-method‏ requestActivityTransitionUpdates()‎. השיטה requestActivityTransitionUpdates() מחזירה אובייקט Task שאפשר לבדוק אם הוא הצליח או נכשל, כפי שמוצג בבלוק הבא של הקוד:

// Register for Transitions Updates.
Task<Void> task =
        ActivityRecognition.getClient(this)
                .requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);


task.addOnSuccessListener(
        new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
                activityTrackingEnabled = true;
                printToScreen("Transitions Api was successfully registered.");

            }
        });
task.addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions Api could NOT be registered: " + e);
                Log.e(TAG, "Transitions Api could NOT be registered: " + e);

            }
        });

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

הסרת עדכונים כשהאפליקציה נסגרת

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

במודול base, מחפשים את האפשרות Stop listening for activity changes (הפסקת ההאזנה לשינויים בפעילות) ב-MainActivity.java. מוסיפים את הקוד הבא אחרי התגובה.

// TODO: Stop listening for activity changes.
ActivityRecognition.getClient(this).removeActivityTransitionUpdates(mActivityTransitionsPendingIntent)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                activityTrackingEnabled = false;
                printToScreen("Transitions successfully unregistered.");
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions could not be unregistered: " + e);
                Log.e(TAG,"Transitions could not be unregistered: " + e);
            }
        });

עכשיו צריך לקרוא ל-method שמכיל את הקוד שלמעלה כשהאפליקציה נסגרת

במודול base, מחפשים את TODO: Disable activity transitions when user leaves the app ב-MainActivity.java ב-onPause(). מוסיפים את הקוד הבא אחרי התגובה.

// TODO: Disable activity transitions when user leaves the app.
if (activityTrackingEnabled) {
    disableActivityTransitions();
}

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

7. עיבוד אירועים

כשמעבר הפעילות המבוקש מתרחש, האפליקציה מקבלת קריאה חוזרת (callback) של Intent. אפשר לחלץ מהכוונה אובייקט ActivityTransitionResult שכולל רשימה של אובייקטים מסוג ActivityTransitionEvent. האירועים מסודרים בסדר כרונולוגי. לדוגמה, אם אפליקציה מבקשת את סוג הפעילות IN_VEHICLE במעברים ACTIVITY_TRANSITION_ENTER ו-ACTIVITY_TRANSITION_EXIT, היא מקבלת אובייקט ActivityTransitionEvent כשהמשתמש מתחיל לנהוג, ואובייקט נוסף כשהמשתמש עובר לפעילות אחרת.

נוסיף את הקוד לטיפול באירועים האלה.

במודול base, מחפשים את TODO: Extract activity transition information from listener ב-MainActivity.java ב-onReceive() של BroadcastReceiver שיצרנו מקודם. מוסיפים את הקוד הבא אחרי התגובה.

// TODO: Extract activity transition information from listener.
if (ActivityTransitionResult.hasResult(intent)) {

    ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);

    for (ActivityTransitionEvent event : result.getTransitionEvents()) {

        String info = "Transition: " + toActivityString(event.getActivityType()) +
                " (" + toTransitionType(event.getTransitionType()) + ")" + "   " +
                new SimpleDateFormat("HH:mm:ss", Locale.US).format(new Date());

        printToScreen(info);
    }
}

הפעולה הזו תמיר את המידע ל-String ותדפיס אותו במסך.

זהו, סיימתם! מנסים להפעיל את האפליקציה.

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

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

כדי לקבל את התוצאות הטובות ביותר, מומלץ להתקין את האפליקציה במכשיר פיזי ולצאת לטיול. :)

8. בדיקת הקוד

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

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