Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Membuat aplikasi pesan untuk Android Auto

Dapat tetap terhubung melalui pesan merupakan kebutuhan yang cukup penting bagi banyak pengemudi. Aplikasi chat dapat memberi tahu pengguna jika ada anak yang perlu dijemput, atau jika lokasi makan malam berubah. Framework Android memungkinkan aplikasi pesan memperluas layanannya ke dalam pengalaman berkendara melalui antarmuka pengguna standar yang memungkinkan pengemudi tetap fokus berkendara.

Aplikasi yang mendukung pesan dapat mengirim notifikasi pesannya ke Android Auto untuk digunakan saat Auto sedang berjalan. Notifikasi ini ditampilkan di Auto dan memungkinkan pengguna untuk membaca dan membalas pesan dalam antarmuka yang konsisten dan minim gangguan. Selain itu, jika menggunakan MessagingStyle API, Anda mendapatkan manfaat berupa notifikasi pesan yang dioptimalkan untuk semua perangkat Android, termasuk Android Auto. Pengoptimalan tersebut mencakup UI yang dikhususkan bagi notifikasi pesan, animasi yang disempurnakan, dan dukungan untuk gambar inline.

Tutorial ini menganggap Anda sudah membuat aplikasi yang dapat menampilkan pesan kepada pengguna dan menerima balasan pengguna, seperti aplikasi chat. Tutorial ini juga menunjukkan cara mengembangkan aplikasi Anda untuk meneruskan pesan tersebut ke perangkat Auto agar dapat ditampilkan dan dibalas.

Memulai

Agar dapat menyediakan layanan pesan untuk perangkat Auto, aplikasi Anda harus dapat melakukan hal berikut:

  1. Membuat dan mengirim objek NotificationCompat.MessagingStyle yang berisi objek Action balas dan tandai sudah dibaca.
  2. Menangani proses membalas pesan dan menandai percakapan sebagai sudah dibaca dengan Service.
  3. Mengonfigurasi manifes untuk mengindikasikan bahwa aplikasi mendukung Android Auto.

Konsep dan objek

Sebelum mulai mendesain aplikasi, sebaiknya pahami terlebih dahulu cara Android Auto menangani pesan.

Setiap bagian komunikasi disebut sebagai pesan dan direpresentasikan oleh class MessagingStyle.Message. Pesan menyertakan informasi pengirim, isi pesan, dan waktu pengiriman.

Komunikasi antarpengguna disebut percakapan dan direpresentasikan oleh objek MessagingStyle. Percakapan (atau MessagingStyle) berisi judul, pesan, dan apakah percakapan merupakan bagian dari grup (yaitu, percakapan memiliki lebih dari satu penerima lainnya).

Untuk memberi tahu pengguna tentang informasi baru dalam percakapan (seperti pesan baru), aplikasi akan memposting Notification ke sistem Android. Notifikasi ini menggunakan objek MessagingStyle untuk menampilkan UI khusus pesan di menu notifikasi. Platform Android juga meneruskan Notification ini ke Android Auto, kemudian MessagingStyle diekstrak dan digunakan untuk memposting notifikasi melalui layar di mobil.

Aplikasi juga dapat menambahkan objek Action ke Notification agar pengguna dapat membalas pesan dengan cepat, atau menandainya sebagai sudah dibaca dari menu notifikasi secara langsung. Untuk mengelola percakapan, Android Auto membutuhkan objek Action balas dan tandai sudah dibaca.

Singkatnya, satu percakapan direpresentasikan oleh satu objek Notification yang diberi gaya dengan satu objek MessagingStyle. MessagingStyle berisi semua pesan dalam percakapan tersebut dengan satu atau lebih objek MessagingStyle.Message. Terakhir, agar memenuhi ketentuan Android Auto sepenuhnya, Anda harus menambahkan Action balas dan tandai sudah dibaca pada Notification .

Alur pesan

Bagian ini menjelaskan alur pesan standar antara aplikasi Anda dan Android Auto.

  1. Aplikasi Anda menerima pesan.
  2. Aplikasi Anda kemudian menghasilkan notifikasi MessagingStyle dengan Action balas dan tandai sudah dibaca.
  3. Android Auto menerima peristiwa “notifikasi baru” dari sistem Android, lalu menemukan MessagingStyle, Action balas, serta Action tandai sudah dibaca.
  4. Android Auto menghasilkan dan menampilkan notifikasi di mobil.
  5. Jika pengguna mengetuk notifikasi pada layar mobil, Android Auto akan memicu Action tandai sudah dibaca.
    • Di latar belakang, aplikasi Anda harus menangani peristiwa tandai sudah dibaca ini.
  6. Jika pengguna merespons notifikasi melalui suara, Android Auto akan menyertakan transkripsi respons pengguna ke dalam Action balas, lalu memicunya.
    • Di latar belakang, aplikasi Anda harus menangani peristiwa balas ini.

Asumsi awal

Halaman ini tidak menyertakan panduan untuk membuat seluruh aplikasi pesan. Namun, contoh kode di bawah menyertakan beberapa hal yang seharusnya sudah dimiliki aplikasi Anda sebelum mulai mendukung pesan dengan Android Auto.

data class YourAppConversation(
            val id: Int,
            val title: String,
            val recipients: MutableList<YourAppUser>,
            val icon: Bitmap) {
        companion object {
            /** Fetches [YourAppConversation] by its [id]. */
            fun getById(id: Int): YourAppConversation = // ...
        }

        /** Replies to this conversation with the given [message]. */
        fun reply(message: String) {}

        /** Marks this conversation as read. */
        fun markAsRead() {}

        /** Retrieves all unread messages from this conversation. */
        fun getUnreadMessages(): List<YourAppMessage> { return /* ... */ }
    }
    data class YourAppUser(val id: Int, val name: String, val icon: Uri)
    data class YourAppMessage(
        val id: Int,
        val sender: YourAppUser,
        val body: String,
        val timeReceived: Long)
    

Mendeklarasikan dukungan Android Auto

Saat menerima notifikasi dari aplikasi pesan, Android Auto akan memeriksa apakah aplikasi tersebut telah mendeklarasikan dukungan untuk Android Auto atau belum. Untuk memungkinkan dukungan ini, sertakan entri berikut dalam manifes aplikasi Anda:

<application>
        ...
        <meta-data
            android:name="com.google.android.gms.car.application"
            android:resource="@xml/automotive_app_desc"/>
        ...
    </application>
    

Entri manifes ini mengacu pada file XML lain yang harus Anda buat dengan jalur berikut: YourAppProject/app/src/main/res/xml/automotive_app_desc.xml, yang digunakan untuk mendeklarasikan kemampuan Android Auto yang didukung aplikasi Anda. Misalnya, untuk menyertakan dukungan bagi notifikasi, sertakan berikut ini dalam automotive_app_desc.xml Anda:

<automotiveApp>
        <uses name="notification" />
    </automotiveApp>
    

Jika aplikasi Anda memerlukan dukungan untuk menangani SMS, MMS, dan RCS, Anda juga harus menyertakan baris berikut:

<automotiveApp>
        ...
        <uses name="sms" />
    </automotiveApp>
    

Mengimpor library inti AndroidX

Pembuatan notifikasi yang akan digunakan dengan perangkat Auto memerlukan library inti AndroidX yang dapat diimpor ke project Anda seperti berikut:

  1. Di file build.gradle tingkat atas, pastikan Anda menyertakan repositori Google Maven, seperti yang ditampilkan di bawah.

    allprojects {
            repositories {
                google()
            }
        }
        
  2. Dalam file build.gradle modul aplikasi Anda, sertakan dependensi library inti AndroidX, seperti berikut:

    dependencies {
            implementation 'androidx.core:core:1.0.0'
        }
        

Menangani tindakan pengguna

Aplikasi pesan memerlukan cara untuk menangani pembaruan percakapan melalui Action. Untuk Android Auto, ada dua jenis objek Action yang perlu ditangani oleh aplikasi Anda: balas dan tandai sudah dibaca. Cara yang direkomendasikan untuk melakukannya adalah dengan menggunakan IntentService. IntentService memungkinkan penanganan yang fleksibel untuk panggilan yang berpotensi cukup sulit di latar belakang dan membebaskan thread utama aplikasi.

Menentukan tindakan intent

Tindakan Intent (tidak sama dengan tindakan notifikasi) adalah string sederhana yang mengidentifikasi fungsi Intent. Karena satu layanan dapat menangani beberapa jenis intent, akan lebih mudah untuk menentukan beberapa string Intent.action daripada menentukan beberapa IntentServices.

Dalam contoh aplikasi pesan ini, kita memiliki dua jenis tindakan: balas dan tandai sudah dibaca, sebagaimana dideklarasikan dalam contoh kode berikut ini.

private const val ACTION_REPLY = "com.example.REPLY"
    private const val ACTION_MARK_AS_READ = "com.example.MARK_AS_READ"
    

Membuat Layanan

Untuk membuat layanan yang menangani Action, Anda memerlukan ID percakapan, yaitu struktur data arbitrer yang ditentukan oleh aplikasi yang mengidentifikasi percakapan, serta kunci input jarak jauh, yang dibahas secara lebih mendetail selanjutnya di bagian ini. Contoh kode berikut ini membuat layanan seperti yang disebutkan.

private const val EXTRA_CONVERSATION_ID_KEY = "conversation_id"
    private const val REMOTE_INPUT_RESULT_KEY = "reply_input"

    /**
     * An [IntentService] that handles reply and mark-as-read actions for
     * [YourAppConversation]s.
     */
    class MessagingService : IntentService("MessagingService") {
        override fun onHandleIntent(intent: Intent?) {
            // Fetch our internal data
            val conversationId = intent!!.getIntExtra(EXTRA_CONVERSATION_ID_KEY, -1)

            // And search our database for that conversation
            val conversation = YourAppConversation.getById(conversationId)

            // Then handle the action that was requested in the intent. The TODOs
            // are addressed in a section below.
            when (intent.action) {
                ACTION_REPLY -> TODO()
                ACTION_MARK_AS_READ -> TODO()
            }
        }
    }
    

Untuk mengaitkan layanan ini dengan aplikasi Anda, Anda juga perlu mendaftarkan layanan tersebut dalam manifes aplikasi Anda, seperti ditunjukkan di bawah ini.

<application>
        <service android:name="com.example.MessagingService" />
        ...
    </application>
    

Membuat dan menangani Intent

Tidak ada cara bagi aplikasi lain untuk mendapatkan Intent yang memicu MessagingService saat setiap Intent diteruskan ke aplikasi eksternal melalui PendingIntent. Karena batasan ini, Anda perlu membuat objek RemoteInput untuk memungkinkan aplikasi lain tersebut mengembalikan teks "balas" ke aplikasi Anda, seperti yang ditampilkan di bawah ini.

/**
     * Creates a [RemoteInput] which allows remote apps to provide a response string
     * to the underlying [Intent] within a [PendingIntent].
     */
    fun createReplyRemoteInput(context: Context): RemoteInput {
        // RemoteInput.Builder accepts a single parameter: the key to use to store
        // the response in.
        return RemoteInput.Builder(REMOTE_INPUT_RESULT_KEY).build()
        // Note that the RemoteInput has no knowledge of the conversation. This is
        // because the data for the RemoteInput is bound to the reply Intent via
        // static methods in the RemoteInput class.
    }

    /** Creates an [Intent] which handles replying to the given [appConversation]. */
    fun createReplyIntent(
            context: Context, appConversation: YourAppConversation): Intent {
        // Create the intent backed by the MessagingService.
        val intent = Intent(context, MessagingService::class.java)

        // This action string lets the MessagingService know this is a "reply" request.
        intent.action = ACTION_REPLY

        // Provide the conversation id so we know what conversation this applies to.
        intent.putExtra(EXTRA_CONVERSATION_ID_KEY, appConversation.id)

        return intent
    }
    

Setelah mengetahui apa saja yang akan disertakan dalam Intent balas, tangani TODO untuk klausa tombol ACTION_REPLY dalam MessagingService dan ekstrak informasi tersebut, seperti berikut:

ACTION_REPLY -> {
        // Extract reply response from the intent using the same key that the
        // above RemoteInput used.
        val results: Bundle = RemoteInput.getResultsFromIntent(intent)
        val message = results.getString(REMOTE_INPUT_RESULT_KEY)

        // Note this conversation object came from above in the MessagingService
        conversation.reply(message)
    }
    

Anda dapat menangani Intent tandai sudah dibaca dengan cara yang sama (tanpa RemoteInput).

/** Creates an [Intent] which handles marking the [appConversation] as read. */
    fun createMarkAsReadIntent(
            context: Context, appConversation: YourAppConversation): Intent {
        val intent = Intent(context, MessagingService::class.java)
        intent.action = ACTION_MARK_AS_READ
        intent.putExtra(EXTRA_CONVERSATION_ID_KEY, appConversation.id)
        return intent
    }
    

Contoh kode di bawah ini menangani TODO untuk klausa tombol ACTION_MARK_AS_READ dalam MessagingService:

// Marking as read has no other logic.
    ACTION_MARK_AS_READ -> conversation.markAsRead()
    

Memberitahukan pesan kepada pengguna

Setelah penanganan tindakan percakapan aplikasi pesan selesai, kita dapat beralih ke membuat notifikasi yang sesuai dengan Android Auto.

Membuat tindakan

Action adalah objek yang dapat diteruskan ke aplikasi lain melalui Notification untuk memicu metode di aplikasi asalnya. Berikut cara Android Auto menandai percakapan sebagai sudah dibaca, serta membalasnya.

Untuk membuat Action, mulailah dengan Intent, seperti yang ditunjukkan dengan Intent "balas" di bawah ini:

fun createReplyAction(
            context: Context, appConversation: YourAppConversation): Action {
        val replyIntent: Intent = createReplyIntent(context, appConversation)
        // ...
    

Selanjutnya, kami menggabungkan Intent ini dalam PendingIntent yang menyiapkannya untuk penggunaan aplikasi eksternal. PendingIntent mengunci semua akses ke Intent yang digabungkan hanya dengan menunjukkan serangkaian metode tertentu yang memungkinkan aplikasi penerima mengaktifkan Intent atau mendapatkan nama paket aplikasi asal; tetapi tidak mengizinkan aplikasi eksternal untuk mengakses Intent yang digunakan atau data di dalamnya.

    // ...
        val replyPendingIntent = PendingIntent.getService(
            context,
            createReplyId(appConversation), // Method explained later.
            replyIntent,
            PendingIntent.FLAG_UPDATE_CURRENT)
        // ...
    

Sebelum menyiapkan Action balas, perlu diketahui bahwa Android Auto memiliki tiga persyaratan untuk Action balas:

  • Tindakan semantik harus ditetapkan ke Action.SEMANTIC_ACTION_REPLY.
  • Action harus menunjukkan bahwa antarmuka pengguna tidak akan ditampilkan saat diaktifkan.
  • Action harus berisi satu RemoteInput.

Contoh kode berikut menyiapkan Action balas sekaligus memenuhi persyaratan yang tercantum di atas:

    // ...
        val replyAction = Action.Builder(R.drawable.reply, "Reply", replyPendingIntent)
            // Provide context to what firing Action does.
            .setSemanticAction(Action.SEMANTIC_ACTION_REPLY)

            // The action doesn't show any UI (it's a requirement for apps to
            // not show UI for Android Auto).
            .setShowsUserInterface(false)

            // Don't forget the reply RemoteInput. Android Auto will use this to
            // make a system call that will add the response string into
            // the reply intent so it can be extracted by the messaging app.
            .addRemoteInput(createReplyRemoteInput(context))
            .build()

        return replyAction
    }
    

Di sisi tandai sudah dibaca, kami melakukan hal yang sama, tetapi tidak menggunakan RemoteInput. Dengan demikian, Android Auto memiliki 2 persyaratan untuk Action tandai sudah dibaca:

  • Tindakan semantik ditetapkan ke Action.SEMANTIC_ACTION_MARK_AS_READ.
  • Tindakan tersebut menunjukkan bahwa antarmuka pengguna tidak akan ditampilkan saat diaktifkan.
fun createMarkAsReadAction(
            context: Context, appConversation: YourAppConversation): Action {
        val markAsReadIntent = createMarkAsReadIntent(context, appConversation)
        val markAsReadPendingIntent = PendingIntent.getService(
                context,
                createMarkAsReadId(appConversation), // Method explained below.
                markAsReadIntent,
                PendingIntent.FLAG_UPDATE_CURRENT)
        val markAsReadAction = Action.Builder(
                R.drawable.mark_as_read, "Mark as Read", markAsReadPendingIntent)
            .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ)
            .setShowsUserInterface(false)
            .build()
        return markAsReadAction
    }
    

Membuat MessagingStyle

MessagingStyle adalah operator informasi pesan yang digunakan oleh Android Auto untuk membaca setiap pesan dalam percakapan dengan keras. Pertama, pengguna perangkat harus ditentukan dalam bentuk objek Person.

fun createMessagingStyle(
            context: Context, appConversation: YourAppConversation): MessagingStyle {
        // Method defined by our messaging app.
        val appDeviceUser: YourAppUser = getAppDeviceUser()

        val devicePerson = Person.Builder()
            // The display name (also the name that's read aloud in Android auto).
            .setName(appDeviceUser.name)

            // The icon to show in the notification shade in the System UI (outside
            // of Android Auto).
            .setIcon(appDeviceUser.icon)

            // A unique key in case there are multiple people in this conversation with
            // the same name.
            .setKey(appDeviceUser.id)
            .build()
        // ...
    

Anda kemudian dapat membuat objek MessagingStyle dan menambahkan beberapa detail tentang percakapan.

    // ...
        val messagingStyle = MessagingStyle(devicePerson)

        // Set the conversation title. Note that if your app's target version is lower
        // than P, this will automatically mark this conversation as a group (to
        // maintain backwards compatibility). Simply use #setGroupConversation after
        // setting the conversation title to explicitly override this behavior. See
        // the documentation for more information.
        messagingStyle.setConversationTitle(appConversation.title)

        // Group conversation means there is more than 1 recipient, so set it as such.
        messagingStyle.setGroupConversation(appConversation.recipients.size > 1)
        // ...
    

Terakhir, tambahkan pesan yang belum dibaca.

    // ...
        for (appMessage in appConversation.getUnreadMessages()) {
            // The sender is also represented via a Person object
            val senderPerson = Person.Builder()
                .setName(appMessage.sender.name)
                .setIcon(appMessage.sender.icon)
                .setKey(appMessage.sender.id)
                .build()

            // And the message is added. Note that more complex messages (ie. images)
            // may be created and added by instantiating the MessagingStyle.Message
            // class directly. See documentation for details.
            messagingStyle.addMessage(
                    appMessage.body, appMessage.timeReceived, senderPerson)
        }

        return messagingStyle
    }
    

Memaketkan dan mengirim notifikasi

Setelah membuat objek Action dan MessagingStyle, Anda kini dapat membuat dan memposting Notification.

fun notify(context: Context, appConversation: YourAppConversation) {
        // Create our Actions and MessagingStyle from the methods defined above.
        val replyAction = createReplyAction(context, appConversation)
        val markAsReadAction = createMarkAsReadAction(context, appConversation)
        val messagingStyle = createMessagingStyle(context, appConversation)

        // Create the notification.
        val notification = NotificationCompat.Builder(context, channel)
            // A required field for the Android UI.
            .setSmallIcon(R.drawable.notification_icon)

            // Shown in Android Auto as the conversation image.
            .setLargeIcon(appConversation.icon)

            // Add MessagingStyle.
            .setStyle(messagingStyle)

            // Add reply action.
            .addAction(replyAction)

            // Let's say we don't want to show our mark-as-read Action in the
            // notification shade. We can do that by adding it as invisible so it
            // won't appear in Android UI, but still satisfy Android Auto's
            // mark-as-read Action requirement. You're free to add both Actions as
            // visible or invisible. This is just a stylistic choice.
            .addInvisibleAction(markAsReadAction)

            .build()

        // Post the notification for the user to see.
        val notificationManagerCompat = NotificationManagerCompat.from(context)
        notificationManagerCompat.notify(appConversation.id, notification)
    }
    

Lihat juga