Codelab ל-Activity Recognition Transition API

1. מבוא

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

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

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

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

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

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

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

מה תלמדו

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

מה צריך

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

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

משכפלים את מאגר הפרויקט של התבנית

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

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

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

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

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

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

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

d2363db913d8e5ad.png

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

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

c9f23d5336be3cfe.png

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

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

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

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

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

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

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

הגדרת אמולטור

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

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

מריצים את האפליקציה.

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

a640a291ffaf62ad.png

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

f58d4bb92ee77f41.png

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

סיכום

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

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

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

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

  1. מחפשים את המחרוזת TODO: Review play services library required for זיהוי פעילות בקובץ 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 זיהוי פעילות 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. ההרשאה הזו נדרשת להרשאה בתחילת ההפעלה שנוספה בגרסת API‏ 29.

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

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

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

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

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

  • ב-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: הפעל/השבת מעקב אחר פעילות ובקש הרשאות במידת הצורך ב-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.

Unregister 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 שיצרנו בשלב הקודם אל השיטה 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 מה-Intent, שכולל רשימה של אובייקטים מסוג 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. בדיקת הקוד

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

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