NumberPicker.java revision a53efe9923bedab4fe5d578f32eaff308e5b9e76
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
198bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powellimport com.android.internal.R;
208bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powell
21206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.Animator;
22206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.AnimatorListenerAdapter;
23206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.AnimatorSet;
24206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.ObjectAnimator;
25206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.ValueAnimator;
2668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookimport android.annotation.Widget;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
28206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.ColorStateList;
29206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.TypedArray;
30206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Canvas;
31206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Color;
32206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Paint;
3350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.graphics.Rect;
34e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.graphics.Paint.Align;
359cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.graphics.drawable.Drawable;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputFilter;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputType;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.Spanned;
39206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.text.TextUtils;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.NumberKeyListener;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
42206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.util.SparseArray;
439cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.util.TypedValue;
44206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.KeyEvent;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.LayoutInflater;
46206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.MotionEvent;
47206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.VelocityTracker;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
49206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.ViewConfiguration;
50e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.view.LayoutInflater.Filter;
513fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
523fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityManager;
539cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.view.animation.DecelerateInterpolator;
54206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.inputmethod.InputMethodManager;
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook/**
574bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * A widget that enables the user to select a number form a predefined range.
584bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * The widget presents an input filed and up and down buttons for selecting the
594bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * current value. Pressing/long pressing the up and down buttons increments and
604bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * decrements the current value respectively. Touching the input filed shows a
614bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * scroll wheel, tapping on which while shown and not moving allows direct edit
624bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * of the current value. Sliding motions up or down hide the buttons and the
634bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * input filed, show the scroll wheel, and rotate the latter. Flinging is
644bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * also supported. The widget enables mapping from positions to strings such
654bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * that instead the position index the corresponding string is displayed.
664bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * <p>
674bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * For an example of using this widget, see {@link android.widget.TimePicker}.
684bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * </p>
6968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook */
7068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook@Widget
7168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpublic class NumberPicker extends LinearLayout {
72fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
7368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
74e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default update interval during long press.
75e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
76e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300;
77e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
78e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
79206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The index of the middle selector item.
8068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
81206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MIDDLE_ITEM_INDEX = 2;
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
84206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The coefficient by which to adjust (divide) the max fling velocity.
8568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
86206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
88206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
89206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the duration for adjusting the selector wheel.
90206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
91206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
92fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
93206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
94206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the delay for showing the input controls after a single tap on the
95206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * input text.
96206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
97206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SHOW_INPUT_CONTROLS_DELAY_MILLIS = ViewConfiguration
98206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            .getDoubleTapTimeout();
99206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
100206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
101206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The update step for incrementing the current value.
102206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
103206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int UPDATE_STEP_INCREMENT = 1;
104206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
105206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
106206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The update step for decrementing the current value.
107206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int UPDATE_STEP_DECREMENT = -1;
109206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
111206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The strength of fading in the top and bottom while drawing the selector.
112206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f;
114206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1169cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * The default unscaled height of the selection divider.
1179cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
1189cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2;
1199cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1209cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
121206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The numbers accepted by the input text's {@link Filter}
122206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
123206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final char[] DIGIT_CHARACTERS = new char[] {
124206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
125206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    };
126206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
127206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
128206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Use a custom NumberPicker formatting callback to use two-digit minutes
129206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * strings like "01". Keeping a static formatter etc. is the most efficient
130206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * way to do this; it avoids creating temporary objects on every call to
131206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * format().
132e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
133e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @hide
134206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
135206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER = new NumberPicker.Formatter() {
136206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final StringBuilder mBuilder = new StringBuilder();
137206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
138f0ef665299d795df7905897e1c337e37891dafefJean-Baptiste Queru        final java.util.Formatter mFmt = new java.util.Formatter(mBuilder, java.util.Locale.US);
139206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
140206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final Object[] mArgs = new Object[1];
141206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
142e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value) {
143206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mArgs[0] = value;
144206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mBuilder.delete(0, mBuilder.length());
145206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mFmt.format("%02d", mArgs);
146206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return mFmt.toString();
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
150206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
151206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The increment button.
152206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
153206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mIncrementButton;
154206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
155206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
156206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The decrement button.
157206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
158206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mDecrementButton;
159206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
160206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
161206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The text for showing the current value.
162206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
163206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final EditText mInputText;
164206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
165206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
166206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of the text.
167206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
168206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mTextSize;
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
170206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
171206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The values to be displayed instead the indices.
172206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String[] mDisplayedValues;
17468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
17568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
17668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Lower value of the range of numbers allowed for the NumberPicker
17768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
178e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMinValue;
17968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
18068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
18168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Upper value of the range of numbers allowed for the NumberPicker
18268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
183e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMaxValue;
18468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
18568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
18668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Current value of this NumberPicker
18768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
188e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mValue;
18968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
19068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
191206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Listener to be notified upon current value change.
19268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
193cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    private OnValueChangeListener mOnValueChangeListener;
19450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
19550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
19650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Listener to be notified upon scroll state change.
19750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
19850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private OnScrollListener mOnScrollListener;
199206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
200206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Formatter for for displaying the current value.
202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Formatter mFormatter;
204fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
205206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
206206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The speed for updating the value form long press.
207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
208e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL;
209fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
21068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
211206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Cache for the string representation of selector indices.
212206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
213206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final SparseArray<String> mSelectorIndexToStringCache = new SparseArray<String>();
214206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
215206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
216206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The selector indices whose value are show by the selector.
217206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
218206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int[] mSelectorIndices = new int[] {
219206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE,
220206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            Integer.MIN_VALUE
221206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    };
222206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
223206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
224206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Paint} for drawing the selector.
225206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
226206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Paint mSelectorPaint;
227206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
228206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
229206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of a selector element (text + gap).
230206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
231206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mSelectorElementHeight;
232206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
233206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
234206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The initial offset of the scroll selector.
235206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
236206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mInitialScrollOffset = Integer.MIN_VALUE;
237206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
238206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
239206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The current offset of the scroll selector.
240206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
241206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mCurrentScrollOffset;
242206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
243206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
244206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for flinging the selector.
245206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
246206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mFlingScroller;
247206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
248206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
249206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for adjusting the selector.
250206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
251206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mAdjustScroller;
252206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
253206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
254206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The previous Y coordinate while scrolling the selector.
255206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
256206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mPreviousScrollerY;
257206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
258206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
259206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for setting the input text selection.
260206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
261206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private SetSelectionCommand mSetSelectionCommand;
262206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
263206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
264206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for adjusting the scroller.
265206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
266206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private AdjustScrollerCommand mAdjustScrollerCommand;
267206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
268206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
269206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for updating the current value from long
270206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * press.
271206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
272206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private UpdateValueFromLongPressCommand mUpdateFromLongPressCommand;
273206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
274206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
275206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * {@link Animator} for showing the up/down arrows.
276206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
277206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final AnimatorSet mShowInputControlsAnimator;
278206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
279206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
280206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last down event.
281206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
282206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastDownEventY;
283206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
284206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
285206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last motion event.
286206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
287206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastMotionEventY;
288206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
289206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
290206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag if to begin edit on next up event.
291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mBeginEditOnUpEvent;
293206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
294206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
295206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag if to adjust the selector wheel on next up event.
296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
297206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mAdjustScrollerOnUpEvent;
298206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
300206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag if to draw the selector wheel.
301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mDrawSelectorWheel;
303206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
304206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
305206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Determines speed during touch scrolling.
306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private VelocityTracker mVelocityTracker;
308206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledTouchSlop()
311206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
312206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mTouchSlop;
313206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
314206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
315206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMinimumFlingVelocity()
316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMinimumFlingVelocity;
318206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMaximumFlingVelocity()
321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMaximumFlingVelocity;
323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag whether the selector should wrap around.
326206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
327e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean mWrapSelectorWheel;
328206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
329206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
330206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The back ground color used to optimize scroller fading.
33168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
332206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mSolidColor;
333206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
334206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
3354243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov     * Flag indicating if this widget supports flinging.
3364243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov     */
3374243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov    private final boolean mFlingable;
3384243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov
3394243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov    /**
3409cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Divider for showing item to be selected while scrolling
3419cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
3429cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final Drawable mSelectionDivider;
3439cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
3449cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
3459cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * The height of the selection divider.
3469cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
3479cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final int mSelectionDividerHeight;
3489cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
3499cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Reusable {@link Rect} instance.
351206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
352206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Rect mTempRect = new Rect();
353206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
354206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
35550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The current scroll state of the number picker.
35650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
35750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
35850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
35950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
3609cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * The duration of the animation for showing the input controls.
3619cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
3629cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final long mShowInputControlsAnimimationDuration;
3639cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
3649cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
365e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for changes of the current value.
366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
367cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public interface OnValueChangeListener {
368e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
369206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /**
3704bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * Called upon a change of the current value.
3714bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
372206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param picker The NumberPicker associated with this listener.
373206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param oldVal The previous value.
374206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param newVal The new value.
375206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
376e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        void onValueChange(NumberPicker picker, int oldVal, int newVal);
377206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
378206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
379206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
380e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for the picker scroll state.
38150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
38250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public interface OnScrollListener {
38350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
38450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
38550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The view is not scrolling.
38650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
38750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_IDLE = 0;
38850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
38950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
39050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user is scrolling using touch, and their finger is still on the screen.
39150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
39250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_TOUCH_SCROLL = 1;
39350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
39450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
39550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user had previously been scrolling using touch and performed a fling.
39650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
39750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_FLING = 2;
39850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
39950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
400e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Callback invoked while the number picker scroll state has changed.
40150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
402e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param view The view whose scroll state is being reported.
403e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param scrollState The current scroll state. One of
404e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE},
405e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_TOUCH_SCROLL} or
406e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE}.
40750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
4084bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        public void onScrollStateChange(NumberPicker view, int scrollState);
40950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
41050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
41150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
412e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface used to format current value into a string for presentation.
413206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
414206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public interface Formatter {
4154bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
4164bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        /**
417e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Formats a string representation of the current value.
4184bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
4194bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @param value The currently selected value.
4204bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @return A formatted string representation.
4214bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         */
422e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value);
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
424fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
42568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
4264bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
4274bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
4284bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param context The application environment.
4294bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
4304bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public NumberPicker(Context context) {
4314bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        this(context, null);
4324bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
4334bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
4344bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
4354bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
436206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
437206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context The application environment.
438206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs A collection of attributes.
43968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public NumberPicker(Context context, AttributeSet attrs) {
441206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        this(context, attrs, R.attr.numberPickerStyle);
442206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
443206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
444206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
445206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Create a new number picker
446206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
447206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context the application environment.
448206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs a collection of attributes.
449206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param defStyle The default style to apply to this view.
450206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
451206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
452206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super(context, attrs, defStyle);
453206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
454206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // process style attributes
455206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        TypedArray attributesArray = context.obtainStyledAttributes(attrs,
456206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                R.styleable.NumberPicker, defStyle, 0);
457206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
4584243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov        mFlingable = attributesArray.getBoolean(R.styleable.NumberPicker_flingable, true);
4599cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
4609cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        int defSelectionDividerHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
4619cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
4629cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                getResources().getDisplayMetrics());
4639cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
4649cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
465206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        attributesArray.recycle();
466206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
4679cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mShowInputControlsAnimimationDuration = getResources().getInteger(
4689cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                R.integer.config_longAnimTime);
4699cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
470206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // By default Linearlayout that we extend is not drawn. This is
471206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // its draw() method is not called but dispatchDraw() is called
472206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // directly (see ViewGroup.drawChild()). However, this class uses
473206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // the fading edge effect implemented by View and we need our
474206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // draw() method to be called. Therefore, we declare we will draw.
475206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setWillNotDraw(false);
4763fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        setDrawScrollWheel(false);
477206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
478206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
479206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                Context.LAYOUT_INFLATER_SERVICE);
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        inflater.inflate(R.layout.number_picker, this, true);
48168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
482206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnClickListener onClickListener = new OnClickListener() {
48368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            public void onClick(View v) {
484206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
485206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
486e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    changeCurrent(mValue + 1);
487206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
488e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    changeCurrent(mValue - 1);
48968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
49068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
49168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        };
49268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
493206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnLongClickListener onLongClickListener = new OnLongClickListener() {
494206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public boolean onLongClick(View v) {
495206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
496206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
497206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    postUpdateValueFromLongPress(UPDATE_STEP_INCREMENT);
498206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
499206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    postUpdateValueFromLongPress(UPDATE_STEP_DECREMENT);
500206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
501206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return true;
502206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
503206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        };
50468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
505206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // increment button
506206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton = (ImageButton) findViewById(R.id.increment);
507206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setOnClickListener(onClickListener);
508206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setOnLongClickListener(onLongClickListener);
509206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
510206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // decrement button
511206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton = (ImageButton) findViewById(R.id.decrement);
512206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setOnClickListener(onClickListener);
513206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setOnLongClickListener(onLongClickListener);
514206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
515206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // input text
516012dd5a461f18a2e5dad38c60282fac1c21ff7feSvetoslav Ganov        mInputText = (EditText) findViewById(R.id.numberpicker_input);
517206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setOnFocusChangeListener(new OnFocusChangeListener() {
518206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onFocusChange(View v, boolean hasFocus) {
519a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                if (hasFocus) {
520a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.selectAll();
521a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                } else {
522a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.setSelection(0, 0);
523206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    validateInputTextView(v);
52468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
52568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
526206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
527206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setFilters(new InputFilter[] {
528206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            new InputTextFilter()
529206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
53068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
531206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
532206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
533206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // initialize constants
534206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = ViewConfiguration.getTapTimeout();
535206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ViewConfiguration configuration = ViewConfiguration.get(context);
536206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = configuration.getScaledTouchSlop();
537206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
538206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity()
539206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;
540206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTextSize = (int) mInputText.getTextSize();
541206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
542206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the selector wheel paint
543206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Paint paint = new Paint();
544206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setAntiAlias(true);
545206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextAlign(Align.CENTER);
546206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextSize(mTextSize);
547206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTypeface(mInputText.getTypeface());
548206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ColorStateList colors = mInputText.getTextColors();
549206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int color = colors.getColorForState(ENABLED_STATE_SET, Color.WHITE);
550206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setColor(color);
551206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSelectorPaint = paint;
552206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
553206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the animator for showing the input controls
554206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final ValueAnimator fadeScroller = ObjectAnimator.ofInt(this, "selectorPaintAlpha", 255, 0);
555206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final ObjectAnimator showIncrementButton = ObjectAnimator.ofFloat(mIncrementButton,
556206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                "alpha", 0, 1);
557206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final ObjectAnimator showDecrementButton = ObjectAnimator.ofFloat(mDecrementButton,
558206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                "alpha", 0, 1);
559206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator = new AnimatorSet();
560206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.playTogether(fadeScroller, showIncrementButton,
5615203194fdff0c0aaefc405d65871ef0e06e7c682Svetoslav Ganov                showDecrementButton);
562206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.addListener(new AnimatorListenerAdapter() {
563206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            private boolean mCanceled = false;
564206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
565206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            @Override
566206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onAnimationEnd(Animator animation) {
567206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (!mCanceled) {
568206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    // if canceled => we still want the wheel drawn
5693fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov                    setDrawScrollWheel(false);
57068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
571206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCanceled = false;
572206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mSelectorPaint.setAlpha(255);
573206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                invalidate();
57468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
57568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
576206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            @Override
577206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onAnimationCancel(Animator animation) {
578206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mShowInputControlsAnimator.isRunning()) {
579206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mCanceled = true;
580206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
581206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
582206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
58368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
584206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the fling and adjust scrollers
585bf80562d22b2bbe7944d80d0524c69d0238010cbSvetoslav Ganov        mFlingScroller = new Scroller(getContext(), null, true);
5869cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
587fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
588206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateInputTextView();
589206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
5909cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
5914fd9dae6a23a22142f73749c291c9fd18459e381Svetoslav Ganov        if (mFlingable && !isInEditMode()) {
5929cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // Start with shown selector wheel and hidden controls. When made
5939cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // visible hide the selector and fade-in the controls to suggest
5949cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // fling interaction.
5953fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            setDrawScrollWheel(true);
5969cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            hideInputControls();
5979cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
598206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
599206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
600206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
6019cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
6029cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        super.onLayout(changed, left, top, right, bottom);
6039cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // need to do this when we know our size
6049cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        initializeScrollWheel();
605206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
606206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
607206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
608206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onInterceptTouchEvent(MotionEvent event) {
6094243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov        if (!isEnabled() || !mFlingable) {
61051c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
61151c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
612206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (event.getActionMasked()) {
613206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_DOWN:
614206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mLastMotionEventY = mLastDownEventY = event.getY();
615206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                removeAllCallbacks();
616092bda57c4b8caad378400b2bc97b9ac66fd2179Svetoslav Ganov                mShowInputControlsAnimator.cancel();
617206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mBeginEditOnUpEvent = false;
618206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mAdjustScrollerOnUpEvent = true;
619206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mDrawSelectorWheel) {
62050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    boolean scrollersFinished = mFlingScroller.isFinished()
621206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                            && mAdjustScroller.isFinished();
62250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    if (!scrollersFinished) {
62350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        mFlingScroller.forceFinished(true);
62450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        mAdjustScroller.forceFinished(true);
6259cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                        onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
62650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    }
62750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    mBeginEditOnUpEvent = scrollersFinished;
628206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mAdjustScrollerOnUpEvent = true;
629092bda57c4b8caad378400b2bc97b9ac66fd2179Svetoslav Ganov                    hideInputControls();
630206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
631206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
63258f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                if (isEventInViewHitRect(event, mInputText)
63358f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                        || (!mIncrementButton.isShown()
63458f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                                && isEventInViewHitRect(event, mIncrementButton))
63558f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                        || (!mDecrementButton.isShown()
63658f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                                && isEventInViewHitRect(event, mDecrementButton))) {
637206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mAdjustScrollerOnUpEvent = false;
6383fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov                    setDrawScrollWheel(true);
639092bda57c4b8caad378400b2bc97b9ac66fd2179Svetoslav Ganov                    hideInputControls();
640206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
641206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
642206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
643206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_MOVE:
644206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                float currentMoveY = event.getY();
645206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
646206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (deltaDownY > mTouchSlop) {
647206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mBeginEditOnUpEvent = false;
6489cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
6493fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov                    setDrawScrollWheel(true);
650092bda57c4b8caad378400b2bc97b9ac66fd2179Svetoslav Ganov                    hideInputControls();
651206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
653206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
654206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
655206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return false;
656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
657206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
658206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
659206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onTouchEvent(MotionEvent ev) {
66051c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        if (!isEnabled()) {
66151c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
66251c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
663206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mVelocityTracker == null) {
664206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mVelocityTracker = VelocityTracker.obtain();
665206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
666206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mVelocityTracker.addMovement(ev);
667206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int action = ev.getActionMasked();
668206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (action) {
669206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_MOVE:
670206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                float currentMoveY = ev.getY();
67150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                if (mBeginEditOnUpEvent
67250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
673206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
674206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (deltaDownY > mTouchSlop) {
675206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        mBeginEditOnUpEvent = false;
6769cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                        onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
677206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
678206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
679206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int deltaMoveY = (int) (currentMoveY - mLastMotionEventY);
680206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollBy(0, deltaMoveY);
681206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                invalidate();
682206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mLastMotionEventY = currentMoveY;
683206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
684206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_UP:
685206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mBeginEditOnUpEvent) {
6863fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov                    setDrawScrollWheel(false);
6879cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    showInputControls(mShowInputControlsAnimimationDuration);
688206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mInputText.requestFocus();
689206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
690206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                            Context.INPUT_METHOD_SERVICE);
691206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    imm.showSoftInput(mInputText, 0);
692206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
693206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
694206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                VelocityTracker velocityTracker = mVelocityTracker;
695206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
696206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int initialVelocity = (int) velocityTracker.getYVelocity();
697206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
698206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    fling(initialVelocity);
6999cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
700206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
701206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (mAdjustScrollerOnUpEvent) {
702206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        if (mFlingScroller.isFinished() && mAdjustScroller.isFinished()) {
703206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                            postAdjustScrollerCommand(0);
704206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        }
705206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    } else {
706206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postAdjustScrollerCommand(SHOW_INPUT_CONTROLS_DELAY_MILLIS);
707206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
708206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
709206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker.recycle();
710206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker = null;
711206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
712206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
713206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return true;
714206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
715206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
716206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
717206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTouchEvent(MotionEvent event) {
718206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int action = event.getActionMasked();
719206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if ((action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP)
72058f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                && !isEventInViewHitRect(event, mInputText)) {
721206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
722206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
723206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTouchEvent(event);
724206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
725206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
726206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
727206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchKeyEvent(KeyEvent event) {
728206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int keyCode = event.getKeyCode();
729206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
730206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
731206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
732206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchKeyEvent(event);
733206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
734206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
735206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
736206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTrackballEvent(MotionEvent event) {
737206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int action = event.getActionMasked();
738206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
739206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
740206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
741206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTrackballEvent(event);
742206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
744206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
745206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void computeScroll() {
746206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (!mDrawSelectorWheel) {
747206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
748206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
749206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Scroller scroller = mFlingScroller;
750206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
751206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scroller = mAdjustScroller;
752206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (scroller.isFinished()) {
753206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return;
754206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
755206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
756206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scroller.computeScrollOffset();
757206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int currentScrollerY = scroller.getCurrY();
758206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mPreviousScrollerY == 0) {
759206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = scroller.getStartY();
760206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
761206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scrollBy(0, currentScrollerY - mPreviousScrollerY);
762206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = currentScrollerY;
763206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
764206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            onScrollerFinished(scroller);
765206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
766206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
769fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setEnabled(boolean enabled) {
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.setEnabled(enabled);
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIncrementButton.setEnabled(enabled);
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDecrementButton.setEnabled(enabled);
775206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setEnabled(enabled);
776206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
777206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
778206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
779206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void scrollBy(int x, int y) {
780206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int[] selectorIndices = getSelectorIndices();
78134c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y > 0
78234c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
783206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
784206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
785206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
78634c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y < 0
78734c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
788206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
789206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
790206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
791206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mCurrentScrollOffset += y;
7926c742f1e95dfe1f808bf02fadd5fbf9660b95050Svetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset >= mSelectorElementHeight) {
793206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset -= mSelectorElementHeight;
794206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            decrementSelectorIndices(selectorIndices);
795206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
796e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            if (selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
797206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
798206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
799206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
8006c742f1e95dfe1f808bf02fadd5fbf9660b95050Svetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset <= -mSelectorElementHeight) {
801206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset += mSelectorElementHeight;
802206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            incrementScrollSelectorIndices(selectorIndices);
803206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
804e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            if (selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
805206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
806206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
807206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
809fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
8104bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    @Override
8114bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public int getSolidColor() {
8124bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return mSolidColor;
8134bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
8144bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
81568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
8164bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the listener to be notified on change of the current value.
817206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
818e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onValueChangedListener The listener.
81968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
820cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public void setOnValueChangedListener(OnValueChangeListener onValueChangedListener) {
821cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        mOnValueChangeListener = onValueChangedListener;
82250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
82350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
82450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
8254bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set listener to be notified for scroll state changes.
82650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
827e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onScrollListener The listener.
82850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
82950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public void setOnScrollListener(OnScrollListener onScrollListener) {
83050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mOnScrollListener = onScrollListener;
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
832fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
83368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
8344bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set the formatter to be used for formatting the current value.
8354bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
836e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Note: If you have provided alternative values for the values this
837e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * formatter is never invoked.
8384bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
839206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
840e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param formatter The formatter object. If formatter is <code>null</code>,
841e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            {@link String#valueOf(int)} will be used.
8424bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
843e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setDisplayedValues(String[])
84468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFormatter(Formatter formatter) {
846e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (formatter == mFormatter) {
847e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
848e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFormatter = formatter;
850e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        resetSelectorWheelIndices();
8519cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
852206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
853206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
854206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
855e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Set the current value for the number picker.
8564bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
857e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
858e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
859e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
860e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
861e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
862e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
863e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
864e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
865e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
866e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
867e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
868e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
869e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
870e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
871e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
872e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
873e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
874e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
8754bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
876206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
877e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param value The current value.
8784bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @see #setWrapSelectorWheel(boolean)
879e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMinValue(int)
880e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMaxValue(int)
881206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
882e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setValue(int value) {
883e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mValue == value) {
884a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov            return;
885a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
886e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (value < mMinValue) {
887e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            value = mWrapSelectorWheel ? mMaxValue : mMinValue;
888206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
889e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (value > mMaxValue) {
890e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            value = mWrapSelectorWheel ? mMinValue : mMaxValue;
8918bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powell        }
892e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mValue = value;
893e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
894e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
896fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
89768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
898e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets whether the selector wheel wraps when reaching the min/max value.
89968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
900e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return True if the selector wheel wraps.
9014bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
902e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMinValue()
903e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMaxValue()
90468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
905e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public boolean getWrapSelectorWheel() {
906e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mWrapSelectorWheel;
907206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
908206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
909206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
910e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets whether the selector wheel shown during flinging/scrolling should
911e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * wrap around the {@link NumberPicker#getMinValue()} and
912e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getMaxValue()} values.
913e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
914e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * By default if the range (max - min) is more than five (the number of
915e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * items shown on the selector wheel) the selector wheel wrapping is
916e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * enabled.
917e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
918206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
919206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param wrapSelector Whether to wrap.
920206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
9214bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setWrapSelectorWheel(boolean wrapSelector) {
922e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (wrapSelector && (mMaxValue - mMinValue) < mSelectorIndices.length) {
923206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            throw new IllegalStateException("Range less than selector items count.");
924206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
925e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (wrapSelector != mWrapSelectorWheel) {
926206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // force the selector indices array to be reinitialized
927206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] = Integer.MAX_VALUE;
928e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mWrapSelectorWheel = wrapSelector;
929156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            // force redraw since we might look different
930156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            updateIncrementAndDecrementButtonsVisibilityState();
931206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9354bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the speed at which the numbers be incremented and decremented when
9364bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * the up and down buttons are long pressed respectively.
937e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
938e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default value is 300 ms.
939e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
94068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
9414bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param intervalMillis The speed (in milliseconds) at which the numbers
942e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            will be incremented and decremented.
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9444bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setOnLongPressUpdateInterval(long intervalMillis) {
9454bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        mLongPressUpdateInterval = intervalMillis;
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
947fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
948206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
949e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the value of the picker.
950e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
951e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The value.
952e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
953e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getValue() {
954e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mValue;
955e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
956e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
957e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
958e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the min value of the picker.
959e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
960e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The min value
961e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
962e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMinValue() {
963e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
964e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
965e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
966e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
967e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the min value of the picker.
968206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
969e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param minValue The min value.
970206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
971e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMinValue(int minValue) {
972e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue == minValue) {
973e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
974e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
975e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (minValue < 0) {
976e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("minValue must be >= 0");
977e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
978e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMinValue = minValue;
979e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue > mValue) {
980e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMinValue;
981e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
982e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
983e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
984e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        resetSelectorWheelIndices();
985e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
986206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
987206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
9884bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
989e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the max value of the picker.
9904bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
991e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The max value.
9924bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
993e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMaxValue() {
994e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMaxValue;
9954bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
9964bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
9974bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
998e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the max value of the picker.
9994bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1000e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param maxValue The max value.
10014bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1002e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMaxValue(int maxValue) {
1003e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue == maxValue) {
1004e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1005e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1006e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (maxValue < 0) {
1007e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("maxValue must be >= 0");
1008e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1009e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMaxValue = maxValue;
1010e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue < mValue) {
1011e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMaxValue;
1012e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1013e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1014e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1015e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        resetSelectorWheelIndices();
1016e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1017e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1018e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1019e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1020e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the values to be displayed instead of string values.
1021e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1022e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The displayed values.
1023e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1024e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public String[] getDisplayedValues() {
1025e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mDisplayedValues;
1026e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1027e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1028e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1029e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the values to be displayed.
1030e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1031e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param displayedValues The displayed values.
1032e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1033e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setDisplayedValues(String[] displayedValues) {
1034e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues == displayedValues) {
1035e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1036e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1037e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mDisplayedValues = displayedValues;
1038e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues != null) {
1039e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // Allow text entry rather than strictly numeric entry.
1040e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
1041e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
1042e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else {
1043e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
1044e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1045e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1046e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        resetSelectorWheelIndices();
1047206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1048206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1049206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1050206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getTopFadingEdgeStrength() {
1051206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1052206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1053206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1054206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1055206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getBottomFadingEdgeStrength() {
1056206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1057206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1058206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1059206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
10609cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    protected void onAttachedToWindow() {
10619cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        super.onAttachedToWindow();
10629cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // make sure we show the controls only the very
10639cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // first time the user sees this widget
10644fd9dae6a23a22142f73749c291c9fd18459e381Svetoslav Ganov        if (mFlingable && !isInEditMode()) {
10659cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // animate a bit slower the very first time
10669cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            showInputControls(mShowInputControlsAnimimationDuration * 2);
10679cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
10689cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    }
10699cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
10709cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    @Override
1071206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDetachedFromWindow() {
1072206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
1073206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1074206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1075206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1076206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void dispatchDraw(Canvas canvas) {
1077206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // There is a good reason for doing this. See comments in draw().
1078206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1079206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1080206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1081206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void draw(Canvas canvas) {
1082206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Dispatch draw to our children only if we are not currently running
1083206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // the animation for simultaneously fading out the scroll wheel and
1084206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // showing in the buttons. This class takes advantage of the View
1085206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // implementation of fading edges effect to draw the selector wheel.
1086206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // However, in View.draw(), the fading is applied after all the children
1087206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // have been drawn and we do not want this fading to be applied to the
1088206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // buttons which are currently showing in. Therefore, we draw our
10899cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // children after we have completed drawing ourselves.
1090c75ec3324e9e3e6500148cc2978caab90ea962e4Chet Haase        super.draw(canvas);
1091206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1092206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Draw our children if we are not showing the selector wheel of fading
1093206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // it out
1094206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mShowInputControlsAnimator.isRunning() || !mDrawSelectorWheel) {
1095206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            long drawTime = getDrawingTime();
1096206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            for (int i = 0, count = getChildCount(); i < count; i++) {
1097206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                View child = getChildAt(i);
1098206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (!child.isShown()) {
1099206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    continue;
1100206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1101206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                drawChild(canvas, getChildAt(i), drawTime);
1102206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1103206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1104206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1105206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1106206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1107206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDraw(Canvas canvas) {
1108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // we only draw the selector wheel
1109206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (!mDrawSelectorWheel) {
1110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1111206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1112206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float x = (mRight - mLeft) / 2;
1113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float y = mCurrentScrollOffset;
1114206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
11159cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // draw the selector wheel
1116206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int[] selectorIndices = getSelectorIndices();
1117206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length; i++) {
1118206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int selectorIndex = selectorIndices[i];
1119206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
1120206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            canvas.drawText(scrollSelectorValue, x, y, mSelectorPaint);
1121206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            y += mSelectorElementHeight;
1122206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
11239cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
11249cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // draw the selection dividers (only if scrolling and drawable specified)
11259cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mSelectionDivider != null) {
11269cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setAlpha(mSelectorPaint.getAlpha());
11279cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the top divider
11289cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int topOfTopDivider =
11299cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                (getHeight() - mSelectorElementHeight - mSelectionDividerHeight) / 2;
11309cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
11319cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfTopDivider, mRight, bottomOfTopDivider);
11329cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
11339cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
11349cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the bottom divider
11359cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int topOfBottomDivider =  topOfTopDivider + mSelectorElementHeight;
11369cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int bottomOfBottomDivider = bottomOfTopDivider + mSelectorElementHeight;
11379cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfBottomDivider, mRight, bottomOfBottomDivider);
11389cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
11399cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1141fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
11423fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    @Override
11433fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    public void sendAccessibilityEvent(int eventType) {
11443fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        // Do not send accessibility events - we want the user to
11453fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        // perceive this widget as several controls rather as a whole.
11463fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    }
11473fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
114868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1149a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     * Resets the selector indices and clear the cached
1150a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     * string representation of these indices.
1151a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     */
1152e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private void resetSelectorWheelIndices() {
1153a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        mSelectorIndexToStringCache.clear();
1154a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        int[] selectorIdices = getSelectorIndices();
1155a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        for (int i = 0; i < selectorIdices.length; i++) {
1156a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov            selectorIdices[i] = Integer.MIN_VALUE;
1157a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
1158a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    }
1159a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov
1160a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    /**
1161206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Sets the current value of this NumberPicker, and sets mPrevious to the
1162206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * previous value. If current is greater than mEnd less than mStart, the
1163206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * value of mCurrent is wrapped around. Subclasses can override this to
1164206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * change the wrapping behavior
116568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
116668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * @param current the new value of the NumberPicker
116768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1168206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void changeCurrent(int current) {
1169e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mValue == current) {
1170206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1171206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Wrap around the values if we go past the start or end
1173e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel) {
1174206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            current = getWrappedSelectorIndex(current);
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1176e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        int previous = mValue;
1177e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setValue(current);
1178206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        notifyChange(previous, current);
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1180fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
118168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1182206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Sets the <code>alpha</code> of the {@link Paint} for drawing the selector
1183206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * wheel.
118468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1185206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @SuppressWarnings("unused")
1186206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    // Called by ShowInputControlsAnimator via reflection
1187206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void setSelectorPaintAlpha(int alpha) {
1188206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSelectorPaint.setAlpha(alpha);
1189206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mDrawSelectorWheel) {
1190206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
119468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
119558f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov     * @return If the <code>event</code> is in the <code>view</code>.
119668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
119758f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov    private boolean isEventInViewHitRect(MotionEvent event, View view) {
119858f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov        view.getHitRect(mTempRect);
1199206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return mTempRect.contains((int) event.getX(), (int) event.getY());
1200206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1203206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Sets if to <code>drawSelectionWheel</code>.
1204206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
12053fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    private void setDrawScrollWheel(boolean drawSelectorWheel) {
1206206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDrawSelectorWheel = drawSelectorWheel;
1207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // do not fade if the selector wheel not shown
1208206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setVerticalFadingEdgeEnabled(drawSelectorWheel);
12093fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
12103fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        if (mFlingable && mDrawSelectorWheel
12113fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov                && AccessibilityManager.getInstance(mContext).isEnabled()) {
12123fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            AccessibilityManager.getInstance(mContext).interrupt();
12133fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            String text = mContext.getString(R.string.number_picker_increment_scroll_action);
12143fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            mInputText.setContentDescription(text);
12153fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            mInputText.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
12163fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            mInputText.setContentDescription(null);
12173fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        }
1218206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1219206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
12209cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private void initializeScrollWheel() {
12219cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mInitialScrollOffset != Integer.MIN_VALUE) {
12229cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            return;
12239cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
12249cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        int[] selectorIndices = getSelectorIndices();
12259cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        int totalTextHeight = selectorIndices.length * mTextSize;
122601fa0d7aae1a551e1e7cfb90d2aeaf2fcb3978afSvetoslav Ganov        float totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
122701fa0d7aae1a551e1e7cfb90d2aeaf2fcb3978afSvetoslav Ganov        float textGapCount = selectorIndices.length - 1;
122801fa0d7aae1a551e1e7cfb90d2aeaf2fcb3978afSvetoslav Ganov        int selectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
12299cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectorElementHeight = mTextSize + selectorTextGapHeight;
1230eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        // Ensure that the middle item is positioned the same as the text in mInputText
1231eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        int editTextTextPosition = mInputText.getBaseline() + mInputText.getTop();
1232eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        mInitialScrollOffset = editTextTextPosition -
1233eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase                (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
12346a19fcde07527706c2a9175e0b9f2a71549b7ffeSvetoslav Ganov        mCurrentScrollOffset = mInitialScrollOffset;
12359cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
12369cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    }
12379cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1238206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1239206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Callback invoked upon completion of a given <code>scroller</code>.
1240206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1241206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void onScrollerFinished(Scroller scroller) {
1242206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller == mFlingScroller) {
1243206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            postAdjustScrollerCommand(0);
12449cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1246206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
12479cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            showInputControls(mShowInputControlsAnimimationDuration);
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1250fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1251206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
12529cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Handles transition to a given <code>scrollState</code>
125350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
12549cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private void onScrollStateChange(int scrollState) {
12559cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mScrollState == scrollState) {
12569cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            return;
12579cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
12589cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mScrollState = scrollState;
12599cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mOnScrollListener != null) {
12604bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            mOnScrollListener.onScrollStateChange(this, scrollState);
126150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
126250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
126350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
126450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1265206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flings the selector with the given <code>velocityY</code>.
1266206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1267206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void fling(int velocityY) {
1268206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = 0;
1269206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Scroller flingScroller = mFlingScroller;
1270206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1271e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel) {
1272206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (velocityY > 0) {
1273206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
1274206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1275206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                flingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
1276206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1277206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1278206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (velocityY > 0) {
1279e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                int maxY = mTextSize * (mValue - mMinValue);
1280206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, maxY);
1281206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1282e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                int startY = mTextSize * (mMaxValue - mValue);
1283206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int maxY = startY;
1284206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                flingScroller.fling(0, startY, 0, velocityY, 0, 0, 0, maxY);
12852bf761c20253222a763bce9a63d14c48ab08a556Suchi Amalapurapu            }
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1287206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1288206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        invalidate();
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Hides the input controls which is the up/down arrows and the text field.
1293206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1294206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void hideInputControls() {
1295206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.cancel();
1296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setVisibility(INVISIBLE);
1297206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setVisibility(INVISIBLE);
1298206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setVisibility(INVISIBLE);
1299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1300c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Show the input controls by making them visible and animating the alpha
1303206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * property up/down arrows.
13049cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     *
13059cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * @param animationDuration The duration of the animation.
1306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
13079cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private void showInputControls(long animationDuration) {
1308206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
1309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setVisibility(VISIBLE);
13109cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mShowInputControlsAnimator.setDuration(animationDuration);
1311206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.start();
1312206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1313c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1314206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1315206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the visibility state of the increment and decrement buttons.
1316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void updateIncrementAndDecrementButtonsVisibilityState() {
1318e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel || mValue < mMaxValue) {
1319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mIncrementButton.setVisibility(VISIBLE);
1320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mIncrementButton.setVisibility(INVISIBLE);
1322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1323e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel || mValue > mMinValue) {
1324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mDecrementButton.setVisibility(VISIBLE);
1325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1326206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mDecrementButton.setVisibility(INVISIBLE);
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1331206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The selector indices array with proper values with the current as
1332206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *         the middle one.
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1334206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int[] getSelectorIndices() {
1335e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        int current = getValue();
1336206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] != current) {
1337206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            for (int i = 0; i < mSelectorIndices.length; i++) {
1338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
1339e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                if (mWrapSelectorWheel) {
1340206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    selectorIndex = getWrappedSelectorIndex(selectorIndex);
1341206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1342206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mSelectorIndices[i] = selectorIndex;
1343206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                ensureCachedScrollSelectorValue(mSelectorIndices[i]);
1344206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1345206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1346206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return mSelectorIndices;
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1348fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
134968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The wrapped index <code>selectorIndex</code> value.
135168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1352206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getWrappedSelectorIndex(int selectorIndex) {
1353e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex > mMaxValue) {
1354e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMinValue + (selectorIndex - mMaxValue) % (mMaxValue - mMinValue) - 1;
1355e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else if (selectorIndex < mMinValue) {
1356e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMaxValue - (mMinValue - selectorIndex) % (mMaxValue - mMinValue) + 1;
1357206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1358206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return selectorIndex;
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1360fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1361206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1362206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Increments the <code>selectorIndices</code> whose string representations
1363206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1364206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1365206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void incrementScrollSelectorIndices(int[] selectorIndices) {
1366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length - 1; i++) {
1367206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i + 1];
1368206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1369206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1;
1370e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) {
1371e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMinValue;
1372206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1373206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex;
1374206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1375206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1376fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1377206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1378206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Decrements the <code>selectorIndices</code> whose string representations
1379206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1380206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1381206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void decrementSelectorIndices(int[] selectorIndices) {
1382206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = selectorIndices.length - 1; i > 0; i--) {
1383206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i - 1];
1384206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1385206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[1] - 1;
1386e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) {
1387e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMaxValue;
1388206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1389206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[0] = nextScrollSelectorIndex;
1390206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1391206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1392fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1393206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1394206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Ensures we have a cached string representation of the given <code>
1395206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * selectorIndex</code>
1396206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * to avoid multiple instantiations of the same string.
1397206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1398206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void ensureCachedScrollSelectorValue(int selectorIndex) {
1399206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        SparseArray<String> cache = mSelectorIndexToStringCache;
1400206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String scrollSelectorValue = cache.get(selectorIndex);
1401206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scrollSelectorValue != null) {
1402206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1403206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1404e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex < mMinValue || selectorIndex > mMaxValue) {
1405206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scrollSelectorValue = "";
1406206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1407206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues != null) {
1408e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                int displayedValueIndex = selectorIndex - mMinValue;
140950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                scrollSelectorValue = mDisplayedValues[displayedValueIndex];
1410206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1411206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollSelectorValue = formatNumber(selectorIndex);
14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1414206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        cache.put(selectorIndex, scrollSelectorValue);
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1416fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1417206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private String formatNumber(int value) {
1418e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return (mFormatter != null) ? mFormatter.format(value) : String.valueOf(value);
1419206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1421206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void validateInputTextView(View v) {
1422206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String str = String.valueOf(((TextView) v).getText());
1423206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (TextUtils.isEmpty(str)) {
1424206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Restore to the old value as we don't allow empty values
1425206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
1426206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1427206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Check the new value and ensure it's in range
1428206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int current = getSelectedPos(str.toString());
1429206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(current);
14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1431206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1432fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1433206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1434206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the view of this NumberPicker. If displayValues were specified in
14359cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * the string corresponding to the index specified by the current value will
14369cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * be returned. Otherwise, the formatter specified in {@link #setFormatter}
14379cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * will be used to format the number.
1438206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1439206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void updateInputTextView() {
1440206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /*
1441206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * If we don't have displayed values then use the current number else
1442206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * find the correct value in the displayed values for the current
1443206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * number.
1444206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
1445206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mDisplayedValues == null) {
1446e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setText(formatNumber(mValue));
1447206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1448e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setText(mDisplayedValues[mValue - mMinValue]);
14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1450206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setSelection(mInputText.getText().length());
14513fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
14523fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        if (mFlingable && AccessibilityManager.getInstance(mContext).isEnabled()) {
14533fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            String text = mContext.getString(R.string.number_picker_increment_scroll_mode,
14543fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov                    mInputText.getText());
14553fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov            mInputText.setContentDescription(text);
14563fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov        }
1457206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1458fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1459206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1460206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Notifies the listener, if registered, of a change of the value of this
1461206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * NumberPicker.
1462206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1463206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void notifyChange(int previous, int current) {
1464cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        if (mOnValueChangeListener != null) {
1465cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov            mOnValueChangeListener.onValueChange(this, previous, mValue);
1466206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1467206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1469206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1470206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts a command for updating the current value every <code>updateMillis
1471206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * </code>.
1472206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1473206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postUpdateValueFromLongPress(int updateMillis) {
1474206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.clearFocus();
1475206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
1476206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mUpdateFromLongPressCommand == null) {
1477206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mUpdateFromLongPressCommand = new UpdateValueFromLongPressCommand();
1478206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1479206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mUpdateFromLongPressCommand.setUpdateStep(updateMillis);
1480206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(mUpdateFromLongPressCommand);
1481206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1483206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1484206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Removes all pending callback from the message queue.
1485206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1486206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void removeAllCallbacks() {
1487206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mUpdateFromLongPressCommand != null) {
1488206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mUpdateFromLongPressCommand);
1489206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1490206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mAdjustScrollerCommand != null) {
1491206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mAdjustScrollerCommand);
1492206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1493206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand != null) {
1494206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1498206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1499206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The selected index given its displayed <code>value</code>.
1500206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1501206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getSelectedPos(String value) {
15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisplayedValues == null) {
1503aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            try {
1504206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
1505aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            } catch (NumberFormatException e) {
1506206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
1507aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            }
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
15099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < mDisplayedValues.length; i++) {
1510206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Don't force the user to type in jan when ja will do
1511206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                value = value.toLowerCase();
1512206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
1513e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    return mMinValue + i;
15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1516fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1517206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            /*
1518206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov             * The user might have typed in a number into the month field i.e.
15199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             * 10 instead of OCT so support that too.
15209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             */
15219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1522206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
15239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
1524fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1525206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
15269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1528e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
15299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1532206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link SetSelectionCommand} from the given <code>selectionStart
1533206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * </code> to
1534206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * <code>selectionEnd</code>.
15359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1536206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
1537206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand == null) {
1538206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSetSelectionCommand = new SetSelectionCommand();
1539206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1540206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
1541206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1542206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionStart = selectionStart;
1543206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionEnd = selectionEnd;
1544206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(mSetSelectionCommand);
15459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
154668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
154768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1548206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link AdjustScrollerCommand} within the given <code>
1549206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * delayMillis</code>
1550206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * .
155168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1552206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postAdjustScrollerCommand(int delayMillis) {
1553206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mAdjustScrollerCommand == null) {
1554206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mAdjustScrollerCommand = new AdjustScrollerCommand();
1555206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1556206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mAdjustScrollerCommand);
1557206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1558206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        postDelayed(mAdjustScrollerCommand, delayMillis);
155968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
156068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
156168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1562206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Filter for accepting only valid indices or prefixes of the string
1563206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * representation of valid indices.
156468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1565206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class InputTextFilter extends NumberKeyListener {
1566206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1567206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // XXX This doesn't allow for range limits when controlled by a
1568206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // soft input method!
1569206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public int getInputType() {
1570206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return InputType.TYPE_CLASS_TEXT;
1571206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1572206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1573206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1574206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        protected char[] getAcceptedChars() {
1575206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return DIGIT_CHARACTERS;
1576206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1577206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1578206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1579206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
1580206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int dstart, int dend) {
1581206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues == null) {
1582206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
1583206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (filtered == null) {
1584206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    filtered = source.subSequence(start, end);
1585206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1586206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1587206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1588206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1589206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1590206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if ("".equals(result)) {
1591206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return result;
1592206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1593206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int val = getSelectedPos(result);
1594206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1595206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                /*
1596206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * Ensure the user can't type in a value greater than the max
1597206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * allowed. We have to allow less than min as the user might
1598206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * want to delete some numbers and then type a new number.
1599206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 */
1600e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                if (val > mMaxValue) {
1601206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1602206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
1603206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return filtered;
1604206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1605206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1606206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = String.valueOf(source.subSequence(start, end));
1607206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (TextUtils.isEmpty(filtered)) {
1608206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1609206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1610206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1611206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1612206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String str = String.valueOf(result).toLowerCase();
1613206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                for (String val : mDisplayedValues) {
1614206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    String valLowerCase = val.toLowerCase();
1615206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (valLowerCase.startsWith(str)) {
1616206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postSetSelectionCommand(result.length(), val.length());
1617206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        return val.subSequence(dstart, val.length());
1618206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
1619206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1620206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return "";
1621206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1622206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1623206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1624206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1625206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1626206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for setting the input text selection.
1627206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1628206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class SetSelectionCommand implements Runnable {
1629206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionStart;
1630206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1631206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionEnd;
1632206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1633206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1634206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setSelection(mSelectionStart, mSelectionEnd);
1635206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1636206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1637206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1638206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1639206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for adjusting the scroller to show in its center the closest of
1640206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * the displayed items.
1641206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1642206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class AdjustScrollerCommand implements Runnable {
1643206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1644206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = 0;
164534c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            if (mInitialScrollOffset == mCurrentScrollOffset) {
164634c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                updateInputTextView();
16479cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                showInputControls(mShowInputControlsAnimimationDuration);
164834c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                return;
164934c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            }
165034c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            // adjust to the closest value
1651206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
165234c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
165334c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
165434c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov            }
16559cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
1656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
1657206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1658206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1659206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1660206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1661206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for updating the current value from a long press.
1662206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1663206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class UpdateValueFromLongPressCommand implements Runnable {
1664206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mUpdateStep = 0;
1665206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1666206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private void setUpdateStep(int updateStep) {
1667206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mUpdateStep = updateStep;
1668206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1669206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1670206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1671e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            changeCurrent(mValue + mUpdateStep);
16724bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            postDelayed(this, mLongPressUpdateInterval);
1673206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
167468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
16757762d93621f508f4410fd37b5bbdfec0071b0420Paul Westbrook}
1676