Mesajlaşma bildirimlerini Android Auto'ya genişletme

Mesajlaşmayı destekleyen uygulamalar, mesajlaşma bildirimlerini genişleterek Android Auto'nun Auto çalışırken bu bildirimleri kullanmasına olanak tanıyabilir. Bu bildirimler Auto'da gösterilir ve kullanıcıların iletileri tutarlı, dikkat dağıtmayan bir arayüzde okuyup yanıtlamasına olanak tanır. Ayrıca MessagingStyle API'yi kullandığınızda Android Auto dahil tüm Android cihazlar için optimize edilmiş mesaj bildirimleri alırsınız. Optimizasyonlar arasında mesaj bildirimleri için özel olarak tasarlanmış bir kullanıcı arayüzü, iyileştirilmiş animasyonlar ve satır içi resim desteği yer alıyor.

Bu kılavuzda, kullanıcılara mesaj gösteren ve kullanıcılardan yanıt alan bir uygulamayı (ör. sohbet uygulaması) mesaj görüntüleme ve yanıt alma işlemlerini Auto cihazına devredecek şekilde nasıl genişleteceğiniz gösterilmektedir. Bu entegrasyon sayesinde kullanıcılar yalnızca etkin Android Auto oturumları sırasında aldıkları bildirimlerdeki mesaj geçmişini görebilir. Etkin Android Auto oturumları başlamadan önceki mesajları göstermek için şablonlu bir mesajlaşma deneyimi oluşturabilirsiniz.

İlgili tasarım rehberliği için Design for Driving sitesindeki Mesajlaşma uygulamaları bölümüne bakın.

Başlayın

Auto cihazlar için mesajlaşma hizmeti sunmak üzere uygulamanızın manifest dosyasında Android Auto desteğini belirtmesi ve aşağıdakileri yapabilmesi gerekir:

  • Yanıt ve okundu olarak işaretle Action nesnelerini içeren NotificationCompat.MessagingStyle nesneleri oluşturup gönderme.
  • Yanıtlamayı ve ileti dizilerini okundu olarak işaretlemeyi Service ile yapın.

Kavramlar ve nesneler

Uygulamanızı tasarlamaya başlamadan önce Android Auto'nun mesajlaşmayı nasıl işlediğini anlamanız faydalı olacaktır.

İletişimin her bir parçasına mesaj adı verilir ve MessagingStyle.Message sınıfıyla temsil edilir. Bir iletide gönderen, ileti içeriği ve iletinin gönderilme zamanı bulunur.

Kullanıcılar arasındaki iletişime konuşma denir ve MessagingStyle nesnesiyle gösterilir. Bir ileti dizisi veya MessagingStyle, başlık, iletiler ve ileti dizisinin bir grup kullanıcı arasında olup olmadığını içerir.

Uygulamalar, kullanıcıları bir ileti dizisindeki güncellemeler (ör. yeni mesaj) hakkında bilgilendirmek için Android sistemine Notification gönderir. Bu Notification, bildirim gölgesinde mesaja özel kullanıcı arayüzü göstermek için MessagingStyle nesnesini kullanır. Android platformu da bu Notification bilgiyi Android Auto'ya iletir. MessagingStyle bilgisi çıkarılır ve arabanın ekranında bildirim yayınlamak için kullanılır.

Android Auto, kullanıcıların bir iletiye hızlıca yanıt vermesine veya iletiyi doğrudan bildirim gölgesinden okundu olarak işaretlemesine olanak tanımak için uygulamaların Action nesnelerini Notification öğesine eklemesini de gerektirir.

Özetle, tek bir ileti dizisi, MessagingStyle nesnesiyle biçimlendirilmiş bir Notification nesnesiyle gösterilir. MessagingStyle, söz konusu ileti dizisindeki tüm iletileri bir veya daha fazla MessagingStyle.Message nesnesinde içerir. Ayrıca, Android Auto ile uyumlu olmak için bir uygulamanın Action nesnelerini Notification'ye eklemesi gerekir.

Mesajlaşma akışı

Bu bölümde, uygulamanız ile Android Auto arasındaki tipik mesajlaşma akışı açıklanmaktadır.

  1. Uygulamanız bir mesaj aldığında
  2. Uygulamanız, yanıtla ve okunmuş olarak işaretle Action nesneleriyle bir MessagingStyle bildirimi oluşturur.
  3. Android Auto, Android sisteminden "yeni bildirim" etkinliğini alır ve MessagingStyle, yanıtla Action ve okundu olarak işaretle Action seçeneklerini bulur.
  4. Android Auto, araçta bir bildirim oluşturup gösterir.
  5. Kullanıcı, arabanın ekranındaki bildirime dokunursa Android Auto, okundu olarak işaretle Action işlemini tetikler.
    • Uygulamanız, arka planda bu "okundu olarak işaretle" etkinliğini işlemelidir.
  6. Kullanıcı bildirime sesini kullanarak yanıt verirse Android Auto, kullanıcının yanıtının transkriptini yanıta Action yerleştirir ve ardından yanıtı tetikler.
    • Uygulamanız arka planda bu yanıt etkinliğini işlemelidir.

Ön varsayımlar

Bu sayfa, eksiksiz bir mesajlaşma uygulaması oluşturma konusunda size yol göstermez. Aşağıdaki kod örneğinde, Android Auto ile mesajlaşmayı desteklemeye başlamadan önce uygulamanızın ihtiyaç duyduğu bazı şeyler yer almaktadır:

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)

Android Auto desteğini beyan etme

Android Auto, bir mesajlaşma uygulamasından bildirim aldığında uygulamanın Android Auto desteğini beyan edip etmediğini kontrol eder. Bu desteği etkinleştirmek için uygulamanızın manifestine aşağıdaki girişi ekleyin:

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

Bu manifest girişi, aşağıdaki yolla oluşturmanız gereken başka bir XML dosyasını ifade eder: YourAppProject/app/src/main/res/xml/automotive_app_desc.xml. automotive_app_desc.xml içinde uygulamanızın desteklediği Android Auto özelliklerini bildirin. Örneğin, bildirim desteğini bildirmek için aşağıdakileri ekleyin:

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

Uygulamanız varsayılan SMS işleyici olarak ayarlanabiliyorsa aşağıdaki <uses> öğesini eklediğinizden emin olun. Aksi takdirde, uygulamanız varsayılan SMS işleyici olarak ayarlandığında gelen SMS/MMS mesajlarını işlemek için Android Auto'ya yerleşik varsayılan bir işleyici kullanılır. Bu durum, bildirimlerin yinelenmesine neden olabilir.

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

AndroidX çekirdek kitaplığını içe aktarma

Auto cihazlarda kullanılacak bildirimler oluşturmak için AndroidX çekirdek kitaplığı gerekir. Kitaplığı projenize aşağıdaki şekilde aktarın:

  1. En üst düzeydeki build.gradle dosyasında, aşağıdaki örnekte gösterildiği gibi Google'ın Maven deposuna bağımlılık ekleyin:

Groovy

allprojects {
    repositories {
        google()
    }
}

Kotlin

allprojects {
    repositories {
        google()
    }
}
  1. Uygulama modülünüzün build.gradle dosyasında, aşağıdaki örnekte gösterildiği gibi AndroidX Core kitaplık bağımlılığını ekleyin:

Groovy

dependencies {
    // If your app is written in Java
    implementation 'androidx.core:core:1.17.0'

    // If your app is written in Kotlin
    implementation 'androidx.core:core-ktx:1.17.0'
}

Kotlin

dependencies {
    // If your app is written in Java
    implementation("androidx.core:core:1.17.0")

    // If your app is written in Kotlin
    implementation("androidx.core:core-ktx:1.17.0")
}

Kullanıcı işlemlerini yönetme

Mesajlaşma uygulamanızın, Action aracılığıyla görüşmeleri güncelleme yöntemini desteklemesi gerekir. Android Auto'da, uygulamanızın işlemesi gereken iki tür Action nesne vardır: yanıtlama ve okundu olarak işaretleme. Bu işlemleri, olası maliyetli arka plan çağrılarını işleme esnekliği sağlayan ve uygulamanızın ana iş parçacığını serbest bırakan bir IntentService kullanarak yapmanızı öneririz.

Amaca yönelik işlemleri tanımlama

Intent işlemleri, Intent öğesinin ne için olduğunu tanımlayan basit dizelerdir. Tek bir hizmet birden fazla amaç türünü işleyebildiğinden, birden fazla IntentService bileşeni tanımlamak yerine birden fazla işlem dizesi tanımlamak daha kolaydır.

Bu kılavuzdaki örnek mesajlaşma uygulamasında, aşağıdaki kod örneğinde gösterildiği gibi gerekli iki işlem türü vardır: yanıtla ve okundu olarak işaretle.

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

Hizmeti oluşturun

Bu Action nesnelerini işleyen bir hizmet oluşturmak için, uygulamanız tarafından tanımlanan ve görüşmeyi tanımlayan rastgele bir veri yapısı olan görüşme kimliğine ihtiyacınız vardır. Ayrıca, bu bölümün ilerleyen kısımlarında ayrıntılı olarak ele alınan bir uzaktan giriş anahtarına da ihtiyacınız vardır. Aşağıdaki kod örneği, gerekli işlemleri gerçekleştirecek bir hizmet oluşturur:

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?) {
        // Fetches internal data.
        val conversationId = intent!!.getIntExtra(EXTRA_CONVERSATION_ID_KEY, -1)

        // Searches the database for that conversation.
        val conversation = YourAppConversation.getById(conversationId)

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

Bu hizmeti uygulamanızla ilişkilendirmek için hizmeti uygulamanızın manifest dosyasında da kaydetmeniz gerekir. Aşağıdaki örnekte gösterildiği gibi:

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

Intent oluşturma ve işleme

Android Auto da dahil olmak üzere diğer uygulamaların, Intent tetikleyen MessagingService elde etmesinin yolu yoktur. Bunun nedeni, Intent'ların diğer uygulamalara PendingIntent aracılığıyla iletilmesidir. Bu sınırlama nedeniyle, diğer uygulamaların yanıt metnini uygulamanıza geri göndermesine izin vermek için aşağıdaki örnekte gösterildiği gibi bir RemoteInput nesnesi oluşturmanız gerekir:

/**
 * Creates a [RemoteInput] that lets remote apps 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 using
    // static methods in the RemoteInput class.
}

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

    // Lets the MessagingService know this is a reply request.
    intent.action = ACTION_REPLY

    // Provides the ID of the conversation that the reply applies to.
    intent.putExtra(EXTRA_CONVERSATION_ID_KEY, appConversation.id)

    return intent
}

ACTION_REPLY switch ifadesinde, MessagingService içinde, aşağıdaki örnekte gösterildiği gibi, yanıta eklenecek bilgileri Intent ayıklayın:

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

    // This conversation object comes from the MessagingService.
    conversation.reply(message)
}

Okundu olarak işaretle Intent özelliğini de benzer şekilde kullanabilirsiniz. Ancak aşağıdaki örnekte gösterildiği gibi RemoteInput gerektirmez:

/** Creates an [Intent] that 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
}

MessagingService içindeki ACTION_MARK_AS_READ switch ifadesi, aşağıdaki örnekte gösterildiği gibi başka bir mantık gerektirmez:

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

Kullanıcıları mesajlar hakkında bilgilendirme

Konuşma işlemi işleme tamamlandıktan sonraki adım, Android Auto ile uyumlu bildirimler oluşturmaktır.

İşlem oluşturma

Action nesneleri, orijinal uygulamadaki yöntemleri tetiklemek için Notification kullanılarak diğer uygulamalara aktarılabilir. Android Auto, bu şekilde bir görüşmeyi okundu olarak işaretleyebilir veya yanıtlayabilir.

Action oluşturmak için Intent ile başlayın. Aşağıdaki örnekte, nasıl "yanıt" oluşturulacağı gösterilmektedir Intent:

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

Ardından, bu Intent öğesini PendingIntent içine alın. Bu işlem, öğeyi harici uygulama kullanımına hazırlar. PendingIntent, sarmalanmış Intent'ye erişimi yalnızca alıcı uygulamanın Intent'yi tetiklemesine veya kaynak uygulamanın paket adını almasına olanak tanıyan belirli bir yöntem grubu sunarak kısıtlar. Harici uygulama, temel Intent'ya veya içindeki verilere hiçbir zaman erişemez.

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

Yanıt Action özelliğini ayarlamadan önce Android Auto'nun yanıt Action için üç şartı olduğunu unutmayın:

  • Anlamsal işlem Action.SEMANTIC_ACTION_REPLY olarak ayarlanmalıdır.
  • Action, tetiklendiğinde herhangi bir kullanıcı arayüzü göstermeyeceğini belirtmelidir.
  • Action, tek bir RemoteInput içermelidir.

Aşağıdaki kod örneğinde, yukarıda listelenen şartları karşılayan bir yanıt Action oluşturulmaktadır:

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

        // The action doesn't show any UI, as required by 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
}

Okundu olarak işaretle işleminin işlenmesi de benzerdir ancak RemoteInput yoktur. Bu nedenle, Android Auto'nun okunmuş olarak işaretleme Action için iki şartı vardır:

  • Anlamsal işlem Action.SEMANTIC_ACTION_MARK_AS_READ olarak ayarlandı.
  • İşlem, tetiklendiğinde herhangi bir kullanıcı arayüzü göstermeyeceğini belirtir.

Aşağıdaki kod örneğinde, bu koşulları karşılayan bir okundu olarak işaretle Action ayarlanmaktadır:

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  or PendingIntent.FLAG_IMMUTABLE)
    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
}

Bekleyen amaçlar oluşturulurken iki yöntem kullanılır: createReplyId() ve createMarkAsReadId(). Bu yöntemler, her PendingIntent için istek kodları olarak işlev görür. Bu kodlar, Android tarafından mevcut bekleyen amaçları kontrol etmek için kullanılır. create() yöntemleri her görüşme için benzersiz kimlikler döndürmelidir ancak aynı görüşme için tekrarlanan çağrılar, daha önce oluşturulmuş benzersiz kimliği döndürmelidir.

A ve B olmak üzere iki görüşmenin olduğu bir örneği ele alalım: A görüşmesinin yanıt kimliği 100, okunmuş olarak işaretleme kimliği ise 101'dir. B görüşmesinin yanıt kimliği 102, okunmuş olarak işaretleme kimliği ise 103'tür. A ileti dizisi güncellenirse yanıtlama ve okundu olarak işaretleme kimlikleri 100 ve 101 olarak kalır. Daha fazla bilgi için PendingIntent.FLAG_UPDATE_CURRENT konusuna bakın.

MessagingStyle oluşturma

MessagingStyle, mesajlaşma bilgilerinin taşıyıcısıdır ve Android Auto, görüşmedeki her mesajı yüksek sesle okumak için bu bilgiyi kullanır.

İlk olarak, cihazın kullanıcısı aşağıdaki örnekte gösterildiği gibi bir Person nesnesi biçiminde belirtilmelidir:

fun createMessagingStyle(
        context: Context, appConversation: YourAppConversation): MessagingStyle {
    // Method defined by the 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()
    // ...

Ardından MessagingStyle nesnesini oluşturabilir ve görüşmeyle ilgili bazı ayrıntıları sağlayabilirsiniz.

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

    // Sets the conversation title. If the app's target version is lower
    // than P, this will automatically mark the conversation as a group (to
    // maintain backward compatibility). 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)
    // ...

Son olarak, okunmamış iletileri ekleyin.

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

        // Adds the message. More complex messages, like images,
        // can be created and added by instantiating the MessagingStyle.Message
        // class directly. See documentation for details.
        messagingStyle.addMessage(
                appMessage.body, appMessage.timeReceived, senderPerson)
    }

    return messagingStyle
}

Paketleyin ve bildirimi gönderin

Action ve MessagingStyle nesnelerini oluşturduktan sonra Notification öğesini oluşturup yayınlayabilirsiniz.

fun notify(context: Context, appConversation: YourAppConversation) {
    // Creates the actions and MessagingStyle.
    val replyAction = createReplyAction(context, appConversation)
    val markAsReadAction = createMarkAsReadAction(context, appConversation)
    val messagingStyle = createMessagingStyle(context, appConversation)

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

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

        // Adds MessagingStyle.
        .setStyle(messagingStyle)

        // Adds reply action.
        .addAction(replyAction)

        // Makes the mark-as-read action invisible, so it doesn't appear
        // in the Android UI but the app satisfies Android Auto's
        // mark-as-read Action requirement. Both required actions can be made
        // visible or invisible; it is a stylistic choice.
        .addInvisibleAction(markAsReadAction)

        .build()

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

Ek kaynaklar

Android Auto Mesajlaşma ile ilgili sorun bildirme

Android Auto için mesajlaşma uygulamanızı geliştirirken bir sorunla karşılaşırsanız Google Sorun İzleyici'yi kullanarak sorunu bildirebilirsiniz. Sorun şablonunda istenen tüm bilgileri doldurduğunuzdan emin olun.

Yeni sorun oluşturma

Yeni bir sorun kaydı oluşturmadan önce, sorunun sorunlar listesinde bildirilip bildirilmediğini kontrol edin. İzleyicideki bir sorunun yıldızını tıklayarak sorunlara abone olabilir ve oy verebilirsiniz. Daha fazla bilgi için Bir soruna abone olma başlıklı makaleyi inceleyin.