FocusHighlightHelper.java revision 76c3b90228d8c4afc6d24c683e9c95f41ae619c9
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.widget;
15
16import android.support.v17.leanback.R;
17import android.view.View;
18import android.view.animation.AccelerateDecelerateInterpolator;
19import android.view.animation.Interpolator;
20import android.animation.TimeAnimator;
21import android.content.res.Resources;
22
23/**
24 * Setup the behavior how to highlight when a item gains focus.
25 */
26public class FocusHighlightHelper {
27
28    static class FocusAnimator implements TimeAnimator.TimeListener {
29        private final View mView;
30        private final int mDuration;
31        private final ShadowOverlayContainer mWrapper;
32        private final float mScaleDiff;
33        private float mFocusLevel = 0f;
34        private float mFocusLevelStart;
35        private float mFocusLevelDelta;
36        private final TimeAnimator mAnimator = new TimeAnimator();
37        private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
38
39        void animateFocus(boolean select, boolean immediate) {
40            endAnimation();
41            final float end = select ? 1 : 0;
42            if (immediate) {
43                setFocusLevel(end);
44            } else if (mFocusLevel != end) {
45                mFocusLevelStart = mFocusLevel;
46                mFocusLevelDelta = end - mFocusLevelStart;
47                mAnimator.start();
48            }
49        }
50
51        FocusAnimator(View view, float scale, int duration) {
52            mView = view;
53            mDuration = duration;
54            mScaleDiff = scale - 1f;
55            if (view instanceof ShadowOverlayContainer) {
56                mWrapper = (ShadowOverlayContainer) view;
57            } else {
58                mWrapper = null;
59            }
60            mAnimator.setTimeListener(this);
61        }
62
63        void setFocusLevel(float level) {
64            mFocusLevel = level;
65            float scale = 1f + mScaleDiff * level;
66            mView.setScaleX(scale);
67            mView.setScaleY(scale);
68            if (mWrapper != null) {
69                mWrapper.setShadowFocusLevel(level);
70            }
71        }
72
73        float getFocusLevel() {
74            return mFocusLevel;
75        }
76
77        void endAnimation() {
78            mAnimator.end();
79        }
80
81        @Override
82        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
83            float fraction;
84            if (totalTime >= mDuration) {
85                fraction = 1;
86                mAnimator.end();
87            } else {
88                fraction = (float) (totalTime / (double) mDuration);
89            }
90            if (mInterpolator != null) {
91                fraction = mInterpolator.getInterpolation(fraction);
92            }
93            setFocusLevel(mFocusLevelStart + fraction * mFocusLevelDelta);
94        }
95    }
96
97    static class BrowseItemFocusHighlight implements FocusHighlight {
98        private static final int DURATION_MS = 150;
99
100        private static float[] sScaleFactor = new float[4];
101
102        private int mScaleIndex;
103
104        BrowseItemFocusHighlight(int zoomIndex) {
105            mScaleIndex = (zoomIndex >= 0 && zoomIndex < sScaleFactor.length) ?
106                    zoomIndex : ZOOM_FACTOR_MEDIUM;
107        }
108
109        private static void lazyInit(Resources resources) {
110            if (sScaleFactor[ZOOM_FACTOR_NONE] == 0f) {
111                sScaleFactor[ZOOM_FACTOR_NONE] = 1f;
112                sScaleFactor[ZOOM_FACTOR_SMALL] =
113                        resources.getFraction(R.fraction.lb_focus_zoom_factor_small, 1, 1);
114                sScaleFactor[ZOOM_FACTOR_MEDIUM] =
115                        resources.getFraction(R.fraction.lb_focus_zoom_factor_medium, 1, 1);
116                sScaleFactor[ZOOM_FACTOR_LARGE] =
117                        resources.getFraction(R.fraction.lb_focus_zoom_factor_large, 1, 1);
118            }
119        }
120
121        private float getScale(View view) {
122            lazyInit(view.getResources());
123            return sScaleFactor[mScaleIndex];
124        }
125
126        private void viewFocused(View view, boolean hasFocus) {
127            view.setSelected(hasFocus);
128            FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
129            if (animator == null) {
130                animator = new FocusAnimator(view, getScale(view), DURATION_MS);
131                view.setTag(R.id.lb_focus_animator, animator);
132            }
133            animator.animateFocus(hasFocus, false);
134        }
135
136        @Override
137        public void onItemFocused(View view, boolean hasFocus) {
138            viewFocused(view, hasFocus);
139        }
140    }
141
142    /**
143     * Setup the focus highlight behavior of a focused item in browse list row.
144     * @param adapter  adapter of the list row.
145     */
146    public static void setupBrowseItemFocusHighlight(ItemBridgeAdapter adapter, int zoomIndex) {
147        adapter.setFocusHighlight(new BrowseItemFocusHighlight(zoomIndex));
148    }
149
150    /**
151     * Setup the focus highlight behavior of a focused item in header list.
152     * @param gridView  the header list.
153     */
154    public static void setupHeaderItemFocusHighlight(VerticalGridView gridView) {
155        if (gridView.getAdapter() instanceof ItemBridgeAdapter) {
156            ((ItemBridgeAdapter) gridView.getAdapter())
157                    .setFocusHighlight(new HeaderItemFocusHighlight(gridView));
158        }
159    }
160
161    static class HeaderItemFocusHighlight implements FocusHighlight {
162        private static boolean sInitialized;
163        private static float sSelectScale;
164        private static int sDuration;
165        private BaseGridView mGridView;
166
167        HeaderItemFocusHighlight(BaseGridView gridView) {
168            mGridView = gridView;
169            lazyInit(gridView.getContext().getResources());
170        }
171
172        private static void lazyInit(Resources res) {
173            if (!sInitialized) {
174                sSelectScale =
175                        Float.parseFloat(res.getString(R.dimen.lb_browse_header_select_scale));
176                sDuration =
177                        Integer.parseInt(res.getString(R.dimen.lb_browse_header_select_duration));
178                sInitialized = true;
179            }
180        }
181
182        class HeaderFocusAnimator extends FocusAnimator {
183
184            ItemBridgeAdapter.ViewHolder mViewHolder;
185            HeaderFocusAnimator(View view, float scale, int duration) {
186                super(view, scale, duration);
187                mViewHolder = (ItemBridgeAdapter.ViewHolder) mGridView.getChildViewHolder(view);
188            }
189
190            @Override
191            void setFocusLevel(float level) {
192                Presenter presenter = mViewHolder.getPresenter();
193                if (presenter instanceof RowHeaderPresenter) {
194                    ((RowHeaderPresenter) presenter).setSelectLevel(
195                            ((RowHeaderPresenter.ViewHolder) mViewHolder.getViewHolder()), level);
196                }
197                super.setFocusLevel(level);
198            }
199
200        }
201
202        private void viewFocused(View view, boolean hasFocus) {
203            view.setSelected(hasFocus);
204            FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
205            if (animator == null) {
206                animator = new HeaderFocusAnimator(view, sSelectScale, sDuration);
207                view.setTag(R.id.lb_focus_animator, animator);
208            }
209            animator.animateFocus(hasFocus, false);
210        }
211
212        @Override
213        public void onItemFocused(View view, boolean hasFocus) {
214            viewFocused(view, hasFocus);
215        }
216    }
217}
218