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