إنشاء تطبيقات المراسلة لتطبيق Android Auto

ستتوفّر فئة "المراسلات" قريبًا
جارٍ توسيع فئة "المراسلة" لتشمل إمكانات جديدة، بما في ذلك سجلّ الرسائل وتجارب المكالمات

البقاء على اتصال من خلال الرسائل أمر مهم للعديد من السائقين. يمكن أن تسمح تطبيقات Chat للمستخدمين بمعرفة ما إذا كان طفلاً بحاجة إلى أن يتم اصطحابه معك أو ما إذا تم تغيير مكان العشاء. يتيح إطار عمل Android لتطبيقات المراسلة زيادة الخدمات في تجربة القيادة باستخدام واجهة مستخدم قياسية تتيح للسائقين إبقاء المستخدم على الطريق.

يمكن للتطبيقات التي تتيح المراسلة توسيع نطاق إشعارات المراسلة للسماح لتطبيق Android Auto. يستهلكها عند تشغيل Auto. يتم عرض هذه الإشعارات في "تلقائي" وتتيح للمستخدمين قراءة الرسائل والرد عليها بطريقة متسقة وقليلة تشتيت الانتباه. وعند استخدام MessagingStyle API: يمكنك الحصول على إشعارات محسَّنة للرسائل لجميع أجهزة Android الأجهزة، بما في ذلك Android Auto. تتضمن التحسينات واجهة مستخدم مخصصة وإشعارات الرسائل والرسوم المتحركة المحسّنة ودعم الصور المضمّنة.

يوضح لك هذا الدليل كيفية توسيع نطاق تطبيق يعرض الرسائل للمستخدم تلقّي ردود المستخدم، مثل تطبيق المحادثة، لعرض الرسائل إيقاف إيصال الرد على جهاز Auto. للحصول على إرشادات التصميم ذات الصلة، يُرجى الاطّلاع على المراسلة. التطبيقات على موقع Design for قيادة.

البدء

لتوفير خدمة المراسلة لأجهزة Auto، يجب أن يفصح تطبيقك عن التوافق مع Android Auto في ملف البيان وستتمكّن من إجراء ما يلي:

  • إنشاء وإرسال NotificationCompat.MessagingStyle العناصر التي تحتوي على عنصرَي "الرد" و"وضع علامة مقروءة" Action.
  • التعامل مع الرد ووضع علامة "مقروءة" على محادثة باستخدام Service

المفاهيم والكائنات

قبل البدء في تصميم تطبيقك، من المفيد التعرّف على آلية عمل Android Auto. المراسلة.

يُسمى قطعة الاتصال الفردية رسالة ويتم تمثيلها الفئة MessagingStyle.Message. تتضمن الرسالة المرسل، الرسالة والمحتوى ووقت إرسال الرسالة.

يُطلق على التواصل بين المستخدمين اسم محادثة ويتم تمثيله من خلال كائن MessagingStyle. تحتوي المحادثة أو MessagingStyle على عنوان الرسائل، وما إذا كانت المحادثة بين مجموعة من المستخدمين.

لإعلام المستخدمين بالتعديلات التي تطرأ على المحادثة، مثل رسالة جديدة أو نشر تطبيقات Notification إلى نظام Android. يستخدم Notification هذا الكائن MessagingStyle لعرض الرسائل الخاصة واجهة مستخدم في مركز الإشعارات يجتاز نظام Android الأساسي هذا Notification أيضًا. إلى Android Auto، وسيتم استخراج MessagingStyle واستخدامه لنشر عبر شاشة السيارة.

يتطلّب Android Auto أيضًا من التطبيقات إضافة عناصر Action إلى Notification من أجل يمكنك السماح للمستخدم بالرد سريعًا على رسالة أو وضع علامة "مقروءة" عليها مباشرةً من مركز الإشعارات

باختصار، يتم تمثيل محادثة واحدة بواسطة Notification تم تصميمه باستخدام كائن MessagingStyle. MessagingStyle يحتوي على جميع الرسائل في تلك المحادثة في واحدة أو أكثر MessagingStyle.Message عناصر ولضمان الامتثال إلى Android Auto، يجب إرفاق عناصر Action التي يتم الردّ عليها ووضع علامة "مقروءة" عليها في Notification.

مسار المراسلة

يصف هذا القسم مسار المراسلة النموذجي بين تطبيقك وAndroid Auto.

  1. يتلقّى تطبيقك رسالة.
  2. ينشئ تطبيقك إشعارًا بشأن "MessagingStyle" مع الردّ و عناصر Action التي يتم وضع علامة عليها كمقروءة
  3. يتلقّى Android Auto حدث "إشعار جديد" من نظام Android. والعثور على MessagingStyle، والرد Action، ووضع علامة مقروءة على Action.
  4. ينشئ Android Auto إشعارًا ويعرضه في السيارة.
  5. إذا نقر المستخدم على الإشعار على شاشة السيارة، يمكن استخدام Android Auto. يؤدي إلى ظهور علامة "مقروءة" Action.
    • وفي الخلفية، يجب أن يتعامل تطبيقك مع حدث وضع علامة كمقروءة.
  6. إذا استجاب المستخدم للإشعار باستخدام الصوت، سيضع Android Auto يتم تحويل ردّ المستخدم إلى نص في الرد 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)

الإفصاح عن دعم Android Auto

عندما يتلقّى Android Auto إشعارًا من أحد تطبيقات المراسلة، يتحقّق مما يلي: أعلن التطبيق عن توافقه مع Android Auto. لتفعيل هذا الدعم، يجب تضمين الإدخال التالي في بيان التطبيق:

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

يشير إدخال البيان هذا إلى ملف XML آخر يجب إنشاؤه باستخدام المسار التالي: YourAppProject/app/src/main/res/xml/automotive_app_desc.xml. في automotive_app_desc.xml، يُرجى توضيح إمكانيات Android Auto التي يوفّرها تطبيقك. والدعم. على سبيل المثال، للإبلاغ عن إتاحة الإشعارات، يجب تضمين التالي:

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

إذا كان من الممكن ضبط تطبيقك باعتباره المعالج التلقائي للرسائل القصيرة، تأكَّد من تضمين عنصر <uses> التالي. إذا لم تفعل ذلك، فإن سيتم استخدام المعالِج المُدمَج في Android Auto للتعامل مع رسائل SMS أو رسائل الوسائط المتعددة الواردة. عند ضبط تطبيقك كمعالج تلقائي للرسائل القصيرة، ما قد يؤدي إلى تكرار الإشعارات.

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

استيراد مكتبة AndroidX الأساسية

يجب أن يكون إنشاء الإشعارات لاستخدامها مع الأجهزة التلقائية مكتبة AndroidX الأساسية. استيراد المكتبة إلى للمشروع على النحو التالي:

  1. في ملف build.gradle ذي المستوى الأعلى، أضِف تبعية إلى Maven من Google. المستودع، كما هو موضح في المثال التالي:

Groovy

allprojects {
    repositories {
        google()
    }
}

Kotlin

allprojects {
    repositories {
        google()
    }
}
  1. في ملف build.gradle الخاص بوحدة تطبيقك، ضمِّن AndroidX Core. تبعية المكتبة، كما هو موضح في المثال التالي:

Groovy

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

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

Kotlin

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

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

التعامل مع إجراءات المستخدمين

يحتاج تطبيق المراسلة إلى طريقة للتعامل مع تحديث محادثة من خلال Action بالنسبة إلى Android Auto، هناك نوعان من عناصر Action في تطبيقك. يحتاج إلى معالجة الرد: "الرد" و"وضع علامة مقروءة". لذلك ننصحك بالتعامل معها باستخدام IntentService، المرونة في التعامل مع المكالمات في الخلفية، ما يؤدي إلى تفريغ سلسلة المحادثات الرئيسية لتطبيقك.

تحديد إجراءات الأهداف

إجراءات 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>

إنشاء الأهداف ومعالجتها

وليست هناك طريقة يمكن بها للتطبيقات الأخرى، بما فيها 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 إلى لتشغيل الطرق في التطبيق الأصلي. هذه هي الطريقة التي يمكن بها لتطبيق Android Auto وضع علامة محادثة كمقروءة أو الرد عليها.

لإنشاء Action، ابدأ بـ Intent. يوضح المثال التالي كيفية إنشاء "ردّ" Intent:

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، يجب الانتباه إلى أنّ ميزة Android Auto تحتوي على ثلاث متطلبات الرد Action:

  • ويجب ضبط الإجراء الدلالي على Action.SEMANTIC_ACTION_REPLY.
  • يجب أن يشير Action إلى أنّه لن يعرض أي واجهة مستخدم عند تنشيطه.
  • يجب أن يحتوي Action على RemoteInput واحد.

يعمل نموذج الرمز البرمجي التالي على إعداد ردّ 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. لذلك، يشتمل تطبيق Android Auto على متطلبَين لميزة "وضع علامة مقروءة" 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 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
}

عند إنشاء عناصر intent في انتظار المراجعة، يتم استخدام طريقتين: "createReplyId()" وcreateMarkAsReadId()" تعمل هذه الطرق طلب الرموز لكل PendingIntent، ويستخدمها Android للتحكّم الأهداف المعلّقة الحالية يجب أن تتضمن طرق create() عرض معرّفات فريدة لكل محادثة، ولكن مع تكرار المكالمات نفسها يجب أن تعرض المحادثة المعرف الفريد الذي تم إنشاؤه مسبقًا.

ضع في الاعتبار مثالاً مع محادثتين، "أ" و"ب": معرف رد المحادثة "أ" هو 100، ومعرف العلامة كمقروءة هو 101. معرِّف رد المحادثة "ب" هو 102، ورقم تعريف العلامة كمقروءة هو 103. إذا تم تحديث المحادثة "أ"، لا يزال رقما تعريف الرد والعلامة مقروءة 100 و101. لمزيد من المعلومات، يُرجى مراجعة PendingIntent.FLAG_UPDATE_CURRENT

إنشاء أسلوب مراسلة

MessagingStyle هو مشغّل شبكة معلومات المراسلة وهو Android يُستخدم الإعداد التلقائي لقراءة كل رسالة في المحادثة بصوت عالٍ.

أولاً، يجب تحديد مستخدم الجهاز في شكل 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"

إذا واجهت مشكلة أثناء تطوير تطبيق المراسلة المتوافق مع Android Auto، يمكنك الإبلاغ عن ذلك باستخدام أداة تتبُّع المشاكل من Google تأكَّد من ملء جميع المعلومات المطلوبة في نموذج المشكلة.

إنشاء عدد جديد

قبل تقديم مشكلة جديدة، تأكَّد مما إذا كان قد سبق الإبلاغ عنها ضمن المشاكل. الحالية. يمكنك الاشتراك والتصويت للمشكلات من خلال النقر على النجمة لمشكلة في لجهاز التتبع. لمزيد من المعلومات، يُرجى مراجعة الاشتراك في مشكلة: