اعلان‌های پیام‌رسانی را به Android Auto گسترش دهید

برنامه‌هایی که از پیام‌رسانی پشتیبانی می‌کنند، می‌توانند اعلان‌های پیام‌رسانی خود را گسترش دهند تا به Android Auto اجازه دهند هنگام اجرا، آنها را دریافت کند. این اعلان‌ها توسط Android Auto نمایش داده می‌شوند و به کاربران اجازه می‌دهند پیام‌ها را در یک رابط کاربری ثابت و کم‌حواس‌پرتی بخوانند و به آنها پاسخ دهند. و هنگامی که از API MessagingStyle استفاده می‌کنید، اعلان‌های پیام بهینه‌شده‌ای را برای همه دستگاه‌های Android، از جمله Android Auto، دریافت خواهید کرد. این بهینه‌سازی‌ها شامل یک رابط کاربری تخصصی برای اعلان‌های پیام، انیمیشن‌های بهبود یافته و پشتیبانی از تصاویر درون‌خطی است.

این راهنما به شما نشان می‌دهد که چگونه یک برنامه که پیام‌ها را به کاربر نمایش می‌دهد و پاسخ‌های کاربر را دریافت می‌کند، مانند یک برنامه چت، را طوری توسعه دهید که نمایش پیام و دریافت پاسخ را به Android Auto ارسال کند. از طریق این ادغام، کاربران فقط می‌توانند تاریخچه پیام‌ها را از اعلان‌های دریافت شده در طول جلسه فعال Android Auto خود مشاهده کنند. برای نمایش پیام‌ها از قبل از شروع جلسه فعال Android Auto، می‌توانید یک تجربه پیام‌رسانی قالب‌بندی شده ایجاد کنید .

برای راهنمایی‌های مرتبط با طراحی، به برنامه‌های ارتباطی در بخش طراحی برای خودروها مراجعه کنید.

شروع کنید

برای ارائه سرویس پیام‌رسانی برای Android Auto، برنامه شما باید پشتیبانی خود را از Android Auto در مانیفست اعلام کند و بتواند موارد زیر را انجام دهد:

مفاهیم و اشیاء

قبل از شروع طراحی برنامه، درک نحوه مدیریت پیام‌رسانی در اندروید اتو مفید است.

یک بخش مجزا از ارتباط ، پیام (messagingStyle.Message) نامیده می‌شود و توسط کلاس MessagingStyle.Message نمایش داده می‌شود. یک پیام شامل فرستنده، محتوای پیام و زمان ارسال پیام است.

ارتباط بین کاربران، مکالمه نامیده می‌شود و توسط یک شیء MessagingStyle نمایش داده می‌شود. یک مکالمه یا MessagingStyle شامل یک عنوان، پیام‌ها و اینکه آیا مکالمه بین گروهی از کاربران است یا خیر، می‌باشد.

برای اطلاع‌رسانی به کاربران در مورد به‌روزرسانی‌های یک مکالمه، مانند یک پیام جدید، برنامه‌ها یک Notification به سیستم اندروید ارسال می‌کنند. این Notification از شیء MessagingStyle برای نمایش رابط کاربری مخصوص پیام‌رسانی در پنل اعلان‌ها استفاده می‌کند. پلتفرم اندروید نیز این Notification به Android Auto ارسال می‌کند و MessagingStyle استخراج شده و برای ارسال اعلان روی صفحه نمایش خودرو استفاده می‌شود.

اندروید اتو همچنین از برنامه‌ها می‌خواهد که اشیاء Action را به Notification اضافه کنند تا کاربر بتواند مستقیماً از صفحه نمایش خودرو به یک پیام پاسخ دهد یا آن را به عنوان خوانده شده علامت‌گذاری کند.

به طور خلاصه، یک مکالمه واحد توسط یک شیء Notification نمایش داده می‌شود که با یک شیء MessagingStyle استایل‌بندی شده است. MessagingStyle شامل تمام پیام‌های درون آن مکالمه در یک یا چند شیء MessagingStyle.Message است. و برای سازگاری با Android Auto، یک برنامه باید اشیاء reply و mark-as-read Action به Notification پیوست کند.

جریان پیام‌رسانی

این بخش یک جریان پیام‌رسانی معمول بین برنامه شما و Android Auto را شرح می‌دهد.

  1. برنامه شما یک پیام دریافت می‌کند.
  2. برنامه شما یک اعلان MessagingStyle با اشیاء پاسخ و علامت‌گذاری به عنوان خوانده شده Action تولید می‌کند.
  3. اندروید اتو رویداد "اعلان جدید" را از سیستم اندروید دریافت می‌کند و MessagingStyle ، reply Action و mark-as-read Action پیدا می‌کند.
  4. اندروید اتو یک اعلان (نوتیفیکیشن) در خودرو تولید و نمایش می‌دهد.
  5. اگر کاربر روی اعلان روی صفحه نمایش خودرو ضربه بزند، اندروید اتو Action علامت‌گذاری به عنوان خوانده شده را فعال می‌کند.
    • در پس‌زمینه، برنامه شما باید این رویداد علامت‌گذاری به عنوان خوانده شده را مدیریت کند.
  6. اگر کاربر با استفاده از صدا به اعلان پاسخ دهد، اندروید اتو رونوشتی از پاسخ کاربر را در Action پاسخ قرار می‌دهد و سپس آن را فعال می‌کند.
    • در پس‌زمینه، برنامه شما باید این رویداد پاسخ را مدیریت کند.

فرضیات اولیه

این صفحه شما را در ایجاد کل یک برنامه پیام‌رسان راهنمایی نمی‌کند. نمونه کد زیر شامل برخی از مواردی است که برنامه شما قبل از شروع پشتیبانی از پیام‌رسان با 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)

اعلام پشتیبانی از اندروید اتو

وقتی اندروید اتو از یک برنامه پیام‌رسان اعلانی دریافت می‌کند، بررسی می‌کند که آیا برنامه از اندروید اتو پشتیبانی می‌کند یا خیر. برای فعال کردن این پشتیبانی، ورودی زیر را در مانیفست برنامه خود وارد کنید:

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

این ورودی مانیفست به یک فایل XML دیگر به automotive_app_desc.xml اشاره دارد که باید در دایرکتوری res/xml ماژول برنامه خود ایجاد کنید. در automotive_app_desc.xml ، قابلیت‌های Android Auto که برنامه شما پشتیبانی می‌کند را اعلام کنید. برای اعلام پشتیبانی از اعلان‌ها، موارد زیر را وارد کنید:

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

اگر برنامه شما می‌تواند به عنوان کنترل‌کننده پیش‌فرض پیامک تنظیم شود، حتماً عنصر <uses> زیر را اضافه کنید. در غیر این صورت، اندروید اتو از کنترل‌کننده پیش‌فرض داخلی خود برای مدیریت پیامک‌ها/MMSهای دریافتی استفاده می‌کند، زمانی که برنامه شما به عنوان کنترل‌کننده پیش‌فرض پیامک تنظیم شده باشد، که می‌تواند منجر به دریافت اعلان‌های تکراری شود.

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

کتابخانه اصلی AndroidX را وارد کنید

ساخت اعلان‌ها برای استفاده با Android Auto به کتابخانه اصلی AndroidX نیاز دارد. کتابخانه را به صورت زیر به پروژه خود وارد کنید:

  1. در فایل build.gradle سطح بالا، یک وابستگی به مخزن Maven گوگل اضافه کنید، همانطور که در مثال زیر نشان داده شده است:

گرووی

allprojects {
    repositories {
        google()
    }
}

کاتلین

allprojects {
    repositories {
        google()
    }
}
  1. در فایل build.gradle ماژول برنامه خود، وابستگی کتابخانه AndroidX Core را وارد کنید، همانطور که در مثال زیر نشان داده شده است:

گرووی

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

کاتلین

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

رسیدگی به اقدامات کاربر

برنامه پیام‌رسان شما به روشی برای مدیریت به‌روزرسانی مکالمه از طریق یک Action نیاز دارد. برای اندروید اتو، دو نوع شیء Action وجود دارد که برنامه شما باید مدیریت کند: پاسخ (reply) و علامت‌گذاری به عنوان خوانده شده (mark-as-read). توصیه می‌کنیم آنها را با استفاده از IntentService مدیریت کنید، که انعطاف‌پذیری لازم برای مدیریت فراخوانی‌های بالقوه پرهزینه در پس‌زمینه را فراهم می‌کند و نخ اصلی برنامه شما را آزاد می‌کند.

تعریف اقدامات هدفمند

اکشن‌های Intent رشته‌های پایه‌ای هستند که شما انتخاب می‌کنید و مشخص می‌کنند که Intent برای چیست. از آنجایی که یک سرویس واحد می‌تواند چندین نوع Intent را مدیریت کند، تعریف چندین رشته اکشن به جای تعریف چندین کامپوننت IntentService آسان‌تر است.

برنامه پیام‌رسان نمونه این راهنما، دو نوع اقدام الزامی دارد: پاسخ دادن و علامت‌گذاری به عنوان خوانده شده، همانطور که در نمونه کد زیر نشان داده شده است:

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

سرویس را ایجاد کنید

برای ایجاد سرویسی که این اشیاء Action را مدیریت کند، به شناسه مکالمه نیاز دارید که یک ساختار داده دلخواه است که توسط برنامه شما تعریف شده و مکالمه را شناسایی می‌کند. همچنین به یک کلید ورودی از راه دور نیاز دارید که بعداً در این بخش به تفصیل مورد بحث قرار خواهد گرفت. نمونه کد زیر سرویسی را برای مدیریت اقدامات مورد نیاز ایجاد می‌کند:

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

برای مرتبط کردن این سرویس با برنامه خود، باید سرویس را در مانیفست برنامه خود نیز ثبت کنید، همانطور که در مثال زیر نشان داده شده است:

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

تولید و مدیریت intentها

برنامه‌های دیگر، از جمله Android Auto، نمی‌توانند Intent ای را که MessagingService فعال می‌کند، دریافت کنند زیرا Intentها از طریق PendingIntent به برنامه‌های دیگر منتقل می‌شوند. با توجه به این محدودیت، یک شیء RemoteInput ایجاد کنید تا به برنامه‌های دیگر اجازه دهید متن پاسخ را به برنامه شما ارائه دهند، همانطور که در مثال زیر نشان داده شده است:

/**
 * 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 درون MessagingService ، اطلاعاتی را که در پاسخ Intent قرار می‌گیرد، همانطور که در مثال زیر نشان داده شده است، استخراج کنید:

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

شما با قصد Intent به عنوان خوانده شده به روشی مشابه برخورد می‌کنید. با این حال، همانطور که در مثال زیر نشان داده شده است، به RemoteInput نیازی ندارد:

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

عبارت سوئیچ ACTION_MARK_AS_READ درون MessagingService نیازی به منطق بیشتری ندارد، همانطور که در مثال زیر نشان داده شده است:

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

اطلاع رسانی به کاربران در مورد پیام ها

پس از اتمام مدیریت عملیات مکالمه، مرحله بعدی تولید اعلان‌های سازگار با Android Auto است.

ایجاد اقدامات

اشیاء Action را می‌توان با استفاده از Notification به برنامه‌های دیگر منتقل کرد تا متدها را در برنامه اصلی فعال کنند. اینگونه است که اندروید اتو می‌تواند یک مکالمه را به عنوان خوانده شده علامت‌گذاری کند یا به آن پاسخ دهد.

برای ایجاد یک Action ، با یک Intent شروع کنید. مثال زیر نحوه ایجاد یک Intent نوع "reply" را با استفاده از متد createReplyIntent() از بخش قبل نشان می‌دهد:

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

سپس، این Intent در یک PendingIntent قرار دهید، که آن را برای استفاده برنامه خارجی آماده می‌کند. PendingIntent با افشای مجموعه‌ای از متدهای منتخب که به برنامه گیرنده اجازه می‌دهد Intent اجرا کند یا نام بسته برنامه مبدا را دریافت کند، تمام دسترسی به Intent قرار گرفته در آن را قفل می‌کند. برنامه خارجی نمی‌تواند به Intent اصلی یا داده‌های درون آن دسترسی پیدا کند.

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

قبل از تنظیم Action پاسخ، توجه داشته باشید که اندروید اتو سه الزام برای Action پاسخ دارد:

  • عمل معنایی باید روی Action.SEMANTIC_ACTION_REPLY تنظیم شود.
  • Action باید نشان دهد که هنگام اجرا هیچ رابط کاربری نمایش داده نخواهد شد.
  • Action باید شامل یک RemoteInput واحد باشد.

نمونه کد زیر یک Action پاسخ (response Action) تنظیم می‌کند که الزامات ذکر شده قبلی را برطرف می‌کند:

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

مدیریت عمل علامت‌گذاری به عنوان خوانده شده مشابه است، با این تفاوت که RemoteInput وجود ندارد. بنابراین، اندروید اتو دو الزام برای Action علامت‌گذاری به عنوان خوانده شده دارد:

  • عمل معنایی روی Action.SEMANTIC_ACTION_MARK_AS_READ تنظیم شده است.
  • این اکشن نشان می‌دهد که هنگام اجرا، هیچ رابط کاربری نمایش داده نخواهد شد.

نمونه کد زیر یک Action علامت‌گذاری به عنوان خوانده شده را تنظیم می‌کند که این الزامات را برآورده می‌کند:

fun createMarkAsReadAction(
        context: Context, appConversation: YourAppConversation): Action {
    val markAsReadIntent = createMarkAsReadIntent(context, appConversation)
    val markAsReadPendingIntent = PendingIntent.getService(
            context,
            createMarkAsReadId(appConversation), // Method explained later.
            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
}

وقتی شما intent های در حال انتظار را ایجاد می‌کنید، از دو متد استفاده می‌کنید: createReplyId() و createMarkAsReadId() . این متدها به عنوان کدهای درخواست برای هر PendingIntent عمل می‌کنند که توسط اندروید برای کنترل intent های در حال انتظار موجود استفاده می‌شوند. متدهای create() باید برای هر مکالمه، ID های منحصر به فردی را برگردانند، اما فراخوانی‌های مکرر برای همان مکالمه باید ID منحصر به فردی را که قبلاً تولید شده است، برگردانند.

مثالی با دو مکالمه A و B را در نظر بگیرید: شناسه پاسخ مکالمه A برابر با ۱۰۰ و شناسه علامت‌گذاری شده به عنوان خوانده شده آن ۱۰۱ است. شناسه پاسخ مکالمه B برابر با ۱۰۲ و شناسه علامت‌گذاری شده به عنوان خوانده شده آن ۱۰۳ است. اگر مکالمه A به‌روزرسانی شود، شناسه‌های پاسخ و علامت‌گذاری شده به عنوان خوانده شده همچنان ۱۰۰ و ۱۰۱ هستند. برای اطلاعات بیشتر، به PendingIntent.FLAG_UPDATE_CURRENT مراجعه کنید.

ایجاد یک سبک پیام‌رسانی

MessagingStyle حامل اطلاعات پیام‌رسانی است و همان چیزی است که اندروید اتو برای خواندن هر پیام در یک مکالمه با صدای بلند از آن استفاده می‌کند.

ابتدا، باید کاربر دستگاه را به عنوان یک شیء Person مشخص کنید، همانطور که در مثال زیر نشان داده شده است:

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()
    // ...

سپس می‌توانید شیء MessagingStyle را بسازید و جزئیاتی در مورد مکالمه ارائه دهید.

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

در نهایت، پیام‌های خوانده نشده را اضافه کنید.

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

اعلان را بسته‌بندی و ارسال کنید

پس از تولید اشیاء Action و MessagingStyle ، می‌توانید Notification را ساخته و ارسال کنید.

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

منابع اضافی

گزارش مشکل اعلان پیام‌رسانی خودکار اندروید

اگر هنگام توسعه اعلان‌های پیام‌رسانی برای Android Auto با مشکلی مواجه شدید، می‌توانید آن را با استفاده از ردیاب مشکلات گوگل گزارش دهید. حتماً تمام اطلاعات درخواستی را در الگوی مشکل پر کنید.

ایجاد یک مسئله جدید

قبل از ثبت یک مشکل جدید، بررسی کنید که آیا قبلاً در لیست مشکلات گزارش شده است یا خیر. می‌توانید با کلیک روی ستاره برای یک مشکل در ردیاب، مشترک شوید و به مشکلات رأی دهید. برای اطلاعات بیشتر، به «اشتراک در یک مشکل» مراجعه کنید.