[go: nahoru, domu]

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