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