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