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