17. ***The Android Auto app is currently available in the following countries:
Ecuador
France
Germany
Guatemala
India
Ireland
Italy
Mexico
New Zealand
Panama
Argentina
Australia
Austria
Bolivia
Brazil
Canada
Chile
Colombia
Costa Rica
Dominican Republic
Paraguay
Peru
Puerto Rico
Russia
Spain
Switzerland
United Kingdom
United States
Uruguay
Venezuela
https://www.android.com/auto/
19. EMULATOR SETUP
1. Install Auto Desktop Head Unit emulator from
the SDK Manager
2. Install Android Auto app on phone
A. Tapping the Android Auto toolbar title 10
times to enable developer mode
B. Select Start head unit server from the
Android Auto menu.
20. 1. Install Auto Desktop Head Unit emulator from the
SDK Manager
35. Create MediaBrowserService
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyMediaBrowserService extends MediaBrowserService {
@Nullable
@Override
public BrowserRoot onGetRoot(String packageName, int uid, Bundle root) {
return new BrowserRoot(Const.MEDIA_ID_ROOT, null);
}
@Override
public void onLoadChildren(String parentId,
Result<List<MediaBrowser.MediaItem>> result) {
// ...
}
}
MyMediaBrowserService.java
(3/3)
36. Working with MediaSession
public class MyMediaBrowserService extends MediaBrowserService {
private MediaSession mSession;
@Override
public void onCreate() {
super.onCreate();
mSession = new MediaSession(this, "MyMediaBrowserService");
setSessionToken(mSession.getSessionToken());
mSession.setCallback(new MediaSessionCallback());
mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
}
@Override
public void onDestroy() {
mSession.release();
}
private final class MediaSessionCallback extends MediaSession.Callback {
// ...
}
}
MyMediaBrowserService.java
37. private final class MediaSessionCallback extends MediaSession.Callback {
@Override
public void onPlay() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onSeekTo(long position) {
}
@Override
public void onSkipToNext() {
}
@Override
public void onSkipToPrevious() {
}
// ...
}
38. private final class MediaSessionCallback extends MediaSession.Callback {
// ...
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
}
@Override
public void onSkipToQueueItem(long queueId) {
}
@Override
public void onCustomAction(String action, Bundle extras) {
}
@Override
public void onPlayFromSearch(final String query, final Bundle extras) {
}
}
39. Validate caller package
@Override
public BrowserRoot onGetRoot(String packageName, int uid, Bundle rootHints) {
LogHelper.d(TAG, "OnGetRoot: clientPackageName=" + packageName,
"; clientUid=" + uid + " ; rootHints=", rootHints);
// To ensure you are not allowing any arbitrary app to browse your app's
contents, you need to check the origin:
if (!mPackageValidator.isCallerAllowed(this, packageName, uid)) {
// If the request comes from an untrusted package, return null.
LogHelper.w(TAG, "OnGetRoot: IGNORING request from untrusted package "
+ packageName);
return null;
}
return new BrowserRoot(Const.MEDIA_ID_ROOT, null);
}
MyMediaBrowserService.java
40. Create Sliding Menus
@Override
public void onLoadChildren(final String pId, final Result<List<MediaItem>> result) {
List<MediaItem> mediaItems = new ArrayList<>();
if ("__ROOT__".equals(pId)) {
mediaItems.add(new MediaItem(
new MediaDescription.Builder()
.setMediaId(Const.MEDIA_ID_ITEM1)
.setTitle("Item 01")
.setSubtitle("Some descriptions")
.setIconUri(Uri.parse(
"android.resource://my.package.name/drawable/icon"))
.build(), MediaItem.FLAG_BROWSABLE
));
mediaItems.add(new MediaItem(
new MediaDescription.Builder()
.setMediaId(Const.MEDIA_ID_ITEM2)
.setTitle("Item 02")
.setIconUri(Uri.parse(
"android.resource://my.package.name/drawable/icon"))
.build(), MediaItem.FLAG_PLAYABLE
));
result.sendResult(mediaItems);
}
}
MyMediaBrowserService.java
(1/2)
41. Create Sliding Menus
private final class MediaSessionCallback extends MediaSession.Callback {
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
if (Const.MEDIA_ID_ITEM2.equals(mediaId)) {
// ...
// Play media
// ...
}
}
// ...
}
MyMediaBrowserService.java
(2/2)
42.
43. Create Sliding Menus (Async)
@Override
public void onLoadChildren(final String parentMediaId, final
Result<List<MediaItem>> result) {
result.detach();
mMusicProvider.retrieveMediaAsync(new MusicProvider.Callback() {
@Override
public void onMusicCatalogReady() {
List<MediaItem> mediaItems = new ArrayList<>();
// ...
// Prepare to create items
// ...
result.sendResult(mediaItems);
}
});
}
MyMediaBrowserService.java
44. Setting Playback State
PlaybackState.Builder stateBuilder = new PlaybackState.Builder();
int playbackState = PlaybackState.STATE_PLAYING;
long action = PlaybackState.ACTION_PAUSE;
action |= PlaybackState.ACTION_SKIP_TO_NEXT;
action |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
stateBuilder.setActions(action);
stateBuilder.setState(playbackState, -1, 1.0f);
mSession.setPlaybackState(stateBuilder.build());
MediaMetadata.Builder metaBuilder = new MediaMetadata.Builder();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
metaBuilder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap);
metaBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, "Great Artist");
metaBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, "Song 1");
mSession.setMetadata(metaBuilder.build());
mSession.setActive(true);
MyMediaBrowserService.java
45.
46. Show Error Message
PlaybackState.Builder stateBuilder = new PlaybackState.Builder();
int playbackState = PlaybackState.STATE_ERROR;
stateBuilder.setState(playbackState, -1, 1.0f);
stateBuilder.setErrorMessage("Oh no! Something has gone wrong.");
mSession.setPlaybackState(stateBuilder.build());
MyMediaBrowserService.java
47. Playing Queue
ArrayList<MediaMetadata> mediaMetadatas = new ArrayList<>();
for (int i = 0; i < 5; i++) {
String coverUrl = "android.resource://my.package.name/drawable/icon";
MediaMetadata.Builder builder = new MediaMetadata.Builder();
builder.putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, coverUrl);
builder.putString(MediaMetadata.METADATA_KEY_ARTIST, "Great artist");
builder.putString(MediaMetadata.METADATA_KEY_TITLE, "Song " + (i + 1));
MediaMetadata metadata = builder.build();
mediaMetadatas.add(metadata);
}
MyMediaBrowserService.java
(1/2)
48. Playing Queue
List<MediaSession.QueueItem> queue = convertToQueue(mediaMetadatas);
mSession.setQueue(queue);
mSession.setQueueTitle("Now Playing");
private static List<MediaSession.QueueItem> convertToQueue(
Iterable<MediaMetadata> tracks) {
List<MediaSession.QueueItem> queue = new ArrayList<>();
int count = 0;
for (MediaMetadata track : tracks) {
String hierarchyAwareMediaID = "";
MediaMetadata trackCopy = new MediaMetadata.Builder(track)
.putString(MediaMetadata.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
.build();
MediaSession.QueueItem item = new MediaSession.QueueItem(
trackCopy.getDescription(), count++);
queue.add(item);
}
return queue;
}
MyMediaBrowserService.java
(2/2)
62. MessageReadReceiver
public class MessageReadReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int conversationId = intent.getIntExtra(Const.CONVERSATION_ID, -1);
if (conversationId != -1) {
// Actions with conversation was read
}
}
}
MessageReadReceiver.java
63. MessageReplyReceiver
public class MessageReplyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Const.REPLY_ACTION.equals(intent.getAction())) {
int conversationId = intent.getIntExtra(Const.CONVERSATION_ID, -1);
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
CharSequence reply = "";
if (remoteInput != null) {
reply = remoteInput.getCharSequence(
Const.EXTRA_REMOTE_REPLY);
}
if (conversationId != -1) {
// Actions for receive reply message
}
}
}
}
MessageReplyReceiver.java
64. Prepare PendingIntent
int conversationId = 1;
String name = "Johnny";
String message = "Hello, World!";
// A pending Intent for reads
Intent readIntent = new Intent()
.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
.setAction(Const.READ_ACTION)
.putExtra(Const.CONVERSATION_ID, conversationId);
PendingIntent readPendingIntent = PendingIntent.getBroadcast(this,
conversationId,
readIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
MainActivity.java
(1/2)
65. Prepare PendingIntent
// Build a RemoteInput for receiving voice input in a Car Notification
RemoteInput remoteInput = new RemoteInput.Builder(Const.EXTRA_REMOTE_REPLY)
.setLabel(getString(R.string.reply))
.build();
// Building a Pending Intent for the reply action to trigger
Intent replyIntent = new Intent()
.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
.setAction(Const.REPLY_ACTION)
.putExtra(Const.CONVERSATION_ID, conversationId);
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this,
conversationId,
replyIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
MainActivity.java
(2/2)
66. Build CarExtender & UnreadConversion
// Create the UnreadConversation and populate it with the participant name,
// read and reply intents.
NotificationCompat.CarExtender.UnreadConversation.Builder unreadConvBuilder =
new NotificationCompat.CarExtender.UnreadConversation.Builder(name)
.setLatestTimestamp(System.currentTimeMillis())
.setReadPendingIntent(readPendingIntent)
.setReplyAction(replyPendingIntent, remoteInput)
.addMessage(message);
NotificationCompat.CarExtender carExtender =
new NotificationCompat.CarExtender()
.setUnreadConversation(unreadConvBuilder.build());
MainActivity.java
67. Make a Notification
NotificationCompat.Action replyAction =
new NotificationCompat.Action.Builder(
R.drawable.icon, getString(R.string.reply), replyPendingIntent)
.addRemoteInput(remoteInput)
.build();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.icon)
.setLargeIcon(BitmapFactory.decodeResource(
getResources(), R.drawable.icon_big))
.setContentText(message)
.setWhen(System.currentTimeMillis())
.setContentTitle(name)
.setContentIntent(readPendingIntent)
.extend(carExtender)
.addAction(replyAction);
NotificationManagerCompat manager = NotificationManagerCompat.from(this);
manager.notify(conversationId, builder.build());
MainActivity.java