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