PlaybackControlsRowPresenter.java revision 5dac2c723e1d50da356170a6f372dcbab1a7d83c
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 */ 14package android.support.v17.leanback.widget; 15 16import android.support.v17.leanback.R; 17import android.support.v17.leanback.widget.ControlBarPresenter.OnControlClickedListener; 18import android.support.v17.leanback.widget.ControlBarPresenter.OnControlSelectedListener; 19import android.content.Context; 20import android.graphics.Color; 21import android.util.TypedValue; 22import android.view.LayoutInflater; 23import android.view.View; 24import android.view.ViewGroup; 25import android.view.ViewGroup.LayoutParams; 26import android.view.ViewGroup.MarginLayoutParams; 27import android.widget.ImageView; 28 29/** 30 * A PlaybackControlsRowPresenter renders a {@link PlaybackControlsRow} to display a 31 * series of playback control buttons. Typically this row will be the first row in a fragment 32 * such as the {@link android.support.v17.leanback.app.PlaybackOverlayFragment 33 * PlaybackControlsFragment}. 34 */ 35public class PlaybackControlsRowPresenter extends RowPresenter { 36 37 static class BoundData extends PlaybackControlsPresenter.BoundData { 38 ViewHolder mRowViewHolder; 39 } 40 41 /** 42 * A ViewHolder for the PlaybackControlsRow. 43 */ 44 public class ViewHolder extends RowPresenter.ViewHolder { 45 public final Presenter.ViewHolder mDescriptionViewHolder; 46 final ViewGroup mCard; 47 final ImageView mImageView; 48 final ViewGroup mDescriptionDock; 49 final ViewGroup mControlsDock; 50 final ViewGroup mSecondaryControlsDock; 51 final View mSpacer; 52 final View mBottomSpacer; 53 View mBgView; 54 int mCardHeight; 55 int mControlsDockMarginStart; 56 int mControlsDockMarginEnd; 57 PlaybackControlsPresenter.ViewHolder mControlsVh; 58 Presenter.ViewHolder mSecondaryControlsVh; 59 BoundData mControlsBoundData = new BoundData(); 60 BoundData mSecondaryBoundData = new BoundData(); 61 Presenter.ViewHolder mSelectedViewHolder; 62 Object mSelectedItem; 63 final PlaybackControlsRow.OnPlaybackStateChangedListener mListener = 64 new PlaybackControlsRow.OnPlaybackStateChangedListener() { 65 @Override 66 public void onCurrentTimeChanged(int ms) { 67 mPlaybackControlsPresenter.setCurrentTime(mControlsVh, ms); 68 } 69 @Override 70 public void onBufferedProgressChanged(int ms) { 71 mPlaybackControlsPresenter.setSecondaryProgress(mControlsVh, ms); 72 } 73 }; 74 75 ViewHolder(View rootView, Presenter descriptionPresenter) { 76 super(rootView); 77 mCard = (ViewGroup) rootView.findViewById(R.id.controls_card); 78 mImageView = (ImageView) rootView.findViewById(R.id.image); 79 mDescriptionDock = (ViewGroup) rootView.findViewById(R.id.description_dock); 80 mControlsDock = (ViewGroup) rootView.findViewById(R.id.controls_dock); 81 mSecondaryControlsDock = 82 (ViewGroup) rootView.findViewById(R.id.secondary_controls_dock); 83 mSpacer = rootView.findViewById(R.id.spacer); 84 mBottomSpacer = rootView.findViewById(R.id.bottom_spacer); 85 mDescriptionViewHolder = descriptionPresenter == null ? null : 86 descriptionPresenter.onCreateViewHolder(mDescriptionDock); 87 if (mDescriptionViewHolder != null) { 88 mDescriptionDock.addView(mDescriptionViewHolder.view); 89 } 90 } 91 92 void dispatchItemSelection() { 93 if (!isSelected()) { 94 return; 95 } 96 if (mSelectedViewHolder == null) { 97 if (getOnItemSelectedListener() != null) { 98 getOnItemSelectedListener().onItemSelected(null, getRow()); 99 } 100 if (getOnItemViewSelectedListener() != null) { 101 getOnItemViewSelectedListener().onItemSelected(null, null, 102 ViewHolder.this, getRow()); 103 } 104 } else { 105 if (getOnItemSelectedListener() != null) { 106 getOnItemSelectedListener().onItemSelected(mSelectedItem, getRow()); 107 } 108 if (getOnItemViewSelectedListener() != null) { 109 getOnItemViewSelectedListener().onItemSelected(mSelectedViewHolder, mSelectedItem, 110 ViewHolder.this, getRow()); 111 } 112 } 113 }; 114 115 Presenter getPresenter(boolean primary) { 116 ObjectAdapter adapter = primary ? 117 ((PlaybackControlsRow) getRow()).getPrimaryActionsAdapter() : 118 ((PlaybackControlsRow) getRow()).getSecondaryActionsAdapter(); 119 if (adapter == null) { 120 return null; 121 } 122 if (adapter.getPresenterSelector() instanceof ControlButtonPresenterSelector) { 123 ControlButtonPresenterSelector selector = 124 (ControlButtonPresenterSelector) adapter.getPresenterSelector(); 125 return primary ? selector.getPrimaryPresenter() : 126 selector.getSecondaryPresenter(); 127 } 128 return adapter.getPresenter(adapter.size() > 0 ? adapter.get(0) : null); 129 } 130 131 void setBackground(View view) { 132 if (mBgView != null) { 133 RoundedRectHelper.getInstance().clearBackground(mBgView); 134 ShadowHelper.getInstance().setZ(mBgView, 0); 135 } 136 mBgView = view; 137 RoundedRectHelper.getInstance().setRoundedRectBackground(view, mBackgroundColorSet ? 138 mBackgroundColor : getDefaultBackgroundColor(view.getContext())); 139 140 if (sShadowZ == 0) { 141 sShadowZ = view.getResources().getDimensionPixelSize( 142 R.dimen.lb_playback_controls_z); 143 } 144 ShadowHelper.getInstance().setZ(view, sShadowZ); 145 } 146 } 147 148 private int mBackgroundColor = Color.TRANSPARENT; 149 private boolean mBackgroundColorSet; 150 private int mProgressColor = Color.TRANSPARENT; 151 private boolean mProgressColorSet; 152 private boolean mSecondaryActionsHidden; 153 private Presenter mDescriptionPresenter; 154 private PlaybackControlsPresenter mPlaybackControlsPresenter; 155 private ControlBarPresenter mSecondaryControlsPresenter; 156 private OnActionClickedListener mOnActionClickedListener; 157 private static float sShadowZ; 158 159 private final OnControlSelectedListener mOnControlSelectedListener = 160 new OnControlSelectedListener() { 161 @Override 162 public void onControlSelected(Presenter.ViewHolder itemViewHolder, Object item, 163 ControlBarPresenter.BoundData data) { 164 ViewHolder vh = ((BoundData) data).mRowViewHolder; 165 if (vh.mSelectedViewHolder != itemViewHolder || vh.mSelectedItem != item) { 166 vh.mSelectedViewHolder = itemViewHolder; 167 vh.mSelectedItem = item; 168 vh.dispatchItemSelection(); 169 } 170 } 171 }; 172 173 private final OnControlClickedListener mOnControlClickedListener = 174 new OnControlClickedListener() { 175 @Override 176 public void onControlClicked(Presenter.ViewHolder itemViewHolder, Object item, 177 ControlBarPresenter.BoundData data) { 178 ViewHolder vh = ((BoundData) data).mRowViewHolder; 179 if (getOnItemClickedListener() != null) { 180 getOnItemClickedListener().onItemClicked(item, vh.getRow()); 181 } 182 if (getOnItemViewClickedListener() != null) { 183 getOnItemViewClickedListener().onItemClicked(itemViewHolder, item, 184 vh, vh.getRow()); 185 } 186 if (mOnActionClickedListener != null && item instanceof Action) { 187 mOnActionClickedListener.onActionClicked((Action) item); 188 } 189 } 190 }; 191 192 /** 193 * Constructor for a PlaybackControlsRowPresenter. 194 * 195 * @param descriptionPresenter Presenter for displaying item details. 196 */ 197 public PlaybackControlsRowPresenter(Presenter descriptionPresenter) { 198 setHeaderPresenter(null); 199 setSelectEffectEnabled(false); 200 201 mDescriptionPresenter = descriptionPresenter; 202 mPlaybackControlsPresenter = new PlaybackControlsPresenter(R.layout.lb_playback_controls); 203 mSecondaryControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar); 204 205 mPlaybackControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener); 206 mSecondaryControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener); 207 mPlaybackControlsPresenter.setOnControlClickedListener(mOnControlClickedListener); 208 mSecondaryControlsPresenter.setOnControlClickedListener(mOnControlClickedListener); 209 } 210 211 /** 212 * Constructor for a PlaybackControlsRowPresenter. 213 */ 214 public PlaybackControlsRowPresenter() { 215 this(null); 216 } 217 218 /** 219 * Sets the listener for {@link Action} click events. 220 */ 221 public void setOnActionClickedListener(OnActionClickedListener listener) { 222 mOnActionClickedListener = listener; 223 } 224 225 /** 226 * Gets the listener for {@link Action} click events. 227 */ 228 public OnActionClickedListener getOnActionClickedListener() { 229 return mOnActionClickedListener; 230 } 231 232 /** 233 * Sets the background color. If not set, a default from the theme will be used. 234 */ 235 public void setBackgroundColor(int color) { 236 mBackgroundColor = color; 237 mBackgroundColorSet = true; 238 } 239 240 /** 241 * Returns the background color. If no background color was set, transparent 242 * is returned. 243 */ 244 public int getBackgroundColor() { 245 return mBackgroundColor; 246 } 247 248 /** 249 * Sets the primary color for the progress bar. If not set, a default from 250 * the theme will be used. 251 */ 252 public void setProgressColor(int color) { 253 mProgressColor = color; 254 mProgressColorSet = true; 255 } 256 257 /** 258 * Returns the primary color for the progress bar. If no color was set, transparent 259 * is returned. 260 */ 261 public int getProgressColor() { 262 return mProgressColor; 263 } 264 265 /** 266 * Sets the secondary actions to be hidden behind a "more actions" button. 267 * When "more actions" is selected, the primary actions are replaced with 268 * the secondary actions. 269 */ 270 public void setSecondaryActionsHidden(boolean hidden) { 271 mSecondaryActionsHidden = hidden; 272 } 273 274 /** 275 * Returns true if secondary actions are hidden. 276 */ 277 public boolean areSecondaryActionsHidden() { 278 return mSecondaryActionsHidden; 279 } 280 281 /** 282 * Shows or hides space at the bottom of the playback controls row. 283 * This allows the row to hug the bottom of the display when no 284 * other rows are present. 285 */ 286 public void showBottomSpace(ViewHolder vh, boolean show) { 287 vh.mBottomSpacer.setVisibility(show ? View.VISIBLE : View.GONE); 288 } 289 290 /** 291 * Display the primary actions. 292 */ 293 public void showPrimaryActions(ViewHolder vh) { 294 mPlaybackControlsPresenter.showPrimaryActions(vh.mControlsVh); 295 } 296 297 private int getDefaultBackgroundColor(Context context) { 298 TypedValue outValue = new TypedValue(); 299 context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true); 300 return context.getResources().getColor(outValue.resourceId); 301 } 302 303 private int getDefaultProgressColor(Context context) { 304 TypedValue outValue = new TypedValue(); 305 context.getTheme().resolveAttribute(R.attr.playbackProgressPrimaryColor, outValue, true); 306 return context.getResources().getColor(outValue.resourceId); 307 } 308 309 @Override 310 protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) { 311 View v = LayoutInflater.from(parent.getContext()) 312 .inflate(R.layout.lb_playback_controls_row, parent, false); 313 ViewHolder vh = new ViewHolder(v, mDescriptionPresenter); 314 initRow(vh); 315 return vh; 316 } 317 318 private void initRow(ViewHolder vh) { 319 vh.mCardHeight = vh.mCard.getLayoutParams().height; 320 321 MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams(); 322 vh.mControlsDockMarginStart = lp.getMarginStart(); 323 vh.mControlsDockMarginEnd = lp.getMarginEnd(); 324 325 vh.mControlsVh = (PlaybackControlsPresenter.ViewHolder) 326 mPlaybackControlsPresenter.onCreateViewHolder(vh.mControlsDock); 327 mPlaybackControlsPresenter.setProgressColor(vh.mControlsVh, 328 mProgressColorSet ? mProgressColor : 329 getDefaultProgressColor(vh.mControlsDock.getContext())); 330 vh.mControlsDock.addView(vh.mControlsVh.view); 331 332 vh.mSecondaryControlsVh = 333 mSecondaryControlsPresenter.onCreateViewHolder(vh.mSecondaryControlsDock); 334 if (!mSecondaryActionsHidden) { 335 vh.mSecondaryControlsDock.addView(vh.mSecondaryControlsVh.view); 336 } 337 } 338 339 @Override 340 protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) { 341 super.onBindRowViewHolder(holder, item); 342 343 ViewHolder vh = (ViewHolder) holder; 344 PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow(); 345 346 mPlaybackControlsPresenter.enableSecondaryActions(mSecondaryActionsHidden); 347 348 if (row.getItem() == null) { 349 LayoutParams lp = vh.mCard.getLayoutParams(); 350 lp.height = LayoutParams.WRAP_CONTENT; 351 vh.mCard.setLayoutParams(lp); 352 vh.mDescriptionDock.setVisibility(View.GONE); 353 vh.mSpacer.setVisibility(View.GONE); 354 } else { 355 LayoutParams lp = vh.mCard.getLayoutParams(); 356 lp.height = vh.mCardHeight; 357 vh.mCard.setLayoutParams(lp); 358 vh.mDescriptionDock.setVisibility(View.VISIBLE); 359 if (vh.mDescriptionViewHolder != null) { 360 mDescriptionPresenter.onBindViewHolder(vh.mDescriptionViewHolder, row.getItem()); 361 } 362 vh.mSpacer.setVisibility(View.VISIBLE); 363 } 364 365 MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams(); 366 if (row.getImageDrawable() == null || row.getItem() == null) { 367 vh.mImageView.setImageDrawable(null); 368 vh.setBackground(vh.mControlsDock); 369 lp.setMarginStart(0); 370 lp.setMarginEnd(0); 371 mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, true); 372 } else { 373 vh.mImageView.setImageDrawable(row.getImageDrawable()); 374 vh.setBackground(vh.mCard); 375 lp.setMarginStart(vh.mControlsDockMarginStart); 376 lp.setMarginEnd(vh.mControlsDockMarginEnd); 377 mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, false); 378 } 379 vh.mControlsDock.setLayoutParams(lp); 380 381 vh.mControlsBoundData.adapter = row.getPrimaryActionsAdapter(); 382 vh.mControlsBoundData.secondaryActionsAdapter = row.getSecondaryActionsAdapter(); 383 vh.mControlsBoundData.presenter = vh.getPresenter(true); 384 vh.mControlsBoundData.mRowViewHolder = vh; 385 mPlaybackControlsPresenter.onBindViewHolder(vh.mControlsVh, vh.mControlsBoundData); 386 387 vh.mSecondaryBoundData.adapter = row.getSecondaryActionsAdapter(); 388 vh.mSecondaryBoundData.presenter = vh.getPresenter(false); 389 vh.mSecondaryBoundData.mRowViewHolder = vh; 390 mSecondaryControlsPresenter.onBindViewHolder(vh.mSecondaryControlsVh, 391 vh.mSecondaryBoundData); 392 393 mPlaybackControlsPresenter.setTotalTime(vh.mControlsVh, row.getTotalTime()); 394 mPlaybackControlsPresenter.setCurrentTime(vh.mControlsVh, row.getCurrentTime()); 395 mPlaybackControlsPresenter.setSecondaryProgress(vh.mControlsVh, row.getBufferedProgress()); 396 row.setOnPlaybackStateChangedListener(vh.mListener); 397 } 398 399 @Override 400 protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) { 401 ViewHolder vh = (ViewHolder) holder; 402 PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow(); 403 404 if (vh.mDescriptionViewHolder != null) { 405 mDescriptionPresenter.onUnbindViewHolder(vh.mDescriptionViewHolder); 406 } 407 mPlaybackControlsPresenter.onUnbindViewHolder(vh.mControlsVh); 408 mSecondaryControlsPresenter.onUnbindViewHolder(vh.mSecondaryControlsVh); 409 row.setOnPlaybackStateChangedListener(null); 410 411 super.onUnbindRowViewHolder(holder); 412 } 413 414 protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) { 415 super.onRowViewSelected(vh, selected); 416 if (selected) { 417 ((ViewHolder) vh).dispatchItemSelection(); 418 } 419 } 420} 421