Slide 1

Slide 1 text

Android TV This is not the idiot box you are looking for

Slide 2

Slide 2 text

+DavidGonzalezMalmstein malmstein David González Technical Product Owner at Novoda @dggonzalez +DavidGonzalezMalmstein malmstein Android TV

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

“Designers are also nice people*” Sebastiano Poggi Android Developer @Novoda This is not the idiot box you are looking for *allegedly Dave Clements Head of Design @Novoda

Slide 5

Slide 5 text

The 10 foot experience http://rantfarm.com/wp-content/uploads/2011/01/football.jpg

Slide 6

Slide 6 text

This is not a big tablet http://www.oneextraordinarymarriage.com/wp-content/uploads/2013/02/Couple-watching-tv.jpg

Slide 7

Slide 7 text

What is Android TV? Smarter way of using your TV

Slide 8

Slide 8 text

Android TV Devices ADT-1, Nexus Player, Sony Bravia, LG, Panasonic

Slide 9

Slide 9 text

Adapting for Android TV Distinguish between phone, tablet and television

Slide 10

Slide 10 text

Manifest.xml 
 
 
 
 
 
 
 
 


Slide 11

Slide 11 text

Theming 
 <item name=“android:colorPrimary">@color/search_opaque</item> 
 <item name="android:windowEnterTransition">@android:transition/fade</item>
 <item name=“android:windowExitTransition">@android:transition/fade</item> 


Slide 12

Slide 12 text

There is no touchable screen The remote control travels with you

Slide 13

Slide 13 text

There is no touchable screen

Slide 14

Slide 14 text

There is no touchable screen 
 KeyEvent.KEYCODE_DPAD_CENTER;
 KeyEvent.KEYCODE_DPAD_LEFT;
 KeyEvent.KEYCODE_DPAD_RIGHT;
 KeyEvent.KEYCODE_DPAD_DOWN;
 KeyEvent.KEYCODE_DPAD_UP ;


Slide 15

Slide 15 text

There is no touchable screen 
 KeyEvent.KEYCODE_DPAD_CENTER;
 KeyEvent.KEYCODE_DPAD_LEFT;
 KeyEvent.KEYCODE_DPAD_RIGHT;
 KeyEvent.KEYCODE_DPAD_DOWN;
 KeyEvent.KEYCODE_DPAD_UP ;


Slide 16

Slide 16 text

Check for a TV Device public static final String TAG = "DeviceTypeRuntimeCheck";
 
 UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
 if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { 
 Log.d(TAG, "Running on a TV Device")
 } else {
 Log.d(TAG, "Running on a non-TV Device")
 }

Slide 17

Slide 17 text

Leanback library http://images.blastro.com/images/artist_images/full/full_ciara_artist_photo10.jpg

Slide 18

Slide 18 text

Gradle dependencies dependencies {
 compile 'com.android.support:recyclerview-v7:22.0.0'
 compile 'com.android.support:leanback-v17:22.0.0'
 compile 'com.android.support:appcompat-v7:22.0.0'
 }

Slide 19

Slide 19 text

Leanback Library - BrowseFragment

Slide 20

Slide 20 text

Customising the BrowseFragment public class MainFragment extends BrowseFragment setBadgeDrawable(getDrawable(R.drawable.videos_by_google_banner));
 setTitle(getString(R.string.browse_title)); 
 setHeadersState(HEADERS_ENABLED);
 setHeadersTransitionOnBackEnabled(true); 
 setBrandColor(getResources().getColor(R.color.fastlane_background));
 setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
 


Slide 21

Slide 21 text

Presenting data setHeaderPresenterSelector(new PresenterSelector() {
 @Override
 public Presenter getPresenter(Object o) {
 return new IconHeaderItemPresenter();
 }
 }); public class IconHeaderItemPresenter extends Presenter { @Override
 public ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
 return new ViewHolder(inflater.inflate(R.layout.icon_header_item, null));
 } @Override
 public void onBindViewHolder(ViewHolder viewHolder, Object o) {
 View rootView = viewHolder.view;
 } }

Slide 22

Slide 22 text

Presenting data setHeaderPresenterSelector(new PresenterSelector() {
 @Override
 public Presenter getPresenter(Object o) {
 return new IconHeaderItemPresenter();
 }
 }); public class IconHeaderItemPresenter extends Presenter { @Override
 public ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
 return new ViewHolder(inflater.inflate(R.layout.icon_header_item, null));
 } @Override
 public void onBindViewHolder(ViewHolder viewHolder, Object o) {
 View rootView = viewHolder.view;
 } }

Slide 23

Slide 23 text

Presenting data mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
 CardPresenter cardPresenter = new CardPresenter(); for (Map.Entry> entry : data.entrySet()) {
 ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
 List list = entry.getValue();
 
 for (int j = 0; j < list.size(); j++) {
 listRowAdapter.add(list.get(j));
 }
 HeaderItem header = new HeaderItem(i, entry.getKey());
 i++;
 mRowsAdapter.add(new ListRow(header, listRowAdapter));
 } setAdapter(mRowsAdapter);

Slide 24

Slide 24 text

Presenting data mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
 CardPresenter cardPresenter = new CardPresenter(); for (Map.Entry> entry : data.entrySet()) {
 ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
 List list = entry.getValue();
 
 for (int j = 0; j < list.size(); j++) {
 listRowAdapter.add(list.get(j));
 }
 HeaderItem header = new HeaderItem(i, entry.getKey());
 i++;
 mRowsAdapter.add(new ListRow(header, listRowAdapter));
 } setAdapter(mRowsAdapter);

Slide 25

Slide 25 text

Presenting data mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
 CardPresenter cardPresenter = new CardPresenter(); for (Map.Entry> entry : data.entrySet()) {
 ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
 List list = entry.getValue();
 
 for (int j = 0; j < list.size(); j++) {
 listRowAdapter.add(list.get(j));
 }
 HeaderItem header = new HeaderItem(i, entry.getKey());
 i++;
 mRowsAdapter.add(new ListRow(header, listRowAdapter));
 } setAdapter(mRowsAdapter);

Slide 26

Slide 26 text

Leanback Library - Event listeners

Slide 27

Slide 27 text

Setup event listeners setOnSearchClickedListener(new View.OnClickListener() {
 
 @Override
 public void onClick(View view) {
 Intent intent = new Intent(getActivity(), SearchActivity.class);
 startActivity(intent);
 }
 });
 
 setOnItemViewClickedListener(new ItemViewClickedListener());
 setOnItemViewSelectedListener(new ItemViewSelectedListener());

Slide 28

Slide 28 text

Setup event listeners setOnSearchClickedListener(new View.OnClickListener() {
 
 @Override
 public void onClick(View view) {
 Intent intent = new Intent(getActivity(), SearchActivity.class);
 startActivity(intent);
 }
 });
 
 setOnItemViewClickedListener(new ItemViewClickedListener());
 setOnItemViewSelectedListener(new ItemViewSelectedListener());

Slide 29

Slide 29 text

Setup event listeners private final class ViewSelectedListener implements OnItemViewSelectedListener {
 @Override
 public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) {
 
 mBackgroundURI = ((Movie) item).getBackgroundImageURI();
 startBackgroundTimer(); 
 }
 }

Slide 30

Slide 30 text

Leanback Library - Background Manager

Slide 31

Slide 31 text

Setting a background image private void prepareBackgroundManager() {
 mBackgroundManager = BackgroundManager.getInstance(getActivity());
 mBackgroundManager.attach(getActivity().getWindow());
 } Glide.with(getActivity())
 .load(uri)
 .centerCrop()
 .error(mDefaultBackground)
 .into(new SimpleTarget(width, height) {
 @Override
 public void onResourceReady(GlideDrawable resource, GlideAnimation super GlideDrawable> glideAnimation) {
 mBackgroundManager.setDrawable(resource);
 }
 });

Slide 32

Slide 32 text

Setting a background image private void prepareBackgroundManager() {
 mBackgroundManager = BackgroundManager.getInstance(getActivity());
 mBackgroundManager.attach(getActivity().getWindow());
 } Glide.with(getActivity())
 .load(uri)
 .centerCrop()
 .error(mDefaultBackground)
 .into(new SimpleTarget(width, height) {
 @Override
 public void onResourceReady(GlideDrawable resource, GlideAnimation super GlideDrawable> glideAnimation) {
 mBackgroundManager.setDrawable(resource);
 }
 });

Slide 33

Slide 33 text

Leanback Library - Playback Controls

Slide 34

Slide 34 text

Presenting details /*
 * LeanbackDetailsFragment extends DetailsFragment, a Wrapper fragment for leanback details screens.
 * It shows a detailed view of video and its meta plus related videos.
 */
 public class MovieDetailsFragment extends android.support.v17.leanback.app.DetailsFragment {

Slide 35

Slide 35 text

Presenting details private void setupAdapter() {
 mPresenterSelector = new ClassPresenterSelector();
 mAdapter = new ArrayObjectAdapter(mPresenterSelector);
 setAdapter(mAdapter);
 } private void setupDetailsOverviewRowPresenter() { DetailsOverviewRowPresenter detailsPresenter =
 new DetailsOverviewRowPresenter( new DetailsDescriptionPresenter());
 detailsPresenter.setBackgroundColor(R.color.selected_background);
 detailsPresenter.setStyleLarge(true); mPresenterSelector.addClassPresenter(DetailsOverviewRow.class, detailsPresenter); }

Slide 36

Slide 36 text

Presenting details private void setupAdapter() {
 mPresenterSelector = new ClassPresenterSelector();
 mAdapter = new ArrayObjectAdapter(mPresenterSelector);
 setAdapter(mAdapter);
 } private void setupDetailsOverviewRowPresenter() { DetailsOverviewRowPresenter detailsPresenter =
 new DetailsOverviewRowPresenter( new DetailsDescriptionPresenter());
 detailsPresenter.setBackgroundColor(R.color.selected_background);
 detailsPresenter.setStyleLarge(true); mPresenterSelector.addClassPresenter(DetailsOverviewRow.class, detailsPresenter); }

Slide 37

Slide 37 text

Presenting details 
 public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter {
 
 @Override
 protected void onBindDescription(ViewHolder viewHolder, Object item) {
 Movie movie = (Movie) item;
 
 if (movie != null) {
 viewHolder.getTitle().setText(movie.getTitle());
 viewHolder.getSubtitle().setText(movie.getStudio());
 viewHolder.getBody().setText(movie.getDescription());
 }
 }
 }

Slide 38

Slide 38 text

Adding actions private void setupDetailsOverviewRow() {
 final DetailsOverviewRow row = new DetailsOverviewRow(mSelectedMovie);
 row.addAction(new Action(ACTION_WATCH_TRAILER, 
 getResources().getString(
 R.string.watch_trailer_1), 
 getResources().getString(R.string.watch_trailer_2))); 
 row.addAction(new Action(ACTION_RENT, 
 getResources().getString(R.string.rent_1),
 getResources().getString(R.string.rent_2))); 
 row.addAction(new Action(ACTION_BUY, 
 getResources().getString(R.string.buy_1),
 getResources().getString(R.string.buy_2))); 
 mAdapter.add(row);
 }

Slide 39

Slide 39 text

Adding actions private void setupDetailsOverviewRow() {
 final DetailsOverviewRow row = new DetailsOverviewRow(mSelectedMovie);
 row.addAction(new Action(ACTION_WATCH_TRAILER, 
 getResources().getString(
 R.string.watch_trailer_1), 
 getResources().getString(R.string.watch_trailer_2))); 
 row.addAction(new Action(ACTION_RENT, 
 getResources().getString(R.string.rent_1),
 getResources().getString(R.string.rent_2))); 
 row.addAction(new Action(ACTION_BUY, 
 getResources().getString(R.string.buy_1),
 getResources().getString(R.string.buy_2))); 
 mAdapter.add(row);
 }

Slide 40

Slide 40 text

Adding actions private void setupDetailsOverviewRow() {
 final DetailsOverviewRow row = new DetailsOverviewRow(mSelectedMovie);
 row.addAction(new Action(ACTION_WATCH_TRAILER, 
 getResources().getString(
 R.string.watch_trailer_1), 
 getResources().getString(R.string.watch_trailer_2))); 
 row.addAction(new Action(ACTION_RENT, 
 getResources().getString(R.string.rent_1),
 getResources().getString(R.string.rent_2))); 
 row.addAction(new Action(ACTION_BUY, 
 getResources().getString(R.string.buy_1),
 getResources().getString(R.string.buy_2))); 
 mAdapter.add(row);
 }

Slide 41

Slide 41 text

Leanback Library - Play Overlay

Slide 42

Slide 42 text

Add a PlaybackOverlayFragment 
 
 
 
 
 
 


Slide 43

Slide 43 text

Playing a video in the background /*
 * Class for video playback with media control
 */
 public class PlayFragment extends android.support.v17.leanback.app.PlaybackOverlayFragment

Slide 44

Slide 44 text

Playing a video in the background PlaybackControlsRowPresenter playbackControlsRowPresenter;
 playbackControlsRowPresenter = new PlaybackControlsRowPresenter(
 new DescriptionPresenter()); ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter); mRowsAdapter = new ArrayObjectAdapter(ps); setAdapter(mRowsAdapter); static class DescriptionPresenter extends AbstractDetailsDescriptionPresenter {
 @Override
 protected void onBindDescription(ViewHolder viewHolder, Object item) {
 viewHolder.getTitle().setText(((Movie) item).getTitle());
 viewHolder.getSubtitle().setText(((Movie) item).getStudio());
 }
 }


Slide 45

Slide 45 text

Playing a video in the background PlaybackControlsRowPresenter playbackControlsRowPresenter;
 playbackControlsRowPresenter = new PlaybackControlsRowPresenter(
 new DescriptionPresenter()); ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter); mRowsAdapter = new ArrayObjectAdapter(ps); setAdapter(mRowsAdapter); static class DescriptionPresenter extends AbstractDetailsDescriptionPresenter {
 @Override
 protected void onBindDescription(ViewHolder viewHolder, Object item) {
 viewHolder.getTitle().setText(((Movie) item).getTitle());
 viewHolder.getSubtitle().setText(((Movie) item).getStudio());
 }
 }


Slide 46

Slide 46 text

Playback Controls private void addPlaybackControlsRow() { ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector();
 mPrimaryActionsAdapter = new ArrayObjectAdapter(presenterSelector);
 mSecondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); 
 mPlaybackControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter);
 mPlaybackControlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter); mPlayPauseAction = new PlayPauseAction(sContext);
 mRepeatAction = new RepeatAction(sContext); mPrimaryActionsAdapter.add(mPlayPauseAction); mSecondaryActionsAdapter.add(mRepeatAction); }

Slide 47

Slide 47 text

Playback Controls private void addPlaybackControlsRow() { ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector();
 mPrimaryActionsAdapter = new ArrayObjectAdapter(presenterSelector);
 mSecondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); 
 mPlaybackControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter);
 mPlaybackControlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter); mPlayPauseAction = new PlayPauseAction(sContext);
 mRepeatAction = new RepeatAction(sContext); mPrimaryActionsAdapter.add(mPlayPauseAction); mSecondaryActionsAdapter.add(mRepeatAction); }

Slide 48

Slide 48 text

Request behind playback @Override
 public void onPause() {
 super.onPause();
 requestVisibleBehind(true);
 } @Override
 public void onVisibleBehindCanceled() {
 super.onVisibleBehindCanceled();
 stopPlayback();
 } @Override
 protected void onStop() {
 super.onStop();
 mSession.release();
 }

Slide 49

Slide 49 text

Android TV How to build a rich experience

Slide 50

Slide 50 text

Recommendations

Slide 51

Slide 51 text

Add Service to the Manifest 
 
 
 
 
 


Slide 52

Slide 52 text

Create the notification Bundle extras = new Bundle();
 extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, imageUri);
 NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
 .setContentTitle(title)
 .setContentText(description)
 .setPriority(PRIORITY)
 .setLocalOnly(true)
 .setOngoing(true)
 .setColor(CARD_TEXT_BACKGROUND_COLOR_RESOURCE)
 .setCategory(Notification.CATEGORY_RECOMMENDATION)
 .setLargeIcon(filmPoster)
 .setSmallIcon(CARD_SMALL_APPLICATION_LOGO)
 .setContentIntent(intent)
 .setExtras(extras);


Slide 53

Slide 53 text

And trigger it 
 Notification notification = new NotificationCompat.BigPictureStyle(builder).build();
 
 NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
 notificationManager.notify(notificationUniqueId, notification);

Slide 54

Slide 54 text

Now Playing Card

Slide 55

Slide 55 text

Setup the Media Session mSession = new MediaSession (this, "MyApp");
 mSession.setCallback(new MediaSessionCallback());
 mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
 MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); if (!mSession.isActive()) {
 mSession.setActive(true);
 }

Slide 56

Slide 56 text

Use the Media Session private void updatePlaybackState() {
 long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
 position = mMediaPlayer.getCurrentPosition();
 
 PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
 .setActions(getAvailableActions());
 stateBuilder.setState(mState, position, 1.0f);
 mSession.setPlaybackState(stateBuilder.build());
 }

Slide 57

Slide 57 text

Use the Media Session private void updateMetadata(MediaData myData) {
 MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
 metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
 myData.displayTitle);
 metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
 myData.displaySubtitle);
 metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI,
 myData.artUri);
 mSession.setMetadata(metadataBuilder.build());
 }

Slide 58

Slide 58 text

Global search

Slide 59

Slide 59 text

Because everybody loves Content Providers 
 


Slide 60

Slide 60 text

Because everybody loves Content Providers private static UriMatcher buildUriMatcher() {
 UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
 matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
 matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
 return matcher;
 } @Override
 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
 switch (URI_MATCHER.match(uri)) {
 case SEARCH_SUGGEST:
 return getSuggestions(selectionArgs[0]);
 default:
 throw new IllegalArgumentException("Unknown Uri: " + uri);
 }
 }

Slide 61

Slide 61 text

Because everybody loves Content Providers private static UriMatcher buildUriMatcher() {
 UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
 matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
 matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
 return matcher;
 } @Override
 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
 switch (URI_MATCHER.match(uri)) {
 case SEARCH_SUGGEST:
 return getSuggestions(selectionArgs[0]);
 default:
 throw new IllegalArgumentException("Unknown Uri: " + uri);
 }
 }

Slide 62

Slide 62 text

Provide Cursor with your own search results private void addFilmToCursor(Film film, MatrixCursor matrixCursor) {
 matrixCursor.addRow(
 new Object[]{
 film.getId().toString(),
 film.getTitle(),
 getDirectorsLabel(film),
 VIDEO_MP4,
 film.getYear(),
 TimeUnit.MINUTES.toMillis(film.getDuration()),
 film.getPosterUrl(),
 film.getId().toString(),
 MubiIntentAction.SEARCH.getAction()
 }
 );
 }

Slide 63

Slide 63 text

Not everything has to look the same

Slide 64

Slide 64 text

ExoPlayer minSDK > 16 youtube.com/watch?v=6VjF638VObA

Slide 65

Slide 65 text

ExoPlayer 
 
 
 
 
 


Slide 66

Slide 66 text

Prepare ExoPlayer private void preparePlayer() { 
 SampleSource sampleSource =
 new FrameworkSampleSource(this, Uri.parse(mVideo.getContentUrl()), null, RENDERER_COUNT);
 
 MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT); 
 TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);
 
 }

Slide 67

Slide 67 text

Prepare ExoPlayer private void preparePlayer() { 
 SampleSource sampleSource =
 new FrameworkSampleSource(this, Uri.parse(mVideo.getContentUrl()), null, RENDERER_COUNT);
 
 MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT); 
 TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);
 
 }

Slide 68

Slide 68 text

Prepare ExoPlayer private void preparePlayer() { 
 SampleSource sampleSource =
 new FrameworkSampleSource(this, Uri.parse(mVideo.getContentUrl()), null, RENDERER_COUNT);
 
 MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT); 
 TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);
 
 }

Slide 69

Slide 69 text

Setup the player and start player = ExoPlayer.Factory.newInstance(RENDERER_COUNT, 1000, 5000);
 player.addListener(this);
 player.prepare(videoRenderer, audioRenderer); Surface surface = surfaceView.getHolder().getSurface();
 player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); playerControl = new PlayerControl(player);
 playerControl.start();

Slide 70

Slide 70 text

Setup the player and start player = ExoPlayer.Factory.newInstance(RENDERER_COUNT, 1000, 5000);
 player.addListener(this);
 player.prepare(videoRenderer, audioRenderer); Surface surface = surfaceView.getHolder().getSurface();
 player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); playerControl = new PlayerControl(player);
 playerControl.start();

Slide 71

Slide 71 text

Setup the player and start player = ExoPlayer.Factory.newInstance(RENDERER_COUNT, 1000, 5000);
 player.addListener(this);
 player.prepare(videoRenderer, audioRenderer); Surface surface = surfaceView.getHolder().getSurface();
 player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); playerControl = new PlayerControl(player);
 playerControl.start();

Slide 72

Slide 72 text

Android TV ExoPlayer on Github github.com/google/ExoPlayer What’s next? Android TV Samples github.com/googlesamples/androidtv-Leanback

Slide 73

Slide 73 text

“Thank You! @dggonzalez +DavidGonzalezMalmstein malmstein Any questions? for putting up with all the blue”