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