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