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