Приложения, поддерживающие обмен сообщениями, могут расширить свои уведомления, чтобы Android Auto мог их отображать во время работы. Эти уведомления отображаются Android Auto и позволяют пользователям читать сообщения и отвечать на них в едином, не отвлекающем интерфейсе. А при использовании API MessagingStyle вы получаете оптимизированные уведомления о сообщениях для всех устройств Android, включая Android Auto. Оптимизация включает в себя специализированный пользовательский интерфейс для уведомлений о сообщениях, улучшенную анимацию и поддержку встроенных изображений.
В этом руководстве показано, как расширить функциональность приложения, отображающего сообщения пользователю и получающего ответы, например, чата, чтобы передать отображение сообщений и получение ответов в Android Auto. Благодаря этой интеграции пользователи смогут видеть историю сообщений только из уведомлений, полученных во время активной сессии Android Auto. Чтобы отображать сообщения, полученные до начала активной сессии Android Auto, можно создать шаблон для обмена сообщениями .
Дополнительные рекомендации по дизайну см. в разделе «Коммуникационные приложения» в центре «Дизайн для автомобилей».
Начать
Для предоставления службы обмена сообщениями для Android Auto ваше приложение должно указать поддержку Android 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, приложение должно прикрепить к Notification объекты Action «ответить» и «пометить как прочитанное».
Поток обмена сообщениями
В этом разделе описывается типичный процесс обмена сообщениями между вашим приложением и Android Auto.
- Ваше приложение получает сообщение.
- Ваше приложение генерирует уведомление
MessagingStyle, содержащее объектыAction«Ответить» и «Пометить как прочитанное». - Android Auto получает событие «новое уведомление» от системы Android и определяет тип
MessagingStyle,Actionответа (reply Action) иActionпометки как прочитанного (mark-as-read 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-файл, automotive_app_desc.xml , который необходимо создать в каталоге res/xml вашего модуля приложения. В файле automotive_app_desc.xml укажите возможности Android Auto, поддерживаемые вашим приложением. Чтобы указать поддержку уведомлений, включите следующее:
<automotiveApp>
<uses name="notification" />
</automotiveApp>
Если ваше приложение может быть установлено в качестве обработчика SMS по умолчанию , обязательно добавьте следующий элемент <uses> . В противном случае Android Auto будет использовать свой встроенный обработчик по умолчанию для обработки входящих SMS/MMS-сообщений, когда ваше приложение установлено в качестве обработчика SMS по умолчанию, что может привести к дублированию уведомлений.
<automotiveApp>
...
<uses name="sms" />
</automotiveApp>
Импортируйте основную библиотеку AndroidX.
Для создания уведомлений для использования с Android Auto требуется основная библиотека AndroidX . Импортируйте библиотеку в свой проект следующим образом:
- В файле
build.gradleверхнего уровня укажите зависимость от репозитория Maven от Google, как показано в следующем примере:
Классный
allprojects { repositories { google() } }
Котлин
allprojects { repositories { google() } }
- В файле
build.gradleвашего модуля приложения добавьте зависимость от основной библиотеки AndroidX , как показано в следующем примере:
Классный
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 . Для Android Auto существует два типа объектов Action , которые ваше приложение должно обрабатывать: ответ и пометка как прочитанное. Мы рекомендуем обрабатывать их с помощью 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 , вам потребуется идентификатор беседы (consover ID), представляющий собой произвольную структуру данных, определенную вашим приложением и идентифицирующую беседу. Вам также потребуется ключ удаленного ввода (remote input key), который подробно рассматривается далее в этом разделе. Следующий пример кода создает сервис для обработки необходимых действий:
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 «ответ» (Reply Intent) с помощью метода 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 ответа, учтите, что 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 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
}
При создании ожидающих запросов используются два метода: createReplyId() и createMarkAsReadId() . Эти методы служат кодами запроса для каждого PendingIntent , которые используются Android для управления существующими ожидающими запросами. Методы create() должны возвращать уникальные идентификаторы для каждого диалога, но повторные вызовы для одного и того же диалога должны возвращать уже сгенерированный уникальный идентификатор.
Рассмотрим пример с двумя диалогами, A и B: идентификатор ответа в диалоге A равен 100, а идентификатор пометки как прочитанного — 101. Идентификатор ответа в диалоге B равен 102, а идентификатор пометки как прочитанного — 103. Если диалог A обновляется, идентификаторы ответа и пометки как прочитанного остаются равными 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 . Обязательно заполните всю необходимую информацию в шаблоне сообщения об ошибке.
Перед созданием новой темы проверьте, не была ли она уже зарегистрирована в списке тем. Вы можете подписаться на темы и голосовать за них, нажав на звездочку рядом с темой в трекере. Для получения дополнительной информации см. раздел «Подписка на тему» .