Upgrade to Pro — share decks privately, control downloads, hide ads and more …

First meet with Android Auto

First meet with Android Auto

Introduction of Android Auto features and developing apps with Android Auto
(2016.05.28 Android Taipei @ Yahoo!)

8a6e58b272b266faf22d8a3b2927624f?s=128

Johnny Sung

May 27, 2016
Tweet

Transcript

  1. FIRST MEET WITH ANDROID AUTO Johnny Sung 2016.05.28 Android Taipei

    @ Yahoo! Slides URL: http://goo.gl/EasR9V
  2. MOBILE DEVICES DEVELOPER Johnny Sung https://fb.com/j796160836 https://plus.google.com/+JohnnySung http://about.me/j796160836

  3. None
  4. None
  5. None
  6. None
  7. http://www.pioneerelectronics.com/androidauto/

  8. FEATURES & LIMITATIONS INTRODUCING ANDROID AUTO

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

  10. DEMO

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

  12. Phone Tab

  13. Notification Tab

  14. Music Tab

  15. Vehicle info Tab

  16. Vehicle info detail

  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/
  18. SETUP & INSTALLATION

  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
  21. https://play.google.com/store/apps/details?id=com.google.android.projection.gearhead 2. Install Android Auto app on phone

  22. A. enable developer mode B. Select Start head unit server

    from menu.
  23. #!/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
  24. 3. Connect your phone to computer via USB. 4. Run

    scripts
  25. Mac Android phone

  26. Emulator Commands ▸ day ▸ night ▸ daynight

  27. Day mode Night mode

  28. For safety reasons, TOUCHES 4 operation is limited within

  29. Limited Operations ▸ 11 items per page ▸ 3 level

    depth
  30. None
  31. AUDIO APPS MAKING FOR ANDROID AUTO

  32. None
  33. Create MediaBrowserService <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package=“my.package.name">
 <application> <!-- ... --> <meta-data


    android:name="com.google.android.gms.car.application"
 android:resource="@xml/automotive_app_desc"/>
 <service
 android:name=".MyMediaBrowserService"
 android:exported="true">
 <intent-filter>
 <action android:name="android.media.browse.MediaBrowserService"/>
 </intent-filter>
 </service>
 
 </application>
 </manifest> AndroidManifest.xml (1/3)
  34. Create MediaBrowserService <?xml version="1.0" encoding="utf-8"?>
 <automotiveApp>
 <uses name="media"/>
 </automotiveApp> automotive_app_desc.xml

    (2/3)
  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. None
  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. None
  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)
  49. None
  50. VOICE COMMAND

  51. None
  52. Ok Google,

  53. None
  54. Ok Google, Listen Jazz music on <YourApp>

  55. MediaSession Callback private final class MediaSessionCallback extends MediaSession.Callback { 


    @Override
 public void onPlayFromSearch(final String query, final Bundle extras) { // Perform voice actions
 }
 } MyMediaBrowserService.java
  56. 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
  57. Examples ▸ MediaBrowserService ▸ https://github.com/googlesamples/android-MediaBrowserService/ ▸ UniversalMusicPlayer ▸ https://github.com/googlesamples/android-UniversalMusicPlayer

  58. MESSAGING APPS MAKING FOR ANDROID AUTO

  59. None
  60. Create MessageReceivers AndroidManifest.xml <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="my.package.name">
 
 <application>
 <!-- ...

    -->
 <meta-data android:name="com.google.android.gms.car.application"
 android:resource="@xml/automotive_app_desc"/>
 
 <receiver
 android:name=".MessageReadReceiver"
 android:exported="false">
 <intent-filter>
 <action android:name="my.package.name.ACTION_MESSAGE_READ"/>
 </intent-filter>
 </receiver>
 
 <receiver
 android:name=".MessageReplyReceiver"
 android:exported="false">
 <intent-filter>
 <action android:name="my.package.name.ACTION_MESSAGE_REPLY"/>
 </intent-filter>
 </receiver>
 </application>
 </manifest> (1/2)
  61. automotive_app_desc.xml <?xml version="1.0" encoding="utf-8"?>
 <automotiveApp>
 <uses name="notification"/>
 </automotiveApp> Create MessageReceivers

    (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
  68. Examples ▸ MessagingService ▸ https://github.com/googlesamples/android-MessagingService

  69. DEMO

  70. Q & A