Kontaktowanie się za pomocą wiadomości jest ważne dla wielu kierowców. Aplikacje do czatu mogą informować użytkowników, jeśli dziecko potrzebuje odebrania lub jeśli zmieniła się lokalizacja spotkania. Platforma Androida umożliwia aplikacjom do obsługi wiadomości rozszerzanie zakresu usług na potrzeby jazdy za pomocą standardowego interfejsu użytkownika, który pozwala kierowcom zachować wzrok na drodze.
Aplikacje obsługujące wiadomości mogą rozszerzać swoje powiadomienia o wiadomościach, aby Android Auto mógł je wyświetlać, gdy aplikacja jest uruchomiona. Powiadomienia te wyświetlają się w trybie automatycznym i umożliwiają użytkownikom czytanie oraz odpowiadanie na wiadomości w spójnym, nierozpraszającym interfejsie. Gdy używasz interfejsu MessagingStyle API, otrzymujesz zoptymalizowane powiadomienia o wiadomościach na wszystkich urządzeniach z Androidem, w tym na Androidzie Auto. Optymalizacje obejmują interfejs dostosowany do powiadomień o wiadomościach, ulepszone animacje i obsługę obrazów w tekście.
Z tego przewodnika dowiesz się, jak rozszerzyć aplikację, która wyświetla wiadomości użytkownikowi i otrzymuje od niego odpowiedzi, np. aplikację do czatu, o możliwość wyświetlania wiadomości i przekazywania potwierdzenia odpowiedzi urządzeniu Auto. Powiązane wskazówki dotyczące projektowania znajdziesz w sekcji Aplikacje do obsługi wiadomości na stronie Design for Driving.
.Rozpocznij
Aby udostępniać usługę przesyłania wiadomości na urządzeniach Auto, aplikacja musi w pliku manifestu zadeklarować obsługę Androida Auto i mieć możliwość wykonywania tych czynności:
- Tworzenie i wysyłanie obiektów
NotificationCompat.MessagingStyle
, które zawierają obiekty odpowiedzi i oznaczone jako przeczytaneAction
. - Odpowiadanie i oznaczanie rozmowy jako przeczytanej za pomocą
Service
.
Pojęcia i obiekty
Zanim zaczniesz projektować aplikację, warto dowiedzieć się, jak Android Auto obsługuje wiadomości.
Pojedynczy element komunikacji to wiadomość, która jest reprezentowana przez klasę MessagingStyle.Message
. Wiadomość zawiera nadawcę, treść wiadomości oraz godzinę wysłania.
Komunikacja między użytkownikami nazywana jest konwersacją i jest reprezentowana przez obiekt MessagingStyle
. Rozmowa (MessagingStyle
) zawiera tytuł, wiadomości oraz informacje o tym, czy rozmowa dotyczy grupy użytkowników.
Aby powiadomić użytkowników o aktualizacjach rozmowy, np. o nowej wiadomości, aplikacje wysyłają Notification
do systemu Android.
Ten obiekt Notification
używa obiektu MessagingStyle
do wyświetlania interfejsu użytkownika dotyczącego wiadomości w pasku powiadomień. Platforma Androida przekazuje też ten Notification
do Androida Auto, a MessagingStyle
jest wyodrębniany i używany do wyświetlania powiadomień na wyświetlaczu samochodu.
Android Auto wymaga też, aby aplikacje dodawały obiekty Action
do obiektu Notification
, aby użytkownik mógł szybko odpowiadać na wiadomości lub oznaczać je jako przeczytane bezpośrednio z paska powiadomień.
Podsumowując, pojedyncza rozmowa jest reprezentowana przez obiekt Notification
, który jest stylizowany za pomocą obiektu MessagingStyle
. Obiekt MessagingStyle
zawiera wszystkie wiadomości z tej rozmowy w co najmniej 1 obiekcie MessagingStyle.Message
. Aby aplikacja była zgodna z Androidem Auto, musi dołączyć do obiektu Notification
obiekty odpowiedzi i oznaczone jako przeczytane Action
.
Proces przesyłania wiadomości
W tej sekcji opisano typowy przepływ wiadomości między aplikacją a Androidem Auto.
- Aplikacja otrzymuje wiadomość.
- Twoja aplikacja generuje powiadomienie
MessagingStyle
z opcjami odpowiedzi i oznaczania jako przeczytaneAction
. - Android Auto otrzymuje zdarzenie „nowe powiadomienie” od systemu Androida
i odnajduje
MessagingStyle
, odpowiedźAction
i oznaczenie jako przeczytaneAction
. - Android Auto generuje i wyświetla powiadomienie w samochodzie.
- Jeśli użytkownik kliknie powiadomienie na wyświetlaczu samochodu, Android Auto uruchamia funkcję oznaczenia jako przeczytane
Action
.- Aplikacja musi obsługiwać to zdarzenie oznaczenia jako przeczytane w tle.
- Jeśli użytkownik odpowie na powiadomienie głosowo, Android Auto wstawi transkrypcję odpowiedzi użytkownika do odpowiedzi
Action
, a następnie ją uruchomi.- W tle aplikacja musi obsłużyć to zdarzenie odpowiedzi.
Założenia wstępne
Ta strona nie zawiera instrukcji tworzenia całej aplikacji do obsługi wiadomości. Poniższy przykład kodu zawiera niektóre elementy, których potrzebuje Twoja aplikacja, zanim zaczniesz obsługiwać wiadomości w Androidzie 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)
Zadeklarowanie obsługi Androida Auto
Gdy Android Auto otrzyma powiadomienie z aplikacji do obsługi wiadomości, sprawdzi, czy aplikacja ma deklarowaną obsługę Androida Auto. Aby włączyć tę obsługę, dodaj w pliku manifestu aplikacji ten wpis:
<application>
...
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
...
</application>
Ten wpis manifestu odnosi się do innego pliku XML, który musisz utworzyć na tej ścieżce: YourAppProject/app/src/main/res/xml/automotive_app_desc.xml
.
W automotive_app_desc.xml
zadeklaruj funkcje Androida Auto, które obsługuje Twoja aplikacja. Aby na przykład zadeklarować obsługę powiadomień, podaj:
<automotiveApp>
<uses name="notification" />
</automotiveApp>
Jeśli aplikacja może być ustawiona jako domyślny menedżer SMS-ów, uwzględnij element <uses>
. Jeśli tego nie zrobisz, domyślny moduł obsługi wbudowany w Androida Auto będzie używany do obsługi przychodzących SMS-ów i MMS-ów, gdy Twoja aplikacja jest ustawiona jako domyślny moduł obsługi SMS-ów, co może powodować duplikowanie powiadomień.
<automotiveApp>
...
<uses name="sms" />
</automotiveApp>
Importowanie podstawowej biblioteki AndroidX
Tworzenie powiadomień do stosowania na urządzeniach Auto wymaga biblioteki podstawowej AndroidX. Aby zaimportować bibliotekę do projektu, wykonaj te czynności:
- W pliku najwyższego poziomu
build.gradle
dodaj zależność od repozytorium Maven firmy Google, jak w tym przykładzie:
Groovy
allprojects { repositories { google() } }
Kotlin
allprojects { repositories { google() } }
- W pliku
build.gradle
modułu aplikacji dodaj zależność od biblioteki AndroidX Core, jak pokazano w tym przykładzie:
Groovy
dependencies { // If your app is written in Java implementation 'androidx.core:core:1.15.0' // If your app is written in Kotlin implementation 'androidx.core:core-ktx:1.15.0' }
Kotlin
dependencies { // If your app is written in Java implementation("androidx.core:core:1.15.0") // If your app is written in Kotlin implementation("androidx.core:core-ktx:1.15.0") }
Obsługa działań użytkowników
Aplikacja do obsługi wiadomości musi mieć możliwość aktualizowania rozmowy za pomocą interfejsu Action
. W przypadku Androida Auto aplikacja musi obsługiwać 2 typy obiektów Action
: odpowiedź i oznaczenie jako przeczytane. Zalecamy ich obsługę za pomocą IntentService
, który zapewnia elastyczność w obsługiwaniu potencjalnie kosztownych wywołań w tle, co odciąża główny wątek aplikacji.
Definiowanie działań związanych z intencją
Działania Intent
to proste ciągi znaków, które określają, do czego służy element Intent
.
Ponieważ jedna usługa może obsługiwać wiele rodzajów intencji, łatwiej jest zdefiniować wiele ciągów znaków działania zamiast definiowania wielu komponentów IntentService
.
Przykładowa aplikacja do obsługi wiadomości w tym przewodniku ma 2 wymagane typy działań: odpowiedź i oznaczenie jako przeczytane, jak pokazano w poniższym przykładowym kodzie.
private const val ACTION_REPLY = "com.example.REPLY"
private const val ACTION_MARK_AS_READ = "com.example.MARK_AS_READ"
Tworzenie usługi
Aby utworzyć usługę, która obsługuje te obiekty Action
, musisz mieć identyfikator rozmowy, czyli dowolną strukturę danych zdefiniowaną przez Twoją aplikację, która identyfikuje rozmowę. Potrzebujesz też klucza zdalnego wejścia, który opisujemy szczegółowo w dalszej części tej sekcji. Poniższy przykładowy kod tworzy usługę do obsługi wymaganych działań:
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()
}
}
}
Aby powiązać tę usługę z aplikacją, musisz też zarejestrować ją w pliku manifestu aplikacji, jak pokazano w tym przykładzie:
<application>
<service android:name="com.example.MessagingService" />
...
</application>
Generowanie i obsługa intencji
Inne aplikacje, w tym Android Auto, nie mogą uzyskać Intent
, które powoduje MessagingService
, ponieważ Intent
są przekazywane do innych aplikacji za pomocą PendingIntent
. Z powodu tego ograniczenia musisz utworzyć obiekt RemoteInput
, aby inne aplikacje mogły przekazywać tekst odpowiedzi do Twojej aplikacji, jak w tym przykładzie:
/**
* 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
}
W klauzuli ACTION_REPLY
w bloku MessagingService
wyodrębnij informacje, które mają być zawarte w odpowiedzi Intent
, jak w tym przykładzie:
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)
}
Oznaczenie jako przeczytane Intent
działa podobnie. Nie wymaga jednak RemoteInput
, jak w tym przykładzie:
/** 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
}
Klauzula przełącznika ACTION_MARK_AS_READ
w bloku MessagingService
nie wymaga dodatkowej logiki, jak w tym przykładzie:
// Marking as read has no other logic.
ACTION_MARK_AS_READ -> conversation.markAsRead()
Powiadamianie użytkowników o wiadomościach
Po zakończeniu obsługi działania związanego z rozmową należy wygenerować powiadomienia zgodne z Androidem Auto.
Tworzenie działań
Obiekty Action
można przekazywać do innych aplikacji za pomocą obiektu Notification
, aby wywołać metody w pierwotnej aplikacji. W ten sposób Android Auto może oznaczyć rozmowę jako przeczytaną lub na nią odpowiedzieć.
Aby utworzyć Action
, zacznij od Intent
. Ten przykład pokazuje, jak utworzyć odpowiedź Intent
:
fun createReplyAction(
context: Context, appConversation: YourAppConversation): Action {
val replyIntent: Intent = createReplyIntent(context, appConversation)
// ...
Następnie owiń ten Intent
w element PendingIntent
, który przygotuje go do użycia w zewnętrznej aplikacji. PendingIntent
blokuje cały dostęp do opakowanego Intent
, udostępniając tylko wybrany zestaw metod, które umożliwiają aplikacji odbiorczej wywołanie Intent
lub uzyskanie nazwy pakietu aplikacji źródłowej. Aplikacja zewnętrzna nie ma dostępu do podstawowego zasobu Intent
ani zawartych w nim danych.
// ...
val replyPendingIntent = PendingIntent.getService(
context,
createReplyId(appConversation), // Method explained later.
replyIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
// ...
Zanim skonfigurujesz odpowiedź Action
, pamiętaj, że Android Auto ma 3 wymagania dotyczące odpowiedzi Action
:
- Działanie semantyczne musi mieć wartość
Action.SEMANTIC_ACTION_REPLY
. - Funkcja
Action
musi wskazywać, że po wywołaniu nie wyświetla żadnego interfejsu użytkownika. - Wartość
Action
musi zawierać tylko 1 wartośćRemoteInput
.
Poniższy przykładowy kod konfiguruje odpowiedź Action
, która spełnia wymagania wymienione powyżej:
// ...
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
}
Obsługa działania polegającego na oznaczeniu jako przeczytane jest podobna, z tym że nie ma RemoteInput
.
Android Auto ma więc 2 wymagania dotyczące oznaczenia jako przeczytane Action
:
- Działanie semantyczne jest ustawione na
Action.SEMANTIC_ACTION_MARK_AS_READ
. - Działania wskazują, że po wywołaniu nie wyświetlają żadnego interfejsu.
Ten przykładowy kod konfiguruje oznaczenie jako przeczytane Action
, które spełnia te wymagania:
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
}
Do generowania oczekujących intencji używane są 2 metody: createReplyId()
i createMarkAsReadId()
. Te metody służą jako kody żądań dla każdego PendingIntent
, które są używane przez Androida do kontrolowania istniejących oczekujących intencji. Metody create()
muszą zwracać unikalne identyfikatory dla każdej rozmowy, ale powtarzane wywołania tej samej rozmowy muszą zwracać już wygenerowany unikalny identyfikator.
Rozważ przykład z 2 rozmową: A i B. Identyfikator odpowiedzi rozmowy A to 100, a identyfikator oznaczenia jako przeczytanej to 101. Identyfikator odpowiedzi w rozmowie B to 102, a identyfikator oznaczenia jako przeczytane to 103. Jeśli rozmowa A zostanie zaktualizowana, identyfikatory odpowiedzi i oznaczenia jako przeczytane nadal będą miały wartości 100 i 101. Więcej informacji znajdziesz w sekcji PendingIntent.FLAG_UPDATE_CURRENT
.
Tworzenie stylu wiadomości
MessagingStyle
to nośnik informacji o wiadomościach i to, czego Android Auto używa do odczytania na głos każdej wiadomości w rozmowie.
Najpierw użytkownik urządzenia musi zostać określony w postaci obiektu Person
, jak w tym przykładzie:
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()
// ...
Następnie możesz utworzyć obiekt MessagingStyle
i podać szczegóły dotyczące rozmowy.
// ...
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)
// ...
Na koniec dodaj nieprzeczytane wiadomości.
// ...
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
}
Pakowanie i wysyłanie powiadomienia
Po wygenerowaniu obiektów Action
i MessagingStyle
możesz utworzyć i opublikować obiekt 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)
}
Dodatkowe materiały
Zgłaszanie problemów z funkcją wiadomości w Androidzie Auto
Jeśli podczas tworzenia aplikacji do obsługi wiadomości na Androida Auto napotkasz problem, możesz go zgłosić za pomocą śledzika problemów Google. Pamiętaj, aby podać wszystkie wymagane informacje w szablonie zgłoszenia.
Zanim zgłosisz nowy problem, sprawdź, czy nie został już zgłoszony na liście problemów. Możesz subskrybować problemy i głosować na nie, klikając gwiazdkę obok problemu w śledzącym. Więcej informacji znajdziesz w artykule Subskrybowanie problemu.