| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package android.support.v17.leanback.widget; |
| |
| import android.support.v17.leanback.R; |
| import android.support.v17.leanback.graphics.ColorOverlayDimmer; |
| import android.view.View; |
| import android.view.animation.AccelerateDecelerateInterpolator; |
| import android.view.animation.Interpolator; |
| import android.animation.TimeAnimator; |
| import android.content.res.Resources; |
| import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_NONE; |
| import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_SMALL; |
| import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_XSMALL; |
| import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_MEDIUM; |
| import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_LARGE; |
| |
| /** |
| * Sets up the highlighting behavior when an item gains focus. |
| */ |
| public class FocusHighlightHelper { |
| |
| static boolean isValidZoomIndex(int zoomIndex) { |
| return zoomIndex == ZOOM_FACTOR_NONE || getResId(zoomIndex) > 0; |
| } |
| |
| private static int getResId(int zoomIndex) { |
| switch (zoomIndex) { |
| case ZOOM_FACTOR_SMALL: |
| return R.fraction.lb_focus_zoom_factor_small; |
| case ZOOM_FACTOR_XSMALL: |
| return R.fraction.lb_focus_zoom_factor_xsmall; |
| case ZOOM_FACTOR_MEDIUM: |
| return R.fraction.lb_focus_zoom_factor_medium; |
| case ZOOM_FACTOR_LARGE: |
| return R.fraction.lb_focus_zoom_factor_large; |
| default: |
| return 0; |
| } |
| } |
| |
| |
| static class FocusAnimator implements TimeAnimator.TimeListener { |
| private final View mView; |
| private final int mDuration; |
| private final ShadowOverlayContainer mWrapper; |
| private final float mScaleDiff; |
| private float mFocusLevel = 0f; |
| private float mFocusLevelStart; |
| private float mFocusLevelDelta; |
| private final TimeAnimator mAnimator = new TimeAnimator(); |
| private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator(); |
| private final ColorOverlayDimmer mDimmer; |
| |
| void animateFocus(boolean select, boolean immediate) { |
| endAnimation(); |
| final float end = select ? 1 : 0; |
| if (immediate) { |
| setFocusLevel(end); |
| } else if (mFocusLevel != end) { |
| mFocusLevelStart = mFocusLevel; |
| mFocusLevelDelta = end - mFocusLevelStart; |
| mAnimator.start(); |
| } |
| } |
| |
| FocusAnimator(View view, float scale, boolean useDimmer, int duration) { |
| mView = view; |
| mDuration = duration; |
| mScaleDiff = scale - 1f; |
| if (view instanceof ShadowOverlayContainer) { |
| mWrapper = (ShadowOverlayContainer) view; |
| } else { |
| mWrapper = null; |
| } |
| mAnimator.setTimeListener(this); |
| if (useDimmer) { |
| mDimmer = ColorOverlayDimmer.createDefault(view.getContext()); |
| } else { |
| mDimmer = null; |
| } |
| } |
| |
| void setFocusLevel(float level) { |
| mFocusLevel = level; |
| float scale = 1f + mScaleDiff * level; |
| mView.setScaleX(scale); |
| mView.setScaleY(scale); |
| if (mWrapper != null) { |
| mWrapper.setShadowFocusLevel(level); |
| } else { |
| ShadowOverlayHelper.setNoneWrapperShadowFocusLevel(mView, level); |
| } |
| if (mDimmer != null) { |
| mDimmer.setActiveLevel(level); |
| int color = mDimmer.getPaint().getColor(); |
| if (mWrapper != null) { |
| mWrapper.setOverlayColor(color); |
| } else { |
| ShadowOverlayHelper.setNoneWrapperOverlayColor(mView, color); |
| } |
| } |
| } |
| |
| float getFocusLevel() { |
| return mFocusLevel; |
| } |
| |
| void endAnimation() { |
| mAnimator.end(); |
| } |
| |
| @Override |
| public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { |
| float fraction; |
| if (totalTime >= mDuration) { |
| fraction = 1; |
| mAnimator.end(); |
| } else { |
| fraction = (float) (totalTime / (double) mDuration); |
| } |
| if (mInterpolator != null) { |
| fraction = mInterpolator.getInterpolation(fraction); |
| } |
| setFocusLevel(mFocusLevelStart + fraction * mFocusLevelDelta); |
| } |
| } |
| |
| static class BrowseItemFocusHighlight implements FocusHighlightHandler { |
| private static final int DURATION_MS = 150; |
| |
| private int mScaleIndex; |
| private final boolean mUseDimmer; |
| |
| BrowseItemFocusHighlight(int zoomIndex, boolean useDimmer) { |
| if (!isValidZoomIndex(zoomIndex)) { |
| throw new IllegalArgumentException("Unhandled zoom index"); |
| } |
| mScaleIndex = zoomIndex; |
| mUseDimmer = useDimmer; |
| } |
| |
| private float getScale(Resources res) { |
| return mScaleIndex == ZOOM_FACTOR_NONE ? 1f : |
| res.getFraction(getResId(mScaleIndex), 1, 1); |
| } |
| |
| @Override |
| public void onItemFocused(View view, boolean hasFocus) { |
| view.setSelected(hasFocus); |
| getOrCreateAnimator(view).animateFocus(hasFocus, false); |
| } |
| |
| @Override |
| public void onInitializeView(View view) { |
| getOrCreateAnimator(view).animateFocus(false, true); |
| } |
| |
| private FocusAnimator getOrCreateAnimator(View view) { |
| FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator); |
| if (animator == null) { |
| animator = new FocusAnimator( |
| view, getScale(view.getResources()), mUseDimmer, DURATION_MS); |
| view.setTag(R.id.lb_focus_animator, animator); |
| } |
| return animator; |
| } |
| |
| } |
| |
| /** |
| * Sets up the focus highlight behavior of a focused item in browse list row. |
| * @param zoomIndex One of {@link FocusHighlight#ZOOM_FACTOR_SMALL} |
| * {@link FocusHighlight#ZOOM_FACTOR_XSMALL} |
| * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} |
| * {@link FocusHighlight#ZOOM_FACTOR_LARGE} |
| * {@link FocusHighlight#ZOOM_FACTOR_NONE}. |
| * @param useDimmer Allow dimming browse item when unselected. |
| * @param adapter adapter of the list row. |
| */ |
| public static void setupBrowseItemFocusHighlight(ItemBridgeAdapter adapter, int zoomIndex, |
| boolean useDimmer) { |
| adapter.setFocusHighlight(new BrowseItemFocusHighlight(zoomIndex, useDimmer)); |
| } |
| |
| /** |
| * Sets up the focus highlight behavior of a focused item in header list. |
| * @param gridView the header list. |
| */ |
| public static void setupHeaderItemFocusHighlight(VerticalGridView gridView) { |
| if (gridView.getAdapter() instanceof ItemBridgeAdapter) { |
| ((ItemBridgeAdapter) gridView.getAdapter()) |
| .setFocusHighlight(new HeaderItemFocusHighlight(gridView)); |
| } |
| } |
| |
| static class HeaderItemFocusHighlight implements FocusHighlightHandler { |
| private static boolean sInitialized; |
| private static float sSelectScale; |
| private static int sDuration; |
| private BaseGridView mGridView; |
| |
| HeaderItemFocusHighlight(BaseGridView gridView) { |
| mGridView = gridView; |
| lazyInit(gridView.getContext().getResources()); |
| } |
| |
| private static void lazyInit(Resources res) { |
| if (!sInitialized) { |
| sSelectScale = |
| Float.parseFloat(res.getString(R.dimen.lb_browse_header_select_scale)); |
| sDuration = |
| Integer.parseInt(res.getString(R.dimen.lb_browse_header_select_duration)); |
| sInitialized = true; |
| } |
| } |
| |
| class HeaderFocusAnimator extends FocusAnimator { |
| |
| ItemBridgeAdapter.ViewHolder mViewHolder; |
| HeaderFocusAnimator(View view, float scale, int duration) { |
| super(view, scale, false, duration); |
| mViewHolder = (ItemBridgeAdapter.ViewHolder) mGridView.getChildViewHolder(view); |
| } |
| |
| @Override |
| void setFocusLevel(float level) { |
| Presenter presenter = mViewHolder.getPresenter(); |
| if (presenter instanceof RowHeaderPresenter) { |
| ((RowHeaderPresenter) presenter).setSelectLevel( |
| ((RowHeaderPresenter.ViewHolder) mViewHolder.getViewHolder()), level); |
| } |
| super.setFocusLevel(level); |
| } |
| |
| } |
| |
| private void viewFocused(View view, boolean hasFocus) { |
| view.setSelected(hasFocus); |
| FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator); |
| if (animator == null) { |
| animator = new HeaderFocusAnimator(view, sSelectScale, sDuration); |
| view.setTag(R.id.lb_focus_animator, animator); |
| } |
| animator.animateFocus(hasFocus, false); |
| } |
| |
| @Override |
| public void onItemFocused(View view, boolean hasFocus) { |
| viewFocused(view, hasFocus); |
| } |
| |
| @Override |
| public void onInitializeView(View view) { |
| } |
| |
| } |
| } |