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