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