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