Skip to content

Most visited

Recently visited

navigation

Providing Messaging for Auto

Video

DevBytes: Android Auto Messaging

Staying connected through messages is important to many drivers. Chat apps can let users know if a child needs to be picked up, or if a dinner location has been changed. The Android framework enables messaging apps to extend their services into the driving experience using a standard user interface that lets drivers keep their eyes on the road.

Apps that support messaging can extend their messaging notifications to allow Android Auto to consume them when Auto is running. These notifications are displayed in Auto and allow users to read and respond to messages in a consistent, low distraction, interface.

This lesson assumes that you have built an app that displays messages to the user and receives the user's replies, such as a chat app. The lesson shows you how to extend your app to hand those messages off to an Auto device for display and replies.

Provide Messaging Services

To enable your app to provide messaging services for Auto devices:

Concepts and objects

Before you start designing your app, it's helpful to understand how Auto handles messaging.

Each individual chunk of communication is a message. A message is a short length of text, suitable for the Auto device to read aloud. In a chat app, this might be a single message from one person to another: "Fitzy -- Jane can't come to the ball, her youngest has the croup. :-( --Liz".

A conversation is a group of messages that are all grouped together in some way. Auto uses the conversation information to group the messages together when presenting them to the user. In a chat app, a conversation might be all the messages between the user and another person (for example, all the messages back and forth between Darcy and Elizabeth). Every message belongs to a conversation, even if it's the only message in that conversation. Each conversation has a conversation name. The conversation name is used by Android Auto to present the messages; it's up to your app to choose an appropriate conversation name. In a chat app, the conversation name is usually the person your user is talking to.

The v4 support library defines an UnreadConversation object. This object holds all messages in a conversation which have not yet been read by the user. To give those messages to the user, you attach that UnreadConversation to a notification. However, you do not attach messages to the UnreadConversation directly. Instead, you must first set up an UnreadConversation.Builder object for the conversation. The messages are added to the builder, then when you are ready to send the messages, you use the builder to create the actual UnreadConversation and attach the UnreadConversation to the notification.

Note: When Auto presents messages to the user, it uses the notification tag and ID to determine which conversation the messages belong to. It is important to use the same tag and ID for all messages in a conversation, and to not use that tag for other conversations.

Workflow

This section describes how the mobile device interacts with Auto to present messages to the user.

  1. The app receives a message that it wants to pass on to the user. The app attaches the message to an UnreadConversation.Builder object, then uses the UnreadConversation.Builder to generate an UnreadConversation. The app attaches that UnreadConversation to a notification. That notification is associated with a CarExtender object, which indicates that the notification can be handled by Android Auto.
  2. The app posts the notification. The Android notification framework passes the message to Auto. Auto uses the notification tag and ID to determine which conversation the message belongs to, and presents the message to the user in an appropriate way.
  3. When the user listens to the message, Auto triggers the app's "message read" pending intent. Apps can handle the intent as described in Handling a message read action.
  4. If the user sends a reply, Auto triggers the app's "message reply" intent and attaches a transcript of the user's response. The app can take appropriate action, based on the app's logic. For example, a chat app might interpret the reply as a message to go to the other conversation participants.

Configure Your Manifest

You configure your app manifest to indicate that it supports messaging services for Auto devices and handle message actions. This section describes what changes to make to your manifest to support messaging for Auto devices.

Declare Auto messaging support

When a user connects a Android mobile device to a dashboard running Android, the dashboard device looks for apps that declare support for vehicle services, such as messaging. You indicate that your app supports cars capabilities using the following manifest entry:

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

This manifest entry refers to a secondary xml file, where you declare what Auto capabilities your app supports. For an app that supports messaging for Auto devices, add an xml file to the res/xml/ your app's development project directory as automotive_app_desc.xml, with the following content:

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

For more information about declaring capabilities for Auto devices, see Getting Started with Auto.

Define read and reply intent filters

Auto devices use Intent objects that indicate a user has read or replied to a message provided by your app. Your app defines intent types for reading and replying to messages and adds this information to messaging notifications for Auto devices, so that Android Auto can notify your app when a user takes one of these actions.

You define the read action and reply action intent types for your app and the BroadcastReceiver classes that handle them in the manifest. The following code example demonstrates how to declare these intents and their associated receivers.

<application>
    ...
    <receiver android:name=".MyMessageReadReceiver"
            android:exported="false">
        <intent-filter>
          <action android:name="com.myapp.android.messagingservice.MY_ACTION_MESSAGE_READ"/>
        </intent-filter>
    </receiver>

    <receiver android:name=".MyMessageReplyReceiver"
          android:exported="false">
        <intent-filter>
          <action android:name="com.myapp.android.messagingservice.MY_ACTION_MESSAGE_REPLY"/>
        </intent-filter>
    </receiver>
    ...
</application>

In this example, "MyMessageReadReceiver" and "MyMessageReplyReceiver" are the names of the BroadcastReceiver subclasses you define to handle the intents. You can choose whatever you like as the action names, but it's best to prepend your package name to ensure that the action names are unique. For more information about handling actions, see Handle User Actions.

Import Support Library for Messaging

Building notifications for use with Auto devices requires classes from the v4 support library. Use the Android SDK Manager to update the Extras > Android Support Repository to version 9 or higher and the Extras > Android Support Library to version 21.0.2 or higher.

After you have updated the support libraries, import them into your Android Studio development project by adding this dependency to your build.gradle file:

dependencies {
    ...
    compile 'com.android.support:support-v4:21.0.2'
}

For information about importing the support library into development projects for other development environments, see Support Library Setup.

Notify Users of Messages

A messaging app provides messages to a connected Auto dashboard using the notifications framework. When your messaging app has a message for a user, you build a specially configured notification that Android Auto receives and presents to the user. The Auto device manages the presentation of the message on the appropriate display and may play the message via text-to-speech. Auto also handles voice interaction if the user replies to a message using verbal input.

The messaging user interface for Auto presents users with two levels of information about messages. The first level of notification tells users what conversations are available, and who they are with, but not the content of the messages. Typically, a conversation is one or more messages from another user to the Auto user.

The second level of the notification is the actual content of messages in the conversation. If a user indicates they want to hear the messages in a conversation, the Auto user interface plays the messages using text-to-speech.

This section describes how to notify Auto users that conversations are available and provide the content of messages in those conversations.

Create conversation read and reply intents

Unread conversation objects contain intents for reading and replying to a conversation. You create a PendingIntent object for each of these actions, so the Auto device can notify your app of action taken by the Auto user on a particular conversation.

The following example code demonstrates how to define a PendingIntent to let your app know if a conversation was read to the Auto user:

Caution:To avoid third party apps from intercepting intents sent by Android Auto, always explicitly set the intent's target package name, using setPackage(). Setting the package name explicitly ensures that only the intended app can handle requests from Android Auto, preventing other apps from intercepting those requests.

Intent msgReadIntent = new Intent()
    .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
    .setAction("com.myapp.android.messagingservice.MY_ACTION_MESSAGE_READ")
    .putExtra("conversation_id", thisConversationId)
    .setPackage("com.myapp.android");

PendingIntent msgReadPendingIntent =
    PendingIntent.getBroadcast(getApplicationContext(),
        thisConversationId,
        msgReadIntent,
        PendingIntent.FLAG_UPDATE_CURRENT);

In this example, thisConversationId is an integer that identifies the current conversation. The value of Intent.setAction() is the intent filter identifier for read messages which you defined in your app manifest, as shown in Define read and reply intent filters.

If your app supports read receipts or other indicators that messages are read, you should define the PendingIntent so that you can determine which messages were read in Auto:

Intent msgReadIntent = new Intent()
    .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
    .setAction("com.myapp.android.messagingservice.MY_ACTION_MESSAGE_READ")
    .putExtra("conversation_id", thisConversationId)
    .putExtra("current_timestamp", currentTimestamp)
    .setPackage("com.myapp.android");

PendingIntent msgReadPendingIntent =
    PendingIntent.getBroadcast(getApplicationContext(),
        currentTimestamp.intValue(),
        msgReadIntent,
        0);

Here, the currentTimestamp extra allows you to determine the point in your app message history up to which messages should be marked as read. Note that using the current timestamp is not the only way to achieve this; for example, one could also use the sent timestamp or message IDs as long as the value can be associated with the specific moment in the conversation. The PendingIntent request code should be unique for each notification and the FLAG_UPDATE_CURRENT should not be set. This ensures that the extras in the PendingIntent are not overwritten by subsequent notifications, avoiding a race condition that could lead to marking messages as read before the Auto user receives them.

If your app supports replying to conversations, you also create a PendingIntent for each conversation to notify your app that the user has replied. The following code example shows you how to build this intent for use with a particular conversation:

Intent msgReplyIntent = new Intent()
    .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
    .setAction("com.myapp.android.messagingservice.MY_ACTION_MESSAGE_REPLY")
    .putExtra("conversation_id", thisConversationId)
    .setPackage("com.myapp.android");

PendingIntent msgReplyPendingIntent = PendingIntent.getBroadcast(
    getApplicationContext(),
    thisConversationId,
    msgReplyIntent,
    PendingIntent.FLAG_UPDATE_CURRENT);

Once again, thisConversationId is an integer that uniquely identifies this conversation, and the value you pass to Intent.setAction() is the intent filter identifier you defined for replies in your app manifest.

Your app should not assume that read and reply intents will be received in a specific order. It is possible for an Auto user to reply to a message without reading it; therefore, updating messages as read in your app when a reply broadcast is received is not recommended. Messages can also be read multiple times, so your app should idempotently handle read intents.

Set up the conversation builder

Messaging notifications for Auto organize messages into conversations using the NotificationCompat.CarExtender.UnreadConversation class, that represents an unread or new portion of a conversation as a list of messages.

You do not configure the UnreadConversation directly. Instead, you configure an UnreadConversation.Builder with the information about the conversation, as shown in the following example code.

// Build a RemoteInput for receiving voice input in a Car Notification
RemoteInput remoteInput = new RemoteInput.Builder(MY_VOICE_REPLY_KEY)
        .setLabel(getApplicationContext().getString(R.string.notification_reply))
        .build();

// Create an unread conversation object to organize a group of messages

UnreadConversation.Builder unreadConvBuilder =
    new UnreadConversation.Builder(conversationName)
        .setReadPendingIntent(msgReadPendingIntent)
        .setReplyAction(msgReplyPendingIntent, remoteInput);

Note: You won't actually create the UnreadConversation until you are almost ready to send the message.

This conversation object includes a PendingIntent, which allows the Auto device to signal your app that the conversation has been read by the Auto user. The construction of this intent is discussed in the Creating conversation read and reply intents section.

If your app supports replying to a conversation, you must call the UnreadConversation.Builder.setReplyAction() method and provide a pending intent to pass that user action back to your app. The UnreadConversation object you create must also include a RemoteInput object. When the Auto user receiving this conversation speaks a reply, the remote input objects lets your app get a text version of the voice reply.

In the code above, conversationName is the name to be displayed for the conversation. For one-on-one conversations, it is the name of the sender. For group conversations, it is best to choose one of two options for the name:

  1. Conversation title: If your app supports adding a title to group conversations, use the title for conversationName to be consistent with your in-app experience. The conversationName parameter in the above example is similar to the conversationTitle parameter in the NotificationCompat.MessagingStyle.setConversationTitle() method.
  2. A list of participants: Build a comma-separated list of participants for the conversationName parameter to identify the group. Note that this is read aloud by the text-to-speech system, so you may need to abbreviate the list for large groups. If you are abbreviating the participant list, you need to balance between allowing users to uniquely identify the group and the time taken to listen to messages.

Sending Messages

When a message arrives for a conversation, you take the following steps to dispatch it as a notification to Auto.

First, add the message to the UnreadConversation.Builder for this conversation, and update its timestamp:

unreadConvBuilder.addMessage(messageString).setLatestTimestamp(currentTimestamp);
messageString
The text for a message you want to send.
currentTimestamp
The message timestamp. (If you are sending several messages at once, use the timestamp of the most recent message.)

Note: If you are sending several messages at once, add them to the UnreadConversation.Builder in order, from oldest to newest. If there are multiple speakers participating, prepend the speaker's name to the message when the speaker changes so that the Auto user can differentiate who's speaking. For example, "Alice: How do you do? Bob: I am quite well. How are you?".


CharSequence currentSender = null;
for (Message message : myMessages) {
    StringBuilder messageText = new StringBuilder();
    CharSequence sender = message.getSender();
    // Maybe append sender to indicate who is speaking.
    if (!TextUtils.isEmpty(sender) && !sender.equals(currentSender)) {
        if (currentSender != null) {
            // Punctuation will briefly pause TTS readout between senders.
            messageText.append(". ");
        }
        currentSender = sender;
        messageText.append(sender.toString().toLowerCase(Locale.getDefault()));
        // Punctuation will separate sender from message in TTS readout.
        messageText.append(": ");
    }
    messageText.append(message.getText());
    unreadConvBuilder.addMessage(messageText.toString());
}

Some things to note about the above sample code:

Next, create the NotificationCompat.Builder object that builds the actual notification. You need to use the pending intents you created in the previous step.

NotificationCompat.Builder notificationBuilder =
    new NotificationCompat.Builder(getApplicationContext())
        .setSmallIcon(smallIconResourceID)
        .setLargeIcon(largeIconBitmap);
smallIconResourceID
The resource ID of a small icon to use for the conversation. This is typically a generic icon for the messaging app.
largeIconBitmap
A Bitmap of a large version of the icon. This is typically a conversation-specific graphic. For example, if this is a chat app, the large icon would be a picture of the person the user is chatting with.

You'll also need to extend the NotificationCompat.Builder with the CarExtender. This is where you actually create the UnreadConversation object using the builder you just created, and attach it to the CarExtender:

notificationBuilder.extend(new CarExtender()
    .setUnreadConversation(unreadConvBuilder.build());

Note: If you wish, you can set an override icon or color for the CarExtender by calling setLargeIcon() or setColor() . The override icon or color is used when the notification is handled by Auto, and has no effect on the notification shown in the Android system notifications.This is useful if the notification's default icon or color are not suitable for the car's display.

Once you've done all this, you use your app's NotificationManagerCompat to send the notification:

NotificationManagerCompat msgNotificationManager =
    NotificationManagerCompat.from(context);
msgNotificationManager.notify(notificationTag,
    notificationId, notificationBuilder.build());

Handle User Actions

When your create and dispatch a notification for messaging, you specify intents to be triggered when message is read to the Auto user and when the user dictates a reply. Your app indicates to the Android framework that it handles these intends by registering them through its manifest, as discussed in Define read and reply intent filters.

In addition to registering these intent filters, your app must provide code to handle these actions. Your app can do this by providing a service or BroadcastReceiver objects that handle these intents.

Note: Do not handle these actions using Activities.

For more information about intents, see Intents and Intent Filters.

Handling a message read action

When a user listens to a messaging conversation through the Auto user interface, Auto sends a read intent based on how your app defined the messaging notification. Your app catches that intent and invokes the broadcast receiver class associated with it, or the service method set up to handle that action.

The following code example shows how to define a BroadcastReceiver class to handle a received message read intent:

public class MyMessageReadReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        // If you set up the intent as described in
        // "Create conversation read and reply intents",
        // you can get the conversation ID by calling:
        int thisConversationId = intent.getIntExtra("conversation_id", -1);

        // Remove the notification
        ...
        // Update the list of unread conversations in your app.
        ...
    }
}

Once a notification is read, your app can remove it by calling NotificationManagerCompat.cancel() with the notification ID. Within your app, you should mark the messages provided in the notification as read. Use the conversation marker (for example, current_timestamp) included in the intent extras for marking messages as read up to a specific moment in the conversation history, if applicable.

Note: An alternative to this implementation is to use a service in a PendingIntent.

Handling a reply action

When a user replies to a messaging conversation through the Auto user interface, the dashboard system sends a reply intent based on how your app defined the messaging notification. Your app catches that intent and invokes the broadcast receiver class associated with it, or the service method set up to handle that action.

The following code example shows how to define a BroadcastReceiver class to handle a received message reply intent:

  public class MyMessageReplyReceiver extends BroadcastReceiver {


    @Override
    public void onReceive(Context context, Intent intent) {
        // If you set up the intent as described in
        // "Create conversation read and reply intents",
        // you can get the conversation ID by calling:
        int thisConversationId = intent.getIntExtra("conversation_id", -1).

    }

    /**
     * Get the message text from the intent.
     * Note that you should call
     * RemoteInput.getResultsFromIntent() to process
     * the RemoteInput.
     */
    private CharSequence getMessageText(Intent intent) {
        Bundle remoteInput =
            RemoteInput.getResultsFromIntent(intent);
        if (remoteInput != null) {
            return remoteInput.getCharSequence(MY_VOICE_REPLY_KEY);
        }
        return null;
    }

}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.