Mở rộng lối tắt động đến Trợ lý Google bằng Hành động trong ứng dụng

1. Tổng quan

Trong lớp học lập trình trước, bạn đã dùng lối tắt tĩnh để triển khai các ý định tích hợp (BII) thường dùng trong một ứng dụng mẫu. Nhà phát triển Android sử dụng Hành động trong ứng dụng để mở rộng chức năng của ứng dụng sang Trợ lý Google.

Lối tắt tĩnh đi kèm với một ứng dụng và bạn chỉ có thể cập nhật bằng cách phát hành các phiên bản mới của ứng dụng. Bạn có thể bật chức năng thoại cho các phần tử động trong ứng dụng, chẳng hạn như nội dung do người dùng tạo, bằng cách sử dụng phím tắt động. Ứng dụng sẽ đẩy các lối tắt động sau khi người dùng thực hiện các thao tác liên quan, chẳng hạn như tạo một ghi chú mới trong ứng dụng theo dõi công việc. Với Hành động trong ứng dụng, bạn có thể bật các lối tắt này cho giọng nói bằng cách liên kết chúng với một BII, cho phép người dùng truy cập vào nội dung của họ thông qua Trợ lý bằng cách nói những câu như "Ok Google, mở danh sách hàng tạp hóa của tôi trên ExampleApp".

Ba màn hình tiến bộ cho thấy Trợ lý Google đang chạy một lối tắt động.

Hình 1. Ba màn hình tiến bộ cho thấy một việc cần làm do người dùng tạo và Trợ lý Google đang chạy một lối tắt động đến mục việc cần làm đó.

Sản phẩm bạn sẽ tạo ra

Trong lớp học lập trình này, bạn sẽ bật lối tắt động cho giọng nói trong một ứng dụng Android mẫu về danh sách việc cần làm. Nhờ đó, người dùng có thể yêu cầu Trợ lý mở các mục trong danh sách việc cần làm mà họ tạo trong ứng dụng. Bạn có thể thực hiện việc này bằng cách sử dụng các mẫu cấu trúc Android, cụ thể là kho lưu trữ, công cụ định vị dịch vụ và mẫu ViewModel.

Điều kiện tiên quyết

Lớp học lập trình này dựa trên các khái niệm về Hành động trong ứng dụng đã đề cập trong lớp học lập trình trước, đặc biệt là BII và lối tắt tĩnh. Nếu mới sử dụng Hành động trong ứng dụng, bạn nên hoàn tất lớp học lập trình đó trước khi tiếp tục.

Ngoài ra, hãy đảm bảo rằng môi trường phát triển của bạn có cấu hình sau đây trước khi tiếp tục:

  • Một thiết bị đầu cuối để chạy các lệnh shell khi cài đặt git.
  • Bản phát hành ổn định mới nhất của Android Studio.
  • Một thiết bị Android thực hoặc ảo có khả năng truy cập Internet.
  • Một Tài khoản Google đã đăng nhập vào Android Studio, ứng dụng Google và ứng dụng Trợ lý Google.

2. Tìm hiểu cách hoạt động

Việc bật lối tắt động để truy cập bằng giọng nói bao gồm các bước sau:

  • Liên kết lối tắt động với một BII đủ điều kiện.
  • Cho phép Trợ lý nhập lối tắt bằng cách thêm thư viện Tích hợp lối tắt của Google.
  • Đẩy lối tắt bất cứ khi nào người dùng hoàn thành nhiệm vụ có liên quan trong ứng dụng.

Lối tắt liên kết

Để có thể truy cập lối tắt động qua Trợ lý, lối tắt đó cần phải được liên kết với một BII phù hợp. Khi một BII có lối tắt được kích hoạt, Trợ lý sẽ so khớp các tham số trong yêu cầu của người dùng với những từ khoá được xác định trong lối tắt được liên kết. Ví dụ:

  • Một lối tắt liên kết với BII GET_THING có thể cho phép người dùng yêu cầu nội dung cụ thể trong ứng dụng, trực tiếp từ Trợ lý. * "Ok Google, mở danh sách hàng tạp hoá của tôi trên ExampleApp."
  • Một lối tắt liên kết với BII ORDER_MENU_ITEM có thể cho phép người dùng phát lại các đơn đặt hàng trước đó. * "Ok Google, đặt hàng như tôi thường thấy trên ExampleApp."

Hãy xem Tài liệu tham khảo về ý định tích hợp để biết danh sách phân loại đầy đủ về các BII.

Cung cấp lối tắt cho Trợ lý

Sau khi liên kết lối tắt với một BII, bước tiếp theo là cho phép Trợ lý nhập các lối tắt này bằng cách thêm thư viện Tích hợp lối tắt của Google vào dự án của bạn. Với thư viện này, Trợ lý sẽ nhận biết được từng lối tắt mà ứng dụng của bạn đẩy ra, nhờ đó, người dùng có thể chạy các lối tắt đó bằng cách sử dụng cụm từ kích hoạt của lối tắt đó trong Trợ lý.

3. Chuẩn bị môi trường phát triển

Lớp học lập trình này sử dụng ứng dụng mẫu về danh sách việc cần làm được tạo cho Android. Với ứng dụng này, người dùng có thể thêm mục vào danh sách, tìm kiếm các mục trong danh sách công việc theo danh mục và lọc công việc theo trạng thái hoàn thành. Tải xuống và chuẩn bị ứng dụng mẫu bằng cách hoàn tất phần này.

Tải tệp cơ sở xuống

Chạy lệnh sau để sao chép kho lưu trữ GitHub của ứng dụng mẫu:

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

Sau khi bạn sao chép kho lưu trữ, hãy làm theo các bước sau để mở kho lưu trữ đó trong Android Studio:

  1. Trong hộp thoại Welcome to Android Studio (Chào mừng bạn đến với Android Studio), hãy nhấp vào Import project (Nhập dự án).
  2. Chọn thư mục mà bạn đã sao chép kho lưu trữ.

Ngoài ra, bạn có thể xem phiên bản của ứng dụng mẫu đại diện cho lớp học lập trình đã hoàn thành bằng cách sao chép nhánh codelab-complete của kho lưu trữ GitHub:

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

Cập nhật mã ứng dụng Android

Việc cập nhật mã ứng dụng của ứng dụng sẽ giúp nhận dạng duy nhất ứng dụng trên thiết bị thử nghiệm và tránh việc "Tên gói trùng lặp" nếu ứng dụng được tải lên Play Console. Để cập nhật mã ứng dụng, hãy mở app/build.gradle:

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

Thay thế "MYUNIQUENAME" trong trường applicationId thành một giá trị độc đáo của riêng bạn.

Thêm phần phụ thuộc Shortcuts API (API Phím tắt)

Thêm các thư viện Jetpack sau vào tệp tài nguyên 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'
   ...
}

Kiểm thử ứng dụng trên thiết bị

Trước khi thực hiện thêm thay đổi nào đối với ứng dụng, bạn nên nắm được những việc mà ứng dụng mẫu có thể làm. Để chạy ứng dụng trên trình mô phỏng, hãy làm theo các bước sau:

  1. Trong Android Studio, chọn Run (Chạy) > Chạy ứng dụng hoặc nhấp vào biểu tượng ChạyChạy biểu tượng ứng dụng trong Android Studio trên thanh công cụ.
  2. Trong hộp thoại Select Deployment Target (Chọn đối tượng triển khai), hãy chọn một thiết bị rồi nhấp vào OK. Phiên bản hệ điều hành được đề xuất là Android 10 (API cấp 30) trở lên, mặc dù Hành động trong ứng dụng hoạt động trên các thiết bị từ Android 5 (API cấp 21) trở lên.
  3. Nhấn và giữ nút Màn hình chính để thiết lập Trợ lý và xác nhận rằng Trợ lý đang hoạt động. Bạn sẽ cần đăng nhập vào Trợ lý trên thiết bị nếu chưa đăng nhập.

Để biết thêm thông tin về thiết bị Android ảo, hãy xem nội dung Tạo và quản lý thiết bị ảo.

Khám phá nhanh ứng dụng để biết những tính năng của ứng dụng. Việc nhấn vào biểu tượng Dấu cộng sẽ tạo một mục việc cần làm mới, và các mục trong trình đơn ở trên cùng bên phải cho phép bạn tìm kiếm và lọc các mục việc cần làm theo trạng thái hoàn thành.

4. Tạo một lớp kho lưu trữ lối tắt

Một số lớp trong ứng dụng mẫu của chúng ta sẽ gọi API ShortcutManagerCompat để đẩy và quản lý lối tắt động. Để giảm tình trạng dư thừa mã, bạn sẽ triển khai một kho lưu trữ để cho phép các lớp dự án dễ dàng quản lý lối tắt động.

Mẫu thiết kế kho lưu trữ cung cấp một API rõ ràng để quản lý lối tắt. Ưu điểm của kho lưu trữ là các chi tiết của API cơ bản được tóm tắt thống nhất sau một API tối thiểu. Triển khai kho lưu trữ bằng cách làm theo các bước sau:

  1. Tạo một lớp ShortcutsRepository để tóm tắt API ShortcutManagerCompat.
  2. Thêm các phương thức ShortcutsRepository vào công cụ định vị dịch vụ của ứng dụng.
  3. Đăng ký dịch vụ ShortcutRepository trong Ứng dụng chính.

Tạo kho lưu trữ

Tạo một lớp Kotlin mới có tên là ShortcutsRepository trong gói com.example.android.architecture.blueprints.todoapp.data.source. Bạn có thể tìm thấy gói này được sắp xếp trong thư mục app/src/main/java. Bạn sẽ dùng lớp này để triển khai một giao diện cung cấp một tập hợp các phương thức tối thiểu dùng cho trường hợp sử dụng của lớp học lập trình này.

Cửa sổ Android Studio hiển thị vị trí của lớp ShortcutsRepository.

Hình 2. Cửa sổ Project Files (Tệp dự án) trong Android Studio hiển thị vị trí của lớp ShortcutsRepository.

Dán mã sau vào lớp mới:

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>) {
       //...
   }
}

Tiếp theo, hãy cập nhật phương thức pushShortcut để gọi API ShortcutManagerCompat. Cập nhật lớp ShortcutsRepository bằng đoạn mã sau:

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))
}

Trong mã mẫu trước đó, chúng tôi đã truyền appContext đến API. Đây là một thuộc tính lớp chứa Application Context. Bạn cần phải sử dụng Ngữ cảnh ứng dụng (thay vì Ngữ cảnh hoạt động) để tránh rò rỉ bộ nhớ, vì ngữ cảnh có thể được giữ lại lâu hơn vòng đời hoạt động của máy chủ lưu trữ.

Ngoài ra, API này yêu cầu chúng ta truyền đối tượng ShortcutInfoCompat cho đối tượng Tác vụ. Trong mã mẫu trước đó, chúng ta thực hiện điều này bằng cách gọi phương thức riêng tư createShortcutCompat. Chúng ta sẽ cập nhật phương thức này để tạo và trả về một đối tượng ShortcutInfoCompat. Để thực hiện việc này, hãy cập nhật mã giả lập createShortcutCompat bằng mã sau:

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()
}

Các mã giả lập hàm còn lại trong lớp này xử lý việc cập nhật và xoá lối tắt động. Bật các hàm này bằng cách cập nhật chúng bằng đoạn mã sau:

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 })
}

Thêm lớp học vào công cụ định vị dịch vụ

Sau khi tạo lớp ShortcutsRepository, bước tiếp theo là cung cấp các đối tượng tạo thực thể của lớp này cho phần còn lại của ứng dụng. Ứng dụng này quản lý các phần phụ thuộc của lớp bằng cách triển khai mẫu công cụ định vị dịch vụ. Mở lớp công cụ định vị dịch vụ bằng trình duyệt lớp trong Android Studio bằng cách chuyển đến Navigate (Di chuyển) > Class (Lớp) rồi nhập "ServiceLocator". Nhấp vào tệp Kotlin thu được để mở tệp đó trong IDE.

Ở đầu ServiceLocator.kt, hãy dán mã sau để nhập các gói ShortcutsRepositorySuppressLint:

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

Thêm thành phần và phương thức dịch vụ ShortcutRepository bằng cách dán mã sau vào phần nội dung của 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)
       }
   }
 }

Đăng ký dịch vụ lối tắt

Bước cuối cùng là đăng ký dịch vụ ShortcutsRepository mới với Ứng dụng. Trong Android Studio, hãy mở TodoApplication.kt rồi sao chép đoạn mã sau ở gần đầu tệp:

TodoApplication.kt

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

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

Tiếp theo, hãy đăng ký dịch vụ bằng cách thêm mã sau vào phần nội dung của lớp:

TodoApplication.kt

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

   //...

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

   //...
}

Tạo bản dựng ứng dụng và đảm bảo ứng dụng tiếp tục chạy.

5. Tạo lối tắt mới

Sau khi tạo dịch vụ lối tắt, bạn đã có thể bắt đầu đẩy lối tắt. Vì người dùng tạo nội dung (mục công việc) trong ứng dụng này và muốn quay lại sau, nên chúng tôi sẽ cho phép truy cập bằng giọng nói vào nội dung này bằng cách đẩy một lối tắt động được liên kết với BII GET_THING mỗi khi người dùng tạo một công việc mới. Việc này cho phép Trợ lý khởi chạy người dùng ngay đến mục việc cần làm mà họ yêu cầu khi họ kích hoạt BII bằng cách hỏi những câu như: "Ok Google, mở danh sách hàng tạp hoá của tôi trên SampleApp".

Bạn có thể bật chức năng này trong ứng dụng mẫu bằng cách hoàn tất các bước sau:

  1. Nhập dịch vụ ShortcutsRepository vào lớp AddEditTaskViewModel, chịu trách nhiệm quản lý các đối tượng trong danh sách công việc.
  2. Đẩy một lối tắt động khi người dùng tạo một việc cần làm mới.

Nhập ShortcutsRepository

Trước tiên, chúng ta cần cung cấp dịch vụ ShortcutsRepository cho AddEditTaskViewModel. Để thực hiện việc này, hãy nhập dịch vụ vào ViewModelFactory, lớp ban đầu mà ứng dụng dùng để tạo thực thể cho các đối tượng ViewModel, bao gồm cả AddEditTaskViewModel.

Mở trình duyệt lớp trong Android Studio bằng cách chuyển đến Navigate (Di chuyển) > Class (Lớp) rồi nhập "ViewModelFactory". Nhấp vào tệp Kotlin thu được để mở tệp đó trong IDE.

Ở đầu ViewModelFactory.kt, hãy dán mã sau để nhập các gói ShortcutsRepositorySuppressLint:

ViewModelFactory.kt

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

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

Tiếp theo, hãy thay thế phần nội dung của ViewModelFactory bằng mã sau:

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
}

Hoàn tất các thay đổi về ViewModelFactory bằng cách di chuyển lên một lớp rồi truyền ShortcutsRepository đến hàm khởi tạo của nhà máy. Mở trình duyệt tệp của Android Studio bằng cách chuyển đến Navigate (Di chuyển) > Tệp và nhập "FragmentExt.kt". Nhấp vào tệp Kotlin thu được nằm trong gói util (phần mềm) để mở tệp đó trong IDE.

Thay thế phần nội dung của FragmentExt.kt bằng mã sau:

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

Đẩy lối tắt

Với lớp trừu tượng ShortcutsRepository có sẵn cho các lớp ViewModel của ứng dụng mẫu, bạn cần cập nhật AddEditTaskViewModel (lớp ViewModel chịu trách nhiệm tạo ghi chú) để đẩy một lối tắt động mỗi khi người dùng tạo một ghi chú mới.

Trong Android Studio, hãy mở trình duyệt lớp rồi nhập "AddEditTaskViewModel". Nhấp vào tệp Kotlin thu được để mở tệp đó trong IDE.

Trước tiên, hãy thêm gói ShortcutsRepository vào lớp này bằng câu lệnh nhập sau:

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

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

Tiếp theo, hãy thêm thuộc tính lớp shortcutsRepository bằng cách cập nhật hàm khởi tạo lớp bằng đoạn mã sau:

AddEditTaskViewModel.kt

//...

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

    //...

Sau khi thêm lớp ShortcutsRepository, hãy tạo một hàm mới là pushShortcut() để gọi lớp này. Dán hàm riêng tư sau đây vào phần nội dung của AddEditTaskViewModel:

AddEditTaskViewModel.kt

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

Cuối cùng, đẩy một lối tắt động mới mỗi khi có một việc cần làm được tạo. Thay thế nội dung của hàm saveTask() bằng đoạn mã sau:

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)
    }
}

Kiểm thử mã

Cuối cùng, chúng ta đã sẵn sàng kiểm thử mã! Ở bước này, bạn đẩy một lối tắt động có bật giọng nói rồi kiểm tra lối tắt đó bằng ứng dụng Trợ lý Google.

Tạo bản xem trước

Việc tạo bản xem trước bằng trình bổ trợ Trợ lý Google sẽ cho phép các lối tắt động xuất hiện trong Trợ lý trên thiết bị thử nghiệm.

Cài đặt trình bổ trợ kiểm thử

Nếu bạn chưa có trình bổ trợ Trợ lý Google, hãy cài đặt bằng cách làm theo các bước sau trong Android Studio:

  1. Chuyển đến **Tệp > Cài đặt (Android Studio > Preferences trên MacOS).
  2. Trong mục Plugins (Trình bổ trợ), hãy chọn Marketplace rồi tìm "Google Assistant" (Trợ lý Google).
  3. Cài đặt công cụ rồi khởi động lại Android Studio.

Tạo bản xem trước

Hãy tạo bản xem trước bằng cách làm theo các bước sau trong Android Studio:

  1. Nhấp vào Tools (Công cụ) > Trợ lý Google > "Công cụ kiểm tra Hành động trong ứng dụng".
  2. Trong hộp Tên ứng dụng, hãy xác định một cái tên như "Danh sách việc cần làm".
  3. Nhấp vào Tạo bản xem trước. Nếu được yêu cầu, hãy xem lại và chấp nhận các chính sách cũng như điều khoản dịch vụ về Hành động trong ứng dụng.

Ngăn tạo bản xem trước Công cụ kiểm thử Hành động trong ứng dụng.

Hình 3. Ngăn tạo bản xem trước Công cụ kiểm thử Hành động trong ứng dụng.

Trong quá trình thử nghiệm, các lối tắt động mà bạn đẩy sang Trợ lý sẽ xuất hiện trong Trợ lý được sắp xếp theo Tên ứng dụng bạn đã cung cấp cho bản xem trước.

Đẩy và kiểm tra một lối tắt

Chạy lại ứng dụng mẫu trên thiết bị thử nghiệm và thực hiện các bước sau :

  1. Tạo một nhiệm vụ mới có tên "Bắt đầu lớp học lập trình"
  2. Mở ứng dụng Trợ lý Google rồi nói hoặc nhập: "Lối tắt của tôi".
  3. Nhấn vào thẻ Khám phá. Bạn sẽ thấy phím tắt mẫu.
  4. Nhấn vào lối tắt để gọi. Bạn sẽ thấy lần khởi chạy ứng dụng với tên của lối tắt được điền sẵn trong hộp bộ lọc, giúp bạn dễ dàng tìm thấy mục tác vụ được yêu cầu.

6. (Không bắt buộc) Cập nhật và xoá lối tắt

Ngoài việc đẩy các lối tắt động mới trong thời gian chạy, ứng dụng của bạn có thể cập nhật các lối tắt đó để phản ánh trạng thái hiện tại của nội dung và lựa chọn ưu tiên của người dùng. Bạn nên cập nhật các lối tắt hiện có mỗi khi người dùng sửa đổi mục đích, chẳng hạn như đổi tên một tác vụ trong ứng dụng mẫu. Bạn cũng nên xoá lối tắt tương ứng mỗi khi tài nguyên đích bị xoá để tránh hiển thị lối tắt bị hỏng cho người dùng.

Cập nhật lối tắt

Sửa đổi AddEditTaskViewModel để cập nhật lối tắt động mỗi khi người dùng thay đổi thông tin chi tiết của một mục công việc. Trước tiên, hãy cập nhật phần nội dung của lớp bằng mã sau để thêm một hàm cập nhật sử dụng lớp kho lưu trữ của chúng ta:

AddEditTaskViewModel.kt

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

Tiếp theo, hãy sửa đổi hàm saveTask() để gọi phương thức mới mỗi khi cập nhật một công việc hiện có.

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)
   }
}

Hãy kiểm thử mã của bạn bằng cách chạy lại ứng dụng và làm theo các bước sau:

  1. Đổi tên tiêu đề của mục nhiệm vụ hiện có thành "Hoàn tất lớp học lập trình".
  2. Mở Trợ lý Google bằng cách nói "Ok Google, lối tắt của tôi".
  3. Nhấn vào thẻ Khám phá. Bạn sẽ thấy một nhãn ngắn được cập nhật cho Lối tắt thử nghiệm của mình.

Xoá lối tắt

Chúng ta sẽ xoá các lối tắt ứng dụng mẫu mỗi khi người dùng xoá một công việc. Trong ứng dụng mẫu, logic xoá công việc nằm trong lớp TaskDetailViewModel. Trước khi cập nhật lớp này, chúng ta cần cập nhật lại ViewModelFactory để truyền shortcutsRepository vào TaskDetailViewModel.

Mở ViewModelFactory rồi thay thế nội dung của phương thức hàm khởi tạo bằng đoạn mã sau:

//...
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
}

Tiếp theo, hãy mở TaskDetailViewModel. Nhập mô-đun ShortcutsRepository và khai báo một biến thực thể cho mô-đun đó bằng mã sau:

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() {
...
}

Cuối cùng, hãy sửa đổi hàm deleteTask() để gọi shortcutsRepository nhằm xoá một lối tắt dựa trên mã nhận dạng của lối tắt đó bất cứ khi nào một tác vụ có taskId tương ứng bị xoá:

TaskDetailViewModel.kt

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

Để kiểm thử mã của bạn, hãy chạy lại ứng dụng và làm theo các bước sau:

  1. Xoá nhiệm vụ kiểm thử.
  2. Đổi tên tiêu đề của mục nhiệm vụ hiện có thành "Hoàn tất lớp học lập trình".
  3. Mở Trợ lý Google bằng cách nói "Ok Google, lối tắt của tôi".
  4. Nhấn vào thẻ Khám phá. Xác nhận rằng lối tắt thử nghiệm của bạn không còn xuất hiện nữa.

7. Các bước tiếp theo

Xin chúc mừng! Nhờ bạn, người dùng ứng dụng mẫu của chúng tôi có thể dễ dàng quay lại các ghi chú mà họ đã tạo bằng cách hỏi Trợ lý những câu như "Ok Google, mở danh sách hàng tạp hoá của tôi trên ExampleApp". Lối tắt khuyến khích người dùng tương tác sâu hơn bằng cách giúp người dùng dễ dàng phát lại các hành động thường được sử dụng trong ứng dụng của bạn.

Nội dung đã đề cập

Trong lớp học lập trình này, bạn đã tìm hiểu cách:

  • Xác định các trường hợp sử dụng việc đẩy lối tắt động vào ứng dụng.
  • Giảm độ phức tạp của mã bằng cách sử dụng kho lưu trữ, tính năng chèn phần phụ thuộc và mẫu thiết kế của trình định vị dịch vụ.
  • Đẩy các lối tắt động có hỗ trợ giọng nói đến nội dung ứng dụng do người dùng tạo.
  • Cập nhật và xoá các lối tắt hiện có.

Các bước tiếp theo

Từ đây, bạn có thể thử tinh chỉnh thêm ứng dụng Danh sách việc cần làm. Để tham chiếu dự án đã hoàn tất, hãy xem -nhánh codelab-complete của kho lưu trữ trên GitHub.

Dưới đây là một số đề xuất để bạn tìm hiểu thêm về cách mở rộng ứng dụng này bằng Hành động trong ứng dụng:

Để tiếp tục hành trình Actions on Google, hãy khám phá các tài nguyên sau:

Hãy theo dõi chúng tôi trên Twitter @ActionsOnGoogle để luôn cập nhật những thông báo mới nhất của chúng tôi và đăng bài lên Twitter #appActions để chia sẻ những thành tựu bạn đã xây dựng được!

Khảo sát nhận phản hồi

Cuối cùng, vui lòng điền vào bản khảo sát này để đưa ra ý kiến phản hồi về trải nghiệm của bạn khi tham gia lớp học lập trình này.