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