توسيع نطاق الاختصارات الديناميكية إلى "مساعد Google" باستخدام ميزة "مهامّ في التطبيقات"

1. نظرة عامة

في درس تطبيقي حول الترميز السابق، استخدمت اختصارات ثابتة لتنفيذ الأهداف المضمّنة (BII) الشائعة الاستخدام في نموذج تطبيق. يستخدم مطوّرو تطبيقات Android "مهامّ في التطبيقات" لتوسيع وظائف التطبيقات لتشمل "مساعد Google".

يتم تجميع الاختصارات الثابتة مع التطبيق ولا يمكن تعديلها إلا من خلال إصدار إصدارات جديدة من التطبيق. ويتم تفعيل وظيفة الصوت للعناصر الديناميكية في التطبيق، مثل المحتوى من إنشاء المستخدمين، باستخدام الاختصارات الديناميكية. تُرسِل التطبيقات اختصارات ديناميكية بعد أن ينفّذ المستخدمون إجراءات ذات صلة، مثل إنشاء ملاحظة جديدة في تطبيق لتتبُّع المهام. باستخدام "مهامّ في التطبيقات"، يمكنك تفعيل هذه الاختصارات للوصول إليها صوتيًا من خلال ربطها بواجهة BII، ما يتيح للمستخدمين الوصول إلى المحتوى من "مساعد Google" من خلال قول عبارات مثل "Ok Google، افتح قائمة التسوّق الخاصة بي على ExampleApp"۔

ثلاث شاشات متتالية تعرض "مساعد Google" وهو يطلق اختصارًا ديناميكيًا.

الشكل 1: ثلاث شاشات متتالية تعرض مهمة أنشأها المستخدم، و"مساعد Google" يطلق اختصارًا ديناميكيًا إلى عنصر المهمة هذا.

ما ستنشئه

في هذا الدرس التطبيقي حول الترميز، ستفعّل اختصارات ديناميكية للصوت في نموذج تطبيق Android لقائمة مهام، ما يتيح للمستخدمين الطلب من "مساعد Google" فتح عناصر قائمة المهام التي ينشئونها في التطبيق. ويمكنك تحقيق ذلك باستخدام أنماط بنية Android، وتحديدًا أنماط المستودع ومحدد موقع الخدمة وViewModel.

المتطلبات الأساسية

يستند هذا الدرس التطبيقي حول الترميز إلى مفاهيم "مهامّ في التطبيقات" التي تم تناولها في الدرس التطبيقي السابق حول الترميز، وخاصةً واجهات BII والاختصارات الثابتة. إذا كنت جديدًا على مهامّ في التطبيقات، ننصحك بإكمال هذا الدرس العملي قبل المتابعة.

بالإضافة إلى ذلك، تأكَّد من أنّ بيئة التطوير تتضمّن الإعدادات التالية قبل المتابعة:

  • وحدة طرفية لتنفيذ أوامر shell مع تثبيت git
  • أحدث إصدار ثابت من استوديو Android
  • جهاز Android فعلي أو افتراضي متصل بالإنترنت
  • حساب Google تم تسجيل الدخول عبره إلى استوديو Android وتطبيق Google وتطبيق "مساعد Google"

2. التعرّف على طريقة العمل

لتفعيل اختصار ديناميكي لتطبيق "الوصول الصوتي"، اتّبِع الخطوات التالية:

  • ربط اختصار ديناميكي بواجهة BII مؤهّلة
  • تفعيل إمكانية استيعاب "مساعد Google" للاختصارات من خلال إضافة مكتبة دمج اختصارات Google
  • إرسال اختصار كلما أكمل المستخدم المهمة ذات الصلة داخل التطبيق

ربط الاختصارات

لكي يكون الاختصار الديناميكي متاحًا من "مساعد Google"، يجب ربطه بواجهة BII ذات الصلة. عندما يتم تفعيل BII باستخدام اختصار، يطابق "مساعد Google" المَعلمات في طلب المستخدم مع الكلمات الرئيسية المحدّدة في الاختصار المرتبط. على سبيل المثال:

  • يمكن أن يسمح اختصار مرتبط بـ GET_THING BII للمستخدمين بطلب محتوى معيّن داخل التطبيق مباشرةً من "مساعد Google". * "Ok Google، افتح قائمة البقالة على ExampleApp".
  • يمكن أن يسمح اختصار مرتبط بـ START_EXERCISE BII للمستخدمين بالاطّلاع على جلسات التمارين الرياضية. * "Ok Google، اطلب من ExampleApp بدء التمرين المعتاد".

راجِع مرجع النوايا المضمّنة للحصول على قائمة كاملة ومصنّفة حسب الفئات بالنوايا المضمّنة.

توفير اختصارات لـ "مساعد Google"

بعد ربط اختصاراتك بـ BII، تتمثّل الخطوة التالية في السماح لمساعد Google بتضمين هذه الاختصارات من خلال إضافة مكتبة Google Shortcuts Integration إلى مشروعك. بعد إعداد هذه المكتبة، سيتعرّف "مساعد Google" على كل اختصار يوفّره تطبيقك، ما يتيح للمستخدمين تشغيل هذه الاختصارات باستخدام عبارة التشغيل الخاصة بالاختصار في "مساعد Google".

3- إعداد بيئة التطوير

يستخدم هذا الدرس التطبيقي حول الترميز نموذجًا لتطبيق قائمة مهام تم إنشاؤه لنظام التشغيل Android. باستخدام هذا التطبيق، يمكن للمستخدمين إضافة عناصر إلى القوائم والبحث عن عناصر قائمة المهام حسب الفئة وتصفية المهام حسب حالة الإكمال. نزِّل نموذج التطبيق وأعِده من خلال إكمال هذا القسم.

تنزيل ملفاتك الأساسية

نفِّذ الأمر التالي لاستنساخ مستودع GitHub الخاص بالتطبيق النموذجي:

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

بعد استنساخ المستودع، اتّبِع الخطوات التالية لفتحه في استوديو Android:

  1. في مربّع الحوار مرحبًا بك في "استوديو Android"، انقر على استيراد مشروع.
  2. اختَر المجلد الذي استنسخت المستودع فيه.

بدلاً من ذلك، يمكنك عرض إصدار من نموذج التطبيق يمثّل الدرس التطبيقي حول الترميز المكتمل عن طريق استنساخ الفرع 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 باسم فريد خاص بك.

إضافة تبعيات واجهة برمجة التطبيقات Add Shortcuts

أضِف مكتبات 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"، اختَر "تشغيل" > "تشغيل التطبيق" أو انقر على تشغيلتشغيل رمز التطبيق في "استوديو Android" في شريط الأدوات.
  2. في مربّع الحوار اختيار هدف النشر، اختَر جهازًا وانقر على حسنًا. إصدار نظام التشغيل Android 10 (المستوى 30 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث هو الإصدار الموصى به، على الرغم من أنّ "مهامّ في التطبيقات" تعمل على الأجهزة التي تعمل بالإصدار Android 5 (المستوى 21 لواجهة برمجة التطبيقات).
  3. اضغط مع الاستمرار على زر "الشاشة الرئيسية" لإعداد "مساعد Google" والتأكّد من عمله. عليك تسجيل الدخول إلى "مساعد Google" على جهازك إذا لم يسبق لك ذلك.

لمزيد من المعلومات حول الأجهزة الافتراضية التي تعمل بنظام التشغيل Android، يُرجى الاطّلاع على إنشاء الأجهزة الافتراضية وإدارتها.

استكشِف التطبيق سريعًا لمعرفة ما يمكنه فعله. يؤدي النقر على رمز علامة الجمع إلى إنشاء عنصر مهمة جديد، وتتيح لك عناصر القائمة في أعلى يسار الشاشة البحث عن عناصر المهام وفلترتها حسب حالة الإكمال.

4. إنشاء فئة مستودع اختصارات

ستطلب عدة فئات في تطبيقنا النموذجي استخدام واجهة برمجة التطبيقات ShortcutManagerCompat لإرسال الاختصارات الديناميكية وإدارتها. للحدّ من تكرار الرموز البرمجية، عليك تنفيذ مستودع لتتمكّن فئات مشروعك من إدارة الاختصارات الديناميكية بسهولة.

يوفر نمط تصميم المستودع واجهة برمجة تطبيقات واضحة لإدارة الاختصارات. تتمثّل ميزة المستودع في أنّ تفاصيل واجهة برمجة التطبيقات الأساسية يتم تجريدها بشكل موحّد خلف واجهة برمجة تطبيقات بسيطة. نفِّذ المستودع باتّباع الخطوات التالية:

  1. أنشئ فئة ShortcutsRepository لتجريد واجهة برمجة التطبيقات ShortcutManagerCompat.
  2. أضِف طرق ShortcutsRepository إلى محدد الخدمة في التطبيق.
  3. سجِّل خدمة ShortcutRepository في التطبيق الرئيسي.

إنشاء المستودع

أنشِئ فئة Kotlin جديدة باسم ShortcutsRepository في الحزمة com.example.android.architecture.blueprints.todoapp.data.source. يمكنك العثور على هذه الحزمة منظَّمة في المجلد app/src/main/java. ستستخدم هذه الفئة لتنفيذ واجهة توفّر مجموعة الحد الأدنى من الطرق التي تغطي حالة الاستخدام في الدرس التطبيقي حول الترميز.

نافذة "استوديو Android" تعرض موقع فئة ShortcutsRepository.

الشكل 2: نافذة "ملفات المشروع" في "استوديو Android" تعرض موقع الفئة 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 لاستدعاء واجهة برمجة التطبيقات 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 إلى واجهة برمجة التطبيقات. هذه سمة فئة تحتوي على سياق التطبيق. من المهم استخدام سياق التطبيق (بدلاً من سياق النشاط) لتجنُّب تسرُّب الذاكرة، لأنّه قد يتم الاحتفاظ بالسياق لفترة أطول من مدة نشاط المضيف.

بالإضافة إلى ذلك، تتطلّب واجهة برمجة التطبيقات أن نمرّر عنصر ShortcutInfoCompat لعنصر المهمة. في عينة التعليمات البرمجية السابقة، نحقّق ذلك من خلال استدعاء الطريقة الخاصة createShortcutCompat، والتي سنعدّلها لإنشاء عنصر ShortcutInfoCompat وعرضه. لإجراء ذلك، عدِّل عنصر نائب 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، تتمثّل الخطوة التالية في إتاحة العناصر التي تم إنشاء مثيل لها من هذه الفئة لبقية التطبيق. يدير هذا التطبيق تبعيات الفئات من خلال تنفيذ نمط محدد موقع الخدمة. افتح فئة أداة تحديد موقع الخدمة باستخدام متصفّح الفئات في "استوديو Android" من خلال الانتقال إلى تنقّل > الفئة وكتابة "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، افتح 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)

   //...
}

أنشئ التطبيق وتأكَّد من استمرار تشغيله.

5- إضافة اختصار جديد

بعد إنشاء خدمة الاختصارات، يمكنك البدء في إرسال الاختصارات. بما أنّ المستخدمين ينشئون محتوًى (عناصر المهام) في هذا التطبيق ويتوقّعون أن يتمكّنوا من الرجوع إليه لاحقًا، سنوفّر إمكانية الوصول إلى هذا المحتوى من خلال الصوت عن طريق إرسال اختصار ديناميكي مرتبط بواجهة برمجة التطبيقات المضمّنة GET_THING في كل مرة ينشئ فيها المستخدم مهمة جديدة. يتيح ذلك للمساعد توجيه المستخدمين مباشرةً إلى عنصر المهمة المطلوب عند تفعيل ميزة "الميزات المضمّنة في التطبيق" من خلال طرح طلبات مثل "Ok Google، افتح قائمة التسوّق الخاصة بي على SampleApp".

يمكنك تفعيل هذه الوظيفة في نموذج التطبيق من خلال إكمال الخطوات التالية:

  1. استيراد خدمة ShortcutsRepository إلى فئة AddEditTaskViewModel المسؤولة عن إدارة عناصر قائمة المهام
  2. إرسال اختصار ديناميكي عندما ينشئ المستخدم مهمة جديدة

Import ShortcutsRepository

علينا أولاً إتاحة خدمة ShortcutsRepository لـ AddEditTaskViewModel. لتحقيق ذلك، استورِد الخدمة إلى ViewModelFactory، وهي فئة المصنع التي يستخدمها التطبيق لإنشاء عناصر ViewModel، بما في ذلك AddEditTaskViewModel.

افتح متصفّح الفئات في "استوديو Android" من خلال الانتقال إلى تنقّل > الفئة وكتابة "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" من خلال الانتقال إلى تنقّل (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"، افتح متصفّح الفئات واكتب "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".

إنشاء معاينة

يتيح لك إنشاء معاينة باستخدام مكوّن "مساعد Google" الإضافي عرض اختصاراتك الديناميكية في "مساعد Google" على جهاز الاختبار.

تثبيت المكوّن الإضافي التجريبي

إذا لم يكن لديك مكوّن "مساعد Google" الإضافي، ثبِّته باتّباع الخطوات التالية في "استوديو Android":

  1. انتقِل إلى **ملف > الإعدادات (استوديو Android > الإعدادات المفضّلة على نظام التشغيل MacOS).
  2. في قسم المكوّنات الإضافية (Plugins)، انتقِل إلى Marketplace وابحث عن "مساعد Google".
  3. ثبِّت الأداة وأعِد تشغيل "استوديو Android".

إنشاء المعاينة

لإنشاء معاينة، اتّبِع الخطوات التالية في استوديو Android:

  1. انقر على الأدوات > مساعد Google > أداة اختبار "مهامّ في التطبيقات".
  2. في مربّع اسم التطبيق، حدِّد اسمًا مثل "قائمة المهام".
  3. انقر على إنشاء معاينة. راجِع سياسات وبنود خدمة "مهامّ في التطبيقات" واقبلها إذا طُلب منك ذلك.

جزء إنشاء المعاينة في &quot;أداة اختبار مهامّ في التطبيقات&quot;

الشكل 3: لوحة إنشاء معاينة "أداة اختبار مهامّ في التطبيقات"

أثناء الاختبار، ستظهر الاختصارات الديناميكية التي ترسلها إلى "مساعد Google" في "مساعد Google" منظَّمة حسب اسم التطبيق الذي قدّمته للمعاينة.

الضغط على اختصار وفحصه

أعِد تشغيل نموذج التطبيق على جهازك الاختباري ونفِّذ الخطوات التالية :

  1. أنشئ مهمة جديدة بعنوان "بدء برنامج codelab"
  2. افتح تطبيق "مساعد Google" وقُل أو اكتب "اختصاراتي".
  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. أعِد تسمية عنوان عنصر المهمة الحالي إلى "إنهاء الدرس العملي".
  2. افتح "مساعد Google" من خلال قول "Ok Google، اختصاراتي".
  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. أعِد تسمية عنوان عنصر المهمة الحالي إلى "إنهاء الدرس العملي".
  3. افتح "مساعد Google" من خلال قول "Ok Google، اختصاراتي".
  4. انقر على علامة التبويب استكشاف. تأكَّد من أنّ الاختصار التجريبي لم يعُد يظهر.

7. الخطوات التالية

تهانينا! بفضل هذه الميزة، يمكن لمستخدمي تطبيقنا النموذجي العودة بسهولة إلى الملاحظات التي ينشئونها من خلال توجيه طلبات إلى "مساعد Google"، مثل "Ok Google، افتح قائمة التسوّق الخاصة بي على ExampleApp". تشجّع الاختصارات المستخدمين على التفاعل بشكل أكبر من خلال تسهيل إعادة تشغيل الإجراءات المستخدَمة بشكل متكرّر في تطبيقك.

المواضيع التي تناولناها

في هذا الدرس العملي، تعلّمت كيفية:

  • تحديد حالات الاستخدام لإرسال اختصارات ديناميكية في تطبيق
  • تقليل تعقيد الرمز البرمجي باستخدام أنماط تصميم مستودع البيانات وإدخال التبعية ومحدد موقع الخدمة
  • إرسال اختصارات ديناميكية متوافقة مع الصوت إلى محتوى التطبيق من إنشاء المستخدمين
  • تعديل الاختصارات الحالية وإزالتها

الخطوات التالية

من هنا، يمكنك محاولة إجراء المزيد من التحسينات على تطبيق "قائمة المهام". وللاطّلاع على المشروع المكتمل، يمكنك الرجوع إلى مستودع فرع codelab-complete على GitHub.

في ما يلي بعض الاقتراحات لمزيد من المعلومات حول توسيع نطاق هذا التطبيق باستخدام "مهام في التطبيقات":

لمواصلة رحلتك في "إجراءات Google"، يمكنك الاطّلاع على المراجع التالية:

تابِعنا على Twitter ‎@ActionsOnGoogle لتبقى على اطّلاع على آخر إعلاناتنا، وغرِّد باستخدام ‎#appActions لمشاركة ما أنشأته.

استطلاع لجمع الملاحظات

أخيرًا، يُرجى ملء هذا الاستطلاع لتقديم ملاحظات حول تجربتك مع هذا الدرس العملي.