הרחבת קיצורי הדרך הדינמיים ל-Google Assistant באמצעות 'פעולות באפליקציה'

1. סקירה כללית

בCodelab הקודם השתמשתם בקיצורי דרך סטטיים כדי להטמיע כוונות מובנות (BII) שנמצאות בשימוש נפוץ באפליקציה לדוגמה. מפתחי Android משתמשים בפעולות באפליקציה כדי להרחיב את תכונות האפליקציה ל-Google Assistant.

קיצורי דרך סטטיים מצורפים לאפליקציה ואפשר לעדכן אותם רק כשמפרסמים גרסאות חדשות של האפליקציה. כדי להפעיל פונקציונליות קולית לרכיבים דינמיים באפליקציה, כמו תוכן שנוצר על ידי משתמשים, צריך להשתמש בקיצורי דרך דינמיים. אפליקציות דוחפות קיצורי דרך דינמיים אחרי שהמשתמשים מבצעים פעולות רלוונטיות, כמו יצירת הערה חדשה באפליקציה למעקב אחרי משימות. באמצעות פעולות באפליקציות, אתם יכולים להפעיל את קיצורי הדרך האלה באמצעות קשירה שלהם ל-BII, וכך לאפשר למשתמשים לגשת לתוכן שלהם מ-Assistant באמצעות פקודות כמו "Ok Google, open my grocery list on ExampleApp" (אוקיי Google, תפתח את רשימת הקניות שלי באפליקציה ExampleApp).

שלושה מסכים מתקדמים שבהם Google Assistant מפעילה קיצור דרך דינמי.

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

מה תפַתחו

ב-codelab הזה תפעילו קיצורי דרך דינמיים לדיבור באפליקציית רשימת מטלות לדוגמה ל-Android, כדי לאפשר למשתמשים לבקש מ-Assistant לפתוח את הפריטים ברשימת המטלות שהם יוצרים באפליקציה. כדי לעשות את זה, תשתמשו בתבניות ארכיטקטורה של Android, ובאופן ספציפי בתבניות repository, ‏ service locator ו-ViewModel.

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

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

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

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

2. איך זה עובד

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

הקצאת מקשי קיצור

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

  • קיצור דרך שמקושר ל-BII‏ GET_THING יכול לאפשר למשתמשים לבקש תוכן ספציפי באפליקציה ישירות מ-Assistant. * ‫"Ok Google, open my grocery list on ExampleApp".
  • קיצור דרך שמשויך ל-START_EXERCISE BII יכול לאפשר למשתמשים לראות את אימוני הכושר שלהם. * "Ok Google, Ask ExampleApp to start my usual exercise."

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

מתן קיצורי דרך ל-Assistant

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

3. הכנת סביבת הפיתוח

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

הורדת קבצי הבסיס

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

git clone https://github.com/actions-on-google/app-actions-dynamic-shortcuts.git

אחרי שיבוט המאגר, פועלים לפי השלבים הבאים כדי לפתוח אותו ב-Android Studio:

  1. בתיבת הדו-שיח Welcome to Android Studio (ברוכים הבאים ל-Android Studio), לוחצים על Import project (ייבוא פרויקט).
  2. בוחרים את התיקייה שבה שיבטתם את המאגר.

אפשר גם לראות גרסה של אפליקציית הדוגמה שמייצגת את ה-Codelab המלא. לשם כך, צריך לשכפל את הענף codelab-complete של מאגר GitHub שלה:

git clone https://github.com/actions-on-google/app-actions-dynamic-shortcuts.git --branch codelab-complete

עדכון מזהה האפליקציה ל-Android

עדכון מזהה האפליקציה של האפליקציה מזהה באופן ייחודי את האפליקציה במכשיר הבדיקה ומונע את השגיאה 'שם חבילה כפול' אם האפליקציה מועלית ל-Play Console. כדי לעדכן את מזהה האפליקציה, פותחים את app/build.gradle:

android {
...
  defaultConfig {
    applicationId "com.MYUNIQUENAME.android.fitactions"
    ...
  }
}

מחליפים את הערך MYUNIQUENAME בשדה applicationId במשהו ייחודי לכם.

הוספת יחסי תלות של Shortcuts API

מוסיפים את ספריות Jetpack הבאות לקובץ המשאבים app/build.gradle:

app/build.gradle

dependencies {
   ...
   // Shortcuts library
   implementation "androidx.core:core:1.6.0"
   implementation 'androidx.core:core-google-shortcuts:1.0.1'
   ...
}

בדיקת האפליקציה במכשיר

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

  1. ב-Android Studio, בוחרים באפשרות Run > Run app (הפעלה > הפעלת האפליקציה) או לוחצים על Run (הפעלה) הפעלת סמל האפליקציה ב-Android Studio בסרגל הכלים.
  2. בתיבת הדו-שיח Select Deployment Target, בוחרים מכשיר ולוחצים על OK. גרסת מערכת ההפעלה המומלצת היא Android 10 (רמת API‏ 30) ומעלה, אבל פעולות באפליקציה פועלות במכשירים עם Android 5 (רמת API‏ 21) ומעלה.
  3. לוחצים לחיצה ארוכה על הכפתור הראשי כדי להגדיר את Assistant ולוודא שהיא פועלת. אם עדיין לא נכנסתם לחשבון שלכם ב-Assistant במכשיר, תצטרכו לעשות זאת.

מידע נוסף על מכשירים וירטואליים של Android זמין במאמר יצירה וניהול של מכשירים וירטואליים.

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

4. יצירת מחלקה של מאגר קיצורי דרך

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

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

  1. יוצרים מחלקה ShortcutsRepository כדי לבצע הפשטה של API ‏ShortcutManagerCompat.
  2. מוסיפים שיטות ShortcutsRepository למאתר השירותים של האפליקציה.
  3. רושמים את השירות ShortcutRepository באפליקציה הראשית.

יצירת המאגר

יוצרים מחלקה חדשה ב-Kotlin בשם ShortcutsRepository בחבילה com.example.android.architecture.blueprints.todoapp.data.source. החבילה הזו מאורגנת בתיקייה app/src/main/java. תשתמשו במחלקה הזו כדי להטמיע ממשק שמספק קבוצה מינימלית של שיטות שמתאימות לתרחיש השימוש בסדנת הקוד שלנו.

חלון Android Studio שבו מוצג המיקום של המחלקה ShortcutsRepository.

איור 2. חלון קובצי הפרויקט של Android Studio שמציג את המיקום של המחלקה ShortcutsRepository.

מדביקים את הקוד הבא בכיתה החדשה:

package com.example.android.architecture.blueprints.todoapp.data.source

import android.content.Context
import android.content.Intent
import androidx.annotation.WorkerThread
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import com.example.android.architecture.blueprints.todoapp.data.Task
import com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity

private const val GET_THING_KEY = "q"

/**
* ShortcutsRepository provides an interface for managing dynamic shortcuts.
*/
class ShortcutsRepository(val context: Context) {

   private val appContext = context.applicationContext

   /**
    * Pushes a dynamic shortcut. The task ID is used as the shortcut ID.
    * The task's title and description are used as shortcut's short and long labels.
    * The resulting shortcut corresponds to the GET_THING capability with task's
    * title used as BII's "name" argument.
    *
    * @param task Task object for which to create a shortcut.
    */
   @WorkerThread
   fun pushShortcut(task: Task) {
      // TODO
   }

   private fun createShortcutCompat(task: Task): ShortcutInfoCompat {
      //...
   }

   /**
    *  Updates a dynamic shortcut for the provided task. If the shortcut
    *  associated with this task doesn't exist, this method throws an error.
    *  This operation may take a few seconds to complete.
    *
    * @param tasks list of tasks to update.
    */
   @WorkerThread
   fun updateShortcuts(tasks: List<Task>) {
       //...
   }

   /**
    * Removes shortcuts if IDs are known.
    *
    * @param ids list of shortcut IDs
    */
   @WorkerThread
   fun removeShortcutsById(ids: List<String>) {
       //...
   }

   /**
    * Removes shortcuts associated with the tasks.
    *
    * @param tasks list of tasks to remove.
    */
   @WorkerThread
   fun removeShortcuts(tasks: List<Task>) {
       //...
   }
}

בשלב הבא, מעדכנים את השיטה pushShortcut כדי לקרוא ל-API ‏ShortcutManagerCompat. מעדכנים את המחלקה ShortcutsRepository באמצעות הקוד הבא:

ShortcutsRepository.kt

/**
* Pushes a dynamic shortcut for the task. The task's ID is used as a shortcut
* ID. The task's title and description are used as shortcut's short and long
* labels. The created shortcut corresponds to GET_THING capability with task's
* title used as BII's "name" argument.
*
* @param task Task object for which to create a shortcut.
*/


@WorkerThread
fun pushShortcut(task: Task) {
   ShortcutManagerCompat.pushDynamicShortcut(appContext, createShortcutCompat(task))
}

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

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

ShortcutsRepository.kt

private fun createShortcutCompat(task: Task): ShortcutInfoCompat {
   val intent = Intent(appContext, TasksActivity::class.java)
   intent.action = Intent.ACTION_VIEW
   // Filtering is set based on currentTitle.
   intent.putExtra(GET_THING_KEY, task.title)

   // A unique ID is required to avoid overwriting an existing shortcut.
   return ShortcutInfoCompat.Builder(appContext, task.id)
           .setShortLabel(task.title)
           .setLongLabel(task.title)
           // Call addCapabilityBinding() to link this shortcut to a BII. Enables user to invoke a shortcut using its title in Assistant.
           .addCapabilityBinding(
                   "actions.intent.GET_THING", "thing.name", listOf(task.title))
           .setIntent(intent)
           .setLongLived(false)
       .build()
}

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

ShortcutsRepository.kt

/**
* Updates a Dynamic Shortcut for the task. If the shortcut associated with this task
* doesn't exist, throws an error. This operation may take a few seconds to complete.
*
* @param tasks list of tasks to update.
*/
@WorkerThread
fun updateShortcuts(tasks: List<Task>) {
   val scs = tasks.map { createShortcutCompat(it) }
   ShortcutManagerCompat.updateShortcuts(appContext, scs)
}

/**
* Removes shortcuts if IDs are known.
* @param ids list of shortcut IDs
*/
@WorkerThread
fun removeShortcutsById(ids: List<String>) {
   ShortcutManagerCompat.removeDynamicShortcuts(appContext, ids)
}

/**
* Removes shortcuts associated with the tasks.
*
* @param tasks list of tasks to remove.
*/
@WorkerThread
fun removeShortcuts(tasks: List<Task>) {
   ShortcutManagerCompat.removeDynamicShortcuts (appContext,
           tasks.map { it.id })
}

הוספת כיתה למאתר השירות

אחרי שיוצרים את המחלקה ShortcutsRepository, השלב הבא הוא להנגיש את האובייקטים של המחלקה הזו לשאר האפליקציה. האפליקציה הזו מנהלת את התלות במחלקה באמצעות הטמעה של התבנית service locator. פותחים את המחלקה של איתור השירותים באמצעות דפדפן המחלקות ב-Android Studio. לשם כך, עוברים אל Navigate (ניווט) > Class (מחלקה) ומקלידים ServiceLocator. לוחצים על קובץ ה-Kotlin שנוצר כדי לפתוח אותו בסביבת הפיתוח המשולבת.

בחלק העליון של ServiceLocator.kt, מדביקים את הקוד הבא כדי לייבא את החבילות ShortcutsRepository ו-SuppressLint:

ServiceLocator.kt

package com.example.android.architecture.blueprints.todoapp

// ...Other import statements
import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository
import android.annotation.SuppressLint

מוסיפים את חברי השירות ואת השיטות של ShortcutRepository על ידי הדבקת הקוד הבא בגוף של ServiceLocator.kt:

ServiceLocator.kt

object ServiceLocator {

   // ...
   // Only the code immediately below this comment needs to be copied and pasted
   // into the body of ServiceLocator.kt:

   @SuppressLint("StaticFieldLeak")
   @Volatile
   var shortcutsRepository: ShortcutsRepository? = null


   private fun createShortcutsRepository(context: Context): ShortcutsRepository {
       val newRepo = ShortcutsRepository(context.applicationContext)
       shortcutsRepository = newRepo
       return newRepo
   }

   fun provideShortcutsRepository(context: Context): ShortcutsRepository {
       synchronized(this) {
           return shortcutsRepository ?: shortcutsRepository ?: createShortcutsRepository(context)
       }
   }
 }

רישום שירות קיצורי הדרך

השלב האחרון הוא לרשום את שירות ShortcutsRepository החדש באפליקציה. ב-Android Studio, פותחים את TodoApplication.kt ומעתיקים את הקוד הבא לחלק העליון של הקובץ:

TodoApplication.kt

package com.example.android.architecture.blueprints.todoapp
/// ... Other import statements

import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository

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

TodoApplication.kt

//...
class TodoApplication : Application() {

   //...

   val shortcutsRepository: ShortcutsRepository
       get() = ServiceLocator.provideShortcutsRepository(this)

   //...
}

מבצעים Build לאפליקציה ומוודאים שהיא ממשיכה לפעול.

5. לחיצה על מקש קיצור חדש

אחרי שיוצרים את שירות קיצורי הדרך, אפשר להתחיל להוסיף קיצורי דרך. מכיוון שהמשתמשים יוצרים תוכן (פריטי משימות) באפליקציה הזו ומצפים שיוכלו לחזור אליו מאוחר יותר, אנחנו נאפשר גישה לתוכן הזה באמצעות קול על ידי שליחת קיצור דרך דינמי שמשויך ל-BII‏ GET_THING בכל פעם שמשתמש יוצר משימה חדשה. כך Assistant יכולה להפנות משתמשים ישירות לפריט המשימה שהם מבקשים כשהם מפעילים את ה-BII בשאלות כמו Ok Google, open my grocery list on SampleApp.

כדי להפעיל את הפונקציונליות הזו באפליקציה לדוגמה:

  1. ייבוא השירות ShortcutsRepository לכיתה AddEditTaskViewModel, שאחראית לניהול אובייקטים של רשימת המשימות.
  2. שליחת קיצור דרך דינמי כשהמשתמש יוצר משימה חדשה.

ייבוא של ShortcutsRepository

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

פותחים את סייר הכיתות ב-Android Studio דרך Navigate (ניווט) > Class (כיתה) ומקלידים ViewModelFactory. לוחצים על קובץ ה-Kotlin שנוצר כדי לפתוח אותו בסביבת הפיתוח המשולבת.

בחלק העליון של ViewModelFactory.kt, מדביקים את הקוד הבא כדי לייבא את החבילות ShortcutsRepository ו-SuppressLint:

ViewModelFactory.kt

package com.example.android.architecture.blueprints.todoapp

// ...Other import statements
import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository

לאחר מכן, מחליפים את התוכן של התג ViewModelFactory בקוד הבא:

ViewModelFactory.kt

/**
 * Factory for all ViewModels.
 */
@Suppress("UNCHECKED_CAST")
class ViewModelFactory constructor(
    private val tasksRepository: TasksRepository,
    private val shortcutsRepository: ShortcutsRepository,
    owner: SavedStateRegistryOwner,
    defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {

    override fun <T : ViewModel> create(
        key: String,
        modelClass: Class<T>,
        handle: SavedStateHandle
    ) = with(modelClass) {
        when {
            isAssignableFrom(StatisticsViewModel::class.java) ->
                StatisticsViewModel(tasksRepository)
            isAssignableFrom(TaskDetailViewModel::class.java) ->
                TaskDetailViewModel(tasksRepository)
            isAssignableFrom(AddEditTaskViewModel::class.java) ->
                AddEditTaskViewModel(tasksRepository, shortcutsRepository)
            isAssignableFrom(TasksViewModel::class.java) ->
                TasksViewModel(tasksRepository, handle)
            else ->
                throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
        }
    } as T
}

כדי לסיים את ViewModelFactory השינויים, עולים רמה אחת ומעבירים את ShortcutsRepository לקונסטרוקטור של המפעל. פותחים את מנהל הקבצים והתיקיות של Android Studio דרך Navigate (ניווט) > File (קובץ) ומקלידים FragmentExt.kt. לוחצים על קובץ ה-Kotlin שנוצר שנמצא בחבילה util כדי לפתוח אותו בסביבת הפיתוח המשולבת.

מחליפים את גוף הקובץ FragmentExt.kt בקוד הבא:

fun Fragment.getViewModelFactory(): ViewModelFactory {
   val taskRepository = (requireContext().applicationContext as TodoApplication).taskRepository
   val shortcutsRepository = (requireContext().applicationContext as TodoApplication).shortcutsRepository
   return ViewModelFactory(taskRepository, shortcutsRepository, this)
}

הפעלת קיצור דרך

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

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

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

package com.example.android.architecture.blueprints.todoapp.addedittask

//Other import statements
import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository

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

AddEditTaskViewModel.kt

//...

/**
* ViewModel for the Add/Edit screen.
*/
class AddEditTaskViewModel(
   private val tasksRepository: TasksRepository,
   private val shortcutsRepository: ShortcutsRepository
) : ViewModel() {

    //...

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

AddEditTaskViewModel.kt

//...
private fun pushShortcut(newTask: Task) = viewModelScope.launch {
   shortcutsRepository.pushShortcut(newTask)
}

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

AddEditTaskViewModel.kt

fun saveTask() {
    val currentTitle = title.value
    val currentDescription = description.value

    if (currentTitle == null || currentDescription == null) {
        _snackbarText.value = Event(R.string.empty_task_message)
        return
    }
    if (Task(currentTitle, currentDescription).isEmpty) {
        _snackbarText.value = Event(R.string.empty_task_message)
        return
    }

    val currentTaskId = taskId
    if (isNewTask || currentTaskId == null) {
        val task = Task(currentTitle, currentDescription)
        createTask(task)
        pushShortcut(task)
    } else {
        val task = Task(currentTitle, currentDescription, taskCompleted, currentTaskId)
        updateTask(task)
    }
}

בדיקת הקוד

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

יצירת קטע מקדים

יצירת תצוגה מקדימה באמצעות התוסף של Google Assistant מאפשרת לקיצורי הדרך הדינמיים להופיע ב-Assistant במכשיר הבדיקה.

התקנת פלאגין הבדיקה

אם עדיין לא התקנתם את הפלאגין של Google Assistant, אתם יכולים להתקין אותו באמצעות השלבים הבאים ב-Android Studio:

  1. עוברים אל **File > Settings (קובץ > הגדרות) (Android Studio > Preferences (העדפות) ב-MacOS).
  2. בקטע Plugins (פלאגינים), עוברים אל Marketplace (חנות) ומחפשים את Google Assistant.
  3. מתקינים את הכלי ומפעילים מחדש את Android Studio.

יצירת התצוגה המקדימה

כדי ליצור תצוגה מקדימה, פועלים לפי השלבים הבאים ב-Android Studio:

  1. לוחצים על כלים > Google Assistant > כלי לבדיקת פעולות באפליקציה.
  2. בתיבה שם האפליקציה, מגדירים שם כמו "רשימת מטלות".
  3. לוחצים על יצירת תצוגה מקדימה. אם תופיע בקשה, תצטרכו לקרוא את התנאים וההגבלות ואת מדיניות 'פעולות באפליקציה' ולאשר אותם.

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

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

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

הצגה ובדיקה של קיצור דרך

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

  1. יצירת משימה חדשה עם הכותרת 'התחלת Codelab'
  2. פותחים את אפליקציית Google Assistant ואומרים או מקלידים: "הקיצורים שלי".
  3. מקישים על הכרטיסייה מה חדש. קיצור הדרך לדוגמה אמור להופיע.
  4. מקישים על קיצור הדרך כדי להפעיל אותו. האפליקציה תיפתח עם שם קיצור הדרך שמופיע מראש בתיבת הסינון, כך שיהיה קל למצוא את פריט המשימה המבוקש.

6. (אופציונלי) עדכון ומחיקה של קיצור דרך

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

עדכון קיצור דרך

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

AddEditTaskViewModel.kt

private fun updateShortcut(newTask: Task) = viewModelScope.launch {
   shortcutsRepository.updateShortcuts(listOf(newTask))
}

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

AddEditTaskViewModel.kt

// Called when clicking on fab.
fun saveTask() {
   // ...
   // Note: the shortcuts are created/updated in a worker thread.
   if (isNewTask || currentTaskId == null) {
       //...
   } else {
       //...
       updateShortcut(task)
   }
}

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

  1. משנים את השם של פריט המשימה הקיים ל'סיום סדנת ה-codelab'.
  2. אומרים "Ok Google, my shortcuts" (הקיצורים שלי) כדי לפתוח את Google Assistant.
  3. מקישים על הכרטיסייה מה חדש. אמורה להופיע תווית מעודכנת וקצרה לקיצור הדרך של הבדיקה.

הסרת קיצור דרך

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

פותחים את ViewModelFactory ומחליפים את התוכן של שיטת הבנאי בקוד הבא:

//...
class ViewModelFactory constructor(
       private val tasksRepository: TasksRepository,
       private val shortcutsRepository: ShortcutsRepository,
       owner: SavedStateRegistryOwner,
       defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {

   override fun <T : ViewModel> create(
           key: String,
           modelClass: Class<T>,
           handle: SavedStateHandle
   ) = with(modelClass) {
       when {
           isAssignableFrom(StatisticsViewModel::class.java) ->
               StatisticsViewModel(tasksRepository)
           isAssignableFrom(TaskDetailViewModel::class.java) ->
               TaskDetailViewModel(tasksRepository, shortcutsRepository)
           isAssignableFrom(AddEditTaskViewModel::class.java) ->
               AddEditTaskViewModel(tasksRepository, shortcutsRepository)
           isAssignableFrom(TasksViewModel::class.java) ->
               TasksViewModel(tasksRepository, handle)
           else ->
               throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
       }
   } as T
}

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

TaskDetailViewModel.kt

package com.example.android.architecture.blueprints.todoapp.taskdetail

...
import com.example.android.architecture.blueprints.todoapp.data.source.ShortcutsRepository


/**
* ViewModel for the Details screen.
*/
class TaskDetailViewModel(
       //...
       private val shortcutsRepository: ShortcutsRepository
   ) : ViewModel() {
...
}

לבסוף, משנים את הפונקציה deleteTask() כדי לקרוא לפונקציה shortcutsRepository להסרת קיצור דרך על סמך המזהה שלו בכל פעם שמשימה עם taskId תואם נמחקת:

TaskDetailViewModel.kt

fun deleteTask() = viewModelScope.launch {
   _taskId.value?.let {
       //...
       shortcutsRepository.removeShortcutsById(listOf(it))
   }
}

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

  1. מוחקים את משימת הבדיקה.
  2. משנים את השם של פריט המשימה הקיים ל'סיום סדנת ה-codelab'.
  3. אומרים "Ok Google, my shortcuts" (הקיצורים שלי) כדי לפתוח את Google Assistant.
  4. מקישים על הכרטיסייה מה חדש. מוודאים שקיצור הדרך לבדיקה לא מופיע יותר.

7. השלבים הבאים

מזל טוב! הודות לך, משתמשים באפליקציה לדוגמה שלנו יכולים לחזור בקלות להערות שהם יוצרים על ידי בקשה מ-Assistant, למשל "Ok Google, open my grocery list on ExampleApp" (אוקיי Google, פתח את רשימת הקניות שלי באפליקציה לדוגמה). קיצורי דרך מעודדים מעורבות עמוקה יותר של המשתמשים, כי הם מאפשרים להם להפעיל מחדש בקלות פעולות שהם משתמשים בהן לעיתים קרובות באפליקציה.

מה נכלל

ב-Codelab הזה למדתם איך:

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

השלבים הבאים

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

הנה כמה הצעות למידע נוסף על הרחבת האפליקציה באמצעות פעולות באפליקציה:

כדי להמשיך את המסע שלכם עם Actions on Google, כדאי לעיין במקורות המידע הבאים:

כדי להתעדכן בהודעות האחרונות שלנו, אתם יכולים לעקוב אחרינו בטוויטר ‎@ActionsOnGoogle. כדי לשתף את מה שיצרתם, אתם יכולים לצייץ עם ההאשטאג #appActions.

סקר משוב

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