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