• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
14 package android.support.v17.leanback.widget;
15 
16 import android.support.v17.leanback.R;
17 import android.support.v17.leanback.graphics.ColorOverlayDimmer;
18 import android.view.View;
19 import android.view.animation.AccelerateDecelerateInterpolator;
20 import android.view.animation.Interpolator;
21 import android.animation.TimeAnimator;
22 import android.content.res.Resources;
23 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_NONE;
24 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_SMALL;
25 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_XSMALL;
26 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_MEDIUM;
27 import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_LARGE;
28 
29 /**
30  * Sets up the highlighting behavior when an item gains focus.
31  */
32 public class FocusHighlightHelper {
33 
isValidZoomIndex(int zoomIndex)34     static boolean isValidZoomIndex(int zoomIndex) {
35         return zoomIndex == ZOOM_FACTOR_NONE || getResId(zoomIndex) > 0;
36     }
37 
getResId(int zoomIndex)38     private static int getResId(int zoomIndex) {
39         switch (zoomIndex) {
40             case ZOOM_FACTOR_SMALL:
41                 return R.fraction.lb_focus_zoom_factor_small;
42             case ZOOM_FACTOR_XSMALL:
43                 return R.fraction.lb_focus_zoom_factor_xsmall;
44             case ZOOM_FACTOR_MEDIUM:
45                 return R.fraction.lb_focus_zoom_factor_medium;
46             case ZOOM_FACTOR_LARGE:
47                 return R.fraction.lb_focus_zoom_factor_large;
48             default:
49                 return 0;
50         }
51     }
52 
53 
54     static class FocusAnimator implements TimeAnimator.TimeListener {
55         private final View mView;
56         private final int mDuration;
57         private final ShadowOverlayContainer mWrapper;
58         private final float mScaleDiff;
59         private float mFocusLevel = 0f;
60         private float mFocusLevelStart;
61         private float mFocusLevelDelta;
62         private final TimeAnimator mAnimator = new TimeAnimator();
63         private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
64         private final ColorOverlayDimmer mDimmer;
65 
animateFocus(boolean select, boolean immediate)66         void animateFocus(boolean select, boolean immediate) {
67             endAnimation();
68             final float end = select ? 1 : 0;
69             if (immediate) {
70                 setFocusLevel(end);
71             } else if (mFocusLevel != end) {
72                 mFocusLevelStart = mFocusLevel;
73                 mFocusLevelDelta = end - mFocusLevelStart;
74                 mAnimator.start();
75             }
76         }
77 
FocusAnimator(View view, float scale, boolean useDimmer, int duration)78         FocusAnimator(View view, float scale, boolean useDimmer, int duration) {
79             mView = view;
80             mDuration = duration;
81             mScaleDiff = scale - 1f;
82             if (view instanceof ShadowOverlayContainer) {
83                 mWrapper = (ShadowOverlayContainer) view;
84             } else {
85                 mWrapper = null;
86             }
87             mAnimator.setTimeListener(this);
88             if (useDimmer) {
89                 mDimmer = ColorOverlayDimmer.createDefault(view.getContext());
90             } else {
91                 mDimmer = null;
92             }
93         }
94 
setFocusLevel(float level)95         void setFocusLevel(float level) {
96             mFocusLevel = level;
97             float scale = 1f + mScaleDiff * level;
98             mView.setScaleX(scale);
99             mView.setScaleY(scale);
100             if (mWrapper != null) {
101                 mWrapper.setShadowFocusLevel(level);
102             } else {
103                 ShadowOverlayHelper.setNoneWrapperShadowFocusLevel(mView, level);
104             }
105             if (mDimmer != null) {
106                 mDimmer.setActiveLevel(level);
107                 int color = mDimmer.getPaint().getColor();
108                 if (mWrapper != null) {
109                     mWrapper.setOverlayColor(color);
110                 } else {
111                     ShadowOverlayHelper.setNoneWrapperOverlayColor(mView, color);
112                 }
113             }
114         }
115 
getFocusLevel()116         float getFocusLevel() {
117             return mFocusLevel;
118         }
119 
endAnimation()120         void endAnimation() {
121             mAnimator.end();
122         }
123 
124         @Override
onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime)125         public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
126             float fraction;
127             if (totalTime >= mDuration) {
128                 fraction = 1;
129                 mAnimator.end();
130             } else {
131                 fraction = (float) (totalTime / (double) mDuration);
132             }
133             if (mInterpolator != null) {
134                 fraction = mInterpolator.getInterpolation(fraction);
135             }
136             setFocusLevel(mFocusLevelStart + fraction * mFocusLevelDelta);
137         }
138     }
139 
140     static class BrowseItemFocusHighlight implements FocusHighlightHandler {
141         private static final int DURATION_MS = 150;
142 
143         private int mScaleIndex;
144         private final boolean mUseDimmer;
145 
BrowseItemFocusHighlight(int zoomIndex, boolean useDimmer)146         BrowseItemFocusHighlight(int zoomIndex, boolean useDimmer) {
147             if (!isValidZoomIndex(zoomIndex)) {
148                 throw new IllegalArgumentException("Unhandled zoom index");
149             }
150             mScaleIndex = zoomIndex;
151             mUseDimmer = useDimmer;
152         }
153 
getScale(Resources res)154         private float getScale(Resources res) {
155             return mScaleIndex == ZOOM_FACTOR_NONE ? 1f :
156                     res.getFraction(getResId(mScaleIndex), 1, 1);
157         }
158 
159         @Override
onItemFocused(View view, boolean hasFocus)160         public void onItemFocused(View view, boolean hasFocus) {
161             view.setSelected(hasFocus);
162             getOrCreateAnimator(view).animateFocus(hasFocus, false);
163         }
164 
165         @Override
onInitializeView(View view)166         public void onInitializeView(View view) {
167             getOrCreateAnimator(view).animateFocus(false, true);
168         }
169 
getOrCreateAnimator(View view)170         private FocusAnimator getOrCreateAnimator(View view) {
171             FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
172             if (animator == null) {
173                 animator = new FocusAnimator(
174                         view, getScale(view.getResources()), mUseDimmer, DURATION_MS);
175                 view.setTag(R.id.lb_focus_animator, animator);
176             }
177             return animator;
178         }
179 
180     }
181 
182     /**
183      * Sets up the focus highlight behavior of a focused item in browse list row.
184      * @param zoomIndex One of {@link FocusHighlight#ZOOM_FACTOR_SMALL}
185      * {@link FocusHighlight#ZOOM_FACTOR_XSMALL}
186      * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}
187      * {@link FocusHighlight#ZOOM_FACTOR_LARGE}
188      * {@link FocusHighlight#ZOOM_FACTOR_NONE}.
189      * @param useDimmer Allow dimming browse item when unselected.
190      * @param adapter  adapter of the list row.
191      */
setupBrowseItemFocusHighlight(ItemBridgeAdapter adapter, int zoomIndex, boolean useDimmer)192     public static void setupBrowseItemFocusHighlight(ItemBridgeAdapter adapter, int zoomIndex,
193             boolean useDimmer) {
194         adapter.setFocusHighlight(new BrowseItemFocusHighlight(zoomIndex, useDimmer));
195     }
196 
197     /**
198      * Sets up the focus highlight behavior of a focused item in header list.
199      * @param gridView  the header list.
200      */
setupHeaderItemFocusHighlight(VerticalGridView gridView)201     public static void setupHeaderItemFocusHighlight(VerticalGridView gridView) {
202         if (gridView.getAdapter() instanceof ItemBridgeAdapter) {
203             ((ItemBridgeAdapter) gridView.getAdapter())
204                     .setFocusHighlight(new HeaderItemFocusHighlight(gridView));
205         }
206     }
207 
208     static class HeaderItemFocusHighlight implements FocusHighlightHandler {
209         private static boolean sInitialized;
210         private static float sSelectScale;
211         private static int sDuration;
212         private BaseGridView mGridView;
213 
HeaderItemFocusHighlight(BaseGridView gridView)214         HeaderItemFocusHighlight(BaseGridView gridView) {
215             mGridView = gridView;
216             lazyInit(gridView.getContext().getResources());
217         }
218 
lazyInit(Resources res)219         private static void lazyInit(Resources res) {
220             if (!sInitialized) {
221                 sSelectScale =
222                         Float.parseFloat(res.getString(R.dimen.lb_browse_header_select_scale));
223                 sDuration =
224                         Integer.parseInt(res.getString(R.dimen.lb_browse_header_select_duration));
225                 sInitialized = true;
226             }
227         }
228 
229         class HeaderFocusAnimator extends FocusAnimator {
230 
231             ItemBridgeAdapter.ViewHolder mViewHolder;
HeaderFocusAnimator(View view, float scale, int duration)232             HeaderFocusAnimator(View view, float scale, int duration) {
233                 super(view, scale, false, duration);
234                 mViewHolder = (ItemBridgeAdapter.ViewHolder) mGridView.getChildViewHolder(view);
235             }
236 
237             @Override
setFocusLevel(float level)238             void setFocusLevel(float level) {
239                 Presenter presenter = mViewHolder.getPresenter();
240                 if (presenter instanceof RowHeaderPresenter) {
241                     ((RowHeaderPresenter) presenter).setSelectLevel(
242                             ((RowHeaderPresenter.ViewHolder) mViewHolder.getViewHolder()), level);
243                 }
244                 super.setFocusLevel(level);
245             }
246 
247         }
248 
viewFocused(View view, boolean hasFocus)249         private void viewFocused(View view, boolean hasFocus) {
250             view.setSelected(hasFocus);
251             FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
252             if (animator == null) {
253                 animator = new HeaderFocusAnimator(view, sSelectScale, sDuration);
254                 view.setTag(R.id.lb_focus_animator, animator);
255             }
256             animator.animateFocus(hasFocus, false);
257         }
258 
259         @Override
onItemFocused(View view, boolean hasFocus)260         public void onItemFocused(View view, boolean hasFocus) {
261             viewFocused(view, hasFocus);
262         }
263 
264         @Override
onInitializeView(View view)265         public void onInitializeView(View view) {
266         }
267 
268     }
269 }
270