NumberPicker.java revision 234484a9fb53786aa8b7ed2a3df7e24d41817689
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.Animator;
20206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.AnimatorListenerAdapter;
21206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.AnimatorSet;
22206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.ObjectAnimator;
2368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookimport android.annotation.Widget;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
25206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.ColorStateList;
26206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.TypedArray;
27206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Canvas;
28206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Color;
29206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Paint;
30e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.graphics.Paint.Align;
31b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.graphics.Rect;
329cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.graphics.drawable.Drawable;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputFilter;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputType;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.Spanned;
36206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.text.TextUtils;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.NumberKeyListener;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
39206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.util.SparseArray;
409cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.util.TypedValue;
41206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.KeyEvent;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.LayoutInflater;
43b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.view.LayoutInflater.Filter;
44206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.MotionEvent;
45206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.VelocityTracker;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
47206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.ViewConfiguration;
483fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
493fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityManager;
509cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.view.animation.DecelerateInterpolator;
51206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.inputmethod.InputMethodManager;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
53b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport com.android.internal.R;
54b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
5568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook/**
564bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * A widget that enables the user to select a number form a predefined range.
574bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * The widget presents an input filed and up and down buttons for selecting the
584bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * current value. Pressing/long pressing the up and down buttons increments and
594bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * decrements the current value respectively. Touching the input filed shows a
604bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * scroll wheel, tapping on which while shown and not moving allows direct edit
614bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * of the current value. Sliding motions up or down hide the buttons and the
624bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * input filed, show the scroll wheel, and rotate the latter. Flinging is
634bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * also supported. The widget enables mapping from positions to strings such
644bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * that instead the position index the corresponding string is displayed.
654bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * <p>
664bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * For an example of using this widget, see {@link android.widget.TimePicker}.
674bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * </p>
6868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook */
6968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook@Widget
7068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpublic class NumberPicker extends LinearLayout {
71fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
7268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
73e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default update interval during long press.
74e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
75e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300;
76e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
77e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
78206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The index of the middle selector item.
7968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
80206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MIDDLE_ITEM_INDEX = 2;
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
83206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The coefficient by which to adjust (divide) the max fling velocity.
8468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
85206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8;
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
87206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
88206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the duration for adjusting the selector wheel.
89206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
90206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
91fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
92206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
93b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The duration of scrolling to the next/previous value while changing
94b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * the current value by one, i.e. increment or decrement.
95b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
96b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int CHANGE_CURRENT_BY_ONE_SCROLL_DURATION = 300;
97b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
98b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
99206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the delay for showing the input controls after a single tap on the
100206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * input text.
101206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
102206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SHOW_INPUT_CONTROLS_DELAY_MILLIS = ViewConfiguration
103206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            .getDoubleTapTimeout();
104206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
105206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
106b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The strength of fading in the top and bottom while drawing the selector.
107206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
108b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f;
109206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
111b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The default unscaled height of the selection divider.
112206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
113b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2;
114206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
116b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * In this state the selector wheel is not shown.
117206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
118b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int SELECTOR_WHEEL_STATE_NONE = 0;
119206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
120206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
121b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * In this state the selector wheel is small.
122b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
123b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int SELECTOR_WHEEL_STATE_SMALL = 1;
124b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
125b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
126b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * In this state the selector wheel is large.
127b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
128b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int SELECTOR_WHEEL_STATE_LARGE = 2;
129b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
130b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
131b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The alpha of the selector wheel when it is bright.
1329cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
133b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int SELECTOR_WHEEL_BRIGHT_ALPHA = 255;
134b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
135b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
136b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The alpha of the selector wheel when it is dimmed.
137b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
138b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int SELECTOR_WHEEL_DIM_ALPHA = 60;
139b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
140b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
141b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The alpha for the increment/decrement button when it is transparent.
142b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
143b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int BUTTON_ALPHA_TRANSPARENT = 0;
144b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
145b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
146b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The alpha for the increment/decrement button when it is opaque.
147b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
148b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int BUTTON_ALPHA_OPAQUE = 1;
149b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
150b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
151b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The property for setting the selector paint.
152b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
153b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final String PROPERTY_SELECTOR_PAINT_ALPHA = "selectorPaintAlpha";
154b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
155b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
156b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The property for setting the increment/decrement button alpha.
157b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
158b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final String PROPERTY_BUTTON_ALPHA = "alpha";
1599cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1609cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
161206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The numbers accepted by the input text's {@link Filter}
162206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
163206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final char[] DIGIT_CHARACTERS = new char[] {
164206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
165206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    };
166206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
167206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1689f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * Constant for unspecified size.
1699f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     */
1709f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    private static final int SIZE_UNSPECIFIED = -1;
1719f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
1729f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    /**
173206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Use a custom NumberPicker formatting callback to use two-digit minutes
174206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * strings like "01". Keeping a static formatter etc. is the most efficient
175206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * way to do this; it avoids creating temporary objects on every call to
176206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * format().
177e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
178e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @hide
179206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
180206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER = new NumberPicker.Formatter() {
181206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final StringBuilder mBuilder = new StringBuilder();
182206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
183f0ef665299d795df7905897e1c337e37891dafefJean-Baptiste Queru        final java.util.Formatter mFmt = new java.util.Formatter(mBuilder, java.util.Locale.US);
184206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
185206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final Object[] mArgs = new Object[1];
186206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
187e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value) {
188206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mArgs[0] = value;
189206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mBuilder.delete(0, mBuilder.length());
190206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mFmt.format("%02d", mArgs);
191206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return mFmt.toString();
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
195206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
196206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The increment button.
197206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
198206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mIncrementButton;
199206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
200206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The decrement button.
202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
203206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mDecrementButton;
204206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
205206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
206206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The text for showing the current value.
207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
208206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final EditText mInputText;
209206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
210206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
211ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * The min height of this widget.
212ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
213ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final int mMinHeight;
214ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
215ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
216e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * The max height of this widget.
217e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
218e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    private final int mMaxHeight;
219e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
220e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
221e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * The max width of this widget.
222e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
223ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final int mMinWidth;
224ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
225ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
226ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * The max width of this widget.
227ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
228ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private int mMaxWidth;
229ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
230ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
231ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Flag whether to compute the max width.
232ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
233ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final boolean mComputeMaxWidth;
234e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
235e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
236206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of the text.
237206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
238206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mTextSize;
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
240206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
241b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The height of the gap between text elements if the selector wheel.
242b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
243b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private int mSelectorTextGapHeight;
244b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
245b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
246206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The values to be displayed instead the indices.
247206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String[] mDisplayedValues;
24968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
25068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
25168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Lower value of the range of numbers allowed for the NumberPicker
25268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
253e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMinValue;
25468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
25568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
25668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Upper value of the range of numbers allowed for the NumberPicker
25768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
258e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMaxValue;
25968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
26068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
26168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Current value of this NumberPicker
26268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
263e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mValue;
26468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
26568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
266206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Listener to be notified upon current value change.
26768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
268cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    private OnValueChangeListener mOnValueChangeListener;
26950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
27050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
27150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Listener to be notified upon scroll state change.
27250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
27350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private OnScrollListener mOnScrollListener;
274206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
275206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
276206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Formatter for for displaying the current value.
277206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Formatter mFormatter;
279fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
280206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
281206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The speed for updating the value form long press.
282206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
283e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL;
284fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
28568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
286206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Cache for the string representation of selector indices.
287206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
288206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final SparseArray<String> mSelectorIndexToStringCache = new SparseArray<String>();
289206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
290206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The selector indices whose value are show by the selector.
292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
293206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int[] mSelectorIndices = new int[] {
294206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE,
295206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            Integer.MIN_VALUE
296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    };
297206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
298206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Paint} for drawing the selector.
300206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
301b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private final Paint mSelectorWheelPaint;
302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
303206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
304206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of a selector element (text + gap).
305206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mSelectorElementHeight;
307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
308206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The initial offset of the scroll selector.
310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
311206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mInitialScrollOffset = Integer.MIN_VALUE;
312206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
313206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
314206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The current offset of the scroll selector.
315206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mCurrentScrollOffset;
317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
318206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for flinging the selector.
320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mFlingScroller;
322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for adjusting the selector.
325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
326206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mAdjustScroller;
327206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
328206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
329206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The previous Y coordinate while scrolling the selector.
330206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
331206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mPreviousScrollerY;
332206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
333206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
334206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for setting the input text selection.
335206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
336206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private SetSelectionCommand mSetSelectionCommand;
337206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
339206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for adjusting the scroller.
340206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
341206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private AdjustScrollerCommand mAdjustScrollerCommand;
342206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
343206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
344b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Handle to the reusable command for changing the current value from long
345b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * press by one.
346206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
347b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand;
348206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
349206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * {@link Animator} for showing the up/down arrows.
351206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
352206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final AnimatorSet mShowInputControlsAnimator;
353206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
354206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
355b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * {@link Animator} for dimming the selector wheel.
356b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
357b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private final Animator mDimSelectorWheelAnimator;
358b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
359b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
360206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last down event.
361206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
362206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastDownEventY;
363206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
364206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
365206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last motion event.
366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
367206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastMotionEventY;
368206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
369206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
370206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag if to begin edit on next up event.
371206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
372206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mBeginEditOnUpEvent;
373206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
374206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
375206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag if to adjust the selector wheel on next up event.
376206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
377206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mAdjustScrollerOnUpEvent;
378206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
379206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
380b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The state of the selector wheel.
381206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
382b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private int mSelectorWheelState;
383206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
384206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
385206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Determines speed during touch scrolling.
386206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
387206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private VelocityTracker mVelocityTracker;
388206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
389206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
390206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledTouchSlop()
391206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
392206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mTouchSlop;
393206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
394206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
395206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMinimumFlingVelocity()
396206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
397206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMinimumFlingVelocity;
398206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
399206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
400206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMaximumFlingVelocity()
401206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
402206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMaximumFlingVelocity;
403206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
404206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
405206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag whether the selector should wrap around.
406206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
407e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean mWrapSelectorWheel;
408206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
409206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
410206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The back ground color used to optimize scroller fading.
41168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
412206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mSolidColor;
413206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
414206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
4154243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov     * Flag indicating if this widget supports flinging.
4164243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov     */
4174243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov    private final boolean mFlingable;
4184243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov
4194243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov    /**
4209cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Divider for showing item to be selected while scrolling
4219cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
4229cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final Drawable mSelectionDivider;
4239cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4249cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
4259cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * The height of the selection divider.
4269cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
4279cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final int mSelectionDividerHeight;
4289cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4299cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
430206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Reusable {@link Rect} instance.
431206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
432206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Rect mTempRect = new Rect();
433206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
434206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
43550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The current scroll state of the number picker.
43650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
43750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
43850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
43950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
4409cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * The duration of the animation for showing the input controls.
4419cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
4429cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final long mShowInputControlsAnimimationDuration;
4439cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4449cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
445b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Flag whether the scoll wheel and the fading edges have been initialized.
446b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
447b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private boolean mScrollWheelAndFadingEdgesInitialized;
448b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
449b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
450e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for changes of the current value.
451206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
452cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public interface OnValueChangeListener {
453e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
454206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /**
4554bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * Called upon a change of the current value.
4564bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
457206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param picker The NumberPicker associated with this listener.
458206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param oldVal The previous value.
459206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param newVal The new value.
460206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
461e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        void onValueChange(NumberPicker picker, int oldVal, int newVal);
462206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
463206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
464206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
465e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for the picker scroll state.
46650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
46750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public interface OnScrollListener {
46850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
46950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
47050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The view is not scrolling.
47150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
47250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_IDLE = 0;
47350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
47450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
47550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user is scrolling using touch, and their finger is still on the screen.
47650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
47750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_TOUCH_SCROLL = 1;
47850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
47950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
48050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user had previously been scrolling using touch and performed a fling.
48150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
48250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_FLING = 2;
48350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
48450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
485e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Callback invoked while the number picker scroll state has changed.
48650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
487e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param view The view whose scroll state is being reported.
488e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param scrollState The current scroll state. One of
489e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE},
490e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_TOUCH_SCROLL} or
491e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE}.
49250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
4934bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        public void onScrollStateChange(NumberPicker view, int scrollState);
49450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
49550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
49650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
497e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface used to format current value into a string for presentation.
498206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
499206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public interface Formatter {
5004bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
5014bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        /**
502e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Formats a string representation of the current value.
5034bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
5044bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @param value The currently selected value.
5054bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @return A formatted string representation.
5064bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         */
507e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value);
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
509fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
51068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
5114bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
5124bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
5134bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param context The application environment.
5144bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
5154bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public NumberPicker(Context context) {
5164bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        this(context, null);
5174bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
5184bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
5194bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
5204bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
521206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
522206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context The application environment.
523206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs A collection of attributes.
52468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public NumberPicker(Context context, AttributeSet attrs) {
526206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        this(context, attrs, R.attr.numberPickerStyle);
527206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
528206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
529206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
530206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Create a new number picker
531206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
532206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context the application environment.
533206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs a collection of attributes.
534206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param defStyle The default style to apply to this view.
535206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
536206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
537206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super(context, attrs, defStyle);
538206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
539206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // process style attributes
540206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        TypedArray attributesArray = context.obtainStyledAttributes(attrs,
541206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                R.styleable.NumberPicker, defStyle, 0);
542206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
5434243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov        mFlingable = attributesArray.getBoolean(R.styleable.NumberPicker_flingable, true);
5449cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
5459cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        int defSelectionDividerHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
5469cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
5479cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                getResources().getDisplayMetrics());
5489cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
5499cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
5509f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        mMinHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minHeight,
5519f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                SIZE_UNSPECIFIED);
552ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mMaxHeight = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxHeight,
5539f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                SIZE_UNSPECIFIED);
5549f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED
5559f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                && mMinHeight > mMaxHeight) {
556ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            throw new IllegalArgumentException("minHeight > maxHeight");
557ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
5589f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        mMinWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_minWidth,
5599f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                SIZE_UNSPECIFIED);
560ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mMaxWidth = attributesArray.getDimensionPixelSize(R.styleable.NumberPicker_maxWidth,
5619f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                SIZE_UNSPECIFIED);
5629f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED
5639f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                && mMinWidth > mMaxWidth) {
564ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            throw new IllegalArgumentException("minWidth > maxWidth");
565ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
566ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mComputeMaxWidth = (mMaxWidth == Integer.MAX_VALUE);
567206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        attributesArray.recycle();
568206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
5699cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mShowInputControlsAnimimationDuration = getResources().getInteger(
5709cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                R.integer.config_longAnimTime);
5719cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
572206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // By default Linearlayout that we extend is not drawn. This is
573206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // its draw() method is not called but dispatchDraw() is called
574206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // directly (see ViewGroup.drawChild()). However, this class uses
575206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // the fading edge effect implemented by View and we need our
576206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // draw() method to be called. Therefore, we declare we will draw.
577206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setWillNotDraw(false);
578b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setSelectorWheelState(SELECTOR_WHEEL_STATE_NONE);
579206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
580206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
581206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                Context.LAYOUT_INFLATER_SERVICE);
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        inflater.inflate(R.layout.number_picker, this, true);
58368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
584206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnClickListener onClickListener = new OnClickListener() {
58568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            public void onClick(View v) {
5866304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
5876304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
5886304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                    inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
5896304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                }
590206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
591206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
592b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    changeCurrentByOne(true);
593206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
594b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    changeCurrentByOne(false);
59568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
59668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
59768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        };
59868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
599206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnLongClickListener onLongClickListener = new OnLongClickListener() {
600206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public boolean onLongClick(View v) {
601206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
602206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
603b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(true);
604206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
605b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(false);
606206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
607206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return true;
608206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
609206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        };
61068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
611206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // increment button
612206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton = (ImageButton) findViewById(R.id.increment);
613206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setOnClickListener(onClickListener);
614206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setOnLongClickListener(onLongClickListener);
615206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
616206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // decrement button
617206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton = (ImageButton) findViewById(R.id.decrement);
618206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setOnClickListener(onClickListener);
619206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setOnLongClickListener(onLongClickListener);
620206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
621206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // input text
622012dd5a461f18a2e5dad38c60282fac1c21ff7feSvetoslav Ganov        mInputText = (EditText) findViewById(R.id.numberpicker_input);
623206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setOnFocusChangeListener(new OnFocusChangeListener() {
624206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onFocusChange(View v, boolean hasFocus) {
625a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                if (hasFocus) {
626a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.selectAll();
6276304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                    InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
628b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    if (inputMethodManager != null) {
629b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                        inputMethodManager.showSoftInput(mInputText, 0);
630b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    }
631a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                } else {
632a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.setSelection(0, 0);
633206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    validateInputTextView(v);
63468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
63568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
636206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
637206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setFilters(new InputFilter[] {
638206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            new InputTextFilter()
639206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
64068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
641206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
642206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
643206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // initialize constants
644206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = ViewConfiguration.getTapTimeout();
645206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ViewConfiguration configuration = ViewConfiguration.get(context);
646206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = configuration.getScaledTouchSlop();
647206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
648206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity()
649206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;
650206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTextSize = (int) mInputText.getTextSize();
651206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the selector wheel paint
653206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Paint paint = new Paint();
654206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setAntiAlias(true);
655206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextAlign(Align.CENTER);
656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextSize(mTextSize);
657206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTypeface(mInputText.getTypeface());
658206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ColorStateList colors = mInputText.getTextColors();
659206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int color = colors.getColorForState(ENABLED_STATE_SET, Color.WHITE);
660206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setColor(color);
661b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorWheelPaint = paint;
662206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
663206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the animator for showing the input controls
664b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mDimSelectorWheelAnimator = ObjectAnimator.ofInt(this, PROPERTY_SELECTOR_PAINT_ALPHA,
665b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                SELECTOR_WHEEL_BRIGHT_ALPHA, SELECTOR_WHEEL_DIM_ALPHA);
666206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final ObjectAnimator showIncrementButton = ObjectAnimator.ofFloat(mIncrementButton,
667b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                PROPERTY_BUTTON_ALPHA, BUTTON_ALPHA_TRANSPARENT, BUTTON_ALPHA_OPAQUE);
668206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final ObjectAnimator showDecrementButton = ObjectAnimator.ofFloat(mDecrementButton,
669b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                PROPERTY_BUTTON_ALPHA, BUTTON_ALPHA_TRANSPARENT, BUTTON_ALPHA_OPAQUE);
670206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator = new AnimatorSet();
671b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mShowInputControlsAnimator.playTogether(mDimSelectorWheelAnimator, showIncrementButton,
6725203194fdff0c0aaefc405d65871ef0e06e7c682Svetoslav Ganov                showDecrementButton);
673206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.addListener(new AnimatorListenerAdapter() {
674206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            private boolean mCanceled = false;
675206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
676206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            @Override
677206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onAnimationEnd(Animator animation) {
678206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (!mCanceled) {
679206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    // if canceled => we still want the wheel drawn
680b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL);
68168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
682206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCanceled = false;
68368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
68468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
685206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            @Override
686206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onAnimationCancel(Animator animation) {
687206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mShowInputControlsAnimator.isRunning()) {
688206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mCanceled = true;
689206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
690206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
691206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
69268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
693206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the fling and adjust scrollers
694bf80562d22b2bbe7944d80d0524c69d0238010cbSvetoslav Ganov        mFlingScroller = new Scroller(getContext(), null, true);
6959cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
696fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
697206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateInputTextView();
698206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
6999cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
700b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mFlingable) {
701b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov           if (isInEditMode()) {
702b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov               setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL);
703b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov           } else {
704b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                // Start with shown selector wheel and hidden controls. When made
705b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                // visible hide the selector and fade-in the controls to suggest
706b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                // fling interaction.
707b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE);
708b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                hideInputControls();
709b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov           }
7109cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
711206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
712206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
713206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
7149cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
715ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int msrdWdth = getMeasuredWidth();
716ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int msrdHght = getMeasuredHeight();
717ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
718ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        // Increment button at the top.
719ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inctBtnMsrdWdth = mIncrementButton.getMeasuredWidth();
720ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int incrBtnLeft = (msrdWdth - inctBtnMsrdWdth) / 2;
721ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int incrBtnTop = 0;
722ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int incrBtnRight = incrBtnLeft + inctBtnMsrdWdth;
723ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int incrBtnBottom = incrBtnTop + mIncrementButton.getMeasuredHeight();
724ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mIncrementButton.layout(incrBtnLeft, incrBtnTop, incrBtnRight, incrBtnBottom);
725ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
726ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        // Input text centered horizontally.
727ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
728ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
729ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
730ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
731ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
732ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
733ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
734ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
735ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        // Decrement button at the top.
736ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int decrBtnMsrdWdth = mIncrementButton.getMeasuredWidth();
737ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int decrBtnLeft = (msrdWdth - decrBtnMsrdWdth) / 2;
738ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int decrBtnTop = msrdHght - mDecrementButton.getMeasuredHeight();
739ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int decrBtnRight = decrBtnLeft + decrBtnMsrdWdth;
740ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int decrBtnBottom = msrdHght;
741ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mDecrementButton.layout(decrBtnLeft, decrBtnTop, decrBtnRight, decrBtnBottom);
742e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
743b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (!mScrollWheelAndFadingEdgesInitialized) {
744b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mScrollWheelAndFadingEdgesInitialized = true;
745b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            // need to do all this when we know our size
746b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            initializeSelectorWheel();
747b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            initializeFadingEdges();
748b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
749206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
750206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
751206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
752e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
753698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        // Try greedily to fit the max width and height.
754698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth);
755698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight);
756ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
757698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        // Flag if we are measured with width or height less than the respective min.
7589f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, getMeasuredWidth(),
7599f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                widthMeasureSpec);
7609f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, getMeasuredHeight(),
7619f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                heightMeasureSpec);
762698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        setMeasuredDimension(widthSize, heightSize);
763e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
764e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
765e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    @Override
766206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onInterceptTouchEvent(MotionEvent event) {
7674243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov        if (!isEnabled() || !mFlingable) {
76851c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
76951c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
770206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (event.getActionMasked()) {
771206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_DOWN:
772206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mLastMotionEventY = mLastDownEventY = event.getY();
773206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                removeAllCallbacks();
774092bda57c4b8caad378400b2bc97b9ac66fd2179Svetoslav Ganov                mShowInputControlsAnimator.cancel();
775b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                mDimSelectorWheelAnimator.cancel();
776206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mBeginEditOnUpEvent = false;
777206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mAdjustScrollerOnUpEvent = true;
778b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
779234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov                    mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA);
78050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    boolean scrollersFinished = mFlingScroller.isFinished()
781206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                            && mAdjustScroller.isFinished();
78250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    if (!scrollersFinished) {
78350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        mFlingScroller.forceFinished(true);
78450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        mAdjustScroller.forceFinished(true);
7859cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                        onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
78650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    }
78750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    mBeginEditOnUpEvent = scrollersFinished;
788206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mAdjustScrollerOnUpEvent = true;
789092bda57c4b8caad378400b2bc97b9ac66fd2179Svetoslav Ganov                    hideInputControls();
790206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
791206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
792e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov                if (isEventInVisibleViewHitRect(event, mIncrementButton)
793e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov                        || isEventInVisibleViewHitRect(event, mDecrementButton)) {
794e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov                    return false;
795206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
796e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov                mAdjustScrollerOnUpEvent = false;
797e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov                setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE);
798e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov                hideInputControls();
799e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov                return true;
800206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_MOVE:
801206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                float currentMoveY = event.getY();
802206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
803206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (deltaDownY > mTouchSlop) {
804206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mBeginEditOnUpEvent = false;
8059cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
806b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE);
807092bda57c4b8caad378400b2bc97b9ac66fd2179Svetoslav Ganov                    hideInputControls();
808206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
809206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
810206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
811206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
812206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return false;
813206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
814206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
815206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
816206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onTouchEvent(MotionEvent ev) {
81751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        if (!isEnabled()) {
81851c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
81951c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
820206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mVelocityTracker == null) {
821206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mVelocityTracker = VelocityTracker.obtain();
822206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
823206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mVelocityTracker.addMovement(ev);
824206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int action = ev.getActionMasked();
825206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (action) {
826206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_MOVE:
827206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                float currentMoveY = ev.getY();
82850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                if (mBeginEditOnUpEvent
82950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
830206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
831206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (deltaDownY > mTouchSlop) {
832206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        mBeginEditOnUpEvent = false;
8339cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                        onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
834206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
835206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
836206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int deltaMoveY = (int) (currentMoveY - mLastMotionEventY);
837206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollBy(0, deltaMoveY);
838206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                invalidate();
839206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mLastMotionEventY = currentMoveY;
840206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
841206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_UP:
842206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mBeginEditOnUpEvent) {
843b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL);
8449cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    showInputControls(mShowInputControlsAnimimationDuration);
845206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mInputText.requestFocus();
846206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
847206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
848206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                VelocityTracker velocityTracker = mVelocityTracker;
849206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
850206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int initialVelocity = (int) velocityTracker.getYVelocity();
851206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
852206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    fling(initialVelocity);
8539cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
854206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
855206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (mAdjustScrollerOnUpEvent) {
856206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        if (mFlingScroller.isFinished() && mAdjustScroller.isFinished()) {
857206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                            postAdjustScrollerCommand(0);
858206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        }
859206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    } else {
860206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postAdjustScrollerCommand(SHOW_INPUT_CONTROLS_DELAY_MILLIS);
861206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
862206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
863206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker.recycle();
864206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker = null;
865206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
866206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
867206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return true;
868206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
869206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
870206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
871206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTouchEvent(MotionEvent event) {
872b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        final int action = event.getActionMasked();
873b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        switch (action) {
874b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_MOVE:
875b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
876b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    removeAllCallbacks();
877b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                    forceCompleteChangeCurrentByOneViaScroll();
878b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                }
879b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                break;
880b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_CANCEL:
881b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_UP:
882b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                removeAllCallbacks();
883b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                break;
884206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
885206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTouchEvent(event);
886206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
887206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
888206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
889206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchKeyEvent(KeyEvent event) {
890206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int keyCode = event.getKeyCode();
891206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
892206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
893206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
894206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchKeyEvent(event);
895206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
896206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
897206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
898206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTrackballEvent(MotionEvent event) {
899206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int action = event.getActionMasked();
900206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
901206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
902206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
903206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTrackballEvent(event);
904206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
906206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
907206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void computeScroll() {
908b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) {
909206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
910206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
911206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Scroller scroller = mFlingScroller;
912206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
913206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scroller = mAdjustScroller;
914206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (scroller.isFinished()) {
915206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return;
916206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
917206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
918206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scroller.computeScrollOffset();
919206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int currentScrollerY = scroller.getCurrY();
920206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mPreviousScrollerY == 0) {
921206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = scroller.getStartY();
922206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
923206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scrollBy(0, currentScrollerY - mPreviousScrollerY);
924206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = currentScrollerY;
925206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
926206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            onScrollerFinished(scroller);
927206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
928206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
931fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setEnabled(boolean enabled) {
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.setEnabled(enabled);
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIncrementButton.setEnabled(enabled);
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDecrementButton.setEnabled(enabled);
937206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setEnabled(enabled);
938206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
939206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
940206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
941206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void scrollBy(int x, int y) {
942b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) {
943b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            return;
944b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
945b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
94634c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y > 0
94734c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
948206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
949206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
950206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
95134c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y < 0
95234c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
953206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
954206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
955206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
956206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mCurrentScrollOffset += y;
957b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) {
958206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset -= mSelectorElementHeight;
959206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            decrementSelectorIndices(selectorIndices);
960206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
961b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
962206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
963206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
964206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
965b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) {
966206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset += mSelectorElementHeight;
967b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            incrementSelectorIndices(selectorIndices);
968206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
969b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
970206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
971206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
972206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
974fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
9754bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    @Override
9764bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public int getSolidColor() {
9774bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return mSolidColor;
9784bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
9794bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
98068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
9814bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the listener to be notified on change of the current value.
982206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
983e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onValueChangedListener The listener.
98468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
985cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public void setOnValueChangedListener(OnValueChangeListener onValueChangedListener) {
986cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        mOnValueChangeListener = onValueChangedListener;
98750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
98850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
98950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
9904bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set listener to be notified for scroll state changes.
99150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
992e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onScrollListener The listener.
99350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
99450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public void setOnScrollListener(OnScrollListener onScrollListener) {
99550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mOnScrollListener = onScrollListener;
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
997fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
99868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
9994bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set the formatter to be used for formatting the current value.
10004bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
1001e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Note: If you have provided alternative values for the values this
1002e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * formatter is never invoked.
10034bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
1004206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1005e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param formatter The formatter object. If formatter is <code>null</code>,
1006e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            {@link String#valueOf(int)} will be used.
10074bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1008e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setDisplayedValues(String[])
100968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFormatter(Formatter formatter) {
1011e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (formatter == mFormatter) {
1012e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1013e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFormatter = formatter;
1015b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
10169cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
1017206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1018206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1019206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1020e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Set the current value for the number picker.
10214bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
1022e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
1023e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
1024e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
1025e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1026e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1027e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
1028e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
1029e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
1030e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1031e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1032e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
1033e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
1034e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
1035e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1036e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1037e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
1038e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
1039e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
10404bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
1041206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1042e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param value The current value.
10434bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @see #setWrapSelectorWheel(boolean)
1044e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMinValue(int)
1045e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMaxValue(int)
1046206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1047e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setValue(int value) {
1048e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mValue == value) {
1049a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov            return;
1050a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
1051e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (value < mMinValue) {
1052e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            value = mWrapSelectorWheel ? mMaxValue : mMinValue;
1053206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1054e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (value > mMaxValue) {
1055e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            value = mWrapSelectorWheel ? mMinValue : mMaxValue;
10568bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powell        }
1057e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mValue = value;
1058b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1059e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1060e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
1061b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        invalidate();
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1063fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
106468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1065ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Computes the max width if no such specified as an attribute.
1066ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
1067ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private void tryComputeMaxWidth() {
1068ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (!mComputeMaxWidth) {
1069ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            return;
1070ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1071ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        int maxTextWidth = 0;
1072ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (mDisplayedValues == null) {
1073ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            float maxDigitWidth = 0;
1074ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            for (int i = 0; i <= 9; i++) {
1075ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                final float digitWidth = mSelectorWheelPaint.measureText(String.valueOf(i));
1076ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                if (digitWidth > maxDigitWidth) {
1077ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                    maxDigitWidth = digitWidth;
1078ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                }
1079ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1080ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            int numberOfDigits = 0;
1081ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            int current = mMaxValue;
1082ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            while (current > 0) {
1083ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                numberOfDigits++;
1084ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                current = current / 10;
1085ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1086ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            maxTextWidth = (int) (numberOfDigits * maxDigitWidth);
1087ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        } else {
1088ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            final int valueCount = mDisplayedValues.length;
1089ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            for (int i = 0; i < valueCount; i++) {
1090ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                final float textWidth = mSelectorWheelPaint.measureText(mDisplayedValues[i]);
1091ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                if (textWidth > maxTextWidth) {
1092ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                    maxTextWidth = (int) textWidth;
1093ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                }
1094ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1095ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1096ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        maxTextWidth += mInputText.getPaddingLeft() + mInputText.getPaddingRight();
1097ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (mMaxWidth != maxTextWidth) {
1098ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            if (maxTextWidth > mMinWidth) {
1099ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                mMaxWidth = maxTextWidth;
1100ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            } else {
1101ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                mMaxWidth = mMinWidth;
1102ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1103ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            invalidate();
1104ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1105ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    }
1106ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
1107ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
1108e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets whether the selector wheel wraps when reaching the min/max value.
110968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
1110e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return True if the selector wheel wraps.
11114bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1112e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMinValue()
1113e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMaxValue()
111468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1115e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public boolean getWrapSelectorWheel() {
1116e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mWrapSelectorWheel;
1117206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1118206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1119206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1120e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets whether the selector wheel shown during flinging/scrolling should
1121e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * wrap around the {@link NumberPicker#getMinValue()} and
1122e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getMaxValue()} values.
1123e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1124e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * By default if the range (max - min) is more than five (the number of
1125e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * items shown on the selector wheel) the selector wheel wrapping is
1126e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * enabled.
1127e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1128206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
11296304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov     * @param wrapSelectorWheel Whether to wrap.
1130206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
11316304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov    public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
11326304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov        if (wrapSelectorWheel && (mMaxValue - mMinValue) < mSelectorIndices.length) {
1133206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            throw new IllegalStateException("Range less than selector items count.");
1134206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
11356304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov        if (wrapSelectorWheel != mWrapSelectorWheel) {
11366304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            mWrapSelectorWheel = wrapSelectorWheel;
1137156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            updateIncrementAndDecrementButtonsVisibilityState();
1138206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
11424bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the speed at which the numbers be incremented and decremented when
11434bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * the up and down buttons are long pressed respectively.
1144e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1145e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default value is 300 ms.
1146e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
114768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
11484bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param intervalMillis The speed (in milliseconds) at which the numbers
1149e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            will be incremented and decremented.
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
11514bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setOnLongPressUpdateInterval(long intervalMillis) {
11524bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        mLongPressUpdateInterval = intervalMillis;
11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1154fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1155206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1156e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the value of the picker.
1157e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1158e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The value.
1159e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1160e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getValue() {
1161e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mValue;
1162e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1163e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1164e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1165e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the min value of the picker.
1166e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1167e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The min value
1168e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1169e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMinValue() {
1170e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
1171e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1172e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1173e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1174e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the min value of the picker.
1175206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1176e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param minValue The min value.
1177206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1178e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMinValue(int minValue) {
1179e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue == minValue) {
1180e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1181e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1182e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (minValue < 0) {
1183e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("minValue must be >= 0");
1184e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1185e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMinValue = minValue;
1186e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue > mValue) {
1187e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMinValue;
1188e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1189e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1190e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1191b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1192e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1193ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        tryComputeMaxWidth();
1194206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1195206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
11964bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
1197e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the max value of the picker.
11984bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1199e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The max value.
12004bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1201e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMaxValue() {
1202e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMaxValue;
12034bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
12044bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
12054bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
1206e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the max value of the picker.
12074bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1208e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param maxValue The max value.
12094bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1210e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMaxValue(int maxValue) {
1211e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue == maxValue) {
1212e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1213e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1214e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (maxValue < 0) {
1215e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("maxValue must be >= 0");
1216e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1217e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMaxValue = maxValue;
1218e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue < mValue) {
1219e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMaxValue;
1220e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1221e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1222e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1223b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1224e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1225ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        tryComputeMaxWidth();
1226e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1227e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1228e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1229e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the values to be displayed instead of string values.
1230e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1231e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The displayed values.
1232e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1233e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public String[] getDisplayedValues() {
1234e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mDisplayedValues;
1235e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1236e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1237e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1238e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the values to be displayed.
1239e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1240e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param displayedValues The displayed values.
1241e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1242e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setDisplayedValues(String[] displayedValues) {
1243e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues == displayedValues) {
1244e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1245e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1246e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mDisplayedValues = displayedValues;
1247e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues != null) {
1248e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // Allow text entry rather than strictly numeric entry.
1249e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
1250e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
1251e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else {
1252e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
1253e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1254e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1255b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
12569f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        tryComputeMaxWidth();
1257206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1258206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1259206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1260206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getTopFadingEdgeStrength() {
1261206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1262206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1263206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1264206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1265206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getBottomFadingEdgeStrength() {
1266206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1267206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1268206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1269206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
12709cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    protected void onAttachedToWindow() {
12719cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        super.onAttachedToWindow();
12729cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // make sure we show the controls only the very
12739cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // first time the user sees this widget
12744fd9dae6a23a22142f73749c291c9fd18459e381Svetoslav Ganov        if (mFlingable && !isInEditMode()) {
12759cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // animate a bit slower the very first time
12769cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            showInputControls(mShowInputControlsAnimimationDuration * 2);
12779cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
12789cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    }
12799cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
12809cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    @Override
1281206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDetachedFromWindow() {
1282206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
1283206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1284206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1285206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1286206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void dispatchDraw(Canvas canvas) {
1287206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // There is a good reason for doing this. See comments in draw().
1288206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1289206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1290206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void draw(Canvas canvas) {
1292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Dispatch draw to our children only if we are not currently running
1293b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        // the animation for simultaneously dimming the scroll wheel and
1294206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // showing in the buttons. This class takes advantage of the View
1295206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // implementation of fading edges effect to draw the selector wheel.
1296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // However, in View.draw(), the fading is applied after all the children
1297206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // have been drawn and we do not want this fading to be applied to the
1298b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        // buttons. Therefore, we draw our children after we have completed
1299b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        // drawing ourselves.
1300c75ec3324e9e3e6500148cc2978caab90ea962e4Chet Haase        super.draw(canvas);
1301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Draw our children if we are not showing the selector wheel of fading
1303206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // it out
1304b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mShowInputControlsAnimator.isRunning()
1305b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                || mSelectorWheelState != SELECTOR_WHEEL_STATE_LARGE) {
1306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            long drawTime = getDrawingTime();
1307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            for (int i = 0, count = getChildCount(); i < count; i++) {
1308206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                View child = getChildAt(i);
1309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (!child.isShown()) {
1310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    continue;
1311206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1312206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                drawChild(canvas, getChildAt(i), drawTime);
1313206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1314206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1315206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1318206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDraw(Canvas canvas) {
1319b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) {
1320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1322b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float x = (mRight - mLeft) / 2;
1324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float y = mCurrentScrollOffset;
1325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1326b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        final int restoreCount = canvas.save();
1327b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1328b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mSelectorWheelState == SELECTOR_WHEEL_STATE_SMALL) {
1329b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            Rect clipBounds = canvas.getClipBounds();
1330b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            clipBounds.inset(0, mSelectorElementHeight);
1331b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            canvas.clipRect(clipBounds);
1332b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
1333b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
13349cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // draw the selector wheel
1335b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
1336206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length; i++) {
1337206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int selectorIndex = selectorIndices[i];
1338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
13396304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            // Do not draw the middle item if input is visible since the input is shown only
13406304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            // if the wheel is static and it covers the middle item. Otherwise, if the user
13416304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            // starts editing the text via the IME he may see a dimmed version of the old
13426304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            // value intermixed with the new one.
13436304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) {
13446304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
13456304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            }
1346206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            y += mSelectorElementHeight;
1347206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
13489cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
13499cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // draw the selection dividers (only if scrolling and drawable specified)
13509cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mSelectionDivider != null) {
13519cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the top divider
13529cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int topOfTopDivider =
13539cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                (getHeight() - mSelectorElementHeight - mSelectionDividerHeight) / 2;
13549cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
13559cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfTopDivider, mRight, bottomOfTopDivider);
13569cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
13579cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
13589cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the bottom divider
13599cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int topOfBottomDivider =  topOfTopDivider + mSelectorElementHeight;
13609cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int bottomOfBottomDivider = bottomOfTopDivider + mSelectorElementHeight;
13619cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfBottomDivider, mRight, bottomOfBottomDivider);
13629cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
13639cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
1364b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1365b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        canvas.restoreToCount(restoreCount);
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1367fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
13683fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    @Override
13693fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    public void sendAccessibilityEvent(int eventType) {
13703fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        // Do not send accessibility events - we want the user to
13713fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        // perceive this widget as several controls rather as a whole.
13723fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    }
13733fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
137468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1375ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Makes a measure spec that tries greedily to use the max value.
1376e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     *
1377e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * @param measureSpec The measure spec.
1378698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov     * @param maxSize The max value for the size.
1379ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * @return A measure spec greedily imposing the max size.
1380e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
1381698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov    private int makeMeasureSpec(int measureSpec, int maxSize) {
13829f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (maxSize == SIZE_UNSPECIFIED) {
13839f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return measureSpec;
13849f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        }
1385ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int size = MeasureSpec.getSize(measureSpec);
1386e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        final int mode = MeasureSpec.getMode(measureSpec);
1387e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        switch (mode) {
1388e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.EXACTLY:
1389ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                return measureSpec;
1390e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.AT_MOST:
1391698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov                return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY);
1392e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.UNSPECIFIED:
1393698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov                return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY);
1394e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            default:
1395ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                throw new IllegalArgumentException("Unknown measure mode: " + mode);
1396e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        }
1397e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
1398e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
1399e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
14009f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * Utility to reconcile a desired size and state, with constraints imposed by
14019f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * a MeasureSpec. Tries to respect the min size, unless a different size is
14029f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * imposed by the constraints.
14039f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     *
14049f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param minSize The minimal desired size.
14059f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param measuredSize The currently measured size.
14069f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param measureSpec The current measure spec.
14079f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @return The resolved size and state.
14089f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     */
14099f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    private int resolveSizeAndStateRespectingMinSize(int minSize, int measuredSize,
14109f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            int measureSpec) {
14119f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (minSize != SIZE_UNSPECIFIED) {
14129f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            final int desiredWidth = Math.max(minSize, measuredSize);
14139f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return resolveSizeAndState(desiredWidth, measureSpec, 0);
14149f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        } else {
14159f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return measuredSize;
14169f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        }
14179f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    }
14189f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
14199f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    /**
1420a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     * Resets the selector indices and clear the cached
1421a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     * string representation of these indices.
1422a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     */
1423b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeSelectorWheelIndices() {
1424a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        mSelectorIndexToStringCache.clear();
1425b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIdices = mSelectorIndices;
1426b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int current = getValue();
1427b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        for (int i = 0; i < mSelectorIndices.length; i++) {
1428b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
1429b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (mWrapSelectorWheel) {
1430b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                selectorIndex = getWrappedSelectorIndex(selectorIndex);
1431b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1432b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mSelectorIndices[i] = selectorIndex;
1433b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            ensureCachedScrollSelectorValue(mSelectorIndices[i]);
1434a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
1435a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    }
1436a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov
1437a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    /**
1438206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Sets the current value of this NumberPicker, and sets mPrevious to the
1439206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * previous value. If current is greater than mEnd less than mStart, the
1440206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * value of mCurrent is wrapped around. Subclasses can override this to
1441206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * change the wrapping behavior
144268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
144368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * @param current the new value of the NumberPicker
144468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1445206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void changeCurrent(int current) {
1446e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mValue == current) {
1447206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1448206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Wrap around the values if we go past the start or end
1450e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel) {
1451206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            current = getWrappedSelectorIndex(current);
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1453e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        int previous = mValue;
1454e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setValue(current);
1455206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        notifyChange(previous, current);
14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1457fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
145868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1459b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Changes the current value by one which is increment or
1460b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * decrement based on the passes argument.
1461b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1462b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param increment True to increment, false to decrement.
1463b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
1464b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void changeCurrentByOne(boolean increment) {
1465b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mFlingable) {
1466b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mDimSelectorWheelAnimator.cancel();
1467b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mInputText.setVisibility(View.INVISIBLE);
1468b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA);
1469b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mPreviousScrollerY = 0;
1470b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            forceCompleteChangeCurrentByOneViaScroll();
1471b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (increment) {
1472b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight,
1473b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                        CHANGE_CURRENT_BY_ONE_SCROLL_DURATION);
1474b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1475b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight,
1476b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                        CHANGE_CURRENT_BY_ONE_SCROLL_DURATION);
1477b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1478b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            invalidate();
1479b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        } else {
1480b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (increment) {
1481b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                changeCurrent(mValue + 1);
1482b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1483b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                changeCurrent(mValue - 1);
1484b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1485b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
1486b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1487b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1488b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
1489b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Ensures that if we are in the process of changing the current value
1490b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * by one via scrolling the scroller gets to its final state and the
1491b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * value is updated.
1492b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
1493b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void forceCompleteChangeCurrentByOneViaScroll() {
1494b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        Scroller scroller = mFlingScroller;
1495b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (!scroller.isFinished()) {
1496b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            final int yBeforeAbort = scroller.getCurrY();
1497b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            scroller.abortAnimation();
1498b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            final int yDelta = scroller.getCurrY() - yBeforeAbort;
1499b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            scrollBy(0, yDelta);
1500b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
1501b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1502b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1503b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
1504206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Sets the <code>alpha</code> of the {@link Paint} for drawing the selector
1505206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * wheel.
150668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1507206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @SuppressWarnings("unused")
1508b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    // Called via reflection
1509206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void setSelectorPaintAlpha(int alpha) {
1510b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorWheelPaint.setAlpha(alpha);
1511b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        invalidate();
15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
151468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1515e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * @return If the <code>event</code> is in the visible <code>view</code>.
151668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1517e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    private boolean isEventInVisibleViewHitRect(MotionEvent event, View view) {
1518e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        if (view.getVisibility() == VISIBLE) {
1519e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            view.getHitRect(mTempRect);
1520e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            return mTempRect.contains((int) event.getX(), (int) event.getY());
1521e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        }
1522e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        return false;
1523206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1524206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1525206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1526b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Sets the <code>selectorWheelState</code>.
1527206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1528b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void setSelectorWheelState(int selectorWheelState) {
1529b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorWheelState = selectorWheelState;
1530b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (selectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
1531b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA);
1532b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
15333fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
1534b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mFlingable && selectorWheelState == SELECTOR_WHEEL_STATE_LARGE
15353fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov                && AccessibilityManager.getInstance(mContext).isEnabled()) {
15363fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            AccessibilityManager.getInstance(mContext).interrupt();
15373fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            String text = mContext.getString(R.string.number_picker_increment_scroll_action);
15383fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            mInputText.setContentDescription(text);
15393fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            mInputText.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
15403fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            mInputText.setContentDescription(null);
15413fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        }
1542206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1543206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1544b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeSelectorWheel() {
1545b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1546b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
15479cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        int totalTextHeight = selectorIndices.length * mTextSize;
154801fa0d7aae1a551e1e7cfb90d2aeaf2fcb3978afSvetoslav Ganov        float totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
154901fa0d7aae1a551e1e7cfb90d2aeaf2fcb3978afSvetoslav Ganov        float textGapCount = selectorIndices.length - 1;
1550b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
1551b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorElementHeight = mTextSize + mSelectorTextGapHeight;
1552eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        // Ensure that the middle item is positioned the same as the text in mInputText
1553eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        int editTextTextPosition = mInputText.getBaseline() + mInputText.getTop();
1554eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        mInitialScrollOffset = editTextTextPosition -
1555eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase                (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
15566a19fcde07527706c2a9175e0b9f2a71549b7ffeSvetoslav Ganov        mCurrentScrollOffset = mInitialScrollOffset;
15579cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
15589cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    }
15599cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1560b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeFadingEdges() {
1561b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setVerticalFadingEdgeEnabled(true);
1562b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setFadingEdgeLength((mBottom - mTop - mTextSize) / 2);
1563b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1564b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1565206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1566206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Callback invoked upon completion of a given <code>scroller</code>.
1567206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1568206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void onScrollerFinished(Scroller scroller) {
1569206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller == mFlingScroller) {
1570b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) {
1571b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                postAdjustScrollerCommand(0);
1572b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
1573b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1574b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                updateInputTextView();
1575b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                fadeSelectorWheel(mShowInputControlsAnimimationDuration);
1576b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
15779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1578206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
15799cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            showInputControls(mShowInputControlsAnimimationDuration);
15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1582fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1583206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
15849cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Handles transition to a given <code>scrollState</code>
158550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
15869cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private void onScrollStateChange(int scrollState) {
15879cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mScrollState == scrollState) {
15889cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            return;
15899cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
15909cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mScrollState = scrollState;
15919cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mOnScrollListener != null) {
15924bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            mOnScrollListener.onScrollStateChange(this, scrollState);
159350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
159450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
159550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
159650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1597206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flings the selector with the given <code>velocityY</code>.
1598206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1599206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void fling(int velocityY) {
1600206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = 0;
1601206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1602234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov        if (velocityY > 0) {
1603234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov            mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
1604206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1605234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov            mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1607206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1608206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        invalidate();
16099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1611206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1612206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Hides the input controls which is the up/down arrows and the text field.
1613206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1614206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void hideInputControls() {
1615206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.cancel();
1616206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setVisibility(INVISIBLE);
1617206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setVisibility(INVISIBLE);
1618206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setVisibility(INVISIBLE);
1619206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1620c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1621206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1622206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Show the input controls by making them visible and animating the alpha
1623206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * property up/down arrows.
16249cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     *
16259cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * @param animationDuration The duration of the animation.
1626206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
16279cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private void showInputControls(long animationDuration) {
1628206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
1629206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setVisibility(VISIBLE);
16309cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mShowInputControlsAnimator.setDuration(animationDuration);
1631206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.start();
1632206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1633c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1634206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1635b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Fade the selector wheel via an animation.
1636b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1637b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param animationDuration The duration of the animation.
1638b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
1639b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void fadeSelectorWheel(long animationDuration) {
1640b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mInputText.setVisibility(VISIBLE);
1641b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mDimSelectorWheelAnimator.setDuration(animationDuration);
1642b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mDimSelectorWheelAnimator.start();
1643b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1644b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1645b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
1646206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the visibility state of the increment and decrement buttons.
1647206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1648206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void updateIncrementAndDecrementButtonsVisibilityState() {
1649e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel || mValue < mMaxValue) {
1650206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mIncrementButton.setVisibility(VISIBLE);
1651206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mIncrementButton.setVisibility(INVISIBLE);
1653206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1654e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel || mValue > mMinValue) {
1655206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mDecrementButton.setVisibility(VISIBLE);
1656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1657206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mDecrementButton.setVisibility(INVISIBLE);
16589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1662206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The wrapped index <code>selectorIndex</code> value.
166368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1664206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getWrappedSelectorIndex(int selectorIndex) {
1665e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex > mMaxValue) {
1666e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMinValue + (selectorIndex - mMaxValue) % (mMaxValue - mMinValue) - 1;
1667e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else if (selectorIndex < mMinValue) {
1668e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMaxValue - (mMinValue - selectorIndex) % (mMaxValue - mMinValue) + 1;
1669206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1670206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return selectorIndex;
16719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1672fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1673206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1674206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Increments the <code>selectorIndices</code> whose string representations
1675206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1676206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1677b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void incrementSelectorIndices(int[] selectorIndices) {
1678206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length - 1; i++) {
1679206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i + 1];
1680206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1681206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1;
1682e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) {
1683e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMinValue;
1684206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1685206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex;
1686206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1687206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1688fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1689206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1690206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Decrements the <code>selectorIndices</code> whose string representations
1691206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1692206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1693206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void decrementSelectorIndices(int[] selectorIndices) {
1694206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = selectorIndices.length - 1; i > 0; i--) {
1695206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i - 1];
1696206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1697206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[1] - 1;
1698e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) {
1699e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMaxValue;
1700206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1701206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[0] = nextScrollSelectorIndex;
1702206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1703206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1704fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1705206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1706206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Ensures we have a cached string representation of the given <code>
1707206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * selectorIndex</code>
1708206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * to avoid multiple instantiations of the same string.
1709206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1710206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void ensureCachedScrollSelectorValue(int selectorIndex) {
1711206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        SparseArray<String> cache = mSelectorIndexToStringCache;
1712206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String scrollSelectorValue = cache.get(selectorIndex);
1713206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scrollSelectorValue != null) {
1714206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1715206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1716e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex < mMinValue || selectorIndex > mMaxValue) {
1717206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scrollSelectorValue = "";
1718206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1719206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues != null) {
1720e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                int displayedValueIndex = selectorIndex - mMinValue;
172150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                scrollSelectorValue = mDisplayedValues[displayedValueIndex];
1722206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1723206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollSelectorValue = formatNumber(selectorIndex);
17249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1726206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        cache.put(selectorIndex, scrollSelectorValue);
17279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1728fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1729206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private String formatNumber(int value) {
1730e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return (mFormatter != null) ? mFormatter.format(value) : String.valueOf(value);
1731206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
17329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1733206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void validateInputTextView(View v) {
1734206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String str = String.valueOf(((TextView) v).getText());
1735206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (TextUtils.isEmpty(str)) {
1736206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Restore to the old value as we don't allow empty values
1737206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
1738206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1739206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Check the new value and ensure it's in range
1740206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int current = getSelectedPos(str.toString());
1741206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(current);
17429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1743206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1744fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1745206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1746206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the view of this NumberPicker. If displayValues were specified in
17479cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * the string corresponding to the index specified by the current value will
17489cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * be returned. Otherwise, the formatter specified in {@link #setFormatter}
17499cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * will be used to format the number.
1750206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1751206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void updateInputTextView() {
1752206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /*
1753206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * If we don't have displayed values then use the current number else
1754206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * find the correct value in the displayed values for the current
1755206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * number.
1756206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
1757206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mDisplayedValues == null) {
1758e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setText(formatNumber(mValue));
1759206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1760e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setText(mDisplayedValues[mValue - mMinValue]);
17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1762206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setSelection(mInputText.getText().length());
17633fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
17643fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        if (mFlingable && AccessibilityManager.getInstance(mContext).isEnabled()) {
17653fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            String text = mContext.getString(R.string.number_picker_increment_scroll_mode,
17663fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov                    mInputText.getText());
17673fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            mInputText.setContentDescription(text);
17683fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        }
1769206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1770fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1771206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1772206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Notifies the listener, if registered, of a change of the value of this
1773206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * NumberPicker.
1774206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1775206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void notifyChange(int previous, int current) {
1776cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        if (mOnValueChangeListener != null) {
1777cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov            mOnValueChangeListener.onValueChange(this, previous, mValue);
1778206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1779206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
17809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1781206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1782b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Posts a command for changing the current value by one.
1783b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1784b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param increment Whether to increment or decrement the value.
1785206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1786b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void postChangeCurrentByOneFromLongPress(boolean increment) {
1787206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.clearFocus();
1788206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
1789b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand == null) {
1790b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand();
1791206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1792b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mChangeCurrentByOneFromLongPressCommand.setIncrement(increment);
1793b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        post(mChangeCurrentByOneFromLongPressCommand);
1794206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1796206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1797206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Removes all pending callback from the message queue.
1798206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1799206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void removeAllCallbacks() {
1800b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand != null) {
1801b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1802206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1803206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mAdjustScrollerCommand != null) {
1804206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mAdjustScrollerCommand);
1805206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1806206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand != null) {
1807206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
18089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1811206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1812206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The selected index given its displayed <code>value</code>.
1813206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1814206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getSelectedPos(String value) {
18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisplayedValues == null) {
1816aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            try {
1817206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
1818aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            } catch (NumberFormatException e) {
1819206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
1820aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            }
18219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
18229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < mDisplayedValues.length; i++) {
1823206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Don't force the user to type in jan when ja will do
1824206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                value = value.toLowerCase();
1825206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
1826e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    return mMinValue + i;
18279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1829fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1830206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            /*
1831206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov             * The user might have typed in a number into the month field i.e.
18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             * 10 instead of OCT so support that too.
18339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             */
18349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1835206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
18369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
1837fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1838206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
18399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1841e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
18429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1845206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link SetSelectionCommand} from the given <code>selectionStart
1846206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * </code> to
1847206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * <code>selectionEnd</code>.
18489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1849206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
1850206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand == null) {
1851206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSetSelectionCommand = new SetSelectionCommand();
1852206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1853206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
1854206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1855206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionStart = selectionStart;
1856206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionEnd = selectionEnd;
1857206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(mSetSelectionCommand);
18589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
185968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
186068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1861206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link AdjustScrollerCommand} within the given <code>
1862206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * delayMillis</code>
1863206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * .
186468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1865206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postAdjustScrollerCommand(int delayMillis) {
1866206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mAdjustScrollerCommand == null) {
1867206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mAdjustScrollerCommand = new AdjustScrollerCommand();
1868206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1869206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mAdjustScrollerCommand);
1870206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1871206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        postDelayed(mAdjustScrollerCommand, delayMillis);
187268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
187368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
187468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1875206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Filter for accepting only valid indices or prefixes of the string
1876206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * representation of valid indices.
187768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1878206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class InputTextFilter extends NumberKeyListener {
1879206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1880206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // XXX This doesn't allow for range limits when controlled by a
1881206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // soft input method!
1882206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public int getInputType() {
1883206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return InputType.TYPE_CLASS_TEXT;
1884206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1885206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1886206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1887206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        protected char[] getAcceptedChars() {
1888206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return DIGIT_CHARACTERS;
1889206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1890206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1891206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1892206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
1893206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int dstart, int dend) {
1894206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues == null) {
1895206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
1896206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (filtered == null) {
1897206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    filtered = source.subSequence(start, end);
1898206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1899206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1900206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1901206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1902206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1903206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if ("".equals(result)) {
1904206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return result;
1905206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1906206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int val = getSelectedPos(result);
1907206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1908206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                /*
1909206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * Ensure the user can't type in a value greater than the max
1910206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * allowed. We have to allow less than min as the user might
1911206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * want to delete some numbers and then type a new number.
1912206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 */
1913e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                if (val > mMaxValue) {
1914206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1915206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
1916206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return filtered;
1917206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1918206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1919206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = String.valueOf(source.subSequence(start, end));
1920206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (TextUtils.isEmpty(filtered)) {
1921206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1922206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1923206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1924206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1925206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String str = String.valueOf(result).toLowerCase();
1926206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                for (String val : mDisplayedValues) {
1927206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    String valLowerCase = val.toLowerCase();
1928206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (valLowerCase.startsWith(str)) {
1929206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postSetSelectionCommand(result.length(), val.length());
1930206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        return val.subSequence(dstart, val.length());
1931206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
1932206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1933206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return "";
1934206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1935206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1936206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1937206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1938206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1939206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for setting the input text selection.
1940206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1941206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class SetSelectionCommand implements Runnable {
1942206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionStart;
1943206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1944206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionEnd;
1945206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1946206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1947206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setSelection(mSelectionStart, mSelectionEnd);
1948206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1949206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1950206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1951206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1952206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for adjusting the scroller to show in its center the closest of
1953206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * the displayed items.
1954206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1955206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class AdjustScrollerCommand implements Runnable {
1956206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1957206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = 0;
195834c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            if (mInitialScrollOffset == mCurrentScrollOffset) {
195934c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                updateInputTextView();
19609cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                showInputControls(mShowInputControlsAnimimationDuration);
196134c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                return;
196234c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            }
196334c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            // adjust to the closest value
1964206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
196534c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
196634c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
196734c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            }
19689cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
1969206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
1970206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1971206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1972206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1973206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1974b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Command for changing the current value from a long press by one.
1975206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1976b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    class ChangeCurrentByOneFromLongPressCommand implements Runnable {
1977b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        private boolean mIncrement;
1978206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1979b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        private void setIncrement(boolean increment) {
1980b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mIncrement = increment;
1981206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1982206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1983206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1984b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            changeCurrentByOne(mIncrement);
19854bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            postDelayed(this, mLongPressUpdateInterval);
1986206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
198768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
19887762d93621f508f4410fd37b5bbdfec0071b0420Paul Westbrook}
1989