Slide 1

Slide 1 text

FIRST MEET WITH ANDROID AUTO Johnny Sung 2016.05.28 Android Taipei @ Yahoo! Slides URL: http://goo.gl/EasR9V

Slide 2

Slide 2 text

MOBILE DEVICES DEVELOPER Johnny Sung https://fb.com/j796160836 https://plus.google.com/+JohnnySung http://about.me/j796160836

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

http://www.pioneerelectronics.com/androidauto/

Slide 8

Slide 8 text

FEATURES & LIMITATIONS INTRODUCING ANDROID AUTO

Slide 9

Slide 9 text

http://www.pioneerelectronics.com/androidauto/

Slide 10

Slide 10 text

DEMO

Slide 11

Slide 11 text

http://www.greenbot.com/article/2931099/android-auto-review-the-best-way-to-get-google-maps-in-your-car.html Navigation Tab

Slide 12

Slide 12 text

Phone Tab

Slide 13

Slide 13 text

Notification Tab

Slide 14

Slide 14 text

Music Tab

Slide 15

Slide 15 text

Vehicle info Tab

Slide 16

Slide 16 text

Vehicle info detail

Slide 17

Slide 17 text

***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/

Slide 18

Slide 18 text

SETUP & INSTALLATION

Slide 19

Slide 19 text

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.

Slide 20

Slide 20 text

1. Install Auto Desktop Head Unit emulator from the SDK Manager

Slide 21

Slide 21 text

https://play.google.com/store/apps/details?id=com.google.android.projection.gearhead 2. Install Android Auto app on phone

Slide 22

Slide 22 text

A. enable developer mode B. Select Start head unit server from menu.

Slide 23

Slide 23 text

#!/bin/bash adb forward tcp:5277 tcp:5277 $ANDROID_HOME/extras/google/auto/desktop-head-unit EMULATOR SETUP 3. Connect your phone to computer via USB. 4. Run scripts StartAndroidAutoDesktopHeadUnit.sh https://developer.android.com/training/auto/start/index.html

Slide 24

Slide 24 text

3. Connect your phone to computer via USB. 4. Run scripts

Slide 25

Slide 25 text

Mac Android phone

Slide 26

Slide 26 text

Emulator Commands ▸ day ▸ night ▸ daynight

Slide 27

Slide 27 text

Day mode Night mode

Slide 28

Slide 28 text

For safety reasons, TOUCHES 4 operation is limited within

Slide 29

Slide 29 text

Limited Operations ▸ 11 items per page ▸ 3 level depth

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

AUDIO APPS MAKING FOR ANDROID AUTO

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

Create MediaBrowserService 
 
 
 
 
 
 
 
 
 AndroidManifest.xml (1/3)

Slide 34

Slide 34 text

Create MediaBrowserService 
 
 
 automotive_app_desc.xml (2/3)

Slide 35

Slide 35 text

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> result) {
 // ...
 }
 } MyMediaBrowserService.java (3/3)

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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() {
 } 
 // ...
 }

Slide 38

Slide 38 text

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) {
 }
 }

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Create Sliding Menus @Override
 public void onLoadChildren(final String pId, final Result> result) {
 List 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)

Slide 41

Slide 41 text

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)

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

Create Sliding Menus (Async) @Override
 public void onLoadChildren(final String parentMediaId, final Result> result) {
 result.detach();
 mMusicProvider.retrieveMediaAsync(new MusicProvider.Callback() {
 @Override
 public void onMusicCatalogReady() {
 List mediaItems = new ArrayList<>();
 // ... // Prepare to create items // ...
 result.sendResult(mediaItems);
 }
 });
 } MyMediaBrowserService.java

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Playing Queue ArrayList 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)

Slide 48

Slide 48 text

Playing Queue List queue = convertToQueue(mediaMetadatas);
 mSession.setQueue(queue);
 mSession.setQueueTitle("Now Playing"); private static List convertToQueue(
 Iterable tracks) {
 List 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)

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

VOICE COMMAND

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

Ok Google,

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

Ok Google, Listen Jazz music on

Slide 55

Slide 55 text

MediaSession Callback private final class MediaSessionCallback extends MediaSession.Callback { 
 @Override
 public void onPlayFromSearch(final String query, final Bundle extras) { // Perform voice actions
 }
 } MyMediaBrowserService.java

Slide 56

Slide 56 text

Semantic Analysis GOOGLE KNOWLEDGE GRAPH Play music from Lady Gaga. Play Jazz music. Play Starships 
 from Nicki Minaj. Artist Extras Genre Extras Song name Extras

Slide 57

Slide 57 text

Examples ▸ MediaBrowserService ▸ https://github.com/googlesamples/android-MediaBrowserService/ ▸ UniversalMusicPlayer ▸ https://github.com/googlesamples/android-UniversalMusicPlayer

Slide 58

Slide 58 text

MESSAGING APPS MAKING FOR ANDROID AUTO

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

Create MessageReceivers AndroidManifest.xml 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 (1/2)

Slide 61

Slide 61 text

automotive_app_desc.xml 
 
 
 Create MessageReceivers (2/2)

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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)

Slide 65

Slide 65 text

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)

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Examples ▸ MessagingService ▸ https://github.com/googlesamples/android-MessagingService

Slide 69

Slide 69 text

DEMO

Slide 70

Slide 70 text

Q & A