(minSDK=17) Consistency across different android TV’s Fast to develop Higher Performance Built-In (beautifully) animation library compile ‘com.android.support.leanback-‐v17:+’ 33
android:required="true" /> <uses-‐feature android:name="android.hardware.touchscreen" android:required="false" /> <application android:allowBackup="true" android:label="@string/app_name" android:banner=“@drawable/gdg_devfest_banner" android:theme="@style/Theme.Leanback"> <activity android:name=".ui.MainActivity" android:label="@string/app_name" android:theme="@style/Theme.Leanback" > <intent-‐filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" /> </intent-‐filter> </activity> AndroidManifest.xml identifies app has been built for Android TV standard theme for TV’s app launcher (equivalent to android:icon) to be defined as an Android TV app
MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); setSessionToken(mSession.getSessionToken()); ... int result = requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); if(result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { if(!mMediaSession.isActive()) { mMediaSession.setActive(true); } } public void registerCallback(MediaSessionCallback cb) { mMediaSession.setCallback(cb); } MediaPlaybackService extends MediaBrowserService The card will be removed if the application calls setActive(false) or other app requires the audio focus
public void onSkipToNext() { PlaybackState.Builder stateBuilder = new PlaybackState.Builder().setActions(getAvailableActions()); stateBuilder.setState(PlaybackState.STATE_SKIPPING_TO_NEXT, 0, 1.0f); mMediaSession.setPlaybackState(stateBuilder.build()); if (++mCurrentItem >= mRelatedVideosList.size()) { mCurrentItem = 0; } Bundle bundle = new Bundle(); bundle.putBoolean(PlaybackOverlayActivity.AUTO_PLAY, true); String nextId = mRelatedVideos.get(mCurrentItem).getId()); getMediaController().getTransportControls().playFromMediaId(nextId, bundle); } Set media session to skip current video position in time and playback speed
new ArrayObjectAdapter(presenterSelector); mSecondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); mPlaybackControlsRow = new PlaybackControlsRow(mSelectedItem); mPlaybackControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter); mPlaybackControlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter); mPlayPauseAction = new PlayPauseAction(getActivity()); mSkipPreviousAction = new SkipPreviousAction(getActivity()); mPrimaryActionsAdapter.add(mPlayPauseAction); mPrimaryActionsAdapter.add(mSkipPreviousAction);
new NotificationCompat.Builder(mContext) .setContentTitle(item.getTitle()) .setContentText(item.getDescription()) .setPriority(NotificationCompat.PRIORITY_MAX) .setLocalOnly(true) .setOngoing(true) .setColor(getResources().getColor(R.color.selected_background)) .setCategory(Notification.CATEGORY_RECOMMENDATION) .setLargeIcon(item.getFullImage()) .setSmallIcon(item.getIcon()) .setContentIntent(item.getIntent()) .setExtras(NOTIFICATION.EXTRA_BACKGROUND_IMAGE_URI, item.getBgImage()) .build(); defines an image to be displayed on the background when the recommendation is selected
private static final int SEARCH_DELAY_MS = 300; private ArrayObjectAdapter mRowsAdapter; private Handler mHandler = new Handler(); private SearchRunnable mDelayedLoad; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); setSearchResultProvider(this); setOnItemClickedListener(getDefaultItemClickedListener()); mDelayedLoad = new SearchRunnable(); } InAppSearchFragment extends SearchFragment searching for items should not be done on UI level
Search within app Minimum actions required policy Acquire audio focus when playing content Make use of visual indicators for more information Compensate for overscan Don’t try to reinvent the wheel Eat your own dog food versionName “1.0” 72