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