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.app; 15 16import android.graphics.Color; 17import android.graphics.drawable.ColorDrawable; 18import android.animation.Animator; 19import android.animation.AnimatorInflater; 20import android.animation.TimeInterpolator; 21import android.animation.ValueAnimator; 22import android.support.v17.leanback.widget.Action; 23import android.support.v17.leanback.widget.PlaybackControlsRow; 24import android.view.InputEvent; 25import android.view.animation.AccelerateInterpolator; 26import android.animation.ValueAnimator.AnimatorUpdateListener; 27import android.content.Context; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.Message; 31import android.support.v7.widget.RecyclerView; 32import android.support.v17.leanback.R; 33import android.support.v17.leanback.animation.LogAccelerateInterpolator; 34import android.support.v17.leanback.animation.LogDecelerateInterpolator; 35import android.support.v17.leanback.widget.ItemBridgeAdapter; 36import android.support.v17.leanback.widget.ObjectAdapter; 37import android.support.v17.leanback.widget.ObjectAdapter.DataObserver; 38import android.support.v17.leanback.widget.PlaybackControlsRowPresenter; 39import android.support.v17.leanback.widget.Presenter; 40import android.support.v17.leanback.widget.PresenterSelector; 41import android.support.v17.leanback.widget.RowPresenter; 42import android.support.v17.leanback.widget.VerticalGridView; 43import android.util.Log; 44import android.view.KeyEvent; 45import android.view.LayoutInflater; 46import android.view.MotionEvent; 47import android.view.View; 48import android.view.ViewGroup; 49 50import java.util.ArrayList; 51 52/** 53 * A fragment for displaying playback controls and related content. 54 * <p> 55 * A PlaybackOverlayFragment renders the elements of its {@link ObjectAdapter} as a set 56 * of rows in a vertical list. The Adapter's {@link PresenterSelector} must maintain subclasses 57 * of {@link RowPresenter}. 58 * </p> 59 * <p> 60 * An instance of {@link android.support.v17.leanback.widget.PlaybackControlsRow} is expected to be 61 * at position 0 in the adapter. 62 * </p> 63 */ 64public class PlaybackOverlayFragment extends DetailsFragment { 65 66 /** 67 * No background. 68 */ 69 public static final int BG_NONE = 0; 70 71 /** 72 * A dark translucent background. 73 */ 74 public static final int BG_DARK = 1; 75 76 /** 77 * A light translucent background. 78 */ 79 public static final int BG_LIGHT = 2; 80 81 /** 82 * Listener allowing the application to receive notification of fade in and/or fade out 83 * completion events. 84 */ 85 public static class OnFadeCompleteListener { 86 public void onFadeInComplete() { 87 } 88 public void onFadeOutComplete() { 89 } 90 } 91 92 /** 93 * Interface allowing the application to handle input events. 94 */ 95 public interface InputEventHandler { 96 /** 97 * Called when an {@link InputEvent} is received. 98 * 99 * @return If the event should be consumed, return true. To allow the event to 100 * continue on to the next handler, return false. 101 */ 102 public boolean handleInputEvent(InputEvent event); 103 } 104 105 private static final String TAG = "PlaybackOverlayFragment"; 106 private static final boolean DEBUG = false; 107 private static final int ANIMATION_MULTIPLIER = 1; 108 109 private static int START_FADE_OUT = 1; 110 111 // Fading status 112 private static final int IDLE = 0; 113 private static final int IN = 1; 114 private static final int OUT = 2; 115 116 private int mPaddingTop; 117 private int mPaddingBottom; 118 private View mRootView; 119 private int mBackgroundType = BG_DARK; 120 private int mBgDarkColor; 121 private int mBgLightColor; 122 private int mShowTimeMs; 123 private int mMajorFadeTranslateY, mMinorFadeTranslateY; 124 private int mAnimationTranslateY; 125 private OnFadeCompleteListener mFadeCompleteListener; 126 private InputEventHandler mInputEventHandler; 127 private boolean mFadingEnabled = true; 128 private int mFadingStatus = IDLE; 129 private int mBgAlpha; 130 private ValueAnimator mBgFadeInAnimator, mBgFadeOutAnimator; 131 private ValueAnimator mControlRowFadeInAnimator, mControlRowFadeOutAnimator; 132 private ValueAnimator mDescriptionFadeInAnimator, mDescriptionFadeOutAnimator; 133 private ValueAnimator mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator; 134 private boolean mTranslateAnimationEnabled; 135 private boolean mResetControlsToPrimaryActionsPending; 136 private RecyclerView.ItemAnimator mItemAnimator; 137 138 private final Animator.AnimatorListener mFadeListener = 139 new Animator.AnimatorListener() { 140 @Override 141 public void onAnimationStart(Animator animation) { 142 enableVerticalGridAnimations(false); 143 } 144 @Override 145 public void onAnimationRepeat(Animator animation) { 146 } 147 @Override 148 public void onAnimationCancel(Animator animation) { 149 } 150 @Override 151 public void onAnimationEnd(Animator animation) { 152 if (DEBUG) Log.v(TAG, "onAnimationEnd " + mBgAlpha); 153 if (mBgAlpha > 0) { 154 enableVerticalGridAnimations(true); 155 startFadeTimer(); 156 if (mFadeCompleteListener != null) { 157 mFadeCompleteListener.onFadeInComplete(); 158 } 159 } else { 160 VerticalGridView verticalView = getVerticalGridView(); 161 // reset focus to the primary actions only if the selected row was the controls row 162 if (verticalView != null && verticalView.getSelectedPosition() == 0) { 163 resetControlsToPrimaryActions(null); 164 } 165 if (mFadeCompleteListener != null) { 166 mFadeCompleteListener.onFadeOutComplete(); 167 } 168 } 169 mFadingStatus = IDLE; 170 } 171 }; 172 173 private final Handler mHandler = new Handler() { 174 @Override 175 public void handleMessage(Message message) { 176 if (message.what == START_FADE_OUT && mFadingEnabled) { 177 fade(false); 178 } 179 } 180 }; 181 182 private final VerticalGridView.OnTouchInterceptListener mOnTouchInterceptListener = 183 new VerticalGridView.OnTouchInterceptListener() { 184 public boolean onInterceptTouchEvent(MotionEvent event) { 185 return onInterceptInputEvent(event); 186 } 187 }; 188 189 private final VerticalGridView.OnKeyInterceptListener mOnKeyInterceptListener = 190 new VerticalGridView.OnKeyInterceptListener() { 191 public boolean onInterceptKeyEvent(KeyEvent event) { 192 return onInterceptInputEvent(event); 193 } 194 }; 195 196 private void setBgAlpha(int alpha) { 197 mBgAlpha = alpha; 198 if (mRootView != null) { 199 mRootView.getBackground().setAlpha(alpha); 200 } 201 } 202 203 private void enableVerticalGridAnimations(boolean enable) { 204 if (getVerticalGridView() != null) { 205 getVerticalGridView().setAnimateChildLayout(enable); 206 } 207 } 208 209 private void resetControlsToPrimaryActions(ItemBridgeAdapter.ViewHolder vh) { 210 if (vh == null && getVerticalGridView() != null) { 211 vh = (ItemBridgeAdapter.ViewHolder) getVerticalGridView().findViewHolderForPosition(0); 212 } 213 if (vh == null) { 214 mResetControlsToPrimaryActionsPending = true; 215 } else if (vh.getPresenter() instanceof PlaybackControlsRowPresenter) { 216 mResetControlsToPrimaryActionsPending = false; 217 ((PlaybackControlsRowPresenter) vh.getPresenter()).showPrimaryActions( 218 (PlaybackControlsRowPresenter.ViewHolder) vh.getViewHolder()); 219 } 220 } 221 222 /** 223 * Enables or disables view fading. If enabled, 224 * the view will be faded in when the fragment starts, 225 * and will fade out after a time period. The timeout 226 * period is reset each time {@link #tickle} is called. 227 * 228 */ 229 public void setFadingEnabled(boolean enabled) { 230 if (DEBUG) Log.v(TAG, "setFadingEnabled " + enabled); 231 if (enabled != mFadingEnabled) { 232 mFadingEnabled = enabled; 233 if (mFadingEnabled) { 234 if (isResumed() && mFadingStatus == IDLE 235 && !mHandler.hasMessages(START_FADE_OUT)) { 236 startFadeTimer(); 237 } 238 } else { 239 // Ensure fully opaque 240 mHandler.removeMessages(START_FADE_OUT); 241 fade(true); 242 } 243 } 244 } 245 246 /** 247 * Returns true if view fading is enabled. 248 */ 249 public boolean isFadingEnabled() { 250 return mFadingEnabled; 251 } 252 253 /** 254 * Sets the listener to be called when fade in or out has completed. 255 */ 256 public void setFadeCompleteListener(OnFadeCompleteListener listener) { 257 mFadeCompleteListener = listener; 258 } 259 260 /** 261 * Returns the listener to be called when fade in or out has completed. 262 */ 263 public OnFadeCompleteListener getFadeCompleteListener() { 264 return mFadeCompleteListener; 265 } 266 267 /** 268 * Sets the input event handler. 269 */ 270 public final void setInputEventHandler(InputEventHandler handler) { 271 mInputEventHandler = handler; 272 } 273 274 /** 275 * Returns the input event handler. 276 */ 277 public final InputEventHandler getInputEventHandler() { 278 return mInputEventHandler; 279 } 280 281 /** 282 * Tickles the playback controls. Fades in the view if it was faded out, 283 * otherwise resets the fade out timer. Tickling on input events is handled 284 * by the fragment. 285 */ 286 public void tickle() { 287 if (DEBUG) Log.v(TAG, "tickle enabled " + mFadingEnabled + " isResumed " + isResumed()); 288 if (!mFadingEnabled || !isResumed()) { 289 return; 290 } 291 if (mHandler.hasMessages(START_FADE_OUT)) { 292 // Restart the timer 293 startFadeTimer(); 294 } else { 295 fade(true); 296 } 297 } 298 299 /** 300 * Fades out the playback overlay immediately. 301 */ 302 public void fadeOut() { 303 mHandler.removeMessages(START_FADE_OUT); 304 fade(false); 305 } 306 307 private boolean areControlsHidden() { 308 return mFadingStatus == IDLE && mBgAlpha == 0; 309 } 310 311 private boolean onInterceptInputEvent(InputEvent event) { 312 final boolean controlsHidden = areControlsHidden(); 313 if (DEBUG) Log.v(TAG, "onInterceptInputEvent hidden " + controlsHidden + " " + event); 314 boolean consumeEvent = false; 315 int keyCode = KeyEvent.KEYCODE_UNKNOWN; 316 317 if (mInputEventHandler != null) { 318 consumeEvent = mInputEventHandler.handleInputEvent(event); 319 } 320 if (event instanceof KeyEvent) { 321 keyCode = ((KeyEvent) event).getKeyCode(); 322 } 323 324 switch (keyCode) { 325 case KeyEvent.KEYCODE_DPAD_CENTER: 326 case KeyEvent.KEYCODE_DPAD_DOWN: 327 case KeyEvent.KEYCODE_DPAD_UP: 328 case KeyEvent.KEYCODE_DPAD_LEFT: 329 case KeyEvent.KEYCODE_DPAD_RIGHT: 330 // Event may be consumed; regardless, if controls are hidden then these keys will 331 // bring up the controls. 332 if (controlsHidden) { 333 consumeEvent = true; 334 } 335 tickle(); 336 break; 337 case KeyEvent.KEYCODE_BACK: 338 case KeyEvent.KEYCODE_ESCAPE: 339 // If fading enabled and controls are not hidden, back will be consumed to fade 340 // them out (even if the key was consumed by the handler). 341 if (mFadingEnabled && !controlsHidden) { 342 consumeEvent = true; 343 mHandler.removeMessages(START_FADE_OUT); 344 fade(false); 345 } else if (consumeEvent) { 346 tickle(); 347 } 348 break; 349 default: 350 if (consumeEvent) { 351 tickle(); 352 } 353 } 354 return consumeEvent; 355 } 356 357 @Override 358 public void onResume() { 359 super.onResume(); 360 if (mFadingEnabled) { 361 setBgAlpha(0); 362 fade(true); 363 } 364 getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener); 365 getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener); 366 } 367 368 private void startFadeTimer() { 369 if (mHandler != null) { 370 mHandler.removeMessages(START_FADE_OUT); 371 mHandler.sendEmptyMessageDelayed(START_FADE_OUT, mShowTimeMs); 372 } 373 } 374 375 private static ValueAnimator loadAnimator(Context context, int resId) { 376 ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(context, resId); 377 animator.setDuration(animator.getDuration() * ANIMATION_MULTIPLIER); 378 return animator; 379 } 380 381 private void loadBgAnimator() { 382 AnimatorUpdateListener listener = new AnimatorUpdateListener() { 383 @Override 384 public void onAnimationUpdate(ValueAnimator arg0) { 385 setBgAlpha((Integer) arg0.getAnimatedValue()); 386 } 387 }; 388 389 mBgFadeInAnimator = loadAnimator(getActivity(), R.animator.lb_playback_bg_fade_in); 390 mBgFadeInAnimator.addUpdateListener(listener); 391 mBgFadeInAnimator.addListener(mFadeListener); 392 393 mBgFadeOutAnimator = loadAnimator(getActivity(), R.animator.lb_playback_bg_fade_out); 394 mBgFadeOutAnimator.addUpdateListener(listener); 395 mBgFadeOutAnimator.addListener(mFadeListener); 396 } 397 398 private TimeInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100,0); 399 private TimeInterpolator mLogAccelerateInterpolator = new LogAccelerateInterpolator(100,0); 400 401 private View getControlRowView() { 402 if (getVerticalGridView() == null) { 403 return null; 404 } 405 RecyclerView.ViewHolder vh = getVerticalGridView().findViewHolderForPosition(0); 406 if (vh == null) { 407 return null; 408 } 409 return vh.itemView; 410 } 411 412 private void loadControlRowAnimator() { 413 final AnimatorListener listener = new AnimatorListener() { 414 @Override 415 void getViews(ArrayList<View> views) { 416 View view = getControlRowView(); 417 if (view != null) { 418 views.add(view); 419 } 420 } 421 }; 422 final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { 423 @Override 424 public void onAnimationUpdate(ValueAnimator arg0) { 425 View view = getControlRowView(); 426 if (view != null) { 427 final float fraction = (Float) arg0.getAnimatedValue(); 428 if (DEBUG) Log.v(TAG, "fraction " + fraction); 429 view.setAlpha(fraction); 430 view.setTranslationY((float) mAnimationTranslateY * (1f - fraction)); 431 } 432 } 433 }; 434 435 mControlRowFadeInAnimator = loadAnimator( 436 getActivity(), R.animator.lb_playback_controls_fade_in); 437 mControlRowFadeInAnimator.addUpdateListener(updateListener); 438 mControlRowFadeInAnimator.addListener(listener); 439 mControlRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator); 440 441 mControlRowFadeOutAnimator = loadAnimator( 442 getActivity(), R.animator.lb_playback_controls_fade_out); 443 mControlRowFadeOutAnimator.addUpdateListener(updateListener); 444 mControlRowFadeOutAnimator.addListener(listener); 445 mControlRowFadeOutAnimator.setInterpolator(mLogAccelerateInterpolator); 446 } 447 448 private void loadOtherRowAnimator() { 449 final AnimatorListener listener = new AnimatorListener() { 450 @Override 451 void getViews(ArrayList<View> views) { 452 if (getVerticalGridView() == null) { 453 return; 454 } 455 final int count = getVerticalGridView().getChildCount(); 456 for (int i = 0; i < count; i++) { 457 View view = getVerticalGridView().getChildAt(i); 458 if (view != null) { 459 views.add(view); 460 } 461 } 462 } 463 }; 464 final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { 465 @Override 466 public void onAnimationUpdate(ValueAnimator arg0) { 467 if (getVerticalGridView() == null) { 468 return; 469 } 470 final float fraction = (Float) arg0.getAnimatedValue(); 471 for (View view : listener.mViews) { 472 if (getVerticalGridView().getChildPosition(view) > 0) { 473 view.setAlpha(fraction); 474 view.setTranslationY((float) mAnimationTranslateY * (1f - fraction)); 475 } 476 } 477 } 478 }; 479 480 mOtherRowFadeInAnimator = loadAnimator( 481 getActivity(), R.animator.lb_playback_controls_fade_in); 482 mOtherRowFadeInAnimator.addListener(listener); 483 mOtherRowFadeInAnimator.addUpdateListener(updateListener); 484 mOtherRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator); 485 486 mOtherRowFadeOutAnimator = loadAnimator( 487 getActivity(), R.animator.lb_playback_controls_fade_out); 488 mOtherRowFadeOutAnimator.addListener(listener); 489 mOtherRowFadeOutAnimator.addUpdateListener(updateListener); 490 mOtherRowFadeOutAnimator.setInterpolator(new AccelerateInterpolator()); 491 } 492 493 private void loadDescriptionAnimator() { 494 AnimatorUpdateListener listener = new AnimatorUpdateListener() { 495 @Override 496 public void onAnimationUpdate(ValueAnimator arg0) { 497 if (getVerticalGridView() == null) { 498 return; 499 } 500 ItemBridgeAdapter.ViewHolder adapterVh = (ItemBridgeAdapter.ViewHolder) 501 getVerticalGridView().findViewHolderForPosition(0); 502 if (adapterVh != null && adapterVh.getViewHolder() 503 instanceof PlaybackControlsRowPresenter.ViewHolder) { 504 final Presenter.ViewHolder vh = ((PlaybackControlsRowPresenter.ViewHolder) 505 adapterVh.getViewHolder()).mDescriptionViewHolder; 506 if (vh != null) { 507 vh.view.setAlpha((Float) arg0.getAnimatedValue()); 508 } 509 } 510 } 511 }; 512 513 mDescriptionFadeInAnimator = loadAnimator( 514 getActivity(), R.animator.lb_playback_description_fade_in); 515 mDescriptionFadeInAnimator.addUpdateListener(listener); 516 mDescriptionFadeInAnimator.setInterpolator(mLogDecelerateInterpolator); 517 518 mDescriptionFadeOutAnimator = loadAnimator( 519 getActivity(), R.animator.lb_playback_description_fade_out); 520 mDescriptionFadeOutAnimator.addUpdateListener(listener); 521 } 522 523 private void fade(boolean fadeIn) { 524 if (DEBUG) Log.v(TAG, "fade " + fadeIn); 525 if (getView() == null) { 526 return; 527 } 528 if ((fadeIn && mFadingStatus == IN) || (!fadeIn && mFadingStatus == OUT)) { 529 if (DEBUG) Log.v(TAG, "requested fade in progress"); 530 return; 531 } 532 if ((fadeIn && mBgAlpha == 255) || (!fadeIn && mBgAlpha == 0)) { 533 if (DEBUG) Log.v(TAG, "fade is no-op"); 534 return; 535 } 536 537 mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0 ? 538 mMajorFadeTranslateY : mMinorFadeTranslateY; 539 540 if (mFadingStatus == IDLE) { 541 if (fadeIn) { 542 mBgFadeInAnimator.start(); 543 mControlRowFadeInAnimator.start(); 544 mOtherRowFadeInAnimator.start(); 545 mDescriptionFadeInAnimator.start(); 546 } else { 547 mBgFadeOutAnimator.start(); 548 mControlRowFadeOutAnimator.start(); 549 mOtherRowFadeOutAnimator.start(); 550 mDescriptionFadeOutAnimator.start(); 551 } 552 } else { 553 if (fadeIn) { 554 mBgFadeOutAnimator.reverse(); 555 mControlRowFadeOutAnimator.reverse(); 556 mOtherRowFadeOutAnimator.reverse(); 557 mDescriptionFadeOutAnimator.reverse(); 558 } else { 559 mBgFadeInAnimator.reverse(); 560 mControlRowFadeInAnimator.reverse(); 561 mOtherRowFadeInAnimator.reverse(); 562 mDescriptionFadeInAnimator.reverse(); 563 } 564 } 565 566 // If fading in while control row is focused, set initial translationY so 567 // views slide in from below. 568 if (fadeIn && mFadingStatus == IDLE) { 569 final int count = getVerticalGridView().getChildCount(); 570 for (int i = 0; i < count; i++) { 571 getVerticalGridView().getChildAt(i).setTranslationY(mAnimationTranslateY); 572 } 573 } 574 575 mFadingStatus = fadeIn ? IN : OUT; 576 } 577 578 /** 579 * Sets the list of rows for the fragment. 580 */ 581 @Override 582 public void setAdapter(ObjectAdapter adapter) { 583 if (getAdapter() != null) { 584 getAdapter().unregisterObserver(mObserver); 585 } 586 super.setAdapter(adapter); 587 if (adapter != null) { 588 adapter.registerObserver(mObserver); 589 } 590 } 591 592 @Override 593 void setVerticalGridViewLayout(VerticalGridView listview) { 594 if (listview == null) { 595 return; 596 } 597 // Padding affects alignment when last row is focused 598 // (last is first when there's only one row). 599 setPadding(listview, mPaddingTop, mPaddingBottom); 600 601 // Item alignment affects focused row that isn't the last. 602 listview.setItemAlignmentOffset(0); 603 listview.setItemAlignmentOffsetPercent(50); 604 605 // Push rows to the bottom. 606 listview.setWindowAlignmentOffset(0); 607 listview.setWindowAlignmentOffsetPercent(50); 608 listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE); 609 } 610 611 private static void setPadding(View view, int paddingTop, int paddingBottom) { 612 view.setPadding(view.getPaddingLeft(), paddingTop, 613 view.getPaddingRight(), paddingBottom); 614 } 615 616 @Override 617 public void onCreate(Bundle savedInstanceState) { 618 super.onCreate(savedInstanceState); 619 620 mPaddingTop = 621 getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_top); 622 mPaddingBottom = 623 getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_bottom); 624 mBgDarkColor = 625 getResources().getColor(R.color.lb_playback_controls_background_dark); 626 mBgLightColor = 627 getResources().getColor(R.color.lb_playback_controls_background_light); 628 mShowTimeMs = 629 getResources().getInteger(R.integer.lb_playback_controls_show_time_ms); 630 mMajorFadeTranslateY = 631 getResources().getDimensionPixelSize(R.dimen.lb_playback_major_fade_translate_y); 632 mMinorFadeTranslateY = 633 getResources().getDimensionPixelSize(R.dimen.lb_playback_minor_fade_translate_y); 634 635 loadBgAnimator(); 636 loadControlRowAnimator(); 637 loadOtherRowAnimator(); 638 loadDescriptionAnimator(); 639 } 640 641 /** 642 * Sets the background type. 643 * 644 * @param type One of BG_LIGHT, BG_DARK, or BG_NONE. 645 */ 646 public void setBackgroundType(int type) { 647 switch (type) { 648 case BG_LIGHT: 649 case BG_DARK: 650 case BG_NONE: 651 if (type != mBackgroundType) { 652 mBackgroundType = type; 653 updateBackground(); 654 } 655 break; 656 default: 657 throw new IllegalArgumentException("Invalid background type"); 658 } 659 } 660 661 /** 662 * Returns the background type. 663 */ 664 public int getBackgroundType() { 665 return mBackgroundType; 666 } 667 668 private void updateBackground() { 669 if (mRootView != null) { 670 int color = mBgDarkColor; 671 switch (mBackgroundType) { 672 case BG_DARK: break; 673 case BG_LIGHT: color = mBgLightColor; break; 674 case BG_NONE: color = Color.TRANSPARENT; break; 675 } 676 mRootView.setBackground(new ColorDrawable(color)); 677 } 678 } 679 680 private void updateControlsBottomSpace(ItemBridgeAdapter.ViewHolder vh) { 681 // Add extra space between rows 0 and 1 682 if (vh == null && getVerticalGridView() != null) { 683 vh = (ItemBridgeAdapter.ViewHolder) 684 getVerticalGridView().findViewHolderForPosition(0); 685 } 686 if (vh != null && vh.getPresenter() instanceof PlaybackControlsRowPresenter) { 687 final int adapterSize = getAdapter() == null ? 0 : getAdapter().size(); 688 ((PlaybackControlsRowPresenter) vh.getPresenter()).showBottomSpace( 689 (PlaybackControlsRowPresenter.ViewHolder) vh.getViewHolder(), 690 adapterSize > 1); 691 } 692 } 693 694 private final ItemBridgeAdapter.AdapterListener mAdapterListener = 695 new ItemBridgeAdapter.AdapterListener() { 696 @Override 697 public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) { 698 if (DEBUG) Log.v(TAG, "onAttachedToWindow " + vh.getViewHolder().view); 699 if ((mFadingStatus == IDLE && mBgAlpha == 0) || mFadingStatus == OUT) { 700 if (DEBUG) Log.v(TAG, "setting alpha to 0"); 701 vh.getViewHolder().view.setAlpha(0); 702 } 703 if (vh.getPosition() == 0 && mResetControlsToPrimaryActionsPending) { 704 resetControlsToPrimaryActions(vh); 705 } 706 } 707 @Override 708 public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) { 709 if (DEBUG) Log.v(TAG, "onDetachedFromWindow " + vh.getViewHolder().view); 710 // Reset animation state 711 vh.getViewHolder().view.setAlpha(1f); 712 vh.getViewHolder().view.setTranslationY(0); 713 if (vh.getViewHolder() instanceof PlaybackControlsRowPresenter.ViewHolder) { 714 Presenter.ViewHolder descriptionVh = ((PlaybackControlsRowPresenter.ViewHolder) 715 vh.getViewHolder()).mDescriptionViewHolder; 716 if (descriptionVh != null) { 717 descriptionVh.view.setAlpha(1f); 718 } 719 } 720 } 721 @Override 722 public void onBind(ItemBridgeAdapter.ViewHolder vh) { 723 if (vh.getPosition() == 0) { 724 updateControlsBottomSpace(vh); 725 } 726 } 727 }; 728 729 @Override 730 public View onCreateView(LayoutInflater inflater, ViewGroup container, 731 Bundle savedInstanceState) { 732 mRootView = super.onCreateView(inflater, container, savedInstanceState); 733 mBgAlpha = 255; 734 updateBackground(); 735 getRowsFragment().setExternalAdapterListener(mAdapterListener); 736 return mRootView; 737 } 738 739 @Override 740 public void onDestroyView() { 741 mRootView = null; 742 super.onDestroyView(); 743 } 744 745 private final DataObserver mObserver = new DataObserver() { 746 public void onChanged() { 747 updateControlsBottomSpace(null); 748 } 749 }; 750 751 static abstract class AnimatorListener implements Animator.AnimatorListener { 752 ArrayList<View> mViews = new ArrayList<View>(); 753 ArrayList<Integer> mLayerType = new ArrayList<Integer>(); 754 755 public void onAnimationCancel(Animator animation) { 756 } 757 public void onAnimationRepeat(Animator animation) { 758 } 759 public void onAnimationStart(Animator animation) { 760 getViews(mViews); 761 for (View view : mViews) { 762 mLayerType.add(view.getLayerType()); 763 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 764 } 765 } 766 public void onAnimationEnd(Animator animation) { 767 for (int i = 0; i < mViews.size(); i++) { 768 mViews.get(i).setLayerType(mLayerType.get(i), null); 769 } 770 mLayerType.clear(); 771 mViews.clear(); 772 } 773 abstract void getViews(ArrayList<View> views); 774 775 }; 776} 777