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

ThatConference 2015

ThatConference 2015

CHROMECAST & ANDROID: DELIVER AN INCREDIBLE EXPERIENCE FROM YOUR SMARTPHONE TO YOUR TV

C887ad592770a197f114d0a1d3e3a5a7?s=128

Jorge Coca

August 11, 2015
Tweet

Transcript

  1. JORGE COCA - SPR CONSULTING TOPIC DATE SPEAKER THAT CONFERENCE

    2015 CHROMECAST & ANDROID DELIVER AN INCREDIBLE EXPERIENCE FROM YOUR SMARTPHONE TO YOUR TV
  2. None
  3. SHOW ME AN EXAMPLE!

  4. None
  5. None
  6. What is Chromecast? HDMI dongle $35 Smart TV Cast content

    from your device to your TV
  7. You can cast content from… Android iOS Google Chrome

  8. DO I NEED A CHROMECAST TO SEND CONTENT TO MY

    TV?
  9. NO, BUT…

  10. Cast-Ready devices Chromecast Android TV Cast for audio

  11. ARCHITECTURE

  12. How does it work? Two devices are involved in the

    process: SENDER: device used for discovery and media control RECEIVER: device used for streaming media (Cast- ready device)
  13. Sender Will discover Cast-ready device (same Wifi network or guest

    access) Will discover media content and tell the Cast-ready device what to play Will behave as a remote control once the Cast-ready device is streaming content
  14. IT WILL BE OUR “MOBILE” APP

  15. Receiver Will stream media content that the sender tells it

    to reproduce Can display informative content while in “idle” mode
  16. IT WILL BE AN “HTML5” WEBSITE DISPLAYED ON OUR TV

    CONTROLLED BY OUR “SENDER” APP
  17. None
  18. Step by step - Connection (1/3) 1) Receiver advertises itself

    in the “same network” 2) Sender will discover receiver and will give the option to establish connection with the receiver 3) If user decides to connect, a web socket will be opened between sender and receiver
  19. Step by step - Media discovery (2/3) 1) Receiver will

    fetch its first HTML5 screen (Splash Screen) 2) User will select media to play in the sender app, and the sender app will send the URL that the receiver should play
  20. Step by step - Streaming content (3/3) 1) Media is

    downloaded and streamed by the receiver 2) HTML5 receiver app receives media request and starts playing the content
  21. WAIT WAIT WAIT!!!

  22. DO I HAVE TO WRITE TWO DIFFERENT APPS?

  23. WITH TWO DIFFERENT LANGUAGES?

  24. FOR TWO DIFFERENT DEVICES?

  25. YES!

  26. AND YOU WILL HAVE TO DESIGN ALSO FOR TWO DIFFERENT

    DEVICES…
  27. …THAT HAVE NOTHING IN COMMON!

  28. None
  29. … BUT GOOGLE WILL PROVIDE SOME “MAGIC” TOOL TO MAKE

    THE DEVELOPMENT PROCESS EASIER, RIGHT?
  30. THE ANSWER IS…

  31. DEPENDS

  32. Google Cast SDK For sender apps: Full set of APIs

    for Android (Java), iOS (Objective-C & Swift) and Chrome (Javascript) For receiver apps: HTML5/Javascript APIs
  33. THE PROBLEM IS…

  34. YOUR APP IS TRYING TO CONQUER THE LIVING ROOM, SO

    USERS DON’T WANT TO DEAL WITH PROBLEMS, BUGS, CRASHES OR BAD UX
  35. YOUR CHROMECAST APP MUST BE…

  36. EASY TO USE

  37. FAST

  38. “AVAILABLE EVERYWHERE”

  39. THIS IS NOT EASY TO DO

  40. SO YES, GOOGLE HAS SOME “MAGIC TOOLS”

  41. THAT ARE ONLY VALID FOR SOME SCENARIOS

  42. LUCKILY FOR US…

  43. IT WILL COVER MOST OF THE SCENARIOS!

  44. None
  45. WHAT ARE THOSE SCENARIOS? WHAT’S THAT MAGIC TOOL?

  46. GOOGLE CAST UX GUIDELINES

  47. CAST COMPANION LIBRARY

  48. Google Cast UX Guidelines - Key Ideas Your app is

    the remote It’s a shared experience It’s cross platform
  49. INTRODUCE CAST THIS IS A ONE-TIME INTRODUCTION SCREEN, SO USERS

    CAN LEARN HOW TO USE CHROMECAST CAST ICON MUST VISIBLE FROM EVERY SCREEN CAST ICON LOCATION MUST CONSISTENT ACROSS THE APP PREFERRED LOCATION: RIGHT CORNER OF THE NAVIGATION BAR
  50. SENDER DISCONNECTED RECEIVER SHOWS HOME SCREEN

  51. Cast Button - States UNAVAILABLE: while receivers are not available,

    cast button doesn’t appear DISCONNECTED: the cast button appears if receivers are available CONNECTING: while the cast receiver is connecting, the cast button animates the waves CONNECTED: the cast button appears with a filled frame shape
  52. CONNECT & PLAY

  53. TAP CAST BUTTON RECEIVER DOES NOT CHANGE

  54. SELECT A RECEIVER RECEIVER APP LOADING

  55. SENDER IS CONNECTED RECEIVER APP IS LOADED / IDLE

  56. SENDER PLAYS CONTENT - IT BEHAVES AS A REMOTE CONTROL

    RECEIVER IS PLAYING CONTENT
  57. SENDER PLAYS CONTENT - IT BEHAVES AS A REMOTE CONTROL

    RECEIVER IS PLAYING CONTENT
  58. … BUT YOU CAN ALSO PLAY & CONNECT

  59. SENDER IS NOT CONNECTED - PLAYS MEDIA LOCALLY ON THE

    DEVICE RECEIVER SHOWS HOME SCREEN
  60. TAP THE CAST BUTTON RECEIVER LOADS THE APP

  61. TAP THE CAST BUTTON RECEIVER LOADS THE APP

  62. SENDER PLAYS CONTENT - IT BEHAVES AS A REMOTE CONTROL

    RECEIVER IS PLAYING CONTENT
  63. SENDER PLAYS CONTENT - IT BEHAVES AS A REMOTE CONTROL

    RECEIVER IS PLAYING CONTENT
  64. CAST MENU

  65. CAST MENU NOT CONNECTED RECEIVER SHOWS HOME SCREEN

  66. CAST MENU - CONNECTED BUT NOT CASTING RECEIVER APP LOADED

  67. CAST MENU - WHILE CASTING RECEIVER PLAYING CONTENT

  68. CAST CONTROLS “AVAILABLE EVERYWHERE”

  69. DEDICATED SCREEN

  70. PERSISTENT CONTROLLER AVAILABLE IN ALL THE SCREENS OF YOUR APP

  71. NOTIFICATION CONTROLS EVEN IF YOUR APP IS NO LONGER ACTIVE

  72. LOCK SCREEN CONTROLS YOUR DEVICE NOW IS A TV REMOTE

    CONTROL, SO DON’T MAKE YOUR USERS WASTE THEIR TIME
  73. … AND DON’T FORGET VOLUME CONTROLS

  74. WHAT IF WE LOSE CONNECTION WITH THE RECEIVER? WIFI DISCONNECTION

    BATTERY DIES APP PROCESS STOPPED
  75. YOU SHOULD AUTO-RECONNECT

  76. DISCONNECTING

  77. ONLY DISCONNECT THE CAST WHEN THE LAST USER DISCONNECTS!

  78. FINAL DESIGN TIPS

  79. DON’T MAKE YOUR TV APP LOOK INTERACTIVE

  80. None
  81. YOUR TV ONLY SHOWS STATE

  82. YOUR “PHONE” PROVIDES ACTIONS

  83. HOW AM I SUPPOSED TO CODE ALL THAT?

  84. CAST COMPANION LIBRARY THE MAGIG TOOL DEVELOPED BY GOOGLE

  85. Cast Companion Library Wrapper around Cast SDK APIs Makes SUPER

    EASY to develop what’s on the UX guidelines Open source Available on Github: https://github.com/googlecast/CastCompanionLibrary-android
  86. LET’S TALK ABOUT CODE!

  87. … but first Developers must register a test device in

    the Cast Developer Console https://cast.google.com/publish/#/overview Developer fees: $5
  88. Google Cast Developer Console Need to provide app info: package

    name Register your receiver Marketing details Get your CHROMECAST_APP_ID
  89. MEET WOODY! MY LITTLE PROJECT FOR SHOWING HOW THE CAST

    COMPANION LIBRARY WORKS
  90. None
  91. Assumptions We already have an app with screens in place:

    Results screen with a list of movies with multiple frames for Popular movies, Most rated,… A details screen listing information about the selected movie A search screen We will have to check if Google Play Services are available
  92. Architecture We will build a BaseChromecastActivity, and all the activities

    in our app will inherit from it Will only contain Cast related code ActionBarActivity BaseChromecastActivity Rest of activities of the app
  93. Our goals are… 1. Add cast icon 2. Introduce cast

    to first time users 3. Connect to a Cast-ready device 4. Cast content 5. Add a mini controller in our app 6. Add a sticky notification with media controls 7. Add a lock screen with media controls 8. Auto-reconnect when connection is lost
  94. KEY CONCEPTS

  95. CastManager It is the brain of the Cast functionality. It

    will update the NotificationService and the MiniController accordingly. BaseCastManager: abstract class that handles most of connectivity issues that transcends the lifecycle of individual activities. VideoCastManager: designed for video-centric apps
  96. HOW DO WE USE THE VIDEOCASTMANAGER?

  97. VideoCastManager public class WoodyApplication extends Application { @Override public void

    onCreate() { super.onCreate(); videoCastManager = VideoCastManager.initialize(getApplicationContext(), Constants.CHROMECAST_APP_ID, null, null); videoCastManager.enableFeatures(VideoCastManager.FEATURE_NOTIFICATION | VideoCastManager.FEATURE_LOCKSCREEN | VideoCastManager.FEATURE_WIFI_RECONNECT | VideoCastManager.FEATURE_CAPTIONS_PREFERENCE | VideoCastManager.FEATURE_DEBUGGING); } }
  98. VideoCastManager public class WoodyApplication extends Application { @Override public void

    onCreate() { super.onCreate(); videoCastManager = VideoCastManager.initialize(getApplicationContext(), Constants.CHROMECAST_APP_ID, null, null); videoCastManager.enableFeatures(VideoCastManager.FEATURE_NOTIFICATION | VideoCastManager.FEATURE_LOCKSCREEN | VideoCastManager.FEATURE_WIFI_RECONNECT | VideoCastManager.FEATURE_CAPTIONS_PREFERENCE | VideoCastManager.FEATURE_DEBUGGING); } }
  99. VideoCastManager public class WoodyApplication extends Application { @Override public void

    onCreate() { super.onCreate(); videoCastManager = VideoCastManager.initialize(getApplicationContext(), Constants.CHROMECAST_APP_ID, null, null); videoCastManager.enableFeatures(VideoCastManager.FEATURE_NOTIFICATION | VideoCastManager.FEATURE_LOCKSCREEN | VideoCastManager.FEATURE_WIFI_RECONNECT | VideoCastManager.FEATURE_CAPTIONS_PREFERENCE | VideoCastManager.FEATURE_DEBUGGING); } }
  100. Our goals are… 1. Add cast icon 2. Introduce cast

    to first time users 3. Connect to a Cast-ready device 4. Cast content 5. Add a mini controller in our app 6. Add a sticky notification with media controls: ENABLED 7. Add a lock screen with media controls: ENABLED 8. Auto-reconnect when connection is lost: ENABLED
  101. CAST ICON

  102. menu.xml <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <item android:id="@+id/media_route_menu_item" android:title="@string/settings.media_route_menu_title" app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider" app:showAsAction="always"/>

    </menu>
  103. menu.xml <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <item android:id="@+id/media_route_menu_item" android:title="@string/settings.media_route_menu_title" app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider" app:showAsAction="always"/>

    </menu>
  104. BaseChromecastActivity public class BaseChromecastActivity extends ActionBarActivity { protected VideoCastManager videoCastManager;

    protected MiniController miniController; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); VideoCastManager.checkGooglePlayServices(this); videoCastManager = WoodyApplication.getCastManager(); showChromecastTutorial(); videoCastManager.reconnectSessionIfPossible(); }
  105. Check for Google Play Services public class BaseChromecastActivity extends ActionBarActivity

    { protected VideoCastManager videoCastManager; protected MiniController miniController; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); VideoCastManager.checkGooglePlayServices(this); videoCastManager = WoodyApplication.getCastManager(); showChromecastTutorial(); videoCastManager.reconnectSessionIfPossible(); }
  106. BaseChromecastActivity @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_base_navigation, menu); mediaRouteMenuItem

    = videoCastManager .addMediaRouterButton(menu, R.id.media_route_menu_item); return true; }
  107. BaseChromecastActivity @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_base_navigation, menu); mediaRouteMenuItem

    = videoCastManager .addMediaRouterButton(menu, R.id.media_route_menu_item); return true; }
  108. Our goals are… 1. Add cast icon: DONE 2. Introduce

    cast to first time users 3. Connect to a Cast-ready device: DONE 4. Cast content 5. Add a mini controller in our app 6. Add a sticky notification with media controls: ENABLED 7. Add a lock screen with media controls: ENABLED 8. Auto-reconnect when connection is lost: ENABLED
  109. LET’S PLAY SOME CONTENT! VIDEOCASTCONTROLLERACTIVITY WILL PROVIDE A DEFAULT IMPLEMENTATION

    OF THE PLAYER CONTROL SCREEN
  110. Register this new Activity <activity android:name="com.google.sample.castcompanionlibrary.cast.player.VideoCastControllerActivity" android:launchMode="singleTask" android:screenOrientation="portrait" android:parentActivityName=".activity.movie.MovieResultsActivity"/>

  111. Play content MediaInfo mediaInfo = buildMediaInfo(); Intent intent = new

    Intent(getActivity(), VideoCastControllerActivity.class); intent.putExtra(Constants.EXTRA_MEDIA, Utils.fromMediaInfo(mediaInfo)); intent.putExtra(Constants.EXTRA_SHOULD_START, true); startActivity(intent);
  112. Play content MediaInfo mediaInfo = buildMediaInfo(); Intent intent = new

    Intent(getActivity(), VideoastControllerActivity.class); intent.putExtra(Constants.EXTRA_MEDIA, Utils.fromMediaInfo(mediaInfo)); intent.putExtra(Constants.EXTRA_SHOULD_START, true); startActivity(intent);
  113. MediaInfo private MediaInfo buildMediaInfo() { MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);

    movieMetadata.putString(MediaMetadata.KEY_TITLE, movieDetails.getTitle()); movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, movieDetails.getGenresWithCommas()); movieMetadata.putString(MediaMetadata.KEY_STUDIO, movieDetails.getProductionCompaniesWithCommas()); movieMetadata.addImage(new WebImage(Uri.parse(movieDetails.getPosterPath()))); return new MediaInfo.Builder(movieDetails.getMovieUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("video/mp4") .setMetadata(movieMetadata) .build(); }
  114. MediaInfo private MediaInfo buildMediaInfo() { MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);

    movieMetadata.putString(MediaMetadata.KEY_TITLE, movieDetails.getTitle()); movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, movieDetails.getGenresWithCommas()); movieMetadata.putString(MediaMetadata.KEY_STUDIO, movieDetails.getProductionCompaniesWithCommas()); movieMetadata.addImage(new WebImage(Uri.parse(movieDetails.getPosterPath()))); return new MediaInfo.Builder(movieDetails.getMovieUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("video/mp4") .setMetadata(movieMetadata) .build(); }
  115. Our goals are… 1. Add cast icon: DONE 2. Introduce

    cast to first time users 3. Connect to a Cast-ready device: DONE 4. Cast content: DONE 5. Add a mini controller in our app 6. Add a sticky notification with media controls: ENABLED 7. Add a lock screen with media controls: ENABLED 8. Auto-reconnect when connection is lost: ENABLED
  116. MINICONTROLLER WIDGET PROVIDED BY THE CAST COMPANION LIBRARY

  117. In your BaseChromecastActivity layout <com.google.sample.castcompanionlibrary.widgets.MiniController android:id="@+id/mini_controller" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="@color/greySoft"

    android:visibility="gone"/>
  118. BaseChromecastActivity @Override protected void onResume() { super.onResume(); videoCastManager = WoodyApplication.getCastManager();

    miniController.setOnMiniControllerChangedListener(videoCastManager); videoCastManager.incrementUiCounter(); } @Override protected void onPause() { super.onPause(); videoCastManager.decrementUiCounter(); miniController.removeOnMiniControllerChangedListener(videoCastManager); }
  119. BaseChromecastActivity @Override protected void onResume() { super.onResume(); videoCastManager = WoodyApplication.getCastManager();

    miniController.setOnMiniControllerChangedListener(videoCastManager); videoCastManager.incrementUiCounter(); } @Override protected void onPause() { super.onPause(); videoCastManager.decrementUiCounter(); miniController.removeOnMiniControllerChangedListener(videoCastManager); }
  120. BaseChromecastActivity @Override protected void onDestroy() { if (videoCastManager != null)

    { miniController.removeOnMiniControllerChangedListener(videoCastManager); videoCastManager.removeMiniController(miniController); videoCastManager.clearContext(this); } super.onDestroy(); }
  121. BaseChromecastActivity @Override public void setContentView(final int layoutResID) { RelativeLayout layout

    = (RelativeLayout) getLayoutInflater() .inflate(R.layout.activity_base_chromecast, null); miniController = (MiniController) layout.findViewById(R.id.mini_controller); videoCastManager.addMiniController(miniController); super.setContentView(layout); }
  122. BaseChromecastActivity @Override public void setContentView(final int layoutResID) { RelativeLayout layout

    = (RelativeLayout) getLayoutInflater() .inflate(R.layout.activity_base_chromecast, null); miniController = (MiniController) layout.findViewById(R.id.mini_controller); videoCastManager.addMiniController(miniController); super.setContentView(layout); }
  123. BaseChromecastActivity @Override protected void onResume() { super.onResume(); videoCastManager = WoodyApplication.getCastManager();

    miniController.setOnMiniControllerChangedListener(videoCastManager); videoCastManager.incrementUiCounter(); } @Override protected void onPause() { super.onPause(); videoCastManager.decrementUiCounter(); miniController.removeOnMiniControllerChangedListener(videoCastManager); }
  124. Our goals are… 1. Add cast icon: DONE 2. Introduce

    cast to first time users 3. Connect to a Cast-ready device: DONE 4. Cast content: DONE 5. Add a mini controller in our app: DONE 6. Add a sticky notification with media controls: ENABLED 7. Add a lock screen with media controls: ENABLED 8. Auto-reconnect when connection is lost: ENABLED
  125. NOTIFICATION WITH MEDIA CONTROLS

  126. THIS NOTIFICATION WILL APPEAR EVERY TIME OUR APP IS CASTING

    AND IS NOT THE ACTIVE APP
  127. VideoCastNotificationService <service android:name="com.google.sample.castcompanionlibrary.notification.VideoCastNotificationService" android:exported="false"> <intent-filter> <action android:name="com.google.sample.castcompanionlibrary.action.toggleplayback" /> <action android:name="com.google.sample.castcompanionlibrary.action.stop"

    /> <action android:name="com.google.sample.castcompanionlibrary.action.notificationvisibility" /> </intent-filter> </service>
  128. Our goals are… 1. Add cast icon: DONE 2. Introduce

    cast to first time users 3. Connect to a Cast-ready device: DONE 4. Cast content: DONE 5. Add a mini controller in our app: DONE 6. Add a sticky notification with media controls: DONE 7. Add a lock screen with media controls: ENABLED 8. Auto-reconnect when connection is lost: ENABLED
  129. Auto-Reconnect: ReconnectService <service android:name="com.google.sample.castcompanionlibrary.cast.reconnection.ReconnectionService"/> Background service that handles reconnection logic

    when WIFI connectivity is lost
  130. Our goals are… 1. Add cast icon: DONE 2. Introduce

    cast to first time users 3. Connect to a Cast-ready device: DONE 4. Cast content: DONE 5. Add a mini controller in our app: DONE 6. Add a sticky notification with media controls: DONE 7. Add a lock screen with media controls: ENABLED 8. Auto-reconnect when connection is lost: ENABLED
  131. WHY DID WE SKIP THE LOCK SCREEN CONTROLS?

  132. IT IS ALREADY IMPLEMENTED

  133. Remember this code? private MediaInfo buildMediaInfo() { MediaMetadata movieMetadata =

    new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); movieMetadata.putString(MediaMetadata.KEY_TITLE, movieDetails.getTitle()); movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, movieDetails.getGenresWithCommas()); movieMetadata.putString(MediaMetadata.KEY_STUDIO, movieDetails.getProductionCompaniesWithCommas()); movieMetadata.addImage(new WebImage(Uri.parse(movieDetails.getPosterPath()))); return new MediaInfo.Builder(movieDetails.getMovieUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("video/mp4") .setMetadata(movieMetadata) .build(); }
  134. Our goals are… 1. Add cast icon: DONE 2. Introduce

    cast to first time users 3. Connect to a Cast-ready device: DONE 4. Cast content: DONE 5. Add a mini controller in our app: DONE 6. Add a sticky notification with media controls: DONE 7. Add a lock screen with media controls: DONE 8. Auto-reconnect when connection is lost: ENABLED
  135. INTRODUCE CAST TO FIRST TIME USERS USING THE SHOW CASE

    VIEW LIBRARY (DEVELOPED BY @AMLCURRAN)
  136. ShowCaseView Not included in the CastCompanionLibrary, but used by Google

    in their examples https://github.com/amlcurran/ShowcaseView Easy to customize: styles.xml
  137. BaseChromecastActivity public class BaseChromecastActivity extends ActionBarActivity { protected VideoCastManager videoCastManager;

    protected MiniController miniController; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); VideoCastManager.checkGooglePlayServices(this); videoCastManager = WoodyApplication.getCastManager(); showChromecastTutorial(); videoCastManager.reconnectSessionIfPossible(); }
  138. BaseChromecastActivity public class BaseChromecastActivity extends ActionBarActivity { protected VideoCastManager videoCastManager;

    protected MiniController miniController; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); VideoCastManager.checkGooglePlayServices(this); videoCastManager = WoodyApplication.getCastManager(); showChromecastTutorial(); videoCastManager.reconnectSessionIfPossible(); }
  139. ShowCaseView private void showChromecastTutorial() { Menu menu = toolbar.getMenu(); View

    view = menu.findItem(R.id.media_route_menu_item).getActionView(); if (view != null && view instanceof MediaRouteButton) { new ShowcaseView.Builder(this) .setTarget(new ViewTarget(view)) .setContentTitle("Touch here to cast videos") .build(); SharedPreferencesUtils.saveBooleanSharedPreferences(this, PREF_USER_LEARNED_CHROMECAST, true); } }
  140. ShowCaseView private void showChromecastTutorial() { Menu menu = toolbar.getMenu(); View

    view = menu.findItem(R.id.media_route_menu_item).getActionView(); if (view != null && view instanceof MediaRouteButton) { new ShowcaseView.Builder(this) .setTarget(new ViewTarget(view)) .setContentTitle("Touch here to cast videos") .build(); SharedPreferencesUtils.saveBooleanSharedPreferences(this, PREF_USER_LEARNED_CHROMECAST, true); } }
  141. Our goals are… 1. Add cast icon: DONE 2. Introduce

    cast to first time users: DONE 3. Connect to a Cast-ready device: DONE 4. Cast content: DONE 5. Add a mini controller in our app: DONE 6. Add a sticky notification with media controls: DONE 7. Add a lock screen with media controls: DONE 8. Auto-reconnect when connection is lost: ENABLED
  142. COOL, RIGHT?

  143. LET’S CODE THE RECEIVER!

  144. LET’S CODE THE RECEIVER STYLED MEDIA RECEIVER

  145. None
  146. StyledMediaReceiver .background { background: #0c1821; } .logo { background-image: url("http://your_url.com/woody_transparent.png");

    } .progressBar { background-color: #d7263d; } .splash { background-image: url("http://your_url.com/splash.png") } .watermark { background-image: url("http://your_url.com/watermark.png"); background-size: 100px 100px; }
  147. DO YOU WANT TO SEE THE RESULTS?

  148. None
  149. WHY SHOULD I ADD CAST SUPPORT IN MY APP?

  150. None
  151. None
  152. COMEDY CENTRAL SEES 50% MORE VIDEOS VIEWED BY CHROMECAST USERS

  153. JUST DANCE NOW SEES 2.5X MONETIZATION WITH CHROMECAST USERS

  154. FITNET SEES 35% HIGHER ENGAGEMENT IN CHROMECAST USERS

  155. HAYSTACK TV DOUBLED AVERAGE WEEKLY VIEWING TIME

  156. Find this presentation ThatConference 2015 https://speakerdeck.com/jorgecoca/thatconference-2015 Chicago Code Camp 2015

    https://speakerdeck.com/jorgecoca/chromecast-and-android
  157. Follow me on Twitter & Github @jcocaramos jorgecoca

  158. We’re Hiring!

  159. None
  160. None
  161. None