Ketuk untuk Membaca di Asisten Suara

Android Automotive menganggap suara sebagai komponen penting untuk interaksi yang aman saat mengemudi dan salah satu cara teraman bagi pengguna untuk berinteraksi dengan Android Automotive OS saat mengemudi. Oleh karena itu, kami memperluas API asisten suara Android (termasuk VoiceInteractionSession) agar asisten suara dapat melakukan tugas untuk pengguna yang mungkin sulit dilakukan saat mengemudi.

Ketuk untuk Membaca memungkinkan asisten suara membaca dan membalas pesan teks atas nama pengguna, saat pengguna berinteraksi dengan notifikasi pesan. Untuk menyediakan fungsi ini, Anda dapat mengintegrasikan asisten suara dengan CarVoiceInteractionSession.

Di Automotive, notifikasi yang diposting ke Pusat Notifikasi yang diidentifikasi sebagai INBOX atau INBOX_IN_GROUP (misalnya, pesan SMS) menyertakan tombol Putar. Pengguna dapat mengklik Putar agar asisten suara yang dipilih membacakan notifikasi dengan keras, dan secara opsional membalas dengan suara.

Notifikasi ketuk untuk membaca

Gambar 1. Notifikasi Ketuk untuk Membaca dengan tombol Play.

Mengintegrasikan dengan CarVoiceInteractionSession

Bagian berikutnya menjelaskan cara mengintegrasikan asisten suara dengan CarVoiceInteractionSession.

Mendukung interaksi suara

Aplikasi yang menyediakan layanan interaksi suara mobil harus terintegrasi dengan interaksi suara Android yang ada. Untuk mempelajari lebih lanjut, lihat Asisten Google untuk Android (kecuali VoiceInteractionSession). Meskipun semua elemen API interaksi suara tetap sama seperti yang diterapkan di perangkat seluler, CarVoiceInteractionSession (dijelaskan dalam Menerapkan CarVoiceInteractionSession) menggantikan VoiceInteractionSession. Untuk informasi selengkapnya, lihat halaman berikut:

Mengimplementasikan CarVoiceInteractionSession

CarVoiceInteractionSession mengekspos API yang dapat Anda gunakan untuk memungkinkan asisten suara membaca pesan teks dengan lantang, lalu membalas pesan tersebut atas nama pengguna.

Perbedaan utama antara class CarVoiceInteractionSession dan VoiceInteractionSession adalah CarVoiceInteractionSession meneruskan tindakan di onShow sehingga asisten suara dapat mendeteksi konteks permintaan pengguna segera setelah CarVoiceInteractionSession memulai sesi. Parameter untuk onShow untuk setiap class tercantum dalam tabel berikut:

CarVoiceInteractionSession VoiceInteractionSession
onShow menggunakan tiga parameter berikut:
  • args
  • showFlags
  • actions
onShow menggunakan dua parameter berikut:
  • args
  • showFlags

Perubahan di Android 10

Mulai Android 10, platform memanggil VoiceInteractionService.onGetSupportedVoiceActions untuk mendeteksi tindakan yang didukung. Asisten suara mengganti dan menerapkan VoiceInteractionService.onGetSupportedVoiceActions, seperti yang ditunjukkan pada contoh berikut:

public class MyInteractionService extends VoiceInteractionService {
    private static final List SUPPORTED_VOICE_ACTIONS = Arrays.asList(
        CarVoiceInteractionSession.VOICE_ACTION_READ_NOTIFICATION);

    @Override
    public Set onGetSupportedVoiceActions(@NonNull Set voiceActions) {
       Set result = new HashSet<>(voiceActions);
       result.retainAll(SUPPORTED_VOICE_ACTIONS);
       return result;
   }
}

Tindakan yang valid dijelaskan dalam tabel berikut. Untuk mengetahui detail tentang setiap tindakan, lihat Diagram urutan.

Tindakan Payload yang diharapkan Tindakan interaksi suara yang diharapkan
VOICE_ACTION_READ_NOTIFICATION Baca pesan dengan lantang kepada pengguna, lalu aktifkan intent Tandai sebagai Telah Dibaca yang tertunda kembali saat pesan berhasil dibaca. Secara opsional, minta pengguna untuk membalas.
VOICE_ACTION_REPLY_NOTIFICATION Parcelable dengan kunci.
KEY_NOTIFICATION yang dipetakan ke StatusBarNotification.
Memerlukan android.permission.BIND_NOTIFICATION_LISTENER_SERVICE.
Minta pengguna untuk menyatakan pesan balasan, masukkan pesan balasan ke RemoteInputReply intent yang tertunda, lalu aktifkan intent yang tertunda.
VOICE_ACTION_HANDLE_EXCEPTION String dengan kunci.
KEY_EXCEPTION yang dipetakan ke ExceptionValue (dijelaskan dalam Nilai pengecualian).
KEY_FALLBACK_ASSISTANT_ENABLED yang dipetakan ke nilai Boolean. Jika nilainya true, asisten penggantian yang dapat menangani permintaan pengguna telah dinonaktifkan.
Tindakan yang diharapkan untuk dilakukan terhadap pengecualian ditentukan dalam dokumentasi untuk pengecualian tersebut.

Nilai pengecualian

EXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING menunjukkan kepada asisten suara bahwa asisten suara tersebut tidak memiliki izin Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE dan untuk mendapatkan izin ini dari pengguna.

Meminta izin pemroses notifikasi

Jika asisten suara default tidak memiliki izin pemroses notifikasi, FallbackAssistant platform (jika diaktifkan oleh produsen mobil) mungkin membaca pesan dengan lantang sebelum asisten suara diberi tahu untuk meminta izin. Untuk menentukan apakah FallbackAssistant diaktifkan dan telah membaca pesan, asisten suara harus memeriksa nilai Boolean KEY_FALLBACK_ASSISTANT_ENABLED dalam payload.

Platform merekomendasikan asisten suara untuk menambahkan logika pembatasan kapasitas untuk jumlah permintaan izin ini. Tindakan ini menghormati pengguna yang tidak ingin memberikan izin ini kepada asisten suara dan lebih memilih FallbackAssistant untuk membaca pesan teks dengan lantang. Meminta izin kepada pengguna setiap kali pengguna menekan Putar pada notifikasi pesan dapat menjadi pengalaman pengguna yang negatif. Platform ini tidak menerapkan batas kapasitas atas nama asisten suara.

Saat meminta izin pemroses notifikasi, asisten suara harus menggunakan CarUxRestrictionsManager untuk menentukan apakah pengguna sedang parkir atau mengemudi. Jika pengguna sedang mengemudi, asisten suara akan menampilkan notifikasi yang memberikan petunjuk tentang cara memberikan izin. Tindakan ini membantu (dan mengingatkan) pengguna untuk memberikan izin saat lebih aman.

Menggunakan StatusBarNotification

StatusBarNotification yang diteruskan dengan tindakan suara Baca dan Balas selalu dalam notifikasi pesan yang kompatibel dengan mobil seperti yang dijelaskan dalam Memberi tahu pengguna tentang pesan. Meskipun beberapa notifikasi mungkin tidak memiliki intent Reply Pending, semuanya memiliki intent menandai sebagai Telah Dibaca yang tertunda.

Untuk menyederhanakan interaksi dengan notifikasi, gunakan NotificationPayloadHandler, yang menyediakan metode untuk mengekstrak pesan dari notifikasi dan menulis pesan balasan ke intent tertunda yang sesuai dari notifikasi. Setelah asisten suara membaca pesan, asisten suara harus memicu intent Tandai sebagai Telah Dibaca.

Memenuhi prasyarat Ketuk untuk Membaca

Hanya VoiceInteractionSession dari asisten suara default yang diberi tahu saat pengguna memicu tindakan suara untuk membaca dan membalas pesan. Seperti yang disebutkan di atas, asisten suara default ini juga harus memiliki izin pemroses notifikasi.

Diagram urutan

Gambar ini menampilkan alur logika CarVoiceInteractionSession actions:

VOICE_ACTION_READ_NOTIFICATION

Gambar 2. Diagram urutan untuk VOICE_ACTION_READ_NOTIFICATION.

Dalam kasus Gambar 3, aplikasi pembatasan kapasitas pada permintaan izin direkomendasikan:

VOICE_ACTION_REPLY_NOTIFICATION

Gambar 3. Diagram urutan untuk VOICE_ACTION_REPLY_NOTIFICATION.

VOICE_ACTION_HANDLE_EXCEPTION

Gambar 4. Diagram urutan untuk VOICE_ACTION_HANDLE_EXCEPTION.

Membaca nama aplikasi

Jika Anda ingin asisten suara membaca nama aplikasi pesan dengan lantang selama pembacaan pesan (misalnya, "Sam dari Hangouts mengatakan..."), buat fungsi seperti yang ditampilkan dalam contoh kode berikut untuk memastikan asisten membaca nama yang benar:

@Nullable
String getMessageApplicationName(Context context, StatusBarNotification statusBarNotification) {
    ApplicationInfo info = getApplicationInfo(context, statusBarNotification.getPackageName());
    if (info == null) return null;

    Notification notification = statusBarNotification.getNotification();

    // Sometimes system packages will post on behalf of other apps, so check this
    // field for a system app notification.
    if (isSystemApp(info)
            && notification.extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
        return notification.extras.getString(Notification.EXTRA_SUBSTITUTE_APP_NAME);
    } else {
        PackageManager pm = context.getPackageManager();
        return String.valueOf(pm.getApplicationLabel(info));
    }
}

@Nullable
ApplicationInfo getApplicationInfo(Context context, String packageName) {
    final PackageManager pm = context.getPackageManager();
    ApplicationInfo info;
    try {
        info = pm.getApplicationInfo(packageName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
    return info;
}

boolean isSystemApp(ApplicationInfo info) {
    return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}