NumberPicker.java revision 4528b4e882584745f48263fa6626987e63832a2a
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
1968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookimport android.annotation.Widget;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
21206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.ColorStateList;
22206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.TypedArray;
23206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Canvas;
24206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Color;
25206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Paint;
26e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.graphics.Paint.Align;
27b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.graphics.Rect;
289cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.graphics.drawable.Drawable;
29aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganovimport android.os.Bundle;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputFilter;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputType;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.Spanned;
33206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.text.TextUtils;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.NumberKeyListener;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
36206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.util.SparseArray;
379cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.util.TypedValue;
38206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.KeyEvent;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.LayoutInflater;
40b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.view.LayoutInflater.Filter;
41206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.MotionEvent;
42206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.VelocityTracker;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
44206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.ViewConfiguration;
453fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
463fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityManager;
478a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
48d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport android.view.accessibility.AccessibilityNodeProvider;
499cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.view.animation.DecelerateInterpolator;
50a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganovimport android.view.inputmethod.EditorInfo;
51206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.inputmethod.InputMethodManager;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
53b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport com.android.internal.R;
54b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
55d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.ArrayList;
56d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.Collections;
57d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.List;
58d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
5968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook/**
604bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * A widget that enables the user to select a number form a predefined range.
61d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * There are two flavors of this widget and which one is presented to the user
62d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * depends on the current theme.
63d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <ul>
64d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <li>
65d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * If the current theme is derived from {@link android.R.style#Theme} the widget
66d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * presents the current value as an editable input field with an increment button
67d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * above and a decrement button below. Long pressing the buttons allows for a quick
68d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * change of the current value. Tapping on the input field allows to type in
69d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * a desired value.
70d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </li>
71d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <li>
72d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * If the current theme is derived from {@link android.R.style#Theme_Holo} or
73d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * {@link android.R.style#Theme_Holo_Light} the widget presents the current
74d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * value as an editable input field with a lesser value above and a greater
75d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * value below. Tapping on the lesser or greater value selects it by animating
76d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * the number axis up or down to make the chosen value current. Flinging up
77d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * or down allows for multiple increments or decrements of the current value.
78d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * Long pressing on the lesser and greater values also allows for a quick change
79d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * of the current value. Tapping on the current value allows to type in a
80d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * desired value.
81d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </li>
82d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </ul>
834bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * <p>
844bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * For an example of using this widget, see {@link android.widget.TimePicker}.
854bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * </p>
8668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook */
8768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook@Widget
8868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpublic class NumberPicker extends LinearLayout {
89fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
9068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
913f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * The number of items show in the selector wheel.
923f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     */
93d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int SELECTOR_WHEEL_ITEM_COUNT = 3;
943f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov
953f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov    /**
96e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default update interval during long press.
97e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
98e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300;
99e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
100e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
101206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The index of the middle selector item.
10268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
103d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int SELECTOR_MIDDLE_ITEM_INDEX = SELECTOR_WHEEL_ITEM_COUNT / 2;
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
106206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The coefficient by which to adjust (divide) the max fling velocity.
10768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8;
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
111206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the duration for adjusting the selector wheel.
112206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
114fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * The duration of scrolling while snapping to a given position.
117b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
118fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov    private static final int SNAP_SCROLL_DURATION = 300;
119b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
120b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
121b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The strength of fading in the top and bottom while drawing the selector.
122206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
123b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f;
124206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
125206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
126b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The default unscaled height of the selection divider.
127206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
128b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2;
129206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
130206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
131d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The default unscaled distance between the selection dividers.
132b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
133d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48;
134b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
135b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
136d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The resource id for the default layout.
137b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
138d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int DEFAULT_LAYOUT_RESOURCE_ID = R.layout.number_picker;
1399cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1409cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
141206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The numbers accepted by the input text's {@link Filter}
142206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
143206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final char[] DIGIT_CHARACTERS = new char[] {
144206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
145fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov    };
146206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
147206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1489f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * Constant for unspecified size.
1499f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     */
1509f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    private static final int SIZE_UNSPECIFIED = -1;
1519f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
1529f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    /**
153206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Use a custom NumberPicker formatting callback to use two-digit minutes
154206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * strings like "01". Keeping a static formatter etc. is the most efficient
155206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * way to do this; it avoids creating temporary objects on every call to
156206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * format().
157e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
158e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @hide
159206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
160206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER = new NumberPicker.Formatter() {
161206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final StringBuilder mBuilder = new StringBuilder();
162206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
163f0ef665299d795df7905897e1c337e37891dafefJean-Baptiste Queru        final java.util.Formatter mFmt = new java.util.Formatter(mBuilder, java.util.Locale.US);
164206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
165206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final Object[] mArgs = new Object[1];
166206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
167e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value) {
168206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mArgs[0] = value;
169206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mBuilder.delete(0, mBuilder.length());
170206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mFmt.format("%02d", mArgs);
171206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return mFmt.toString();
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
175206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
176206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The increment button.
177206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
178206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mIncrementButton;
179206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
180206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
181206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The decrement button.
182206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
183206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mDecrementButton;
184206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
185206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
186206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The text for showing the current value.
187206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
188206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final EditText mInputText;
189206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
190206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
191d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The distance between the two selection dividers.
192d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
193d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final int mSelectionDividersDistance;
194d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
195d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
196ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * The min height of this widget.
197ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
198ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final int mMinHeight;
199ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
200ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
201e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * The max height of this widget.
202e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
203e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    private final int mMaxHeight;
204e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
205e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
206e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * The max width of this widget.
207e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
208ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final int mMinWidth;
209ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
210ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
211ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * The max width of this widget.
212ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
213ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private int mMaxWidth;
214ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
215ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
216ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Flag whether to compute the max width.
217ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
218ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final boolean mComputeMaxWidth;
219e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
220e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
221206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of the text.
222206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
223206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mTextSize;
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
225206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
226b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The height of the gap between text elements if the selector wheel.
227b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
228b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private int mSelectorTextGapHeight;
229b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
230b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
231206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The values to be displayed instead the indices.
232206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String[] mDisplayedValues;
23468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
23568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
23668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Lower value of the range of numbers allowed for the NumberPicker
23768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
238e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMinValue;
23968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
24068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
24168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Upper value of the range of numbers allowed for the NumberPicker
24268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
243e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMaxValue;
24468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
24568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
24668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Current value of this NumberPicker
24768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
248e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mValue;
24968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
25068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
251206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Listener to be notified upon current value change.
25268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
253cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    private OnValueChangeListener mOnValueChangeListener;
25450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
25550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
25650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Listener to be notified upon scroll state change.
25750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
25850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private OnScrollListener mOnScrollListener;
259206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
260206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
261206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Formatter for for displaying the current value.
262206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Formatter mFormatter;
264fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
265206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
266206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The speed for updating the value form long press.
267206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
268e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL;
269fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
27068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
271206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Cache for the string representation of selector indices.
272206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
273206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final SparseArray<String> mSelectorIndexToStringCache = new SparseArray<String>();
274206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
275206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
276206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The selector indices whose value are show by the selector.
277206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
278d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final int[] mSelectorIndices = new int[SELECTOR_WHEEL_ITEM_COUNT];
279206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
280206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
281206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Paint} for drawing the selector.
282206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
283b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private final Paint mSelectorWheelPaint;
284206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
285206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
286232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * The {@link Drawable} for pressed virtual (increment/decrement) buttons.
287232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
288232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private final Drawable mVirtualButtonPressedDrawable;
289232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
290232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of a selector element (text + gap).
292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
293206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mSelectorElementHeight;
294206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
295206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The initial offset of the scroll selector.
297206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
298206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mInitialScrollOffset = Integer.MIN_VALUE;
299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
300206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The current offset of the scroll selector.
302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
303206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mCurrentScrollOffset;
304206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
305206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for flinging the selector.
307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
308206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mFlingScroller;
309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
311206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for adjusting the selector.
312206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
313206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mAdjustScroller;
314206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
315206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The previous Y coordinate while scrolling the selector.
317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
318206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mPreviousScrollerY;
319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for setting the input text selection.
322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private SetSelectionCommand mSetSelectionCommand;
324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
326b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Handle to the reusable command for changing the current value from long
327b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * press by one.
328206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
329b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand;
330206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
331206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
332d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Command for beginning an edit of the current value via IME on long press.
333b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
334d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private BeginSoftInputOnLongPressCommand mBeginSoftInputOnLongPressCommand;
335b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
336b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
337206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last down event.
338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
339206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastDownEventY;
340206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
341206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
342d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The time of the last down event.
343206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
344d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private long mLastDownEventTime;
345206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
346206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
347d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The Y position of the last down or move event.
348206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
349d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private float mLastDownOrMoveEventY;
350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
351206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
352206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Determines speed during touch scrolling.
353206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
354206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private VelocityTracker mVelocityTracker;
355206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
356206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
357206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledTouchSlop()
358206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
359206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mTouchSlop;
360206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
361206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
362206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMinimumFlingVelocity()
363206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
364206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMinimumFlingVelocity;
365206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
367206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMaximumFlingVelocity()
368206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
369206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMaximumFlingVelocity;
370206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
371206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
372206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag whether the selector should wrap around.
373206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
374e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean mWrapSelectorWheel;
375206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
376206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
377206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The back ground color used to optimize scroller fading.
37868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
379206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mSolidColor;
380206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
381206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
382d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Flag whether this widget has a selector wheel.
3834243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov     */
384d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final boolean mHasSelectorWheel;
3854243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov
3864243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov    /**
3879cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Divider for showing item to be selected while scrolling
3889cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
3899cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final Drawable mSelectionDivider;
3909cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
3919cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
3929cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * The height of the selection divider.
3939cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
3949cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final int mSelectionDividerHeight;
3959cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
3969cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
397d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The current scroll state of the number picker.
398206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
399d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
400206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
401206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
402d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Flag whether to ignore move events - we ignore such when we show in IME
403d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * to prevent the content from scrolling.
40450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
405d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean mIngonreMoveEvents;
40650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
40750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
408d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Flag whether to show soft input on tap.
4099cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
410d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean mShowSoftInputOnTap;
4119cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4129cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
413d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The top of the top selection divider.
414b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
415d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mTopSelectionDividerTop;
416b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
417b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
418d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The bottom of the bottom selection divider.
419a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     */
420d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mBottomSelectionDividerBottom;
421d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
422d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
423d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The virtual id of the last hovered child.
424d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
425d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mLastHoveredChildVirtualViewId;
426d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
427d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
428232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Whether the increment virtual button is pressed.
429232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
430232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private boolean mIncrementVirtualButtonPressed;
431232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
432232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
433232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Whether the decrement virtual button is pressed.
434232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
435232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private boolean mDecrementVirtualButtonPressed;
436232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
437232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
438d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Provider to report to clients the semantic structure of this widget.
439d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
440d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private AccessibilityNodeProviderImpl mAccessibilityNodeProvider;
441a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
442a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    /**
443232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Helper class for managing pressed state of the virtual buttons.
444232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
445232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private final PressedStateHelper mPressedStateHelper;
446232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
447232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
448e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for changes of the current value.
449206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
450cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public interface OnValueChangeListener {
451e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
452206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /**
4534bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * Called upon a change of the current value.
4544bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
455206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param picker The NumberPicker associated with this listener.
456206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param oldVal The previous value.
457206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param newVal The new value.
458206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
459e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        void onValueChange(NumberPicker picker, int oldVal, int newVal);
460206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
461206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
462206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
463e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for the picker scroll state.
46450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
46550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public interface OnScrollListener {
46650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
46750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
46850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The view is not scrolling.
46950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
47050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_IDLE = 0;
47150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
47250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
473d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov         * The user is scrolling using touch, and his finger is still on the screen.
47450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
47550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_TOUCH_SCROLL = 1;
47650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
47750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
47850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user had previously been scrolling using touch and performed a fling.
47950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
48050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_FLING = 2;
48150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
48250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
483e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Callback invoked while the number picker scroll state has changed.
48450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
485e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param view The view whose scroll state is being reported.
486e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param scrollState The current scroll state. One of
487e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE},
488e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_TOUCH_SCROLL} or
489e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE}.
49050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
4914bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        public void onScrollStateChange(NumberPicker view, int scrollState);
49250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
49350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
49450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
495e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface used to format current value into a string for presentation.
496206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
497206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public interface Formatter {
4984bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
4994bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        /**
500e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Formats a string representation of the current value.
5014bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
5024bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @param value The currently selected value.
5034bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @return A formatted string representation.
5044bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         */
505e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value);
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
507fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
50868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
5094bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
5104bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
5114bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param context The application environment.
5124bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
5134bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public NumberPicker(Context context) {
5144bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        this(context, null);
5154bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
5164bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
5174bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
5184bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
519206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
520206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context The application environment.
521206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs A collection of attributes.
52268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public NumberPicker(Context context, AttributeSet attrs) {
524206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        this(context, attrs, R.attr.numberPickerStyle);
525206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
526206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
527206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
528206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Create a new number picker
529206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
530206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context the application environment.
531206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs a collection of attributes.
532206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param defStyle The default style to apply to this view.
533206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
534206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
535206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super(context, attrs, defStyle);
536206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
537206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // process style attributes
538d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        TypedArray attributesArray = context.obtainStyledAttributes(
539d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                attrs, R.styleable.NumberPicker, defStyle, 0);
540d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int layoutResId = attributesArray.getResourceId(
541d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalLayout, DEFAULT_LAYOUT_RESOURCE_ID);
542d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
543d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mHasSelectorWheel = (layoutResId != DEFAULT_LAYOUT_RESOURCE_ID);
544d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
545206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
546d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
5479cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
548d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
549d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int defSelectionDividerHeight = (int) TypedValue.applyDimension(
550d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
5519cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                getResources().getDisplayMetrics());
5529cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
5539cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
554d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
555d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int defSelectionDividerDistance = (int) TypedValue.applyDimension(
556d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE,
557d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                getResources().getDisplayMetrics());
558d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mSelectionDividersDistance = attributesArray.getDimensionPixelSize(
559d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_selectionDividersDistance, defSelectionDividerDistance);
560d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
561e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov        mMinHeight = attributesArray.getDimensionPixelSize(
562e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov                R.styleable.NumberPicker_internalMinHeight, SIZE_UNSPECIFIED);
563d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
564e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov        mMaxHeight = attributesArray.getDimensionPixelSize(
565e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov                R.styleable.NumberPicker_internalMaxHeight, SIZE_UNSPECIFIED);
5669f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED
5679f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                && mMinHeight > mMaxHeight) {
568ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            throw new IllegalArgumentException("minHeight > maxHeight");
569ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
570d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
571d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mMinWidth = attributesArray.getDimensionPixelSize(
572d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalMinWidth, SIZE_UNSPECIFIED);
573d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
574d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mMaxWidth = attributesArray.getDimensionPixelSize(
575d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalMaxWidth, SIZE_UNSPECIFIED);
5769f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED
5779f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                && mMinWidth > mMaxWidth) {
578ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            throw new IllegalArgumentException("minWidth > maxWidth");
579ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
580d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
581f7c83bc4d3e1996b491824902a6a0f98ac69bedeSvetoslav Ganov        mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED);
582206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
583232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mVirtualButtonPressedDrawable = attributesArray.getDrawable(
584232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                R.styleable.NumberPicker_virtualButtonPressedDrawable);
585232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
586d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        attributesArray.recycle();
5879cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
588232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mPressedStateHelper = new PressedStateHelper();
589232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
590206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // By default Linearlayout that we extend is not drawn. This is
591206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // its draw() method is not called but dispatchDraw() is called
592206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // directly (see ViewGroup.drawChild()). However, this class uses
593206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // the fading edge effect implemented by View and we need our
594206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // draw() method to be called. Therefore, we declare we will draw.
595d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        setWillNotDraw(!mHasSelectorWheel);
596206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
597206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
598206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                Context.LAYOUT_INFLATER_SERVICE);
599d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        inflater.inflate(layoutResId, this, true);
60068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
601206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnClickListener onClickListener = new OnClickListener() {
60268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            public void onClick(View v) {
603b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                hideSoftInput();
604206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
605206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
606d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    changeValueByOne(true);
607206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
608d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    changeValueByOne(false);
60968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
61068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
61168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        };
61268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
613206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnLongClickListener onLongClickListener = new OnLongClickListener() {
614206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public boolean onLongClick(View v) {
615b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                hideSoftInput();
616206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
617206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
618d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(true, 0);
619206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
620d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(false, 0);
621206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
622206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return true;
623206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
624206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        };
62568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
626206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // increment button
627d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
628d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton = (ImageButton) findViewById(R.id.increment);
629d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setOnClickListener(onClickListener);
630d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setOnLongClickListener(onLongClickListener);
631d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
632d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton = null;
633d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
634206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
635206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // decrement button
636d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
637d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton = (ImageButton) findViewById(R.id.decrement);
638d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setOnClickListener(onClickListener);
639d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setOnLongClickListener(onLongClickListener);
640d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
641d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton = null;
642d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
643206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
644206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // input text
645012dd5a461f18a2e5dad38c60282fac1c21ff7feSvetoslav Ganov        mInputText = (EditText) findViewById(R.id.numberpicker_input);
646206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setOnFocusChangeListener(new OnFocusChangeListener() {
647206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onFocusChange(View v, boolean hasFocus) {
648a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                if (hasFocus) {
649a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.selectAll();
650a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                } else {
651a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.setSelection(0, 0);
652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    validateInputTextView(v);
65368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
65468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
655206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setFilters(new InputFilter[] {
657206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            new InputTextFilter()
658206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
65968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
660206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
661a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        mInputText.setImeOptions(EditorInfo.IME_ACTION_DONE);
662206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
663206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // initialize constants
664206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ViewConfiguration configuration = ViewConfiguration.get(context);
665206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = configuration.getScaledTouchSlop();
666206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
667206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity()
668206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;
669206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTextSize = (int) mInputText.getTextSize();
670206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
671206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the selector wheel paint
672206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Paint paint = new Paint();
673206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setAntiAlias(true);
674206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextAlign(Align.CENTER);
675206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextSize(mTextSize);
676206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTypeface(mInputText.getTypeface());
677206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ColorStateList colors = mInputText.getTextColors();
678206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int color = colors.getColorForState(ENABLED_STATE_SET, Color.WHITE);
679206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setColor(color);
680b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorWheelPaint = paint;
681206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
682206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the fling and adjust scrollers
683bf80562d22b2bbe7944d80d0524c69d0238010cbSvetoslav Ganov        mFlingScroller = new Scroller(getContext(), null, true);
6849cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
685fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
686206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateInputTextView();
6874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
6884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        // If not explicitly specified this view is important for accessibility.
6894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
6904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
6914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
692206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
693206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
694206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
6959cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
696d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
697d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onLayout(changed, left, top, right, bottom);
698d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return;
699d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
700ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int msrdWdth = getMeasuredWidth();
701ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int msrdHght = getMeasuredHeight();
702ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
703ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        // Input text centered horizontally.
704ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
705ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
706ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
707ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
708ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
709ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
710ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
711ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
712d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (changed) {
713b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            // need to do all this when we know our size
714b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            initializeSelectorWheel();
715b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            initializeFadingEdges();
716d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mTopSelectionDividerTop = (getHeight() - mSelectionDividersDistance) / 2
717d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    - mSelectionDividerHeight;
718d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mBottomSelectionDividerBottom = mTopSelectionDividerTop + 2 * mSelectionDividerHeight
719d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    + mSelectionDividersDistance;
720b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
721206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
722206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
723206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
724e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
725d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
726d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
727d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return;
728d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
729698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        // Try greedily to fit the max width and height.
730698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth);
731698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight);
732ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
733698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        // Flag if we are measured with width or height less than the respective min.
7349f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, getMeasuredWidth(),
7359f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                widthMeasureSpec);
7369f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, getMeasuredHeight(),
7379f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                heightMeasureSpec);
738698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        setMeasuredDimension(widthSize, heightSize);
739e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
740e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
741d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
742d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Move to the final position of a scroller. Ensures to force finish the scroller
743d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * and if it is not at its final position a scroll of the selector wheel is
744d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * performed to fast forward to the final position.
745d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
746d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param scroller The scroller to whose final position to get.
747d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return True of the a move was performed, i.e. the scroller was not in final position.
748d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
749d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean moveToFinalScrollerPosition(Scroller scroller) {
750d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        scroller.forceFinished(true);
751d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int amountToScroll = scroller.getFinalY() - scroller.getCurrY();
752d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int futureScrollOffset = (mCurrentScrollOffset + amountToScroll) % mSelectorElementHeight;
753d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int overshootAdjustment = mInitialScrollOffset - futureScrollOffset;
754d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (overshootAdjustment != 0) {
755d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (Math.abs(overshootAdjustment) > mSelectorElementHeight / 2) {
756d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (overshootAdjustment > 0) {
757d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    overshootAdjustment -= mSelectorElementHeight;
758d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
759d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    overshootAdjustment += mSelectorElementHeight;
760d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
761d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
762d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            amountToScroll += overshootAdjustment;
763d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            scrollBy(0, amountToScroll);
764d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
765d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
766d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
767d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
768d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
769e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    @Override
770206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onInterceptTouchEvent(MotionEvent event) {
771d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel || !isEnabled()) {
77251c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
77351c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
774d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int action = event.getActionMasked();
775d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (action) {
776d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_DOWN: {
777206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                removeAllCallbacks();
778d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.INVISIBLE);
779d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownOrMoveEventY = mLastDownEventY = event.getY();
780d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownEventTime = event.getEventTime();
781d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mIngonreMoveEvents = false;
782d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mShowSoftInputOnTap = false;
783232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                // Handle pressed state before any state change.
784232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                if (mLastDownEventY < mTopSelectionDividerTop) {
785232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
786232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mPressedStateHelper.buttonPressDelayed(
787232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                PressedStateHelper.BUTTON_DECREMENT);
788232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
789232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
790232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
791232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mPressedStateHelper.buttonPressDelayed(
792232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                PressedStateHelper.BUTTON_INCREMENT);
793232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
794232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                }
795232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                // Make sure we support flinging inside scrollables.
79683dc45c65988e9b86e156d59f59ede48195ed1d5Svetoslav Ganov                getParent().requestDisallowInterceptTouchEvent(true);
797d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (!mFlingScroller.isFinished()) {
798d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mFlingScroller.forceFinished(true);
799d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mAdjustScroller.forceFinished(true);
800d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
801d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (!mAdjustScroller.isFinished()) {
802d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mFlingScroller.forceFinished(true);
803d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mAdjustScroller.forceFinished(true);
804d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (mLastDownEventY < mTopSelectionDividerTop) {
805b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                    hideSoftInput();
806d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(
807d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            false, ViewConfiguration.getLongPressTimeout());
808d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
809b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                    hideSoftInput();
810d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(
811d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            true, ViewConfiguration.getLongPressTimeout());
812d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
813d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mShowSoftInputOnTap = true;
814d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postBeginSoftInputOnLongPressCommand();
815206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
816d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return true;
817d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
818206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
819206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return false;
820206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
821206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
822206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
823d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public boolean onTouchEvent(MotionEvent event) {
824d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!isEnabled() || !mHasSelectorWheel) {
82551c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
82651c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
827206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mVelocityTracker == null) {
828206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mVelocityTracker = VelocityTracker.obtain();
829206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
830d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mVelocityTracker.addMovement(event);
831d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int action = event.getActionMasked();
832206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (action) {
833d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_MOVE: {
834d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (mIngonreMoveEvents) {
835d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    break;
836d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
837d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                float currentMoveY = event.getY();
838d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
839206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
840206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (deltaDownY > mTouchSlop) {
841d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        removeAllCallbacks();
8429cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                        onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
843206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
844d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
845232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY));
846d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    scrollBy(0, deltaMoveY);
847d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    invalidate();
848206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
849d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownOrMoveEventY = currentMoveY;
850d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } break;
851d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_UP: {
852d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeBeginSoftInputCommand();
853d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeChangeCurrentByOneFromLongPress();
854232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mPressedStateHelper.cancel();
855206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                VelocityTracker velocityTracker = mVelocityTracker;
856206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
857206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int initialVelocity = (int) velocityTracker.getYVelocity();
858206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
859232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    fling(initialVelocity);
8609cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
861206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
862d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    int eventY = (int) event.getY();
863d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    int deltaMoveY = (int) Math.abs(eventY - mLastDownEventY);
864d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    long deltaTime = event.getEventTime() - mLastDownEventTime;
865d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (deltaMoveY <= mTouchSlop && deltaTime < ViewConfiguration.getTapTimeout()) {
866d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        if (mShowSoftInputOnTap) {
867d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mShowSoftInputOnTap = false;
868d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            showSoftInput();
869d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        } else {
870d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            int selectorIndexOffset = (eventY / mSelectorElementHeight)
871d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                    - SELECTOR_MIDDLE_ITEM_INDEX;
872d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            if (selectorIndexOffset > 0) {
873d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                changeValueByOne(true);
874232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                mPressedStateHelper.buttonTapped(
875232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        PressedStateHelper.BUTTON_INCREMENT);
876d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            } else if (selectorIndexOffset < 0) {
877d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                changeValueByOne(false);
878232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                mPressedStateHelper.buttonTapped(
879232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        PressedStateHelper.BUTTON_DECREMENT);
880d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
881206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        }
882206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    } else {
883d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        ensureScrollWheelAdjusted();
884206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
885d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
886206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
887206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker.recycle();
888206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker = null;
889d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } break;
890206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
891206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return true;
892206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
893206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
894206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
895206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTouchEvent(MotionEvent event) {
896b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        final int action = event.getActionMasked();
897b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        switch (action) {
898b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_CANCEL:
899b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_UP:
900b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                removeAllCallbacks();
901b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                break;
902206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
903206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTouchEvent(event);
904206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
905206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
906206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
907206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchKeyEvent(KeyEvent event) {
908d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int keyCode = event.getKeyCode();
909d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (keyCode) {
910d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case KeyEvent.KEYCODE_DPAD_CENTER:
911d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case KeyEvent.KEYCODE_ENTER:
912d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeAllCallbacks();
913d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                break;
914206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
915206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchKeyEvent(event);
916206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
917206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
918206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
919206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTrackballEvent(MotionEvent event) {
920d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int action = event.getActionMasked();
921d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (action) {
922d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_CANCEL:
923d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_UP:
924d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeAllCallbacks();
925d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                break;
926206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
927206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTrackballEvent(event);
928206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
930206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
931d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    protected boolean dispatchHoverEvent(MotionEvent event) {
932d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
933d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.dispatchHoverEvent(event);
934d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
935d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
936d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int eventY = (int) event.getY();
937d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int hoveredVirtualViewId;
938d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (eventY < mTopSelectionDividerTop) {
939d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_DECREMENT;
940d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } else if (eventY > mBottomSelectionDividerBottom) {
941d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INCREMENT;
942d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } else {
943d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INPUT;
944d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
945d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int action = event.getActionMasked();
946d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeProviderImpl provider =
947d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                (AccessibilityNodeProviderImpl) getAccessibilityNodeProvider();
948d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (action) {
949d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_ENTER: {
950d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
951d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
952d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
953791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    provider.performAction(hoveredVirtualViewId,
954791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
955d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
956d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_MOVE: {
957d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (mLastHoveredChildVirtualViewId != hoveredVirtualViewId
958d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && mLastHoveredChildVirtualViewId != View.NO_ID) {
959d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        provider.sendAccessibilityEventForVirtualView(
960d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                mLastHoveredChildVirtualViewId,
961d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
962d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
963d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
964d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
965791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        provider.performAction(hoveredVirtualViewId,
966791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
967d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
968d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
969d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_EXIT: {
970d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
971d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
972d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mLastHoveredChildVirtualViewId = View.NO_ID;
973d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
974d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
975206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
976d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
977d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
978d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
979d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    @Override
980d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public void computeScroll() {
981206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Scroller scroller = mFlingScroller;
982206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
983206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scroller = mAdjustScroller;
984206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (scroller.isFinished()) {
985206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return;
986206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
987206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
988206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scroller.computeScrollOffset();
989206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int currentScrollerY = scroller.getCurrY();
990206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mPreviousScrollerY == 0) {
991206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = scroller.getStartY();
992206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
993206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scrollBy(0, currentScrollerY - mPreviousScrollerY);
994206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = currentScrollerY;
995206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
996206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            onScrollerFinished(scroller);
997206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
998206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1001fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setEnabled(boolean enabled) {
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.setEnabled(enabled);
1005d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1006d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setEnabled(enabled);
1007d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1008d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1009d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setEnabled(enabled);
1010d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1011206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setEnabled(enabled);
1012206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1013206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1014206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1015206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void scrollBy(int x, int y) {
1016b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
101734c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y > 0
101834c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
1019206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
1020206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1021206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
102234c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y < 0
102334c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
1024206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
1025206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1026206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1027206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mCurrentScrollOffset += y;
1028b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) {
1029206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset -= mSelectorElementHeight;
1030206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            decrementSelectorIndices(selectorIndices);
1031d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
1032b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
1033206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
1034206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1035206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1036b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) {
1037206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset += mSelectorElementHeight;
1038b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            incrementSelectorIndices(selectorIndices);
1039d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
1040b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
1041206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
1042206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1043206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1045fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
10464bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    @Override
10474bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public int getSolidColor() {
10484bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return mSolidColor;
10494bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
10504bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
105168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
10524bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the listener to be notified on change of the current value.
1053206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1054e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onValueChangedListener The listener.
105568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1056cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public void setOnValueChangedListener(OnValueChangeListener onValueChangedListener) {
1057cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        mOnValueChangeListener = onValueChangedListener;
105850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
105950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
106050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
10614bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set listener to be notified for scroll state changes.
106250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
1063e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onScrollListener The listener.
106450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
106550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public void setOnScrollListener(OnScrollListener onScrollListener) {
106650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mOnScrollListener = onScrollListener;
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1068fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
106968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
10704bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set the formatter to be used for formatting the current value.
10714bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
1072e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Note: If you have provided alternative values for the values this
1073e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * formatter is never invoked.
10744bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
1075206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1076e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param formatter The formatter object. If formatter is <code>null</code>,
1077e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            {@link String#valueOf(int)} will be used.
1078d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *@see #setDisplayedValues(String[])
107968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFormatter(Formatter formatter) {
1081e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (formatter == mFormatter) {
1082e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1083e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFormatter = formatter;
1085b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
10869cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
1087206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1088206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1089206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1090e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Set the current value for the number picker.
10914bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
1092e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
1093e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
1094e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
1095e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1096e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1097e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
1098e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
1099e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
1100e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1101e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1102e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
1103e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
1104e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
1105e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1106e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1107e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
1108e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
1109e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
11104bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
1111206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1112e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param value The current value.
11134bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @see #setWrapSelectorWheel(boolean)
1114e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMinValue(int)
1115e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMaxValue(int)
1116206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1117e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setValue(int value) {
1118d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        setValueInternal(value, false);
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1120fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
112168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1122d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Shows the soft input for its input text.
1123d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1124d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void showSoftInput() {
1125d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
1126d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (inputMethodManager != null) {
1127d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mHasSelectorWheel) {
1128d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.VISIBLE);
1129d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1130d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mInputText.requestFocus();
1131d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            inputMethodManager.showSoftInput(mInputText, 0);
1132d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1133d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1134d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1135d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1136d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Hides the soft input if it is active for the input text.
1137b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov     */
1138b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    private void hideSoftInput() {
1139b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
1140b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
1141b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov            inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
1142d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mHasSelectorWheel) {
1143d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.INVISIBLE);
1144d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1145b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        }
1146b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    }
1147b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov
1148b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    /**
1149ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Computes the max width if no such specified as an attribute.
1150ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
1151ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private void tryComputeMaxWidth() {
1152ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (!mComputeMaxWidth) {
1153ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            return;
1154ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1155ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        int maxTextWidth = 0;
1156ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (mDisplayedValues == null) {
1157ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            float maxDigitWidth = 0;
1158ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            for (int i = 0; i <= 9; i++) {
1159ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                final float digitWidth = mSelectorWheelPaint.measureText(String.valueOf(i));
1160ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                if (digitWidth > maxDigitWidth) {
1161ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                    maxDigitWidth = digitWidth;
1162ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                }
1163ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1164ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            int numberOfDigits = 0;
1165ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            int current = mMaxValue;
1166ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            while (current > 0) {
1167ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                numberOfDigits++;
1168ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                current = current / 10;
1169ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1170ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            maxTextWidth = (int) (numberOfDigits * maxDigitWidth);
1171ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        } else {
1172ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            final int valueCount = mDisplayedValues.length;
1173ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            for (int i = 0; i < valueCount; i++) {
1174ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                final float textWidth = mSelectorWheelPaint.measureText(mDisplayedValues[i]);
1175ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                if (textWidth > maxTextWidth) {
1176ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                    maxTextWidth = (int) textWidth;
1177ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                }
1178ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1179ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1180ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        maxTextWidth += mInputText.getPaddingLeft() + mInputText.getPaddingRight();
1181ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (mMaxWidth != maxTextWidth) {
1182ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            if (maxTextWidth > mMinWidth) {
1183ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                mMaxWidth = maxTextWidth;
1184ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            } else {
1185ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                mMaxWidth = mMinWidth;
1186ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1187ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            invalidate();
1188ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1189ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    }
1190ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
1191ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
1192e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets whether the selector wheel wraps when reaching the min/max value.
119368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
1194e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return True if the selector wheel wraps.
11954bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1196e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMinValue()
1197e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMaxValue()
119868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1199e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public boolean getWrapSelectorWheel() {
1200e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mWrapSelectorWheel;
1201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1203206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1204e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets whether the selector wheel shown during flinging/scrolling should
1205e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * wrap around the {@link NumberPicker#getMinValue()} and
1206e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getMaxValue()} values.
1207e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1208d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * By default if the range (max - min) is more than the number of items shown
1209d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * on the selector wheel the selector wheel wrapping is enabled.
1210e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
12113f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * <p>
1212d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * <strong>Note:</strong> If the number of items, i.e. the range (
1213d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()}) is less than
1214d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * the number of items shown on the selector wheel, the selector wheel will
1215d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * not wrap. Hence, in such a case calling this method is a NOP.
12163f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * </p>
1217d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
12186304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov     * @param wrapSelectorWheel Whether to wrap.
1219206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
12206304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov    public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
12213f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov        final boolean wrappingAllowed = (mMaxValue - mMinValue) >= mSelectorIndices.length;
12223f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov        if ((!wrapSelectorWheel || wrappingAllowed) && wrapSelectorWheel != mWrapSelectorWheel) {
12236304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            mWrapSelectorWheel = wrapSelectorWheel;
1224206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12284bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the speed at which the numbers be incremented and decremented when
12294bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * the up and down buttons are long pressed respectively.
1230e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1231e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default value is 300 ms.
1232e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
123368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
12344bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param intervalMillis The speed (in milliseconds) at which the numbers
1235e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            will be incremented and decremented.
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12374bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setOnLongPressUpdateInterval(long intervalMillis) {
12384bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        mLongPressUpdateInterval = intervalMillis;
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1240fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1241206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1242e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the value of the picker.
1243e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1244e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The value.
1245e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1246e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getValue() {
1247e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mValue;
1248e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1249e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1250e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1251e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the min value of the picker.
1252e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1253e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The min value
1254e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1255e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMinValue() {
1256e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
1257e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1258e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1259e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1260e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the min value of the picker.
1261206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1262e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param minValue The min value.
1263206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1264e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMinValue(int minValue) {
1265e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue == minValue) {
1266e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1267e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1268e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (minValue < 0) {
1269e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("minValue must be >= 0");
1270e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1271e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMinValue = minValue;
1272e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue > mValue) {
1273e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMinValue;
1274e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1275e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1276e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1277b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1278e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1279ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        tryComputeMaxWidth();
1280d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        invalidate();
1281206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1282206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
12834bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
1284e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the max value of the picker.
12854bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1286e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The max value.
12874bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1288e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMaxValue() {
1289e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMaxValue;
12904bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
12914bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
12924bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
1293e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the max value of the picker.
12944bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1295e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param maxValue The max value.
12964bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1297e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMaxValue(int maxValue) {
1298e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue == maxValue) {
1299e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1300e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1301e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (maxValue < 0) {
1302e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("maxValue must be >= 0");
1303e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1304e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMaxValue = maxValue;
1305e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue < mValue) {
1306e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMaxValue;
1307e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1308e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1309e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1310b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1311e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1312ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        tryComputeMaxWidth();
1313d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        invalidate();
1314e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1315e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1316e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1317e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the values to be displayed instead of string values.
1318e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1319e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The displayed values.
1320e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1321e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public String[] getDisplayedValues() {
1322e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mDisplayedValues;
1323e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1324e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1325e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1326e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the values to be displayed.
1327e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1328e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param displayedValues The displayed values.
1329e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1330e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setDisplayedValues(String[] displayedValues) {
1331e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues == displayedValues) {
1332e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1333e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1334e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mDisplayedValues = displayedValues;
1335e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues != null) {
1336e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // Allow text entry rather than strictly numeric entry.
1337e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
1338e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
1339e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else {
1340e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
1341e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1342e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1343b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
13449f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        tryComputeMaxWidth();
1345206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1346206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1347206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1348206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getTopFadingEdgeStrength() {
1349206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1351206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1352206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1353206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getBottomFadingEdgeStrength() {
1354206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1355206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1356206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1357206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1358206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDetachedFromWindow() {
1359206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
1360206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1361206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1362206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1363206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDraw(Canvas canvas) {
1364d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1365d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onDraw(canvas);
1366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1367206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1368206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float x = (mRight - mLeft) / 2;
1369206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float y = mCurrentScrollOffset;
1370206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1371232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        // draw the virtual buttons pressed state if needed
1372232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        if (mVirtualButtonPressedDrawable != null
1373232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                && mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
1374232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mDecrementVirtualButtonPressed) {
1375232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
1376232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setBounds(0, 0, mRight, mTopSelectionDividerTop);
1377232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.draw(canvas);
1378232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
1379232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mIncrementVirtualButtonPressed) {
1380232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
1381232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setBounds(0, mBottomSelectionDividerBottom, mRight,
1382232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mBottom);
1383232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.draw(canvas);
1384232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
1385232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
1386232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
13879cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // draw the selector wheel
1388b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
1389206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length; i++) {
1390206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int selectorIndex = selectorIndices[i];
1391206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
1392d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // Do not draw the middle item if input is visible since the input
1393d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // is shown only if the wheel is static and it covers the middle
1394d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // item. Otherwise, if the user starts editing the text via the
1395d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // IME he may see a dimmed version of the old value intermixed
1396d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // with the new one.
13976304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) {
13986304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
13996304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            }
1400206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            y += mSelectorElementHeight;
1401206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
14029cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1403d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // draw the selection dividers
14049cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mSelectionDivider != null) {
14059cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the top divider
1406d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int topOfTopDivider = mTopSelectionDividerTop;
14079cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
14089cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfTopDivider, mRight, bottomOfTopDivider);
14099cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
14109cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
14119cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the bottom divider
1412d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int bottomOfBottomDivider = mBottomSelectionDividerBottom;
1413d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int topOfBottomDivider = bottomOfBottomDivider - mSelectionDividerHeight;
14149cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfBottomDivider, mRight, bottomOfBottomDivider);
14159cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
14169cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1418fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
14193fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    @Override
1420791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
1421791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        // We do not want the real descendant to be considered focus search
1422791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        // since it is managed by the accessibility node provider.
1423791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
1424791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) {
1425791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                views.add(this);
1426791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                return;
1427791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
1428791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
1429791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        super.addFocusables(views, direction, focusableMode);
14303fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    }
14313fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
14328a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
14338a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
14348a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
14358a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        event.setClassName(NumberPicker.class.getName());
1436d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setScrollable(true);
1437d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setScrollY((mMinValue + mValue) * mSelectorElementHeight);
1438d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setMaxScrollY((mMaxValue - mMinValue) * mSelectorElementHeight);
14398a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
14408a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
14418a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
1442d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1443d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1444d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.getAccessibilityNodeProvider();
1445d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1446d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mAccessibilityNodeProvider == null) {
1447d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mAccessibilityNodeProvider = new AccessibilityNodeProviderImpl();
1448d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1449d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return mAccessibilityNodeProvider;
14508a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
14518a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
145268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1453ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Makes a measure spec that tries greedily to use the max value.
1454e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     *
1455e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * @param measureSpec The measure spec.
1456698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov     * @param maxSize The max value for the size.
1457ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * @return A measure spec greedily imposing the max size.
1458e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
1459698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov    private int makeMeasureSpec(int measureSpec, int maxSize) {
14609f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (maxSize == SIZE_UNSPECIFIED) {
14619f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return measureSpec;
14629f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        }
1463ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int size = MeasureSpec.getSize(measureSpec);
1464e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        final int mode = MeasureSpec.getMode(measureSpec);
1465e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        switch (mode) {
1466e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.EXACTLY:
1467ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                return measureSpec;
1468e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.AT_MOST:
1469698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov                return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY);
1470e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.UNSPECIFIED:
1471698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov                return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY);
1472e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            default:
1473ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                throw new IllegalArgumentException("Unknown measure mode: " + mode);
1474e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        }
1475e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
1476e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
1477e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
1478d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Utility to reconcile a desired size and state, with constraints imposed
1479d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * by a MeasureSpec. Tries to respect the min size, unless a different size
1480d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * is imposed by the constraints.
14819f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     *
14829f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param minSize The minimal desired size.
14839f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param measuredSize The currently measured size.
14849f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param measureSpec The current measure spec.
14859f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @return The resolved size and state.
14869f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     */
1487d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int resolveSizeAndStateRespectingMinSize(
1488d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int minSize, int measuredSize, int measureSpec) {
14899f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (minSize != SIZE_UNSPECIFIED) {
14909f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            final int desiredWidth = Math.max(minSize, measuredSize);
14919f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return resolveSizeAndState(desiredWidth, measureSpec, 0);
14929f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        } else {
14939f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return measuredSize;
14949f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        }
14959f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    }
14969f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
14979f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    /**
1498d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Resets the selector indices and clear the cached string representation of
1499d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * these indices.
1500a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     */
1501b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeSelectorWheelIndices() {
1502a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        mSelectorIndexToStringCache.clear();
1503232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
1504b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int current = getValue();
1505b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        for (int i = 0; i < mSelectorIndices.length; i++) {
1506b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
1507b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (mWrapSelectorWheel) {
1508b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                selectorIndex = getWrappedSelectorIndex(selectorIndex);
1509b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1510232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            selectorIndices[i] = selectorIndex;
1511232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            ensureCachedScrollSelectorValue(selectorIndices[i]);
1512a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
1513a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    }
1514a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov
1515a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    /**
1516d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Sets the current value of this NumberPicker.
151768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
1518d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param current The new value of the NumberPicker.
1519d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param notifyChange Whether to notify if the current value changed.
152068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1521d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void setValueInternal(int current, boolean notifyChange) {
1522e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mValue == current) {
1523206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1524206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
15259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Wrap around the values if we go past the start or end
1526e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel) {
1527206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            current = getWrappedSelectorIndex(current);
1528d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1529d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            current = Math.max(current, mMinValue);
1530d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            current = Math.min(current, mMaxValue);
15319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1532e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        int previous = mValue;
1533d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mValue = current;
1534d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        updateInputTextView();
1535d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (notifyChange) {
1536d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            notifyChange(previous, current);
1537d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1538fac14f9731ce7fc765de582c983af751aab697deSvetoslav Ganov        initializeSelectorWheelIndices();
1539fac14f9731ce7fc765de582c983af751aab697deSvetoslav Ganov        invalidate();
15409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1541fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
154268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1543b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Changes the current value by one which is increment or
1544b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * decrement based on the passes argument.
1545d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * decrement the current value.
1546b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1547b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param increment True to increment, false to decrement.
1548b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
1549d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     private void changeValueByOne(boolean increment) {
1550d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mHasSelectorWheel) {
1551b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mInputText.setVisibility(View.INVISIBLE);
1552d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (!moveToFinalScrollerPosition(mFlingScroller)) {
1553d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                moveToFinalScrollerPosition(mAdjustScroller);
1554d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1555b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mPreviousScrollerY = 0;
1556b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (increment) {
1557fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov                mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight, SNAP_SCROLL_DURATION);
1558b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1559fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov                mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight, SNAP_SCROLL_DURATION);
1560b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1561b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            invalidate();
1562b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        } else {
1563b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (increment) {
1564d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                setValueInternal(mValue + 1, true);
1565b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1566d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                setValueInternal(mValue - 1, true);
1567b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1568b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
1569b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1570b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1571b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeSelectorWheel() {
1572b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1573b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
15749cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        int totalTextHeight = selectorIndices.length * mTextSize;
157501fa0d7aae1a551e1e7cfb90d2aeaf2fcb3978afSvetoslav Ganov        float totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
1576d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        float textGapCount = selectorIndices.length;
1577b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
1578b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorElementHeight = mTextSize + mSelectorTextGapHeight;
1579d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // Ensure that the middle item is positioned the same as the text in
1580d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // mInputText
1581eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        int editTextTextPosition = mInputText.getBaseline() + mInputText.getTop();
1582d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mInitialScrollOffset = editTextTextPosition
1583d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                - (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
15846a19fcde07527706c2a9175e0b9f2a71549b7ffeSvetoslav Ganov        mCurrentScrollOffset = mInitialScrollOffset;
15859cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
15869cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    }
15879cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1588b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeFadingEdges() {
1589b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setVerticalFadingEdgeEnabled(true);
1590b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setFadingEdgeLength((mBottom - mTop - mTextSize) / 2);
1591b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1592b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1593206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1594206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Callback invoked upon completion of a given <code>scroller</code>.
1595206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1596206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void onScrollerFinished(Scroller scroller) {
1597206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller == mFlingScroller) {
1598d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (!ensureScrollWheelAdjusted()) {
1599b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                updateInputTextView();
1600b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1601d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1603d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
1604d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                updateInputTextView();
1605d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1608fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1609206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
16109cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Handles transition to a given <code>scrollState</code>
161150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
16129cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private void onScrollStateChange(int scrollState) {
16139cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mScrollState == scrollState) {
16149cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            return;
16159cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
16169cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mScrollState = scrollState;
16179cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mOnScrollListener != null) {
16184bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            mOnScrollListener.onScrollStateChange(this, scrollState);
161950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
162050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
162150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
162250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1623206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flings the selector with the given <code>velocityY</code>.
1624206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1625206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void fling(int velocityY) {
1626206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = 0;
1627206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1628234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov        if (velocityY > 0) {
1629234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov            mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
1630206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1631234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov            mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
16329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1633206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1634206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        invalidate();
16359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1637206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1638206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The wrapped index <code>selectorIndex</code> value.
163968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1640206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getWrappedSelectorIndex(int selectorIndex) {
1641e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex > mMaxValue) {
1642e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMinValue + (selectorIndex - mMaxValue) % (mMaxValue - mMinValue) - 1;
1643e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else if (selectorIndex < mMinValue) {
1644e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMaxValue - (mMinValue - selectorIndex) % (mMaxValue - mMinValue) + 1;
1645206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1646206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return selectorIndex;
16479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1648fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1649206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1650206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Increments the <code>selectorIndices</code> whose string representations
1651206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1653b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void incrementSelectorIndices(int[] selectorIndices) {
1654206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length - 1; i++) {
1655206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i + 1];
1656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1657206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1;
1658e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) {
1659e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMinValue;
1660206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1661206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex;
1662206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1663206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1664fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1665206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1666206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Decrements the <code>selectorIndices</code> whose string representations
1667206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1668206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1669206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void decrementSelectorIndices(int[] selectorIndices) {
1670206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = selectorIndices.length - 1; i > 0; i--) {
1671206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i - 1];
1672206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1673206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[1] - 1;
1674e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) {
1675e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMaxValue;
1676206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1677206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[0] = nextScrollSelectorIndex;
1678206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1679206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1680fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1681206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1682206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Ensures we have a cached string representation of the given <code>
1683d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * selectorIndex</code> to avoid multiple instantiations of the same string.
1684206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1685206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void ensureCachedScrollSelectorValue(int selectorIndex) {
1686206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        SparseArray<String> cache = mSelectorIndexToStringCache;
1687206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String scrollSelectorValue = cache.get(selectorIndex);
1688206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scrollSelectorValue != null) {
1689206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1690206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1691e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex < mMinValue || selectorIndex > mMaxValue) {
1692206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scrollSelectorValue = "";
1693206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1694206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues != null) {
1695e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                int displayedValueIndex = selectorIndex - mMinValue;
169650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                scrollSelectorValue = mDisplayedValues[displayedValueIndex];
1697206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1698206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollSelectorValue = formatNumber(selectorIndex);
16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1701206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        cache.put(selectorIndex, scrollSelectorValue);
17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1703fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1704206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private String formatNumber(int value) {
1705e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return (mFormatter != null) ? mFormatter.format(value) : String.valueOf(value);
1706206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
17079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1708206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void validateInputTextView(View v) {
1709206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String str = String.valueOf(((TextView) v).getText());
1710206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (TextUtils.isEmpty(str)) {
1711206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Restore to the old value as we don't allow empty values
1712206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
1713206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1714206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Check the new value and ensure it's in range
1715206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int current = getSelectedPos(str.toString());
1716d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(current, true);
17179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1718206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1719fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1720206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1721206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the view of this NumberPicker. If displayValues were specified in
17229cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * the string corresponding to the index specified by the current value will
17239cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * be returned. Otherwise, the formatter specified in {@link #setFormatter}
17249cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * will be used to format the number.
1725d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
1726d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return Whether the text was updated.
1727206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1728d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean updateInputTextView() {
1729206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /*
1730206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * If we don't have displayed values then use the current number else
1731206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * find the correct value in the displayed values for the current
1732206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * number.
1733206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
1734d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        String text = (mDisplayedValues == null) ? formatNumber(mValue)
1735d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                : mDisplayedValues[mValue - mMinValue];
1736d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!TextUtils.isEmpty(text) && !text.equals(mInputText.getText().toString())) {
1737d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mInputText.setText(text);
1738d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
17399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17403fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
1741d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
1742206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1743fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1744206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1745206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Notifies the listener, if registered, of a change of the value of this
1746206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * NumberPicker.
1747206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1748206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void notifyChange(int previous, int current) {
1749cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        if (mOnValueChangeListener != null) {
1750cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov            mOnValueChangeListener.onValueChange(this, previous, mValue);
1751206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1752206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1754206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1755b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Posts a command for changing the current value by one.
1756b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1757b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param increment Whether to increment or decrement the value.
1758206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1759d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void postChangeCurrentByOneFromLongPress(boolean increment, long delayMillis) {
1760b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand == null) {
1761b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand();
1762d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1763d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1764d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1765d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mChangeCurrentByOneFromLongPressCommand.setStep(increment);
1766d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        postDelayed(mChangeCurrentByOneFromLongPressCommand, delayMillis);
1767d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1768d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1769d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1770d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Removes the command for changing the current value by one.
1771d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1772d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void removeChangeCurrentByOneFromLongPress() {
1773d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand != null) {
1774d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1775d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1776d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1777d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1778d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1779d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Posts a command for beginning an edit of the current value via IME on
1780d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * long press.
1781d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1782d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void postBeginSoftInputOnLongPressCommand() {
1783d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand == null) {
1784d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mBeginSoftInputOnLongPressCommand = new BeginSoftInputOnLongPressCommand();
1785d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1786d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1787d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1788d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        postDelayed(mBeginSoftInputOnLongPressCommand, ViewConfiguration.getLongPressTimeout());
1789d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1790d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1791d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1792d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Removes the command for beginning an edit of the current value via IME.
1793d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1794d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void removeBeginSoftInputCommand() {
1795d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand != null) {
1796d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1797206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1798206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1800206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1801206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Removes all pending callback from the message queue.
1802206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1803206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void removeAllCallbacks() {
1804b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand != null) {
1805b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1806206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1807206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand != null) {
1808206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1810d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand != null) {
1811d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1812d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1813232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mPressedStateHelper.cancel();
18149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1816206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1817206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The selected index given its displayed <code>value</code>.
1818206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1819206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getSelectedPos(String value) {
18209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisplayedValues == null) {
1821aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            try {
1822206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
1823aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            } catch (NumberFormatException e) {
1824206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
1825aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            }
18269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
18279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < mDisplayedValues.length; i++) {
1828206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Don't force the user to type in jan when ja will do
1829206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                value = value.toLowerCase();
1830206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
1831e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    return mMinValue + i;
18329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
18339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1834fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1835206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            /*
1836206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov             * The user might have typed in a number into the month field i.e.
18379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             * 10 instead of OCT so support that too.
18389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             */
18399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1840206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
18419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
1842fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1843206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
18449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1846e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
18479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
18499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1850206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link SetSelectionCommand} from the given <code>selectionStart
1851d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * </code> to <code>selectionEnd</code>.
18529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1853206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
1854206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand == null) {
1855206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSetSelectionCommand = new SetSelectionCommand();
1856206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1857206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
1858206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1859206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionStart = selectionStart;
1860206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionEnd = selectionEnd;
1861206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(mSetSelectionCommand);
18629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
186368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
186468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1865206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Filter for accepting only valid indices or prefixes of the string
1866206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * representation of valid indices.
186768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1868206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class InputTextFilter extends NumberKeyListener {
1869206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1870206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // XXX This doesn't allow for range limits when controlled by a
1871206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // soft input method!
1872206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public int getInputType() {
1873206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return InputType.TYPE_CLASS_TEXT;
1874206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1875206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1876206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1877206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        protected char[] getAcceptedChars() {
1878206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return DIGIT_CHARACTERS;
1879206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1880206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1881206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1882d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public CharSequence filter(
1883d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
1884206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues == null) {
1885206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
1886206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (filtered == null) {
1887206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    filtered = source.subSequence(start, end);
1888206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1889206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1890206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1891206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1892206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1893206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if ("".equals(result)) {
1894206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return result;
1895206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1896206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int val = getSelectedPos(result);
1897206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1898206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                /*
1899206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * Ensure the user can't type in a value greater than the max
1900206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * allowed. We have to allow less than min as the user might
1901206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * want to delete some numbers and then type a new number.
1902206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 */
1903e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                if (val > mMaxValue) {
1904206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1905206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
1906206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return filtered;
1907206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1908206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1909206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = String.valueOf(source.subSequence(start, end));
1910206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (TextUtils.isEmpty(filtered)) {
1911206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1912206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1913206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1914206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1915206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String str = String.valueOf(result).toLowerCase();
1916206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                for (String val : mDisplayedValues) {
1917206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    String valLowerCase = val.toLowerCase();
1918206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (valLowerCase.startsWith(str)) {
1919206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postSetSelectionCommand(result.length(), val.length());
1920206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        return val.subSequence(dstart, val.length());
1921206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
1922206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1923206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return "";
1924206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1925206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1926206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1927206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1928206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1929d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Ensures that the scroll wheel is adjusted i.e. there is no offset and the
1930d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * middle element is in the middle of the widget.
1931d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
1932d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return Whether an adjustment has been made.
1933d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1934d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean ensureScrollWheelAdjusted() {
1935d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // adjust to the closest value
1936d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
1937d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (deltaY != 0) {
1938d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mPreviousScrollerY = 0;
1939d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
1940d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
1941d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1942d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
1943d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            invalidate();
1944d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
1945d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1946d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
1947d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1948d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1949232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    class PressedStateHelper implements Runnable {
1950232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public static final int BUTTON_INCREMENT = 1;
1951232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public static final int BUTTON_DECREMENT = 2;
1952232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
1953232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private final int MODE_PRESS = 1;
1954232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private final int MODE_TAPPED = 2;
1955232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
1956232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private int mManagedButton;
1957232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private int mMode;
1958232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
1959232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void cancel() {
1960232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = 0;
1961232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = 0;
1962232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.removeCallbacks(this);
1963232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mIncrementVirtualButtonPressed) {
1964232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mIncrementVirtualButtonPressed = false;
1965232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
1966232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
1967232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mDecrementVirtualButtonPressed = false;
1968232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mDecrementVirtualButtonPressed) {
1969232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                invalidate(0, 0, mRight, mTopSelectionDividerTop);
1970fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov            }
1971fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov        }
1972fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov
1973232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void buttonPressDelayed(int button) {
1974232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            cancel();
1975232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = MODE_PRESS;
1976232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = button;
1977232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.postDelayed(this, ViewConfiguration.getTapTimeout());
1978232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
1979232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
1980232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void buttonTapped(int button) {
1981232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            cancel();
1982232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = MODE_TAPPED;
1983232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = button;
1984232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.post(this);
1985232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
1986232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
1987232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        @Override
1988232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void run() {
1989232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            switch (mMode) {
1990232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                case MODE_PRESS: {
1991232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    switch (mManagedButton) {
1992232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_INCREMENT: {
1993232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mIncrementVirtualButtonPressed = true;
1994232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
1995232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        } break;
1996232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_DECREMENT: {
1997232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mDecrementVirtualButtonPressed = true;
1998232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, 0, mRight, mTopSelectionDividerTop);
1999232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        }
2000232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
2001232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } break;
2002232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                case MODE_TAPPED: {
2003232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    switch (mManagedButton) {
2004232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_INCREMENT: {
2005232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            if (!mIncrementVirtualButtonPressed) {
2006232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                NumberPicker.this.postDelayed(this,
2007232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        ViewConfiguration.getPressedStateDuration());
2008232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            }
2009232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mIncrementVirtualButtonPressed ^= true;
2010232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2011232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        } break;
2012232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_DECREMENT: {
2013232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            if (!mDecrementVirtualButtonPressed) {
2014232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                NumberPicker.this.postDelayed(this,
2015232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        ViewConfiguration.getPressedStateDuration());
2016232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            }
2017232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mDecrementVirtualButtonPressed ^= true;
2018232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, 0, mRight, mTopSelectionDividerTop);
2019232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        }
2020232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
2021232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } break;
2022fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov            }
2023fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov        }
2024fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov    }
2025fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov
2026d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
2027206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for setting the input text selection.
2028206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2029206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class SetSelectionCommand implements Runnable {
2030206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionStart;
2031206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2032206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionEnd;
2033206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2034206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
2035206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setSelection(mSelectionStart, mSelectionEnd);
2036206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2037206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
2038206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2039206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
2040b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Command for changing the current value from a long press by one.
2041206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2042b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    class ChangeCurrentByOneFromLongPressCommand implements Runnable {
2043b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        private boolean mIncrement;
2044206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2045d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void setStep(boolean increment) {
2046b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mIncrement = increment;
2047206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2048206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2049d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2050206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
2051d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            changeValueByOne(mIncrement);
20524bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            postDelayed(this, mLongPressUpdateInterval);
2053206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
205468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
2055a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2056a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    /**
2057a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     * @hide
2058a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     */
2059a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    public static class CustomEditText extends EditText {
2060a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2061a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        public CustomEditText(Context context, AttributeSet attrs) {
2062a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            super(context, attrs);
2063a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        }
2064a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2065a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        @Override
2066a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        public void onEditorAction(int actionCode) {
2067a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            super.onEditorAction(actionCode);
2068a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            if (actionCode == EditorInfo.IME_ACTION_DONE) {
2069a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov                clearFocus();
2070a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            }
2071a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        }
2072a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    }
2073d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2074d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
2075d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Command for beginning soft input on long press.
2076d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
2077d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    class BeginSoftInputOnLongPressCommand implements Runnable {
2078d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2079d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2080d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public void run() {
2081d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            showSoftInput();
2082d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIngonreMoveEvents = true;
2083d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2084d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
2085d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2086791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov    /**
2087791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov     * Class for managing virtual view tree rooted at this picker.
2088791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov     */
2089d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    class AccessibilityNodeProviderImpl extends AccessibilityNodeProvider {
2090791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private static final int UNDEFINED = Integer.MIN_VALUE;
2091791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2092d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_INCREMENT = 1;
2093d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2094d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_INPUT = 2;
2095d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2096d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_DECREMENT = 3;
2097d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2098d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private final Rect mTempRect = new Rect();
2099d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2100d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private final int[] mTempArray = new int[2];
2101d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2102791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private int mAccessibilityFocusedView = UNDEFINED;
2103791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2104d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2105d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
2106d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2107d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case View.NO_ID:
2108d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForNumberPicker( mScrollX, mScrollY,
2109d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft), mScrollY + (mBottom - mTop));
2110d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT:
2111d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_DECREMENT,
2112d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            getVirtualDecrementButtonText(), mScrollX, mScrollY,
2113d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft),
2114d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mTopSelectionDividerTop + mSelectionDividerHeight);
2115d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT:
2116d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibiltyNodeInfoForInputText();
2117d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT:
2118d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_INCREMENT,
2119d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            getVirtualIncrementButtonText(), mScrollX,
2120d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mBottomSelectionDividerBottom - mSelectionDividerHeight,
2121d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft), mScrollY + (mBottom - mTop));
2122d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2123d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.createAccessibilityNodeInfo(virtualViewId);
2124d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2125d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2126d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2127d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String searched,
2128d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int virtualViewId) {
2129d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (TextUtils.isEmpty(searched)) {
2130d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return Collections.emptyList();
2131d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2132d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            String searchedLowerCase = searched.toLowerCase();
2133d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            List<AccessibilityNodeInfo> result = new ArrayList<AccessibilityNodeInfo>();
2134d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2135d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case View.NO_ID: {
2136d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2137d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_DECREMENT, result);
2138d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2139d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_INPUT, result);
2140d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2141d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_INCREMENT, result);
2142d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return result;
2143d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
2144d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT:
2145d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT:
2146d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2147d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase, virtualViewId,
2148d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            result);
2149d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return result;
2150d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
2151d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2152d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.findAccessibilityNodeInfosByText(searched, virtualViewId);
2153d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2154d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2155d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2156aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
2157d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2158791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case View.NO_ID: {
2159791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2160791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2161791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2162791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2163791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                requestAccessibilityFocus();
2164791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2165791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2166791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2167791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2168791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2169791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2170791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                clearAccessibilityFocus();
2171791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2172791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2173791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return false;
2174791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
217548d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
217648d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                            if (getWrapSelectorWheel() || getValue() < getMaxValue()) {
217748d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                changeValueByOne(true);
217848d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                return true;
217948d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                            }
218048d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        } return false;
218148d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
218248d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                            if (getWrapSelectorWheel() || getValue() > getMinValue()) {
218348d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                changeValueByOne(false);
218448d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                return true;
218548d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                            }
218648d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        } return false;
2187791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2188791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } break;
2189d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2190d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    switch (action) {
2191d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_FOCUS: {
2192d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            if (!mInputText.isFocused()) {
2193d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                return mInputText.requestFocus();
2194d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
2195d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        } break;
2196d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
2197d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            if (mInputText.isFocused()) {
2198d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                mInputText.clearFocus();
2199d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                return true;
2200d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
2201791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return false;
2202791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2203791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2204791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            showSoftInput();
2205791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return true;
2206791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2207791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2208791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2209791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2210791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2211791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2212791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mInputText.invalidate();
2213791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2214791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2215791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2216791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2217791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2218791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2219791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2220791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2221791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mInputText.invalidate();
2222791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2223791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2224791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2225791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        default: {
2226791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return mInputText.performAccessibilityAction(action, arguments);
2227791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2228d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2229791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2230791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2231791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2232791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2233791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            NumberPicker.this.changeValueByOne(true);
2234791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            sendAccessibilityEventForVirtualView(virtualViewId,
2235791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                    AccessibilityEvent.TYPE_VIEW_CLICKED);
2236791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return true;
2237791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2238791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2239791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2240791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2241791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2242791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2243791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2244791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2245791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2246791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2247791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2248791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2249791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2250791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2251791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2252791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2253791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2254791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2255791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2256791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2257791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2258791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2259791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2260791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            final boolean increment = (virtualViewId == VIRTUAL_VIEW_ID_INCREMENT);
2261791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            NumberPicker.this.changeValueByOne(increment);
2262791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            sendAccessibilityEventForVirtualView(virtualViewId,
2263791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                    AccessibilityEvent.TYPE_VIEW_CLICKED);
2264791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return true;
2265791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2266791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2267791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2268791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2269791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2270791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, 0, mRight, mTopSelectionDividerTop);
2271791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2272791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2273791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2274791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2275791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2276791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2277791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2278791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2279791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, 0, mRight, mTopSelectionDividerTop);
2280791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2281791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2282791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2283791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2284791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2285d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2286aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov            return super.performAction(virtualViewId, action, arguments);
2287d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2288d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2289791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        @Override
2290791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        public AccessibilityNodeInfo findAccessibilityFocus(int virtualViewId) {
2291791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            return createAccessibilityNodeInfo(mAccessibilityFocusedView);
2292791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
2293791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2294791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        @Override
2295791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        public AccessibilityNodeInfo accessibilityFocusSearch(int direction, int virtualViewId) {
2296791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            switch (direction) {
2297791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case View.ACCESSIBILITY_FOCUS_DOWN:
2298791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case View.ACCESSIBILITY_FOCUS_FORWARD: {
2299791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (mAccessibilityFocusedView) {
2300791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case UNDEFINED: {
2301791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return createAccessibilityNodeInfo(View.NO_ID);
2302791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2303791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case View.NO_ID: {
2304791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (hasVirtualDecrementButton()) {
2305791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT);
2306791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2307791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2308791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        //$FALL-THROUGH$
2309791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case VIRTUAL_VIEW_ID_DECREMENT: {
2310791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT);
2311791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2312791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case VIRTUAL_VIEW_ID_INPUT: {
2313791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (hasVirtualIncrementButton()) {
2314791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT);
2315791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2316791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2317791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        //$FALL-THROUGH$
2318791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case VIRTUAL_VIEW_ID_INCREMENT: {
2319791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            View nextFocus = NumberPicker.this.focusSearch(direction);
2320791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (nextFocus != null) {
2321791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return nextFocus.createAccessibilityNodeInfo();
2322791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2323791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return null;
2324791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2325791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2326791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } break;
2327791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case View.ACCESSIBILITY_FOCUS_UP:
2328791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case View.ACCESSIBILITY_FOCUS_BACKWARD: {
2329791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (mAccessibilityFocusedView) {
2330791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case UNDEFINED: {
2331791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return createAccessibilityNodeInfo(View.NO_ID);
2332791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2333791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case View.NO_ID: {
2334791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (hasVirtualIncrementButton()) {
2335791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT);
2336791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2337791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2338791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        //$FALL-THROUGH$
2339791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case VIRTUAL_VIEW_ID_INCREMENT: {
2340791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT);
2341791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2342791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case VIRTUAL_VIEW_ID_INPUT: {
2343791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (hasVirtualDecrementButton()) {
2344791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT);
2345791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2346791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2347791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        //$FALL-THROUGH$
2348791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case VIRTUAL_VIEW_ID_DECREMENT: {
2349791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            View nextFocus = NumberPicker.this.focusSearch(direction);
2350791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (nextFocus != null) {
2351791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return nextFocus.createAccessibilityNodeInfo();
2352791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2353791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return null;
2354791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2355791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2356791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } break;
2357791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2358791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            return null;
2359791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
2360791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2361d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
2362d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2363d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2364791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    if (hasVirtualDecrementButton()) {
2365791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
2366791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                getVirtualDecrementButtonText());
2367791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2368d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2369d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2370d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    sendAccessibilityEventForVirtualText(eventType);
2371d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2372d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2373791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    if (hasVirtualIncrementButton()) {
2374791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
2375791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                getVirtualIncrementButtonText());
2376791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2377d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2378d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2379d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2380d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2381d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void sendAccessibilityEventForVirtualText(int eventType) {
2382d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
2383d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mInputText.onInitializeAccessibilityEvent(event);
2384d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mInputText.onPopulateAccessibilityEvent(event);
2385d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            event.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2386d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            requestSendAccessibilityEvent(NumberPicker.this, event);
2387d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2388d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2389d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
2390d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                String text) {
2391d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
2392d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            event.setClassName(Button.class.getName());
2393d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            event.setPackageName(mContext.getPackageName());
2394d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            event.getText().add(text);
2395d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            event.setEnabled(NumberPicker.this.isEnabled());
2396d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            event.setSource(NumberPicker.this, virtualViewId);
2397d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            requestSendAccessibilityEvent(NumberPicker.this, event);
2398d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2399d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2400d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void findAccessibilityNodeInfosByTextInChild(String searchedLowerCase,
2401d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int virtualViewId, List<AccessibilityNodeInfo> outResult) {
2402d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2403d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2404d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    String text = getVirtualDecrementButtonText();
2405d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text)
2406d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && text.toString().toLowerCase().contains(searchedLowerCase)) {
2407d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT));
2408d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2409d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } return;
2410d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2411d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    CharSequence text = mInputText.getText();
2412d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text) &&
2413d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            text.toString().toLowerCase().contains(searchedLowerCase)) {
2414d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
2415d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        return;
2416d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2417d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    CharSequence contentDesc = mInputText.getText();
2418d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(contentDesc) &&
2419d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            contentDesc.toString().toLowerCase().contains(searchedLowerCase)) {
2420d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
2421d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        return;
2422d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2423d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2424d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2425d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    String text = getVirtualIncrementButtonText();
2426d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text)
2427d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && text.toString().toLowerCase().contains(searchedLowerCase)) {
2428d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT));
2429d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2430d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } return;
2431d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2432d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2433d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2434d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private AccessibilityNodeInfo createAccessibiltyNodeInfoForInputText() {
2435d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo();
2436d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2437791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) {
2438791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2439791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2440791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT) {
2441791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2442791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2443d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2444d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2445d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2446d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private AccessibilityNodeInfo createAccessibilityNodeInfoForVirtualButton(int virtualViewId,
2447d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                String text, int left, int top, int right, int bottom) {
2448d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
2449d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setClassName(Button.class.getName());
2450d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setPackageName(mContext.getPackageName());
2451d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this, virtualViewId);
2452d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setParent(NumberPicker.this);
2453d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setText(text);
2454d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setClickable(true);
2455d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setLongClickable(true);
2456d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setEnabled(NumberPicker.this.isEnabled());
2457d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInParent = mTempRect;
2458d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInParent.set(left, top, right, bottom);
24590d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setVisibleToUser(isVisibleToUser(boundsInParent));
2460d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInParent(boundsInParent);
2461d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInScreen = boundsInParent;
2462d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int[] locationOnScreen = mTempArray;
2463d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            getLocationOnScreen(locationOnScreen);
2464d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
2465d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInScreen(boundsInScreen);
2466791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2467791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != virtualViewId) {
2468791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2469791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2470791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == virtualViewId) {
2471791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2472791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2473791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
2474791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2475d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2476d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2477d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2478d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private AccessibilityNodeInfo createAccessibilityNodeInfoForNumberPicker(int left, int top,
2479d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int right, int bottom) {
2480d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
24810d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setClassName(NumberPicker.class.getName());
2482d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setPackageName(mContext.getPackageName());
2483d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this);
2484791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2485791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (hasVirtualDecrementButton()) {
2486791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
2487791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
24880d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2489791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (hasVirtualIncrementButton()) {
2490791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
2491791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2492791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
24934528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            info.setParent((View) getParentForAccessibility());
2494d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setEnabled(NumberPicker.this.isEnabled());
2495d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setScrollable(true);
2496d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInParent = mTempRect;
2497d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInParent.set(left, top, right, bottom);
2498d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInParent(boundsInParent);
24990d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setVisibleToUser(isVisibleToUser());
2500d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInScreen = boundsInParent;
2501d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int[] locationOnScreen = mTempArray;
2502d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            getLocationOnScreen(locationOnScreen);
2503d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
2504d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInScreen(boundsInScreen);
2505791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2506791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != View.NO_ID) {
2507791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2508791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2509791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == View.NO_ID) {
2510791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2511791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
251248d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov            if (getWrapSelectorWheel() || getValue() < getMaxValue()) {
251348d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
251448d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov            }
251548d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov            if (getWrapSelectorWheel() || getValue() > getMinValue()) {
251648d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
251748d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov            }
2518791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2519d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2520d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2521d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2522791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private boolean hasVirtualDecrementButton() {
2523791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            return getWrapSelectorWheel() || getValue() > getMinValue();
2524791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
2525791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2526791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private boolean hasVirtualIncrementButton() {
2527791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            return getWrapSelectorWheel() || getValue() < getMaxValue();
2528791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
2529791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2530d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private String getVirtualDecrementButtonText() {
2531d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int value = mValue - 1;
2532d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mWrapSelectorWheel) {
2533d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                value = getWrappedSelectorIndex(value);
2534d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2535d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (value >= mMinValue) {
2536d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return (mDisplayedValues == null) ? formatNumber(value)
2537d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        : mDisplayedValues[value - mMinValue];
2538d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2539d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return null;
2540d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2541d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2542d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private String getVirtualIncrementButtonText() {
2543d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int value = mValue + 1;
2544d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mWrapSelectorWheel) {
2545d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                value = getWrappedSelectorIndex(value);
2546d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2547d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (value <= mMaxValue) {
2548d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return (mDisplayedValues == null) ? formatNumber(value)
2549d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        : mDisplayedValues[value - mMinValue];
2550d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2551d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return null;
2552d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2553d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
25547762d93621f508f4410fd37b5bbdfec0071b0420Paul Westbrook}
2555