1b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee/*
2b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * Copyright (C) 2015 The Android Open Source Project
3b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee *
4b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * Licensed under the Apache License, Version 2.0 (the "License");
5b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * you may not use this file except in compliance with the License.
6b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * You may obtain a copy of the License at
7b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee *
8b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee *      http://www.apache.org/licenses/LICENSE-2.0
9b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee *
10b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * Unless required by applicable law or agreed to in writing, software
11b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * distributed under the License is distributed on an "AS IS" BASIS,
12b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * See the License for the specific language governing permissions and
14b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * limitations under the License.
15b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee */
16b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
17b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leepackage android.support.v17.leanback.widget;
18b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
19b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.animation.Animator;
20b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.animation.AnimatorSet;
216a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Leeimport android.animation.ObjectAnimator;
226a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Leeimport android.animation.TimeInterpolator;
23b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.content.Context;
24b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.content.res.Resources;
259b5ef3e2e264a624058bc514e05cf5e5dc8d94b9Chulwoo Leeimport android.content.res.TypedArray;
26b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Bitmap;
27b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.BitmapFactory;
28b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Canvas;
29b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Color;
306cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Leeimport android.graphics.Matrix;
31b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Paint;
32b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Rect;
33b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.support.annotation.ColorInt;
34b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.support.annotation.VisibleForTesting;
35b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.support.v17.leanback.R;
36b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.util.AttributeSet;
376a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Leeimport android.util.Property;
38b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.view.View;
396a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Leeimport android.view.animation.DecelerateInterpolator;
40b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
41b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee/**
42b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * A page indicator with dots.
43b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * @hide
44b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee */
45b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leepublic class PagingIndicator extends View {
466a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private static final long DURATION_ALPHA = 167;
476a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private static final long DURATION_DIAMETER = 417;
486a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private static final long DURATION_TRANSLATION_X = DURATION_DIAMETER;
496a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private static final TimeInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
506a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
516a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private static final Property<Dot, Float> DOT_ALPHA
526a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            = new Property<Dot, Float>(Float.class, "alpha") {
536a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        @Override
546a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        public Float get(Dot dot) {
556a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            return dot.getAlpha();
566a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        }
576a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
586a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        @Override
596a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        public void set(Dot dot, Float value) {
606a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            dot.setAlpha(value);
616a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        }
626a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    };
636a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
646a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private static final Property<Dot, Float> DOT_DIAMETER
656a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            = new Property<Dot, Float>(Float.class, "diameter") {
666a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        @Override
676a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        public Float get(Dot dot) {
686a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            return dot.getDiameter();
696a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        }
706a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
716a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        @Override
726a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        public void set(Dot dot, Float value) {
736a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            dot.setDiameter(value);
746a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        }
756a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    };
766a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
776a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private static final Property<Dot, Float> DOT_TRANSLATION_X
786a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            = new Property<Dot, Float>(Float.class, "translation_x") {
796a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        @Override
806a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        public Float get(Dot dot) {
816a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            return dot.getTranslationX();
826a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        }
836a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
846a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        @Override
856a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        public void set(Dot dot, Float value) {
866a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee            dot.setTranslationX(value);
876a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        }
886a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    };
896a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
90b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    // attribute
916cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    private boolean mIsLtr;
92b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final int mDotDiameter;
93b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final int mDotRadius;
94b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final int mDotGap;
95b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final int mArrowDiameter;
96b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final int mArrowRadius;
97b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final int mArrowGap;
98b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final int mShadowRadius;
99b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private Dot[] mDots;
100b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    // X position when the dot is selected.
101b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private int[] mDotSelectedX;
102b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    // X position when the dot is located to the left of the selected dot.
1036cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    private int[] mDotSelectedPrevX;
104b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    // X position when the dot is located to the right of the selected dot.
1056cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    private int[] mDotSelectedNextX;
106b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private int mDotCenterY;
107b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
108b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    // state
109b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private int mPageCount;
110b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private int mCurrentPage;
111b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private int mPreviousPage;
112b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
113b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    // drawing
114b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    @ColorInt
115b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final int mDotFgSelectColor;
116b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final Paint mBgPaint;
117b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final Paint mFgPaint;
1186a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private final AnimatorSet mShowAnimator;
1196a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private final AnimatorSet mHideAnimator;
120b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final AnimatorSet mAnimator = new AnimatorSet();
1216cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    private Bitmap mArrow;
122b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final Rect mArrowRect;
123b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private final float mArrowToBgRatio;
124b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
125b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    public PagingIndicator(Context context) {
126b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        this(context, null, 0);
127b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
128b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
129b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    public PagingIndicator(Context context, AttributeSet attrs) {
130b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        this(context, attrs, 0);
131b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
132b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
133b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    public PagingIndicator(Context context, AttributeSet attrs, int defStyle) {
134b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        super(context, attrs, defStyle);
135b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        Resources res = getResources();
1369b5ef3e2e264a624058bc514e05cf5e5dc8d94b9Chulwoo Lee        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PagingIndicator,
1379b5ef3e2e264a624058bc514e05cf5e5dc8d94b9Chulwoo Lee                defStyle, 0);
1386a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mDotRadius = getDimensionFromTypedArray(typedArray, R.styleable.PagingIndicator_dotRadius,
1396a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                R.dimen.lb_page_indicator_dot_radius);
140b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mDotDiameter = mDotRadius * 2;
1416a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mArrowRadius = getDimensionFromTypedArray(typedArray,
1426a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                R.styleable.PagingIndicator_arrowRadius, R.dimen.lb_page_indicator_arrow_radius);
1436a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mArrowDiameter = mArrowRadius * 2;
1446a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mDotGap = getDimensionFromTypedArray(typedArray, R.styleable.PagingIndicator_dotToDotGap,
1456a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                R.dimen.lb_page_indicator_dot_gap);
1466a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mArrowGap = getDimensionFromTypedArray(typedArray,
1476a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                R.styleable.PagingIndicator_dotToArrowGap, R.dimen.lb_page_indicator_arrow_gap);
1486a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        int bgColor = getColorFromTypedArray(typedArray, R.styleable.PagingIndicator_dotBgColor,
1496a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                R.color.lb_page_indicator_dot);
150b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
151b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mBgPaint.setColor(bgColor);
1526a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mDotFgSelectColor = getColorFromTypedArray(typedArray,
1536a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                R.styleable.PagingIndicator_arrowBgColor,
1546a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                R.color.lb_page_indicator_arrow_background);
1556a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        typedArray.recycle();
1566a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mIsLtr = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
1576a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        int shadowColor = res.getColor(R.color.lb_page_indicator_arrow_shadow);
158b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mShadowRadius = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_shadow_radius);
159b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mFgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
160b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int shadowOffset = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_shadow_offset);
161b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mFgPaint.setShadowLayer(mShadowRadius, shadowOffset, shadowOffset, shadowColor);
1626cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        mArrow = loadArrow();
163b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mArrowRect = new Rect(0, 0, mArrow.getWidth(), mArrow.getHeight());
164b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mArrowToBgRatio = (float) mArrow.getWidth() / (float) mArrowDiameter;
165b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        // Initialize animations.
1666a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mShowAnimator = new AnimatorSet();
1676a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mShowAnimator.playTogether(createDotAlphaAnimator(0.0f, 1.0f),
1686a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                createDotDiameterAnimator(mDotRadius * 2, mArrowRadius * 2),
1696a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                createDotTranslationXAnimator());
1706a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mHideAnimator = new AnimatorSet();
1716a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mHideAnimator.playTogether(createDotAlphaAnimator(1.0f, 0.0f),
1726a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                createDotDiameterAnimator(mArrowRadius * 2, mDotRadius * 2),
1736a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                createDotTranslationXAnimator());
1746a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        mAnimator.playTogether(mShowAnimator, mHideAnimator);
175b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        // Use software layer to show shadows.
176b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
1776a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    }
1786a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
1796a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private int getDimensionFromTypedArray(TypedArray typedArray, int attr, int defaultId) {
1806a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        return typedArray.getDimensionPixelOffset(attr,
1816a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                getResources().getDimensionPixelOffset(defaultId));
1826a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    }
1831dd331211beded89ec02c37d68afced18d329375Chulwoo Lee
1846a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private int getColorFromTypedArray(TypedArray typedArray, int attr, int defaultId) {
1856a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        return typedArray.getColor(attr, getResources().getColor(defaultId));
186b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
187b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
1886cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    private Bitmap loadArrow() {
1896cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        Bitmap arrow = BitmapFactory.decodeResource(getResources(), R.drawable.lb_ic_nav_arrow);
1906cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        if (mIsLtr) {
1916cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            return arrow;
1926cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        } else {
1936cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            Matrix matrix = new Matrix();
1946cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            matrix.preScale(-1, 1);
1956cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            return Bitmap.createBitmap(arrow, 0, 0, arrow.getWidth(), arrow.getHeight(), matrix,
1966cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                    false);
1976cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        }
1986cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    }
1996cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee
2006a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private Animator createDotAlphaAnimator(float from, float to) {
2016a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_ALPHA, from, to);
2026a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        animator.setDuration(DURATION_ALPHA);
2036a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        animator.setInterpolator(DECELERATE_INTERPOLATOR);
2046a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        return animator;
2056a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    }
2066a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
2076a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private Animator createDotDiameterAnimator(float from, float to) {
2086a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_DIAMETER, from, to);
2096a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        animator.setDuration(DURATION_DIAMETER);
2106a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        animator.setInterpolator(DECELERATE_INTERPOLATOR);
2116a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        return animator;
2126a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    }
2136a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
2146a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    private Animator createDotTranslationXAnimator() {
2156a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        // The direction is determined in the Dot.
2166a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_TRANSLATION_X,
2176a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee                -mArrowGap + mDotGap, 0.0f);
2186a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        animator.setDuration(DURATION_TRANSLATION_X);
2196a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        animator.setInterpolator(DECELERATE_INTERPOLATOR);
2206a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee        return animator;
2216a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee    }
2226a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee
223b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    /**
224b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee     * Sets the page count.
225b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee     */
226b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    public void setPageCount(int pages) {
227b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        if (pages <= 0) {
228b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            throw new IllegalArgumentException("The page count should be a positive integer");
229b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
230b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mPageCount = pages;
231b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mDots = new Dot[mPageCount];
232b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        for (int i = 0; i < mPageCount; ++i) {
233b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mDots[i] = new Dot();
234b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
235b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        calculateDotPositions();
236b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        setSelectedPage(0);
237b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
238b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
239b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    /**
240b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee     * Called when the page has been selected.
241b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee     */
242b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    public void onPageSelected(int pageIndex, boolean withAnimation) {
243b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        if (mCurrentPage == pageIndex) {
244b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            return;
245b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
246b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        if (mAnimator.isStarted()) {
247b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mAnimator.end();
248b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
249b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mPreviousPage = mCurrentPage;
250b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        if (withAnimation) {
251b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mHideAnimator.setTarget(mDots[mPreviousPage]);
252b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mShowAnimator.setTarget(mDots[pageIndex]);
253b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mAnimator.start();
254b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
255b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        setSelectedPage(pageIndex);
256b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
257b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
258b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private void calculateDotPositions() {
259b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int left = getPaddingLeft();
260b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int top = getPaddingTop();
261b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int right = getWidth() - getPaddingRight();
262b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int requiredWidth = getRequiredWidth();
263b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int mid = (left + right) / 2;
264b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mDotSelectedX = new int[mPageCount];
2656cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        mDotSelectedPrevX = new int[mPageCount];
2666cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        mDotSelectedNextX = new int[mPageCount];
2676cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        if (mIsLtr) {
2686cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            int startLeft = mid - requiredWidth / 2;
2696cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            // mDotSelectedX[0] should be mDotSelectedPrevX[-1] + mArrowGap
2706cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mDotSelectedX[0] = startLeft + mDotRadius - mDotGap + mArrowGap;
2716cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mDotSelectedPrevX[0] = startLeft + mDotRadius;
2726cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mDotSelectedNextX[0] = startLeft + mDotRadius - 2 * mDotGap + 2 * mArrowGap;
2736cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            for (int i = 1; i < mPageCount; i++) {
2746cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                mDotSelectedX[i] = mDotSelectedPrevX[i - 1] + mArrowGap;
2756cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                mDotSelectedPrevX[i] = mDotSelectedPrevX[i - 1] + mDotGap;
2766cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                mDotSelectedNextX[i] = mDotSelectedX[i - 1] + mArrowGap;
2776cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            }
2786cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        } else {
2796cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            int startRight = mid + requiredWidth / 2;
2806cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            // mDotSelectedX[0] should be mDotSelectedPrevX[-1] - mArrowGap
2816cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mDotSelectedX[0] = startRight - mDotRadius + mDotGap - mArrowGap;
2826cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mDotSelectedPrevX[0] = startRight - mDotRadius;
2836cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mDotSelectedNextX[0] = startRight - mDotRadius + 2 * mDotGap - 2 * mArrowGap;
2846cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            for (int i = 1; i < mPageCount; i++) {
2856cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                mDotSelectedX[i] = mDotSelectedPrevX[i - 1] - mArrowGap;
2866cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                mDotSelectedPrevX[i] = mDotSelectedPrevX[i - 1] - mDotGap;
2876cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                mDotSelectedNextX[i] = mDotSelectedX[i - 1] - mArrowGap;
2886cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            }
289b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
290b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mDotCenterY = top + mArrowRadius;
291b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        adjustDotPosition();
292b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
293b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
294b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    @VisibleForTesting
295b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    int getPageCount() {
296b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        return mPageCount;
297b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
298b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
299b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    @VisibleForTesting
300b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    int[] getDotSelectedX() {
301b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        return mDotSelectedX;
302b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
303b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
304b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    @VisibleForTesting
305b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    int[] getDotSelectedLeftX() {
3066cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        return mDotSelectedPrevX;
307b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
308b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
309b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    @VisibleForTesting
310b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    int[] getDotSelectedRightX() {
3116cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        return mDotSelectedNextX;
312b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
313b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
314b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    @Override
315b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
316b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int desiredHeight = getDesiredHeight();
317b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int height;
318b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        switch (MeasureSpec.getMode(heightMeasureSpec)) {
319b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            case MeasureSpec.EXACTLY:
320b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                height = MeasureSpec.getSize(heightMeasureSpec);
321b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                break;
322b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            case MeasureSpec.AT_MOST:
323b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                height = Math.min(desiredHeight, MeasureSpec.getSize(heightMeasureSpec));
324b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                break;
325b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            case MeasureSpec.UNSPECIFIED:
326b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            default:
327b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                height = desiredHeight;
328b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                break;
329b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
330b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int desiredWidth = getDesiredWidth();
331b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int width;
332b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        switch (MeasureSpec.getMode(widthMeasureSpec)) {
333b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            case MeasureSpec.EXACTLY:
334b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                width = MeasureSpec.getSize(widthMeasureSpec);
335b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                break;
336b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            case MeasureSpec.AT_MOST:
337b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                width = Math.min(desiredWidth, MeasureSpec.getSize(widthMeasureSpec));
338b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                break;
339b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            case MeasureSpec.UNSPECIFIED:
340b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            default:
341b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                width = desiredWidth;
342b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                break;
343b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
344b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        setMeasuredDimension(width, height);
345b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
346b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
347b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    @Override
348b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
349b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        setMeasuredDimension(width, height);
350b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        calculateDotPositions();
351b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
352b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
353b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private int getDesiredHeight() {
354b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        return getPaddingTop() + mArrowDiameter + getPaddingBottom() + mShadowRadius;
355b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
356b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
357b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private int getRequiredWidth() {
358b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        return 2 * mDotRadius + 2 * mArrowGap + (mPageCount - 3) * mDotGap;
359b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
360b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
361b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private int getDesiredWidth() {
362b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        return getPaddingLeft() + getRequiredWidth() + getPaddingRight();
363b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
364b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
365b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    @Override
366b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    protected void onDraw(Canvas canvas) {
367b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        for (int i = 0; i < mPageCount; ++i) {
368b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mDots[i].draw(canvas);
369b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
370b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
371b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
372b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private void setSelectedPage(int now) {
373b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        if (now == mCurrentPage) {
374b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            return;
375b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
376b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
377b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mCurrentPage = now;
378b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        adjustDotPosition();
379b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
380b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
381b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    private void adjustDotPosition() {
382b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        for (int i = 0; i < mCurrentPage; ++i) {
383b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mDots[i].deselect();
384b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mDots[i].mDirection = i == mPreviousPage ? Dot.LEFT : Dot.RIGHT;
3856cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mDots[i].mCenterX = mDotSelectedPrevX[i];
386b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
387b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mDots[mCurrentPage].select();
388b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mDots[mCurrentPage].mDirection = mPreviousPage < mCurrentPage ? Dot.LEFT : Dot.RIGHT;
389b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        mDots[mCurrentPage].mCenterX = mDotSelectedX[mCurrentPage];
390b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        for (int i = mCurrentPage + 1; i < mPageCount; ++i) {
391b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mDots[i].deselect();
392b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mDots[i].mDirection = Dot.RIGHT;
3936cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mDots[i].mCenterX = mDotSelectedNextX[i];
3946cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        }
3956cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    }
3966cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee
3976cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    @Override
3986cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee    public void onRtlPropertiesChanged(int layoutDirection) {
3996cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        super.onRtlPropertiesChanged(layoutDirection);
4006cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        boolean isLtr = layoutDirection == View.LAYOUT_DIRECTION_LTR;
4016cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        if (mIsLtr != isLtr) {
4026cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mIsLtr = isLtr;
4036cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mArrow = loadArrow();
4046cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            if (mDots != null) {
4056cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                for (Dot dot : mDots) {
4066cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                    dot.onRtlPropertiesChanged();
4076cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee                }
4086cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            }
4096cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            calculateDotPositions();
4106cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            invalidate();
411b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
412b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
413b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
414b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    public class Dot {
415b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        static final float LEFT = -1;
416b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        static final float RIGHT = 1;
4176cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        static final float LTR = 1;
4186cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        static final float RTL = -1;
419b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
420b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        float mAlpha;
421b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        @ColorInt
422b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        int mFgColor;
423b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        float mTranslationX;
424b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        float mCenterX;
425b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        float mDiameter;
426b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        float mRadius;
427b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        float mArrowImageRadius;
428b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        float mDirection = RIGHT;
4296cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        float mLayoutDirection = mIsLtr ? LTR : RTL;
430b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
431b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        void select() {
432b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mTranslationX = 0.0f;
433b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mCenterX = 0.0f;
434b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mDiameter = mArrowDiameter;
435b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mRadius = mArrowRadius;
436b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mArrowImageRadius = mRadius * mArrowToBgRatio;
437b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mAlpha = 1.0f;
438b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            adjustAlpha();
439b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
440b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
441b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        void deselect() {
442b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mTranslationX = 0.0f;
443b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mCenterX = 0.0f;
444b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mDiameter = mDotDiameter;
445b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mRadius = mDotRadius;
446b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mArrowImageRadius = mRadius * mArrowToBgRatio;
447b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mAlpha = 0.0f;
448b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            adjustAlpha();
449b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
450b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
451b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        public void adjustAlpha() {
452b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            int alpha = Math.round(0xFF * mAlpha);
453b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            int red = Color.red(mDotFgSelectColor);
454b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            int green = Color.green(mDotFgSelectColor);
455b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            int blue = Color.blue(mDotFgSelectColor);
456b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            mFgColor = Color.argb(alpha, red, green, blue);
457b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
458b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
459b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        public float getAlpha() {
460b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            return mAlpha;
461b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
462b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
463b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        public void setAlpha(float alpha) {
464b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            this.mAlpha = alpha;
465b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            adjustAlpha();
466b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            invalidate();
467b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
468b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
469b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        public float getTranslationX() {
470b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            return mTranslationX;
471b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
472b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
473b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        public void setTranslationX(float translationX) {
4746cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            this.mTranslationX = translationX * mDirection * mLayoutDirection;
475b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            invalidate();
476b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
477b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
478b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        public float getDiameter() {
479b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            return mDiameter;
480b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
481b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
482b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        public void setDiameter(float diameter) {
483b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            this.mDiameter = diameter;
484b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            this.mRadius = diameter / 2;
485b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            this.mArrowImageRadius = diameter / 2 * mArrowToBgRatio;
486b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            invalidate();
487b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
488b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee
489b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        void draw(Canvas canvas) {
490b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            float centerX = mCenterX + mTranslationX;
491b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            canvas.drawCircle(centerX, mDotCenterY, mRadius, mBgPaint);
492b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            if (mAlpha > 0) {
493b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                mFgPaint.setColor(mFgColor);
494b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                canvas.drawCircle(centerX, mDotCenterY, mRadius, mFgPaint);
495b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                canvas.drawBitmap(mArrow, mArrowRect, new Rect((int) (centerX - mArrowImageRadius),
496b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                        (int) (mDotCenterY - mArrowImageRadius),
497b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                        (int) (centerX + mArrowImageRadius),
498b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee                        (int) (mDotCenterY + mArrowImageRadius)), null);
499b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee            }
500b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee        }
5016cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee
5026cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        void onRtlPropertiesChanged() {
5036cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee            mLayoutDirection = mIsLtr ? LTR : RTL;
5046cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee        }
505b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee    }
506b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee}
507