1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 package com.example.android.leanback; 15 16 import java.util.ArrayList; 17 18 import android.content.Context; 19 import android.content.res.Resources; 20 import android.graphics.drawable.Drawable; 21 import android.os.Bundle; 22 import android.os.Handler; 23 import android.support.v17.leanback.widget.Action; 24 import android.support.v17.leanback.widget.ArrayObjectAdapter; 25 import android.support.v17.leanback.widget.ClassPresenterSelector; 26 import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter; 27 import android.support.v17.leanback.widget.PlaybackControlsRow; 28 import android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction; 29 import android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction; 30 import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction; 31 import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction; 32 import android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction; 33 import android.support.v17.leanback.widget.PlaybackControlsRow.SkipNextAction; 34 import android.support.v17.leanback.widget.PlaybackControlsRow.SkipPreviousAction; 35 import android.support.v17.leanback.widget.PlaybackControlsRowPresenter; 36 import android.support.v17.leanback.widget.HeaderItem; 37 import android.support.v17.leanback.widget.VerticalGridView; 38 import android.support.v17.leanback.widget.Row; 39 import android.support.v17.leanback.widget.ListRow; 40 import android.support.v17.leanback.widget.Presenter; 41 import android.support.v17.leanback.widget.RowPresenter; 42 import android.support.v17.leanback.widget.ListRowPresenter; 43 import android.support.v17.leanback.widget.OnActionClickedListener; 44 import android.support.v17.leanback.widget.OnItemViewSelectedListener; 45 import android.support.v17.leanback.widget.OnItemViewClickedListener; 46 import android.support.v17.leanback.widget.ControlButtonPresenterSelector; 47 import android.util.Log; 48 import android.widget.Toast; 49 50 public class PlaybackOverlayFragment extends android.support.v17.leanback.app.PlaybackOverlayFragment { 51 private static final String TAG = "leanback.PlaybackControlsFragment"; 52 53 private static final boolean SHOW_DETAIL = true; 54 private static final boolean HIDE_MORE_ACTIONS = false; 55 private static final int PRIMARY_CONTROLS = 5; 56 private static final boolean SHOW_IMAGE = PRIMARY_CONTROLS <= 5; 57 private static final int BACKGROUND_TYPE = PlaybackOverlayFragment.BG_LIGHT; 58 private static final int MORE_ROWS = 3; 59 60 private ArrayObjectAdapter mRowsAdapter; 61 private ArrayObjectAdapter mPrimaryActionsAdapter; 62 private ArrayObjectAdapter mSecondaryActionsAdapter; 63 private PlayPauseAction mPlayPauseAction; 64 private RepeatAction mRepeatAction; 65 private ThumbsUpAction mThumbsUpAction; 66 private ThumbsDownAction mThumbsDownAction; 67 private ShuffleAction mShuffleAction; 68 private SkipNextAction mSkipNextAction; 69 private SkipPreviousAction mSkipPreviousAction; 70 private PlaybackControlsRow mPlaybackControlsRow; 71 private ArrayList<MediaItem> mItems = new ArrayList<MediaItem>(); 72 private int mCurrentItem; 73 private Handler mHandler; 74 private Runnable mRunnable; 75 76 @Override onCreate(Bundle savedInstanceState)77 public void onCreate(Bundle savedInstanceState) { 78 Log.i(TAG, "onCreate"); 79 super.onCreate(savedInstanceState); 80 81 mHandler = new Handler(); 82 83 setBackgroundType(BACKGROUND_TYPE); 84 setFadingEnabled(false); 85 86 setupRows(); 87 88 setOnItemViewSelectedListener(new OnItemViewSelectedListener() { 89 @Override 90 public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, 91 RowPresenter.ViewHolder rowViewHolder, Row row) { 92 Log.i(TAG, "onItemSelected: " + item + " row " + row); 93 } 94 }); 95 setOnItemViewClickedListener(new OnItemViewClickedListener() { 96 @Override 97 public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, 98 RowPresenter.ViewHolder rowViewHolder, Row row) { 99 Log.i(TAG, "onItemClicked: " + item + " row " + row); 100 } 101 }); 102 } 103 104 setupRows()105 private void setupRows() { 106 ClassPresenterSelector ps = new ClassPresenterSelector(); 107 108 PlaybackControlsRowPresenter playbackControlsRowPresenter; 109 if (SHOW_DETAIL) { 110 playbackControlsRowPresenter = new PlaybackControlsRowPresenter( 111 new DescriptionPresenter()); 112 } else { 113 playbackControlsRowPresenter = new PlaybackControlsRowPresenter(); 114 } 115 playbackControlsRowPresenter.setOnActionClickedListener(new OnActionClickedListener() { 116 public void onActionClicked(Action action) { 117 if (action.getId() == mPlayPauseAction.getId()) { 118 if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) { 119 startProgressAutomation(); 120 setFadingEnabled(true); 121 } else { 122 stopProgressAutomation(); 123 setFadingEnabled(false); 124 } 125 } else if (action.getId() == mSkipNextAction.getId()) { 126 next(); 127 } else if (action.getId() == mSkipPreviousAction.getId()) { 128 Toast.makeText(getActivity(), "TODO", Toast.LENGTH_SHORT).show(); 129 } 130 if (action instanceof PlaybackControlsRow.MultiAction) { 131 ((PlaybackControlsRow.MultiAction) action).nextIndex(); 132 notifyChanged(action); 133 } 134 } 135 }); 136 playbackControlsRowPresenter.setSecondaryActionsHidden(HIDE_MORE_ACTIONS); 137 138 ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter); 139 ps.addClassPresenter(ListRow.class, new ListRowPresenter()); 140 mRowsAdapter = new ArrayObjectAdapter(ps); 141 142 addPlaybackControlsRow(); 143 addOtherRows(); 144 145 setAdapter(mRowsAdapter); 146 } 147 addPlaybackControlsRow()148 private void addPlaybackControlsRow() { 149 Context context = getActivity(); 150 151 if (SHOW_DETAIL) { 152 mPlaybackControlsRow = new PlaybackControlsRow(new MediaItem()); 153 } else { 154 mPlaybackControlsRow = new PlaybackControlsRow(); 155 } 156 mRowsAdapter.add(mPlaybackControlsRow); 157 158 mItems = new ArrayList<MediaItem>(); 159 mItems.add(new MediaItem("Awesome Tune", "The More Awesome Band", R.drawable.details_img, 15*1000)); 160 mItems.add(new MediaItem("Pretty nice Tune", "The Nice Guys", R.drawable.details_img, 10*1000)); 161 mCurrentItem = 1; 162 updatePlaybackRow(mCurrentItem); 163 164 ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector(); 165 mPrimaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); 166 mSecondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); 167 mPlaybackControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter); 168 mPlaybackControlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter); 169 170 mPlayPauseAction = new PlayPauseAction(context); 171 mRepeatAction = new RepeatAction(context); 172 mThumbsUpAction = new ThumbsUpAction(context); 173 mThumbsDownAction = new ThumbsDownAction(context); 174 mShuffleAction = new ShuffleAction(context); 175 mSkipNextAction = new PlaybackControlsRow.SkipNextAction(context); 176 mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(context); 177 178 if (PRIMARY_CONTROLS > 5) { 179 mPrimaryActionsAdapter.add(mThumbsUpAction); 180 } else { 181 mSecondaryActionsAdapter.add(mThumbsUpAction); 182 } 183 mPrimaryActionsAdapter.add(mSkipPreviousAction); 184 if (PRIMARY_CONTROLS > 3) { 185 mPrimaryActionsAdapter.add(new PlaybackControlsRow.RewindAction(context)); 186 } 187 mPrimaryActionsAdapter.add(mPlayPauseAction); 188 if (PRIMARY_CONTROLS > 3) { 189 mPrimaryActionsAdapter.add(new PlaybackControlsRow.FastForwardAction(context)); 190 } 191 mPrimaryActionsAdapter.add(mSkipNextAction); 192 193 mSecondaryActionsAdapter.add(mRepeatAction); 194 mSecondaryActionsAdapter.add(mShuffleAction); 195 if (PRIMARY_CONTROLS > 5) { 196 mPrimaryActionsAdapter.add(mThumbsDownAction); 197 } else { 198 mSecondaryActionsAdapter.add(mThumbsDownAction); 199 } 200 mSecondaryActionsAdapter.add(new PlaybackControlsRow.HighQualityAction(context)); 201 mSecondaryActionsAdapter.add(new PlaybackControlsRow.ClosedCaptioningAction(context)); 202 } 203 notifyChanged(Action action)204 private void notifyChanged(Action action) { 205 ArrayObjectAdapter adapter = mPrimaryActionsAdapter; 206 if (adapter.indexOf(action) >= 0) { 207 adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1); 208 return; 209 } 210 adapter = mSecondaryActionsAdapter; 211 if (adapter.indexOf(action) >= 0) { 212 adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1); 213 return; 214 } 215 } 216 updatePlaybackRow(int index)217 private void updatePlaybackRow(int index) { 218 if (mPlaybackControlsRow.getItem() != null) { 219 MediaItem item = (MediaItem) mPlaybackControlsRow.getItem(); 220 item.title = mItems.get(index).title; 221 item.subtitle = mItems.get(index).subtitle; 222 } 223 if (SHOW_IMAGE) { 224 mPlaybackControlsRow.setImageDrawable(getResources().getDrawable( 225 mItems.get(mCurrentItem).imageResId)); 226 } 227 mRowsAdapter.notifyArrayItemRangeChanged(0, 1); 228 229 mPlaybackControlsRow.setTotalTime(mItems.get(mCurrentItem).durationMs); 230 mPlaybackControlsRow.setCurrentTime(0); 231 mPlaybackControlsRow.setBufferedProgress(75 * 1000); 232 } 233 addOtherRows()234 private void addOtherRows() { 235 for (int i = 0; i < MORE_ROWS; ++i) { 236 ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new StringPresenter()); 237 listRowAdapter.add("Some related content"); 238 listRowAdapter.add("Other related content"); 239 HeaderItem header = new HeaderItem(i, "Row " + i, null); 240 mRowsAdapter.add(new ListRow(header, listRowAdapter)); 241 } 242 } 243 getUpdatePeriod()244 private int getUpdatePeriod() { 245 if (getView() == null || mPlaybackControlsRow.getTotalTime() <= 0) { 246 return 1000; 247 } 248 return Math.max(16, mPlaybackControlsRow.getTotalTime() / getView().getWidth()); 249 } 250 startProgressAutomation()251 private void startProgressAutomation() { 252 mRunnable = new Runnable() { 253 @Override 254 public void run() { 255 int updatePeriod = getUpdatePeriod(); 256 int currentTime = mPlaybackControlsRow.getCurrentTime() + updatePeriod; 257 int totalTime = mPlaybackControlsRow.getTotalTime(); 258 mPlaybackControlsRow.setCurrentTime(currentTime); 259 if (totalTime > 0 && totalTime <= currentTime) { 260 next(); 261 } 262 mHandler.postDelayed(this, updatePeriod); 263 } 264 }; 265 mHandler.postDelayed(mRunnable, getUpdatePeriod()); 266 } 267 next()268 private void next() { 269 if (++mCurrentItem >= mItems.size()) { 270 mCurrentItem = 0; 271 } 272 updatePlaybackRow(mCurrentItem); 273 } 274 stopProgressAutomation()275 private void stopProgressAutomation() { 276 if (mHandler != null && mRunnable != null) { 277 mHandler.removeCallbacks(mRunnable); 278 } 279 } 280 281 @Override onStop()282 public void onStop() { 283 stopProgressAutomation(); 284 super.onStop(); 285 } 286 287 static class MediaItem { 288 String title; 289 String subtitle; 290 int imageResId; 291 int durationMs; 292 MediaItem()293 MediaItem() { 294 } 295 MediaItem(String title, String subtitle, int imageResId, int durationMs)296 MediaItem(String title, String subtitle, int imageResId, int durationMs) { 297 this.title = title; 298 this.subtitle = subtitle; 299 this.imageResId = imageResId; 300 this.durationMs = durationMs; 301 } 302 } 303 304 static class DescriptionPresenter extends AbstractDetailsDescriptionPresenter { 305 @Override onBindDescription(ViewHolder vh, Object item)306 protected void onBindDescription(ViewHolder vh, Object item) { 307 vh.getTitle().setText(((MediaItem) item).title); 308 vh.getSubtitle().setText(((MediaItem) item).subtitle); 309 } 310 } 311 } 312