يعد البقاء على اتصال من خلال الرسائل أمرًا مهمًا للعديد من السائقين. يمكن لتطبيقات الدردشة أن تتيح للمستخدمين معرفة ما إذا كان الطفل بحاجة إلى الاصطحاب أو تغيير موقع العشاء. يسمح إطار عمل Android لتطبيقات المراسلة بتوسيع خدماتها لتشمل تجربة القيادة باستخدام واجهة مستخدم عادية تتيح للسائقين التركيز على الطريق.
يمكن للتطبيقات التي تتيح المراسلة توسيع إشعارات المراسلة الخاصة بها للسماح لتطبيق Android Auto باستهلاكها أثناء تشغيل Auto. يتم عرض هذه الإشعارات في "تلقائي" وتتيح للمستخدمين قراءة الرسائل والرد عليها من خلال واجهة متسقة ومنخفضة التشتيت. وعند استخدام MessagingStyle API، ستحصل على إشعارات مُحسَّنة بالرسائل لجميع أجهزة Android، بما في ذلك Android Auto. تتضمن التحسينات واجهة مستخدم متخصصة في إشعارات الرسائل والرسوم المتحركة المحسّنة وإتاحة الصور المضمّنة.
يوضح لك هذا الدليل كيفية توسيع تطبيق يعرض الرسائل للمستخدم ويتلقى ردود المستخدم، مثل تطبيق دردشة، لعرض الرسائل وإيصال الرد على جهاز Auto. للحصول على إرشادات التصميم ذات الصلة، راجع تطبيقات المراسلة على موقع "تصميم القيادة".
البدء
لتقديم خدمة المراسلة لأجهزة 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.
- يتلقّى تطبيقك رسالة.
- يُنشئ تطبيقك إشعار
MessagingStyle
يحتوي على كائناتAction
للردّ. - يتلقّى تطبيق Android Auto حدث "إشعار جديد" من نظام Android
ويعثر على
MessagingStyle
والردAction
ووضع علامة كـ "مقروءة"Action
. - ينشئ Android Auto إشعارًا في السيارة ويعرضه.
- إذا نقر المستخدم على الإشعار على شاشة السيارة، سيعرض Android Auto رمز "وضع علامة كمقروءة"
Action
.- في الخلفية، يجب أن يعالج تطبيقك حدث وضع علامة "مقروءة" على هذا الحدث.
- إذا ردّ المستخدم على الإشعار باستخدام الصوت، يرسل تطبيق 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 الأساسية
يتطلب إنشاء إشعارات لاستخدامها مع أجهزة Auto وجود مكتبة AndroidX الأساسية. قم باستيراد المكتبة إلى مشروعك على النحو التالي:
- في ملف
build.gradle
ذي المستوى الأعلى، عليك تضمين اعتمادية على مستودع Maven من Google، كما هو موضّح في المثال التالي:
رائع
allprojects { repositories { google() } }
Kotlin
allprojects { repositories { google() } }
- في ملف
build.gradle
لوحدة تطبيقك، يمكنك تضمين مكتبة AndroidX Core كما هو موضّح في المثال التالي:
رائع
dependencies { // If your app is written in Java implementation 'androidx.core:core:1.13.0' // If your app is written in Kotlin implementation 'androidx.core:core-ktx:1.13.0' }
Kotlin
dependencies { // If your app is written in Java implementation("androidx.core:core:1.13.0") // If your app is written in Kotlin implementation("androidx.core:core-ktx:1.13.0") }
معالجة إجراءات المستخدمين
يحتاج تطبيق المراسلة إلى طريقة لمعالجة تعديل المحادثة من خلال
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
}
عند إنشاء الأغراض في انتظار المراجعة، يتم استخدام طريقتَين:
createReplyId()
وcreateMarkAsReadId()
. تعمل هذه الطرق كرموز طلب لكل PendingIntent
والتي يستخدمها Android للتحكّم في الأغراض الحالية التي تنتظر المراجعة. يجب أن تعرض طرق create()
معرّفات فريدة لكل محادثة، ولكن يجب أن تعرض المكالمات المتكررة للمحادثة نفسها المعرّف الفريد الذي تم إنشاؤه مسبقًا.
فكر في مثال مع محادثتين، "أ" و"ب": معرف رد المحادثة "أ" هو 100، ومعرف العلامة كمقروءة هو 101. معرف الرد الخاص بالمحادثة "ب" هو 102
ومعرف العلامة كمقروءة هو 103. إذا تم تحديث المحادثة "أ"، فلا يزال معرفا
الرد وعلامة كـ "مقروءة" 100 و101. ولمزيد من المعلومات، يمكنك الاطّلاع على PendingIntent.FLAG_UPDATE_CURRENT
.
إنشاء نمط رسالة
إنّ MessagingStyle
هو مشغّل شبكة الجوّال لمعلومات المراسلة، وهو الجهاز الذي يستخدمه
Android Auto لقراءة كل رسالة في المحادثة بصوت عالٍ.
أولاً، يجب تحديد مستخدم الجهاز في شكل عنصر 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. احرص على ملء جميع المعلومات المطلوبة في نموذج المشكلة.
قبل تقديم مشكلة جديدة، تحقق مما إذا كان قد تم الإبلاغ عنها في قائمة المشكلات. يمكنك الاشتراك والتصويت للمشكلات من خلال النقر على رمز النجمة في أداة التتبع. لمزيد من المعلومات، راجِع الاشتراك في مشكلة.