147520b68e50572a9775a662410c5aff8300c8784Craig Stout/*
247520b68e50572a9775a662410c5aff8300c8784Craig Stout * Copyright (C) 2014 The Android Open Source Project
347520b68e50572a9775a662410c5aff8300c8784Craig Stout *
447520b68e50572a9775a662410c5aff8300c8784Craig Stout * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
547520b68e50572a9775a662410c5aff8300c8784Craig Stout * in compliance with the License. You may obtain a copy of the License at
647520b68e50572a9775a662410c5aff8300c8784Craig Stout *
747520b68e50572a9775a662410c5aff8300c8784Craig Stout * http://www.apache.org/licenses/LICENSE-2.0
847520b68e50572a9775a662410c5aff8300c8784Craig Stout *
947520b68e50572a9775a662410c5aff8300c8784Craig Stout * Unless required by applicable law or agreed to in writing, software distributed under the License
1047520b68e50572a9775a662410c5aff8300c8784Craig Stout * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
1147520b68e50572a9775a662410c5aff8300c8784Craig Stout * or implied. See the License for the specific language governing permissions and limitations under
1247520b68e50572a9775a662410c5aff8300c8784Craig Stout * the License.
1347520b68e50572a9775a662410c5aff8300c8784Craig Stout */
1447520b68e50572a9775a662410c5aff8300c8784Craig Stoutpackage android.support.v17.leanback.widget;
1547520b68e50572a9775a662410c5aff8300c8784Craig Stout
1647520b68e50572a9775a662410c5aff8300c8784Craig Stoutimport android.support.v17.leanback.R;
1746e7de54775fc37dc51041629c79249e6dae3242Dake Guimport android.support.v17.leanback.graphics.ColorOverlayDimmer;
1847520b68e50572a9775a662410c5aff8300c8784Craig Stoutimport android.view.View;
19dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.view.animation.AccelerateDecelerateInterpolator;
20dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.view.animation.Interpolator;
21dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.animation.TimeAnimator;
2255c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stoutimport android.content.res.Resources;
2346e7de54775fc37dc51041629c79249e6dae3242Dake Guimport static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_NONE;
2446e7de54775fc37dc51041629c79249e6dae3242Dake Guimport static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_SMALL;
25575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stoutimport static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_XSMALL;
2646e7de54775fc37dc51041629c79249e6dae3242Dake Guimport static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_MEDIUM;
2746e7de54775fc37dc51041629c79249e6dae3242Dake Guimport static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_LARGE;
2847520b68e50572a9775a662410c5aff8300c8784Craig Stout
2947520b68e50572a9775a662410c5aff8300c8784Craig Stout/**
30a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout * Sets up the highlighting behavior when an item gains focus.
3147520b68e50572a9775a662410c5aff8300c8784Craig Stout */
3247520b68e50572a9775a662410c5aff8300c8784Craig Stoutpublic class FocusHighlightHelper {
3347520b68e50572a9775a662410c5aff8300c8784Craig Stout
34575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout    static boolean isValidZoomIndex(int zoomIndex) {
35575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout        return zoomIndex == ZOOM_FACTOR_NONE || getResId(zoomIndex) > 0;
36575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout    }
37575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout
38575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout    private static int getResId(int zoomIndex) {
39575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout        switch (zoomIndex) {
40575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            case ZOOM_FACTOR_SMALL:
41575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                return R.fraction.lb_focus_zoom_factor_small;
42575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            case ZOOM_FACTOR_XSMALL:
43575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                return R.fraction.lb_focus_zoom_factor_xsmall;
44575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            case ZOOM_FACTOR_MEDIUM:
45575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                return R.fraction.lb_focus_zoom_factor_medium;
46575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            case ZOOM_FACTOR_LARGE:
47575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                return R.fraction.lb_focus_zoom_factor_large;
48575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            default:
49575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                return 0;
50575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout        }
51575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout    }
52575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout
53575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout
543c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu    static class FocusAnimator implements TimeAnimator.TimeListener {
553c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private final View mView;
563c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private final int mDuration;
573c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private final ShadowOverlayContainer mWrapper;
583c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private final float mScaleDiff;
593c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private float mFocusLevel = 0f;
603c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private float mFocusLevelStart;
613c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private float mFocusLevelDelta;
623c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private final TimeAnimator mAnimator = new TimeAnimator();
633c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
6446e7de54775fc37dc51041629c79249e6dae3242Dake Gu        private final ColorOverlayDimmer mDimmer;
653c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
663c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        void animateFocus(boolean select, boolean immediate) {
673c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            endAnimation();
683c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            final float end = select ? 1 : 0;
693c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            if (immediate) {
703c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                setFocusLevel(end);
713c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            } else if (mFocusLevel != end) {
723c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                mFocusLevelStart = mFocusLevel;
733c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                mFocusLevelDelta = end - mFocusLevelStart;
743c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                mAnimator.start();
753c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            }
763c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
773c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
7846e7de54775fc37dc51041629c79249e6dae3242Dake Gu        FocusAnimator(View view, float scale, boolean useDimmer, int duration) {
793c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mView = view;
803c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mDuration = duration;
813c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mScaleDiff = scale - 1f;
823c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            if (view instanceof ShadowOverlayContainer) {
833c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                mWrapper = (ShadowOverlayContainer) view;
843c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            } else {
853c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                mWrapper = null;
863c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            }
873c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mAnimator.setTimeListener(this);
884d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu            if (useDimmer) {
8946e7de54775fc37dc51041629c79249e6dae3242Dake Gu                mDimmer = ColorOverlayDimmer.createDefault(view.getContext());
9046e7de54775fc37dc51041629c79249e6dae3242Dake Gu            } else {
9146e7de54775fc37dc51041629c79249e6dae3242Dake Gu                mDimmer = null;
9246e7de54775fc37dc51041629c79249e6dae3242Dake Gu            }
933c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
943c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
953c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        void setFocusLevel(float level) {
963c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mFocusLevel = level;
973c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            float scale = 1f + mScaleDiff * level;
983c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mView.setScaleX(scale);
993c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mView.setScaleY(scale);
1004d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu            if (mWrapper != null) {
1014d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu                mWrapper.setShadowFocusLevel(level);
1024d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu            } else {
10385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu                ShadowOverlayHelper.setNoneWrapperShadowFocusLevel(mView, level);
1044d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu            }
1054d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu            if (mDimmer != null) {
106254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu                mDimmer.setActiveLevel(level);
1074d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu                int color = mDimmer.getPaint().getColor();
1084d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu                if (mWrapper != null) {
1094d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu                    mWrapper.setOverlayColor(color);
1104d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu                } else {
11185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu                    ShadowOverlayHelper.setNoneWrapperOverlayColor(mView, color);
1124d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu                }
1133c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            }
1143c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
1153c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
1163c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        float getFocusLevel() {
1173c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            return mFocusLevel;
1183c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
1193c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
1203c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        void endAnimation() {
1213c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mAnimator.end();
1223c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
1233c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
1243c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        @Override
1253c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
1263c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            float fraction;
1273c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            if (totalTime >= mDuration) {
1283c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                fraction = 1;
1293c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                mAnimator.end();
1303c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            } else {
1313c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                fraction = (float) (totalTime / (double) mDuration);
1323c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            }
1333c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            if (mInterpolator != null) {
1343c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                fraction = mInterpolator.getInterpolation(fraction);
1353c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            }
1363c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            setFocusLevel(mFocusLevelStart + fraction * mFocusLevelDelta);
1373c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
1383c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu    }
1393c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
14046e7de54775fc37dc51041629c79249e6dae3242Dake Gu    static class BrowseItemFocusHighlight implements FocusHighlightHandler {
14147520b68e50572a9775a662410c5aff8300c8784Craig Stout        private static final int DURATION_MS = 150;
14247520b68e50572a9775a662410c5aff8300c8784Craig Stout
143b9e89a1544f8cf582f191184fb9b2a4f24e1fa5bCraig Stout        private int mScaleIndex;
14446e7de54775fc37dc51041629c79249e6dae3242Dake Gu        private final boolean mUseDimmer;
145b9e89a1544f8cf582f191184fb9b2a4f24e1fa5bCraig Stout
14646e7de54775fc37dc51041629c79249e6dae3242Dake Gu        BrowseItemFocusHighlight(int zoomIndex, boolean useDimmer) {
147575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            if (!isValidZoomIndex(zoomIndex)) {
148575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                throw new IllegalArgumentException("Unhandled zoom index");
14947520b68e50572a9775a662410c5aff8300c8784Craig Stout            }
150575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            mScaleIndex = zoomIndex;
151575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            mUseDimmer = useDimmer;
15247520b68e50572a9775a662410c5aff8300c8784Craig Stout        }
15347520b68e50572a9775a662410c5aff8300c8784Craig Stout
154575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout        private float getScale(Resources res) {
155575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout            return mScaleIndex == ZOOM_FACTOR_NONE ? 1f :
156575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                    res.getFraction(getResId(mScaleIndex), 1, 1);
157b9e89a1544f8cf582f191184fb9b2a4f24e1fa5bCraig Stout        }
158b9e89a1544f8cf582f191184fb9b2a4f24e1fa5bCraig Stout
15946e7de54775fc37dc51041629c79249e6dae3242Dake Gu        @Override
16046e7de54775fc37dc51041629c79249e6dae3242Dake Gu        public void onItemFocused(View view, boolean hasFocus) {
161dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu            view.setSelected(hasFocus);
16246e7de54775fc37dc51041629c79249e6dae3242Dake Gu            getOrCreateAnimator(view).animateFocus(hasFocus, false);
16346e7de54775fc37dc51041629c79249e6dae3242Dake Gu        }
16446e7de54775fc37dc51041629c79249e6dae3242Dake Gu
16546e7de54775fc37dc51041629c79249e6dae3242Dake Gu        @Override
16646e7de54775fc37dc51041629c79249e6dae3242Dake Gu        public void onInitializeView(View view) {
16746e7de54775fc37dc51041629c79249e6dae3242Dake Gu            getOrCreateAnimator(view).animateFocus(false, true);
16846e7de54775fc37dc51041629c79249e6dae3242Dake Gu        }
16946e7de54775fc37dc51041629c79249e6dae3242Dake Gu
17046e7de54775fc37dc51041629c79249e6dae3242Dake Gu        private FocusAnimator getOrCreateAnimator(View view) {
171dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu            FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
172dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu            if (animator == null) {
173575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                animator = new FocusAnimator(
174575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout                        view, getScale(view.getResources()), mUseDimmer, DURATION_MS);
175dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu                view.setTag(R.id.lb_focus_animator, animator);
176892181367d658f347d00ea5e091aa31f086b2a20Dake Gu            }
17746e7de54775fc37dc51041629c79249e6dae3242Dake Gu            return animator;
17847520b68e50572a9775a662410c5aff8300c8784Craig Stout        }
17947520b68e50572a9775a662410c5aff8300c8784Craig Stout
18047520b68e50572a9775a662410c5aff8300c8784Craig Stout    }
18147520b68e50572a9775a662410c5aff8300c8784Craig Stout
18247520b68e50572a9775a662410c5aff8300c8784Craig Stout    /**
183a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets up the focus highlight behavior of a focused item in browse list row.
184575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout     * @param zoomIndex One of {@link FocusHighlight#ZOOM_FACTOR_SMALL}
185575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout     * {@link FocusHighlight#ZOOM_FACTOR_XSMALL}
186575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout     * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}
187575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout     * {@link FocusHighlight#ZOOM_FACTOR_LARGE}
188575dc8a5a5c023aee0c0ec297a7d357685cf49e9Craig Stout     * {@link FocusHighlight#ZOOM_FACTOR_NONE}.
18946e7de54775fc37dc51041629c79249e6dae3242Dake Gu     * @param useDimmer Allow dimming browse item when unselected.
19047520b68e50572a9775a662410c5aff8300c8784Craig Stout     * @param adapter  adapter of the list row.
19147520b68e50572a9775a662410c5aff8300c8784Craig Stout     */
19246e7de54775fc37dc51041629c79249e6dae3242Dake Gu    public static void setupBrowseItemFocusHighlight(ItemBridgeAdapter adapter, int zoomIndex,
19346e7de54775fc37dc51041629c79249e6dae3242Dake Gu            boolean useDimmer) {
19446e7de54775fc37dc51041629c79249e6dae3242Dake Gu        adapter.setFocusHighlight(new BrowseItemFocusHighlight(zoomIndex, useDimmer));
19547520b68e50572a9775a662410c5aff8300c8784Craig Stout    }
19655c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout
19755c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout    /**
198a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets up the focus highlight behavior of a focused item in header list.
199c16a0e4e812e393f67b0d55a4df5667a967a8e57Dake Gu     * @param gridView  the header list.
20055c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout     */
201a5f765740ef4451374963e6eabd5f587b94ff50eDake Gu    public static void setupHeaderItemFocusHighlight(VerticalGridView gridView) {
2023c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        if (gridView.getAdapter() instanceof ItemBridgeAdapter) {
2033c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            ((ItemBridgeAdapter) gridView.getAdapter())
2043c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                    .setFocusHighlight(new HeaderItemFocusHighlight(gridView));
2053c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
20655c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout    }
20755c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout
20846e7de54775fc37dc51041629c79249e6dae3242Dake Gu    static class HeaderItemFocusHighlight implements FocusHighlightHandler {
2093c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private static boolean sInitialized;
2103c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private static float sSelectScale;
2113c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private static int sDuration;
2123c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private BaseGridView mGridView;
21355c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout
2143c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        HeaderItemFocusHighlight(BaseGridView gridView) {
2153c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            mGridView = gridView;
2163c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            lazyInit(gridView.getContext().getResources());
2173c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
2183c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
2193c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        private static void lazyInit(Resources res) {
2203c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            if (!sInitialized) {
2213c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                sSelectScale =
22255c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout                        Float.parseFloat(res.getString(R.dimen.lb_browse_header_select_scale));
2233c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                sDuration =
22455c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout                        Integer.parseInt(res.getString(R.dimen.lb_browse_header_select_duration));
2253c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                sInitialized = true;
2263c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            }
2273c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        }
2283c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
2293c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu        class HeaderFocusAnimator extends FocusAnimator {
2303c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
2313c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            ItemBridgeAdapter.ViewHolder mViewHolder;
2323c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            HeaderFocusAnimator(View view, float scale, int duration) {
23346e7de54775fc37dc51041629c79249e6dae3242Dake Gu                super(view, scale, false, duration);
2343c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                mViewHolder = (ItemBridgeAdapter.ViewHolder) mGridView.getChildViewHolder(view);
23555c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout            }
2363c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
2373c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            @Override
2383c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            void setFocusLevel(float level) {
2393c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                Presenter presenter = mViewHolder.getPresenter();
2403c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                if (presenter instanceof RowHeaderPresenter) {
2413c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                    ((RowHeaderPresenter) presenter).setSelectLevel(
2423c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                            ((RowHeaderPresenter.ViewHolder) mViewHolder.getViewHolder()), level);
2433c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                }
2443c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                super.setFocusLevel(level);
2453c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            }
2463c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu
24755c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout        }
24855c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout
24955c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout        private void viewFocused(View view, boolean hasFocus) {
2503c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            view.setSelected(hasFocus);
2513c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
2523c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            if (animator == null) {
2533c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                animator = new HeaderFocusAnimator(view, sSelectScale, sDuration);
2543c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu                view.setTag(R.id.lb_focus_animator, animator);
25555c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout            }
2563c23ada8bc25a05bbaa8c479a9df72e8172c4349Dake Gu            animator.animateFocus(hasFocus, false);
25755c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout        }
25855c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout
25955c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout        @Override
26062d36d44e03a3dd0632b156615b04563c62e83c1Dake Gu        public void onItemFocused(View view, boolean hasFocus) {
26155c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout            viewFocused(view, hasFocus);
26255c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout        }
26346e7de54775fc37dc51041629c79249e6dae3242Dake Gu
26446e7de54775fc37dc51041629c79249e6dae3242Dake Gu        @Override
26546e7de54775fc37dc51041629c79249e6dae3242Dake Gu        public void onInitializeView(View view) {
26646e7de54775fc37dc51041629c79249e6dae3242Dake Gu        }
26746e7de54775fc37dc51041629c79249e6dae3242Dake Gu
26855c9ee4b612ffc7b4632b1e4b7b7ab4900cd47c7Craig Stout    }
26947520b68e50572a9775a662410c5aff8300c8784Craig Stout}
270