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