NumberPicker.java revision 9eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2
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
19d9273d6f289d9b55da3fd0db2f659fdfb48106a8Tor Norbyeimport android.annotation.IntDef;
2068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookimport android.annotation.Widget;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
22206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.ColorStateList;
23206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.TypedArray;
24206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Canvas;
25206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Color;
26206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Paint;
27e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.graphics.Paint.Align;
28b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.graphics.Rect;
299cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.graphics.drawable.Drawable;
30aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganovimport android.os.Bundle;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputFilter;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputType;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.Spanned;
34206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.text.TextUtils;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.NumberKeyListener;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
37206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.util.SparseArray;
389cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.util.TypedValue;
39206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.KeyEvent;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.LayoutInflater;
41b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.view.LayoutInflater.Filter;
42206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.MotionEvent;
43206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.VelocityTracker;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
45206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.ViewConfiguration;
463fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
473fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityManager;
488a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
49d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport android.view.accessibility.AccessibilityNodeProvider;
509cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.view.animation.DecelerateInterpolator;
51a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganovimport android.view.inputmethod.EditorInfo;
52206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.inputmethod.InputMethodManager;
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
54b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport com.android.internal.R;
55d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglioimport libcore.icu.LocaleData;
56b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
57d9273d6f289d9b55da3fd0db2f659fdfb48106a8Tor Norbyeimport java.lang.annotation.Retention;
58d9273d6f289d9b55da3fd0db2f659fdfb48106a8Tor Norbyeimport java.lang.annotation.RetentionPolicy;
59d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.ArrayList;
60d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.Collections;
61d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.List;
62d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglioimport java.util.Locale;
63d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
6468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook/**
650db4c3e0ed0e9a85f11db41285b9adb000c3f211Brian Duff * A widget that enables the user to select a number from a predefined range.
66d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * There are two flavors of this widget and which one is presented to the user
67d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * depends on the current theme.
68d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <ul>
69d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <li>
70d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * If the current theme is derived from {@link android.R.style#Theme} the widget
71d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * presents the current value as an editable input field with an increment button
72d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * above and a decrement button below. Long pressing the buttons allows for a quick
73d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * change of the current value. Tapping on the input field allows to type in
74d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * a desired value.
75d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </li>
76d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <li>
77d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * If the current theme is derived from {@link android.R.style#Theme_Holo} or
78d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * {@link android.R.style#Theme_Holo_Light} the widget presents the current
79d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * value as an editable input field with a lesser value above and a greater
80d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * value below. Tapping on the lesser or greater value selects it by animating
81d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * the number axis up or down to make the chosen value current. Flinging up
82d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * or down allows for multiple increments or decrements of the current value.
83d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * Long pressing on the lesser and greater values also allows for a quick change
84d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * of the current value. Tapping on the current value allows to type in a
85d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * desired value.
86d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </li>
87d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </ul>
884bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * <p>
894bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * For an example of using this widget, see {@link android.widget.TimePicker}.
904bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * </p>
9168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook */
9268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook@Widget
9368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpublic class NumberPicker extends LinearLayout {
94fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
9568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
963f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * The number of items show in the selector wheel.
973f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     */
98d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int SELECTOR_WHEEL_ITEM_COUNT = 3;
993f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov
1003f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov    /**
101e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default update interval during long press.
102e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
103e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300;
104e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
105e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
106206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The index of the middle selector item.
10768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
108d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int SELECTOR_MIDDLE_ITEM_INDEX = SELECTOR_WHEEL_ITEM_COUNT / 2;
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
111206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The coefficient by which to adjust (divide) the max fling velocity.
11268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
116206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the duration for adjusting the selector wheel.
117206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
118206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
119fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
120206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * The duration of scrolling while snapping to a given position.
122b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
123fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov    private static final int SNAP_SCROLL_DURATION = 300;
124b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
125b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
126b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The strength of fading in the top and bottom while drawing the selector.
127206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
128b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f;
129206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
130206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
131b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The default unscaled height of the selection divider.
132206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
133b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2;
134206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
135206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
136d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The default unscaled distance between the selection dividers.
137b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
138d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48;
139b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
140b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
141d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The resource id for the default layout.
142b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
143d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int DEFAULT_LAYOUT_RESOURCE_ID = R.layout.number_picker;
1449cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1459cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
1469f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * Constant for unspecified size.
1479f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     */
1489f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    private static final int SIZE_UNSPECIFIED = -1;
1499f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
1509f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    /**
151206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Use a custom NumberPicker formatting callback to use two-digit minutes
152206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * strings like "01". Keeping a static formatter etc. is the most efficient
153206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * way to do this; it avoids creating temporary objects on every call to
154206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * format().
155206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
156d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    private static class TwoDigitFormatter implements NumberPicker.Formatter {
157206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final StringBuilder mBuilder = new StringBuilder();
158206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
159d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        char mZeroDigit;
160d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        java.util.Formatter mFmt;
161206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
162206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final Object[] mArgs = new Object[1];
163206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
164d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        TwoDigitFormatter() {
165d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            final Locale locale = Locale.getDefault();
166d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            init(locale);
167d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        }
168d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
169d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        private void init(Locale locale) {
170d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            mFmt = createFormatter(locale);
171d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            mZeroDigit = getZeroDigit(locale);
172d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        }
173d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
174e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value) {
175d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            final Locale currentLocale = Locale.getDefault();
176d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            if (mZeroDigit != getZeroDigit(currentLocale)) {
177d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                init(currentLocale);
178d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            }
179206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mArgs[0] = value;
180206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mBuilder.delete(0, mBuilder.length());
181206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mFmt.format("%02d", mArgs);
182206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return mFmt.toString();
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
184d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
185d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        private static char getZeroDigit(Locale locale) {
186d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            return LocaleData.get(locale).zeroDigit;
187d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        }
188d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
189d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        private java.util.Formatter createFormatter(Locale locale) {
190d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            return new java.util.Formatter(mBuilder, locale);
191d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        }
192d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
193d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
194d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    private static final TwoDigitFormatter sTwoDigitFormatter = new TwoDigitFormatter();
195d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
196a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio    /**
197a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio     * @hide
198a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio     */
199a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio    public static final Formatter getTwoDigitFormatter() {
200d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        return sTwoDigitFormatter;
201d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
202d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
203206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
204206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The increment button.
205206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
206206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mIncrementButton;
207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
208206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
209206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The decrement button.
210206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
211206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mDecrementButton;
212206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
213206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
214206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The text for showing the current value.
215206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
216206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final EditText mInputText;
217206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
218206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
219d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The distance between the two selection dividers.
220d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
221d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final int mSelectionDividersDistance;
222d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
223d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
224ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * The min height of this widget.
225ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
226ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final int mMinHeight;
227ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
228ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
229e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * The max height of this widget.
230e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
231e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    private final int mMaxHeight;
232e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
233e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
234e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * The max width of this widget.
235e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
236ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final int mMinWidth;
237ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
238ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
239ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * The max width of this widget.
240ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
241ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private int mMaxWidth;
242ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
243ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
244ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Flag whether to compute the max width.
245ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
246ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final boolean mComputeMaxWidth;
247e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
248e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
249206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of the text.
250206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
251206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mTextSize;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
253206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
254b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The height of the gap between text elements if the selector wheel.
255b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
256b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private int mSelectorTextGapHeight;
257b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
258b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
259206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The values to be displayed instead the indices.
260206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String[] mDisplayedValues;
26268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
26368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
26468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Lower value of the range of numbers allowed for the NumberPicker
26568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
266e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMinValue;
26768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
26868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
26968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Upper value of the range of numbers allowed for the NumberPicker
27068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
271e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMaxValue;
27268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
27368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
27468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Current value of this NumberPicker
27568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
276e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mValue;
27768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
27868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
279206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Listener to be notified upon current value change.
28068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
281cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    private OnValueChangeListener mOnValueChangeListener;
28250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
28350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
28450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Listener to be notified upon scroll state change.
28550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
28650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private OnScrollListener mOnScrollListener;
287206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
288206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
289206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Formatter for for displaying the current value.
290206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Formatter mFormatter;
292fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
293206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
294206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The speed for updating the value form long press.
295206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
296e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL;
297fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
29868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Cache for the string representation of selector indices.
300206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final SparseArray<String> mSelectorIndexToStringCache = new SparseArray<String>();
302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
303206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
304206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The selector indices whose value are show by the selector.
305206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
306d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final int[] mSelectorIndices = new int[SELECTOR_WHEEL_ITEM_COUNT];
307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
308206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Paint} for drawing the selector.
310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
311b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private final Paint mSelectorWheelPaint;
312206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
313206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
314232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * The {@link Drawable} for pressed virtual (increment/decrement) buttons.
315232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
316232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private final Drawable mVirtualButtonPressedDrawable;
317232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
318232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of a selector element (text + gap).
320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mSelectorElementHeight;
322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The initial offset of the scroll selector.
325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
326206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mInitialScrollOffset = Integer.MIN_VALUE;
327206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
328206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
329206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The current offset of the scroll selector.
330206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
331206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mCurrentScrollOffset;
332206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
333206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
334206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for flinging the selector.
335206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
336206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mFlingScroller;
337206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
339206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for adjusting the selector.
340206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
341206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mAdjustScroller;
342206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
343206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
344206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The previous Y coordinate while scrolling the selector.
345206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
346206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mPreviousScrollerY;
347206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
348206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
349206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for setting the input text selection.
350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
351206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private SetSelectionCommand mSetSelectionCommand;
352206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
353206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
354b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Handle to the reusable command for changing the current value from long
355b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * press by one.
356206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
357b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand;
358206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
359206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
360d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Command for beginning an edit of the current value via IME on long press.
361b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
362d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private BeginSoftInputOnLongPressCommand mBeginSoftInputOnLongPressCommand;
363b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
364b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
365206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last down event.
366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
367206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastDownEventY;
368206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
369206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
370d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The time of the last down event.
371206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
372d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private long mLastDownEventTime;
373206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
374206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
375d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The Y position of the last down or move event.
376206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
377d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private float mLastDownOrMoveEventY;
378206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
379206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
380206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Determines speed during touch scrolling.
381206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
382206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private VelocityTracker mVelocityTracker;
383206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
384206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
385206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledTouchSlop()
386206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
387206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mTouchSlop;
388206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
389206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
390206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMinimumFlingVelocity()
391206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
392206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMinimumFlingVelocity;
393206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
394206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
395206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMaximumFlingVelocity()
396206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
397206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMaximumFlingVelocity;
398206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
399206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
400206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag whether the selector should wrap around.
401206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
402e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean mWrapSelectorWheel;
403206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
404206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
405206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The back ground color used to optimize scroller fading.
40668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
407206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mSolidColor;
408206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
409206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
410d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Flag whether this widget has a selector wheel.
4114243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov     */
412d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final boolean mHasSelectorWheel;
4134243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov
4144243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov    /**
4159cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Divider for showing item to be selected while scrolling
4169cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
4179cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final Drawable mSelectionDivider;
4189cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4199cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
4209cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * The height of the selection divider.
4219cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
4229cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final int mSelectionDividerHeight;
4239cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4249cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
425d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The current scroll state of the number picker.
426206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
427d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
428206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
429206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
430d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Flag whether to ignore move events - we ignore such when we show in IME
431d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * to prevent the content from scrolling.
43250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
43346a27efe9528ac3c5b37726707844e7c868815acSvetoslav    private boolean mIgnoreMoveEvents;
43450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
43550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
43646a27efe9528ac3c5b37726707844e7c868815acSvetoslav     * Flag whether to perform a click on tap.
4379cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
43846a27efe9528ac3c5b37726707844e7c868815acSvetoslav    private boolean mPerformClickOnTap;
4399cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4409cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
441d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The top of the top selection divider.
442b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
443d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mTopSelectionDividerTop;
444b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
445b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
446d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The bottom of the bottom selection divider.
447a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     */
448d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mBottomSelectionDividerBottom;
449d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
450d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
451d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The virtual id of the last hovered child.
452d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
453d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mLastHoveredChildVirtualViewId;
454d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
455d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
456232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Whether the increment virtual button is pressed.
457232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
458232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private boolean mIncrementVirtualButtonPressed;
459232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
460232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
461232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Whether the decrement virtual button is pressed.
462232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
463232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private boolean mDecrementVirtualButtonPressed;
464232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
465232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
466d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Provider to report to clients the semantic structure of this widget.
467d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
468d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private AccessibilityNodeProviderImpl mAccessibilityNodeProvider;
469a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
470a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    /**
471232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Helper class for managing pressed state of the virtual buttons.
472232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
473232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private final PressedStateHelper mPressedStateHelper;
474232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
475232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
4765dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov     * The keycode of the last handled DPAD down event.
4775dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov     */
4785dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov    private int mLastHandledDownDpadKeyCode = -1;
4795dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov
4805dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov    /**
4819eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout     * If true then the selector wheel is hidden until the picker has focus.
4829eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout     */
4839eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout    private boolean mHideWheelUntilFocused;
4849eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout
4859eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout    /**
486e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for changes of the current value.
487206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
488cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public interface OnValueChangeListener {
489e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
490206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /**
4914bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * Called upon a change of the current value.
4924bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
493206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param picker The NumberPicker associated with this listener.
494206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param oldVal The previous value.
495206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param newVal The new value.
496206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
497e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        void onValueChange(NumberPicker picker, int oldVal, int newVal);
498206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
499206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
500206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
501e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for the picker scroll state.
50250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
50350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public interface OnScrollListener {
504d9273d6f289d9b55da3fd0db2f659fdfb48106a8Tor Norbye        /** @hide */
505d9273d6f289d9b55da3fd0db2f659fdfb48106a8Tor Norbye        @IntDef({SCROLL_STATE_IDLE, SCROLL_STATE_TOUCH_SCROLL, SCROLL_STATE_FLING})
506d9273d6f289d9b55da3fd0db2f659fdfb48106a8Tor Norbye        @Retention(RetentionPolicy.SOURCE)
507d9273d6f289d9b55da3fd0db2f659fdfb48106a8Tor Norbye        public @interface ScrollState {}
50850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
50950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
51050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The view is not scrolling.
51150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
51250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_IDLE = 0;
51350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
51450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
515d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov         * The user is scrolling using touch, and his finger is still on the screen.
51650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
51750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_TOUCH_SCROLL = 1;
51850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
51950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
52050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user had previously been scrolling using touch and performed a fling.
52150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
52250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_FLING = 2;
52350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
52450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
525e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Callback invoked while the number picker scroll state has changed.
52650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
527e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param view The view whose scroll state is being reported.
528e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param scrollState The current scroll state. One of
529e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE},
530e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_TOUCH_SCROLL} or
531e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE}.
53250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
533d9273d6f289d9b55da3fd0db2f659fdfb48106a8Tor Norbye        public void onScrollStateChange(NumberPicker view, @ScrollState int scrollState);
53450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
53550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
53650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
537e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface used to format current value into a string for presentation.
538206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
539206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public interface Formatter {
5404bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
5414bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        /**
542e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Formats a string representation of the current value.
5434bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
5444bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @param value The currently selected value.
5454bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @return A formatted string representation.
5464bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         */
547e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value);
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
549fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
55068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
5514bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
5524bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
5534bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param context The application environment.
5544bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
5554bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public NumberPicker(Context context) {
5564bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        this(context, null);
5574bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
5584bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
5594bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
5604bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
561206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
562206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context The application environment.
563206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs A collection of attributes.
56468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public NumberPicker(Context context, AttributeSet attrs) {
566206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        this(context, attrs, R.attr.numberPickerStyle);
567206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
568206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
569206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
570206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Create a new number picker
571206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
572206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context the application environment.
573206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs a collection of attributes.
574617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param defStyleAttr An attribute in the current theme that contains a
575617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        reference to a style resource that supplies default values for
576617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        the view. Can be 0 to not look for defaults.
577206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
578617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public NumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {
579617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        this(context, attrs, defStyleAttr, 0);
580617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    }
581617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette
582617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    /**
583617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * Create a new number picker
584617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *
585617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param context the application environment.
586617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param attrs a collection of attributes.
587617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param defStyleAttr An attribute in the current theme that contains a
588617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        reference to a style resource that supplies default values for
589617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        the view. Can be 0 to not look for defaults.
590617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param defStyleRes A resource identifier of a style resource that
591617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        supplies default values for the view, used only if
592617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        defStyleAttr is 0 or can not be found in the theme. Can be 0
593617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        to not look for defaults.
594617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     */
595617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public NumberPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
596617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        super(context, attrs, defStyleAttr, defStyleRes);
597206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
598206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // process style attributes
599617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        final TypedArray attributesArray = context.obtainStyledAttributes(
600617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette                attrs, R.styleable.NumberPicker, defStyleAttr, defStyleRes);
601d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int layoutResId = attributesArray.getResourceId(
602d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalLayout, DEFAULT_LAYOUT_RESOURCE_ID);
603d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
604d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mHasSelectorWheel = (layoutResId != DEFAULT_LAYOUT_RESOURCE_ID);
605d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
6069eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout        mHideWheelUntilFocused = attributesArray.getBoolean(
6079eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout            R.styleable.NumberPicker_hideWheelUntilFocused, false);
6089eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout
609206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
610d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
6119cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
612d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
613d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int defSelectionDividerHeight = (int) TypedValue.applyDimension(
614d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
6159cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                getResources().getDisplayMetrics());
6169cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
6179cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
618d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
619d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int defSelectionDividerDistance = (int) TypedValue.applyDimension(
620d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE,
621d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                getResources().getDisplayMetrics());
622d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mSelectionDividersDistance = attributesArray.getDimensionPixelSize(
623d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_selectionDividersDistance, defSelectionDividerDistance);
624d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
625e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov        mMinHeight = attributesArray.getDimensionPixelSize(
626e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov                R.styleable.NumberPicker_internalMinHeight, SIZE_UNSPECIFIED);
627d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
628e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov        mMaxHeight = attributesArray.getDimensionPixelSize(
629e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov                R.styleable.NumberPicker_internalMaxHeight, SIZE_UNSPECIFIED);
6309f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED
6319f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                && mMinHeight > mMaxHeight) {
632ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            throw new IllegalArgumentException("minHeight > maxHeight");
633ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
634d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
635d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mMinWidth = attributesArray.getDimensionPixelSize(
636d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalMinWidth, SIZE_UNSPECIFIED);
637d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
638d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mMaxWidth = attributesArray.getDimensionPixelSize(
639d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalMaxWidth, SIZE_UNSPECIFIED);
6409f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED
6419f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                && mMinWidth > mMaxWidth) {
642ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            throw new IllegalArgumentException("minWidth > maxWidth");
643ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
644d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
645f7c83bc4d3e1996b491824902a6a0f98ac69bedeSvetoslav Ganov        mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED);
646206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
647232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mVirtualButtonPressedDrawable = attributesArray.getDrawable(
648232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                R.styleable.NumberPicker_virtualButtonPressedDrawable);
649232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
650d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        attributesArray.recycle();
6519cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
652232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mPressedStateHelper = new PressedStateHelper();
653232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
654206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // By default Linearlayout that we extend is not drawn. This is
655206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // its draw() method is not called but dispatchDraw() is called
656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // directly (see ViewGroup.drawChild()). However, this class uses
657206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // the fading edge effect implemented by View and we need our
658206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // draw() method to be called. Therefore, we declare we will draw.
659d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        setWillNotDraw(!mHasSelectorWheel);
660206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
661206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
662206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                Context.LAYOUT_INFLATER_SERVICE);
663d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        inflater.inflate(layoutResId, this, true);
66468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
665206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnClickListener onClickListener = new OnClickListener() {
66668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            public void onClick(View v) {
667b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                hideSoftInput();
668206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
669206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
670d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    changeValueByOne(true);
671206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
672d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    changeValueByOne(false);
67368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
67468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
67568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        };
67668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
677206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnLongClickListener onLongClickListener = new OnLongClickListener() {
678206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public boolean onLongClick(View v) {
679b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                hideSoftInput();
680206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
681206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
682d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(true, 0);
683206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
684d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(false, 0);
685206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
686206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return true;
687206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
688206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        };
68968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
690206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // increment button
691d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
692d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton = (ImageButton) findViewById(R.id.increment);
693d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setOnClickListener(onClickListener);
694d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setOnLongClickListener(onLongClickListener);
695d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
696d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton = null;
697d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
698206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
699206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // decrement button
700d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
701d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton = (ImageButton) findViewById(R.id.decrement);
702d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setOnClickListener(onClickListener);
703d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setOnLongClickListener(onLongClickListener);
704d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
705d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton = null;
706d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
707206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
708206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // input text
709012dd5a461f18a2e5dad38c60282fac1c21ff7feSvetoslav Ganov        mInputText = (EditText) findViewById(R.id.numberpicker_input);
710206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setOnFocusChangeListener(new OnFocusChangeListener() {
711206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onFocusChange(View v, boolean hasFocus) {
712a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                if (hasFocus) {
713a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.selectAll();
714a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                } else {
715a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.setSelection(0, 0);
716206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    validateInputTextView(v);
71768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
71868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
719206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
720206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setFilters(new InputFilter[] {
721206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            new InputTextFilter()
722206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
72368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
724206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
725a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        mInputText.setImeOptions(EditorInfo.IME_ACTION_DONE);
726206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
727206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // initialize constants
728206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ViewConfiguration configuration = ViewConfiguration.get(context);
729206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = configuration.getScaledTouchSlop();
730206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
731206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity()
732206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;
733206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTextSize = (int) mInputText.getTextSize();
734206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
735206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the selector wheel paint
736206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Paint paint = new Paint();
737206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setAntiAlias(true);
738206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextAlign(Align.CENTER);
739206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextSize(mTextSize);
740206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTypeface(mInputText.getTypeface());
741206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ColorStateList colors = mInputText.getTextColors();
742206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int color = colors.getColorForState(ENABLED_STATE_SET, Color.WHITE);
743206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setColor(color);
744b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorWheelPaint = paint;
745206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
746206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the fling and adjust scrollers
747bf80562d22b2bbe7944d80d0524c69d0238010cbSvetoslav Ganov        mFlingScroller = new Scroller(getContext(), null, true);
7489cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
749fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
750206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateInputTextView();
7514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
7524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        // If not explicitly specified this view is important for accessibility.
7534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
7544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
7554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
756206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
757206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
758206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
7599cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
760d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
761d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onLayout(changed, left, top, right, bottom);
762d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return;
763d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
764ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int msrdWdth = getMeasuredWidth();
765ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int msrdHght = getMeasuredHeight();
766ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
767ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        // Input text centered horizontally.
768ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
769ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
770ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
771ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
772ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
773ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
774ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
775ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
776d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (changed) {
777b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            // need to do all this when we know our size
778b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            initializeSelectorWheel();
779b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            initializeFadingEdges();
780d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mTopSelectionDividerTop = (getHeight() - mSelectionDividersDistance) / 2
781d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    - mSelectionDividerHeight;
782d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mBottomSelectionDividerBottom = mTopSelectionDividerTop + 2 * mSelectionDividerHeight
783d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    + mSelectionDividersDistance;
784b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
785206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
786206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
787206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
788e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
789d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
790d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
791d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return;
792d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
793698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        // Try greedily to fit the max width and height.
794698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth);
795698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight);
796ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
797698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        // Flag if we are measured with width or height less than the respective min.
7989f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, getMeasuredWidth(),
7999f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                widthMeasureSpec);
8009f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, getMeasuredHeight(),
8019f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                heightMeasureSpec);
802698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        setMeasuredDimension(widthSize, heightSize);
803e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
804e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
805d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
806d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Move to the final position of a scroller. Ensures to force finish the scroller
807d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * and if it is not at its final position a scroll of the selector wheel is
808d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * performed to fast forward to the final position.
809d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
810d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param scroller The scroller to whose final position to get.
811d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return True of the a move was performed, i.e. the scroller was not in final position.
812d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
813d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean moveToFinalScrollerPosition(Scroller scroller) {
814d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        scroller.forceFinished(true);
815d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int amountToScroll = scroller.getFinalY() - scroller.getCurrY();
816d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int futureScrollOffset = (mCurrentScrollOffset + amountToScroll) % mSelectorElementHeight;
817d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int overshootAdjustment = mInitialScrollOffset - futureScrollOffset;
818d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (overshootAdjustment != 0) {
819d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (Math.abs(overshootAdjustment) > mSelectorElementHeight / 2) {
820d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (overshootAdjustment > 0) {
821d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    overshootAdjustment -= mSelectorElementHeight;
822d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
823d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    overshootAdjustment += mSelectorElementHeight;
824d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
825d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
826d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            amountToScroll += overshootAdjustment;
827d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            scrollBy(0, amountToScroll);
828d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
829d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
830d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
831d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
832d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
833e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    @Override
834206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onInterceptTouchEvent(MotionEvent event) {
835d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel || !isEnabled()) {
83651c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
83751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
838d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int action = event.getActionMasked();
839d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (action) {
840d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_DOWN: {
841206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                removeAllCallbacks();
842d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.INVISIBLE);
843d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownOrMoveEventY = mLastDownEventY = event.getY();
844d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownEventTime = event.getEventTime();
84546a27efe9528ac3c5b37726707844e7c868815acSvetoslav                mIgnoreMoveEvents = false;
84646a27efe9528ac3c5b37726707844e7c868815acSvetoslav                mPerformClickOnTap = false;
847232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                // Handle pressed state before any state change.
848232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                if (mLastDownEventY < mTopSelectionDividerTop) {
849232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
850232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mPressedStateHelper.buttonPressDelayed(
851232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                PressedStateHelper.BUTTON_DECREMENT);
852232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
853232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
854232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
855232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mPressedStateHelper.buttonPressDelayed(
856232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                PressedStateHelper.BUTTON_INCREMENT);
857232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
858232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                }
859232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                // Make sure we support flinging inside scrollables.
86083dc45c65988e9b86e156d59f59ede48195ed1d5Svetoslav Ganov                getParent().requestDisallowInterceptTouchEvent(true);
861d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (!mFlingScroller.isFinished()) {
862d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mFlingScroller.forceFinished(true);
863d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mAdjustScroller.forceFinished(true);
864d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
865d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (!mAdjustScroller.isFinished()) {
866d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mFlingScroller.forceFinished(true);
867d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mAdjustScroller.forceFinished(true);
868d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (mLastDownEventY < mTopSelectionDividerTop) {
869b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                    hideSoftInput();
870d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(
871d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            false, ViewConfiguration.getLongPressTimeout());
872d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
873b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                    hideSoftInput();
874d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(
875d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            true, ViewConfiguration.getLongPressTimeout());
876d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
87746a27efe9528ac3c5b37726707844e7c868815acSvetoslav                    mPerformClickOnTap = true;
878d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postBeginSoftInputOnLongPressCommand();
879206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
880d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return true;
881d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
882206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
883206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return false;
884206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
885206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
886206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
887d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public boolean onTouchEvent(MotionEvent event) {
888d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!isEnabled() || !mHasSelectorWheel) {
88951c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
89051c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
891206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mVelocityTracker == null) {
892206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mVelocityTracker = VelocityTracker.obtain();
893206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
894d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mVelocityTracker.addMovement(event);
895d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int action = event.getActionMasked();
896206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (action) {
897d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_MOVE: {
89846a27efe9528ac3c5b37726707844e7c868815acSvetoslav                if (mIgnoreMoveEvents) {
899d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    break;
900d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
901d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                float currentMoveY = event.getY();
902d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
903206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
904206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (deltaDownY > mTouchSlop) {
905d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        removeAllCallbacks();
9069cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                        onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
907206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
908d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
909232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY));
910d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    scrollBy(0, deltaMoveY);
911d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    invalidate();
912206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
913d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownOrMoveEventY = currentMoveY;
914d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } break;
915d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_UP: {
916d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeBeginSoftInputCommand();
917d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeChangeCurrentByOneFromLongPress();
918232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mPressedStateHelper.cancel();
919206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                VelocityTracker velocityTracker = mVelocityTracker;
920206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
921206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int initialVelocity = (int) velocityTracker.getYVelocity();
922206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
923232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    fling(initialVelocity);
9249cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
925206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
926d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    int eventY = (int) event.getY();
927d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    int deltaMoveY = (int) Math.abs(eventY - mLastDownEventY);
928d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    long deltaTime = event.getEventTime() - mLastDownEventTime;
929d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (deltaMoveY <= mTouchSlop && deltaTime < ViewConfiguration.getTapTimeout()) {
93046a27efe9528ac3c5b37726707844e7c868815acSvetoslav                        if (mPerformClickOnTap) {
93146a27efe9528ac3c5b37726707844e7c868815acSvetoslav                            mPerformClickOnTap = false;
93246a27efe9528ac3c5b37726707844e7c868815acSvetoslav                            performClick();
933d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        } else {
934d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            int selectorIndexOffset = (eventY / mSelectorElementHeight)
935d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                    - SELECTOR_MIDDLE_ITEM_INDEX;
936d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            if (selectorIndexOffset > 0) {
937d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                changeValueByOne(true);
938232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                mPressedStateHelper.buttonTapped(
939232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        PressedStateHelper.BUTTON_INCREMENT);
940d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            } else if (selectorIndexOffset < 0) {
941d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                changeValueByOne(false);
942232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                mPressedStateHelper.buttonTapped(
943232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        PressedStateHelper.BUTTON_DECREMENT);
944d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
945206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        }
946206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    } else {
947d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        ensureScrollWheelAdjusted();
948206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
949d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
950206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
951206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker.recycle();
952206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker = null;
953d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } break;
954206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
955206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return true;
956206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
957206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
958206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
959206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTouchEvent(MotionEvent event) {
960b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        final int action = event.getActionMasked();
961b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        switch (action) {
962b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_CANCEL:
963b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_UP:
964b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                removeAllCallbacks();
965b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                break;
966206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
967206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTouchEvent(event);
968206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
969206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
970206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
971206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchKeyEvent(KeyEvent event) {
972d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int keyCode = event.getKeyCode();
973d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (keyCode) {
974d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case KeyEvent.KEYCODE_DPAD_CENTER:
975d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case KeyEvent.KEYCODE_ENTER:
976d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeAllCallbacks();
977d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                break;
9785dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov            case KeyEvent.KEYCODE_DPAD_DOWN:
9795dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov            case KeyEvent.KEYCODE_DPAD_UP:
9805dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                if (!mHasSelectorWheel) {
9815dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                    break;
9825dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                }
9835dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                switch (event.getAction()) {
9845dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                    case KeyEvent.ACTION_DOWN:
9859eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout                        if (mWrapSelectorWheel || ((keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
9869eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout                                ? getValue() < getMaxValue() : getValue() > getMinValue())) {
9875dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            requestFocus();
9885dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            mLastHandledDownDpadKeyCode = keyCode;
9895dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            removeAllCallbacks();
9905dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            if (mFlingScroller.isFinished()) {
99186cf0cdfe14ce9606d42671b2961081f3220a789Justin Mattson                                changeValueByOne(keyCode == KeyEvent.KEYCODE_DPAD_DOWN);
9925dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            }
9935dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            return true;
9945dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        }
9955dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        break;
9965dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                    case KeyEvent.ACTION_UP:
9975dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        if (mLastHandledDownDpadKeyCode == keyCode) {
9985dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            mLastHandledDownDpadKeyCode = -1;
9995dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            return true;
10005dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        }
10015dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        break;
10025dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                }
1003206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1004206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchKeyEvent(event);
1005206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1006206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1007206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1008206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTrackballEvent(MotionEvent event) {
1009d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int action = event.getActionMasked();
1010d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (action) {
1011d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_CANCEL:
1012d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_UP:
1013d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeAllCallbacks();
1014d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                break;
1015206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1016206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTrackballEvent(event);
1017206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1019206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1020d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    protected boolean dispatchHoverEvent(MotionEvent event) {
1021d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1022d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.dispatchHoverEvent(event);
1023d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1024d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
1025d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int eventY = (int) event.getY();
1026d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int hoveredVirtualViewId;
1027d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (eventY < mTopSelectionDividerTop) {
1028d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_DECREMENT;
1029d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } else if (eventY > mBottomSelectionDividerBottom) {
1030d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INCREMENT;
1031d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } else {
1032d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INPUT;
1033d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1034d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int action = event.getActionMasked();
1035d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeProviderImpl provider =
1036d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                (AccessibilityNodeProviderImpl) getAccessibilityNodeProvider();
1037d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (action) {
1038d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_ENTER: {
1039d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
1040d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
1041d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
1042791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    provider.performAction(hoveredVirtualViewId,
1043791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
1044d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
1045d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_MOVE: {
1046d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (mLastHoveredChildVirtualViewId != hoveredVirtualViewId
1047d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && mLastHoveredChildVirtualViewId != View.NO_ID) {
1048d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        provider.sendAccessibilityEventForVirtualView(
1049d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                mLastHoveredChildVirtualViewId,
1050d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
1051d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
1052d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
1053d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
1054791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        provider.performAction(hoveredVirtualViewId,
1055791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
1056d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
1057d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
1058d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_EXIT: {
1059d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
1060d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
1061d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mLastHoveredChildVirtualViewId = View.NO_ID;
1062d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
1063d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1064206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1065d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
1066d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1067d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1068d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    @Override
1069d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public void computeScroll() {
1070206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Scroller scroller = mFlingScroller;
1071206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
1072206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scroller = mAdjustScroller;
1073206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (scroller.isFinished()) {
1074206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return;
1075206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1076206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1077206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scroller.computeScrollOffset();
1078206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int currentScrollerY = scroller.getCurrY();
1079206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mPreviousScrollerY == 0) {
1080206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = scroller.getStartY();
1081206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1082206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scrollBy(0, currentScrollerY - mPreviousScrollerY);
1083206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = currentScrollerY;
1084206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
1085206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            onScrollerFinished(scroller);
1086206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1087206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1090fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setEnabled(boolean enabled) {
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.setEnabled(enabled);
1094d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1095d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setEnabled(enabled);
1096d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1097d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1098d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setEnabled(enabled);
1099d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1100206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setEnabled(enabled);
1101206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1102206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1103206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1104206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void scrollBy(int x, int y) {
1105b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
110634c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y > 0
110734c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
1108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
1109206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
111134c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y < 0
111234c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
1113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
1114206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1116206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mCurrentScrollOffset += y;
1117b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) {
1118206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset -= mSelectorElementHeight;
1119206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            decrementSelectorIndices(selectorIndices);
1120d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
1121b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
1122206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
1123206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1124206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1125b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) {
1126206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset += mSelectorElementHeight;
1127b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            incrementSelectorIndices(selectorIndices);
1128d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
1129b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
1130206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
1131206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1132206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1134fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
11354bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    @Override
1136fd63917ed31fbf425362d209eff72d5e64c5bb8aAlan Viverette    protected int computeVerticalScrollOffset() {
11375ba99f58b1e90a6faaf5e84d713e10cf0323ac96Alan Viverette        return mCurrentScrollOffset;
11385ba99f58b1e90a6faaf5e84d713e10cf0323ac96Alan Viverette    }
11395ba99f58b1e90a6faaf5e84d713e10cf0323ac96Alan Viverette
11405ba99f58b1e90a6faaf5e84d713e10cf0323ac96Alan Viverette    @Override
1141fd63917ed31fbf425362d209eff72d5e64c5bb8aAlan Viverette    protected int computeVerticalScrollRange() {
1142fd63917ed31fbf425362d209eff72d5e64c5bb8aAlan Viverette        return (mMaxValue - mMinValue + 1) * mSelectorElementHeight;
1143fd63917ed31fbf425362d209eff72d5e64c5bb8aAlan Viverette    }
1144fd63917ed31fbf425362d209eff72d5e64c5bb8aAlan Viverette
1145fd63917ed31fbf425362d209eff72d5e64c5bb8aAlan Viverette    @Override
1146fd63917ed31fbf425362d209eff72d5e64c5bb8aAlan Viverette    protected int computeVerticalScrollExtent() {
1147fd63917ed31fbf425362d209eff72d5e64c5bb8aAlan Viverette        return getHeight();
11485ba99f58b1e90a6faaf5e84d713e10cf0323ac96Alan Viverette    }
11495ba99f58b1e90a6faaf5e84d713e10cf0323ac96Alan Viverette
11505ba99f58b1e90a6faaf5e84d713e10cf0323ac96Alan Viverette    @Override
11514bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public int getSolidColor() {
11524bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return mSolidColor;
11534bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
11544bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
115568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
11564bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the listener to be notified on change of the current value.
1157206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1158e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onValueChangedListener The listener.
115968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1160cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public void setOnValueChangedListener(OnValueChangeListener onValueChangedListener) {
1161cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        mOnValueChangeListener = onValueChangedListener;
116250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
116350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
116450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
11654bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set listener to be notified for scroll state changes.
116650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
1167e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onScrollListener The listener.
116850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
116950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public void setOnScrollListener(OnScrollListener onScrollListener) {
117050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mOnScrollListener = onScrollListener;
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1172fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
117368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
11744bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set the formatter to be used for formatting the current value.
11754bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
1176e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Note: If you have provided alternative values for the values this
1177e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * formatter is never invoked.
11784bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
1179206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1180e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param formatter The formatter object. If formatter is <code>null</code>,
1181e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            {@link String#valueOf(int)} will be used.
1182d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *@see #setDisplayedValues(String[])
118368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFormatter(Formatter formatter) {
1185e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (formatter == mFormatter) {
1186e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1187e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFormatter = formatter;
1189b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
11909cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
1191206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1192206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1193206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1194e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Set the current value for the number picker.
11954bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
1196e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
1197e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
1198e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
1199e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1200e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1201e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
1202e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
1203e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
1204e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1205e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1206e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
1207e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
1208e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
1209e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1210e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1211e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
1212e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
1213e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
12144bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
1215206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1216e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param value The current value.
12174bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @see #setWrapSelectorWheel(boolean)
1218e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMinValue(int)
1219e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMaxValue(int)
1220206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1221e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setValue(int value) {
1222d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        setValueInternal(value, false);
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1224fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
122546a27efe9528ac3c5b37726707844e7c868815acSvetoslav    @Override
122646a27efe9528ac3c5b37726707844e7c868815acSvetoslav    public boolean performClick() {
122746a27efe9528ac3c5b37726707844e7c868815acSvetoslav        if (!mHasSelectorWheel) {
122846a27efe9528ac3c5b37726707844e7c868815acSvetoslav            return super.performClick();
122946a27efe9528ac3c5b37726707844e7c868815acSvetoslav        } else if (!super.performClick()) {
123046a27efe9528ac3c5b37726707844e7c868815acSvetoslav            showSoftInput();
123146a27efe9528ac3c5b37726707844e7c868815acSvetoslav        }
123246a27efe9528ac3c5b37726707844e7c868815acSvetoslav        return true;
123346a27efe9528ac3c5b37726707844e7c868815acSvetoslav    }
123446a27efe9528ac3c5b37726707844e7c868815acSvetoslav
123546a27efe9528ac3c5b37726707844e7c868815acSvetoslav    @Override
123646a27efe9528ac3c5b37726707844e7c868815acSvetoslav    public boolean performLongClick() {
123746a27efe9528ac3c5b37726707844e7c868815acSvetoslav        if (!mHasSelectorWheel) {
123846a27efe9528ac3c5b37726707844e7c868815acSvetoslav            return super.performLongClick();
123946a27efe9528ac3c5b37726707844e7c868815acSvetoslav        } else if (!super.performLongClick()) {
124046a27efe9528ac3c5b37726707844e7c868815acSvetoslav            showSoftInput();
124146a27efe9528ac3c5b37726707844e7c868815acSvetoslav            mIgnoreMoveEvents = true;
124246a27efe9528ac3c5b37726707844e7c868815acSvetoslav        }
124346a27efe9528ac3c5b37726707844e7c868815acSvetoslav        return true;
124446a27efe9528ac3c5b37726707844e7c868815acSvetoslav    }
124546a27efe9528ac3c5b37726707844e7c868815acSvetoslav
124668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1247d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Shows the soft input for its input text.
1248d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1249d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void showSoftInput() {
1250d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
1251d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (inputMethodManager != null) {
1252d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mHasSelectorWheel) {
1253d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.VISIBLE);
1254d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1255d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mInputText.requestFocus();
1256d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            inputMethodManager.showSoftInput(mInputText, 0);
1257d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1258d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1259d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1260d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1261d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Hides the soft input if it is active for the input text.
1262b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov     */
1263b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    private void hideSoftInput() {
1264b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
1265b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
1266b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov            inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
1267d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mHasSelectorWheel) {
1268d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.INVISIBLE);
1269d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1270b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        }
1271b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    }
1272b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov
1273b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    /**
1274ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Computes the max width if no such specified as an attribute.
1275ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
1276ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private void tryComputeMaxWidth() {
1277ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (!mComputeMaxWidth) {
1278ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            return;
1279ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1280ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        int maxTextWidth = 0;
1281ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (mDisplayedValues == null) {
1282ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            float maxDigitWidth = 0;
1283ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            for (int i = 0; i <= 9; i++) {
1284d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                final float digitWidth = mSelectorWheelPaint.measureText(formatNumberWithLocale(i));
1285ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                if (digitWidth > maxDigitWidth) {
1286ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                    maxDigitWidth = digitWidth;
1287ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                }
1288ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1289ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            int numberOfDigits = 0;
1290ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            int current = mMaxValue;
1291ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            while (current > 0) {
1292ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                numberOfDigits++;
1293ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                current = current / 10;
1294ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1295ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            maxTextWidth = (int) (numberOfDigits * maxDigitWidth);
1296ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        } else {
1297ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            final int valueCount = mDisplayedValues.length;
1298ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            for (int i = 0; i < valueCount; i++) {
1299ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                final float textWidth = mSelectorWheelPaint.measureText(mDisplayedValues[i]);
1300ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                if (textWidth > maxTextWidth) {
1301ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                    maxTextWidth = (int) textWidth;
1302ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                }
1303ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1304ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1305ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        maxTextWidth += mInputText.getPaddingLeft() + mInputText.getPaddingRight();
1306ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (mMaxWidth != maxTextWidth) {
1307ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            if (maxTextWidth > mMinWidth) {
1308ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                mMaxWidth = maxTextWidth;
1309ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            } else {
1310ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                mMaxWidth = mMinWidth;
1311ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1312ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            invalidate();
1313ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1314ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    }
1315ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
1316ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
1317e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets whether the selector wheel wraps when reaching the min/max value.
131868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
1319e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return True if the selector wheel wraps.
13204bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1321e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMinValue()
1322e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMaxValue()
132368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1324e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public boolean getWrapSelectorWheel() {
1325e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mWrapSelectorWheel;
1326206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1327206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1328206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1329e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets whether the selector wheel shown during flinging/scrolling should
1330e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * wrap around the {@link NumberPicker#getMinValue()} and
1331e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getMaxValue()} values.
1332e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1333d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * By default if the range (max - min) is more than the number of items shown
1334d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * on the selector wheel the selector wheel wrapping is enabled.
1335e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
13363f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * <p>
1337d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * <strong>Note:</strong> If the number of items, i.e. the range (
1338d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()}) is less than
1339d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * the number of items shown on the selector wheel, the selector wheel will
1340d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * not wrap. Hence, in such a case calling this method is a NOP.
13413f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * </p>
1342d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
13436304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov     * @param wrapSelectorWheel Whether to wrap.
1344206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
13456304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov    public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
13463f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov        final boolean wrappingAllowed = (mMaxValue - mMinValue) >= mSelectorIndices.length;
13473f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov        if ((!wrapSelectorWheel || wrappingAllowed) && wrapSelectorWheel != mWrapSelectorWheel) {
13486304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            mWrapSelectorWheel = wrapSelectorWheel;
1349206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13534bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the speed at which the numbers be incremented and decremented when
13544bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * the up and down buttons are long pressed respectively.
1355e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1356e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default value is 300 ms.
1357e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
135868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
13594bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param intervalMillis The speed (in milliseconds) at which the numbers
1360e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            will be incremented and decremented.
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13624bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setOnLongPressUpdateInterval(long intervalMillis) {
13634bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        mLongPressUpdateInterval = intervalMillis;
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1365fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1367e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the value of the picker.
1368e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1369e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The value.
1370e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1371e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getValue() {
1372e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mValue;
1373e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1374e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1375e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1376e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the min value of the picker.
1377e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1378e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The min value
1379e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1380e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMinValue() {
1381e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
1382e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1383e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1384e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1385e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the min value of the picker.
1386206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
13877018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * @param minValue The min value inclusive.
13887018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     *
13897018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * <strong>Note:</strong> The length of the displayed values array
13907018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * set via {@link #setDisplayedValues(String[])} must be equal to the
13917018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * range of selectable numbers which is equal to
13927018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
1393206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1394e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMinValue(int minValue) {
1395e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue == minValue) {
1396e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1397e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1398e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (minValue < 0) {
1399e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("minValue must be >= 0");
1400e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1401e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMinValue = minValue;
1402e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue > mValue) {
1403e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMinValue;
1404e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1405e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1406e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1407b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1408e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1409ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        tryComputeMaxWidth();
1410d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        invalidate();
1411206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1412206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
14134bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
1414e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the max value of the picker.
14154bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1416e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The max value.
14174bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1418e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMaxValue() {
1419e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMaxValue;
14204bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
14214bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
14224bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
1423e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the max value of the picker.
14244bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
14257018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * @param maxValue The max value inclusive.
14267018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     *
14277018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * <strong>Note:</strong> The length of the displayed values array
14287018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * set via {@link #setDisplayedValues(String[])} must be equal to the
14297018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * range of selectable numbers which is equal to
14307018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
14314bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1432e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMaxValue(int maxValue) {
1433e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue == maxValue) {
1434e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1435e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1436e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (maxValue < 0) {
1437e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("maxValue must be >= 0");
1438e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1439e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMaxValue = maxValue;
1440e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue < mValue) {
1441e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMaxValue;
1442e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1443e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1444e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1445b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1446e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1447ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        tryComputeMaxWidth();
1448d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        invalidate();
1449e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1450e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1451e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1452e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the values to be displayed instead of string values.
1453e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1454e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The displayed values.
1455e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1456e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public String[] getDisplayedValues() {
1457e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mDisplayedValues;
1458e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1459e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1460e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1461e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the values to be displayed.
1462e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1463e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param displayedValues The displayed values.
14647018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     *
14657018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * <strong>Note:</strong> The length of the displayed values array
14667018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * must be equal to the range of selectable numbers which is equal to
14677018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
1468e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1469e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setDisplayedValues(String[] displayedValues) {
1470e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues == displayedValues) {
1471e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1472e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1473e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mDisplayedValues = displayedValues;
1474e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues != null) {
1475e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // Allow text entry rather than strictly numeric entry.
1476e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
1477e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
1478e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else {
1479e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
1480e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1481e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1482b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
14839f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        tryComputeMaxWidth();
1484206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1485206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1486206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1487206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getTopFadingEdgeStrength() {
1488206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1489206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1490206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1491206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1492206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getBottomFadingEdgeStrength() {
1493206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1494206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1495206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1496206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1497206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDetachedFromWindow() {
149846bfc4811094e5b1e3196246e457d4c6b58332ecRomain Guy        super.onDetachedFromWindow();
1499206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
1500206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1501206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1502206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1503206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDraw(Canvas canvas) {
1504d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1505d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onDraw(canvas);
1506206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1507206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
15089eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout        final boolean showSelectorWheel = mHideWheelUntilFocused ? hasFocus() : true;
1509206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float x = (mRight - mLeft) / 2;
1510206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float y = mCurrentScrollOffset;
1511206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1512232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        // draw the virtual buttons pressed state if needed
15139eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout        if (showSelectorWheel && mVirtualButtonPressedDrawable != null
1514232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                && mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
1515232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mDecrementVirtualButtonPressed) {
1516232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
1517232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setBounds(0, 0, mRight, mTopSelectionDividerTop);
1518232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.draw(canvas);
1519232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
1520232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mIncrementVirtualButtonPressed) {
1521232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
1522232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setBounds(0, mBottomSelectionDividerBottom, mRight,
1523232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mBottom);
1524232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.draw(canvas);
1525232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
1526232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
1527232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
15289cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // draw the selector wheel
1529b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
1530206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length; i++) {
1531206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int selectorIndex = selectorIndices[i];
1532206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
1533d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // Do not draw the middle item if input is visible since the input
1534d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // is shown only if the wheel is static and it covers the middle
1535d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // item. Otherwise, if the user starts editing the text via the
1536d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // IME he may see a dimmed version of the old value intermixed
1537d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // with the new one.
15389eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout            if ((showSelectorWheel && i != SELECTOR_MIDDLE_ITEM_INDEX) ||
15399eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout                (i == SELECTOR_MIDDLE_ITEM_INDEX && mInputText.getVisibility() != VISIBLE)) {
15406304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
15416304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            }
1542206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            y += mSelectorElementHeight;
1543206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
15449cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1545d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // draw the selection dividers
15469eef3f4fb92d4abd4e217c13cdf72bd4fc3e59f2Craig Stout        if (showSelectorWheel && mSelectionDivider != null) {
15479cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the top divider
1548d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int topOfTopDivider = mTopSelectionDividerTop;
15499cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
15509cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfTopDivider, mRight, bottomOfTopDivider);
15519cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
15529cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
15539cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the bottom divider
1554d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int bottomOfBottomDivider = mBottomSelectionDividerBottom;
1555d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int topOfBottomDivider = bottomOfBottomDivider - mSelectionDividerHeight;
15569cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfBottomDivider, mRight, bottomOfBottomDivider);
15579cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
15589cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1560fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
15613fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    @Override
15628a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
15638a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
15648a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        event.setClassName(NumberPicker.class.getName());
1565d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setScrollable(true);
1566d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setScrollY((mMinValue + mValue) * mSelectorElementHeight);
1567d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setMaxScrollY((mMaxValue - mMinValue) * mSelectorElementHeight);
15688a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
15698a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
15708a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
1571d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1572d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1573d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.getAccessibilityNodeProvider();
1574d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1575d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mAccessibilityNodeProvider == null) {
1576d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mAccessibilityNodeProvider = new AccessibilityNodeProviderImpl();
1577d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1578d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return mAccessibilityNodeProvider;
15798a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
15808a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
158168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1582ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Makes a measure spec that tries greedily to use the max value.
1583e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     *
1584e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * @param measureSpec The measure spec.
1585698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov     * @param maxSize The max value for the size.
1586ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * @return A measure spec greedily imposing the max size.
1587e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
1588698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov    private int makeMeasureSpec(int measureSpec, int maxSize) {
15899f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (maxSize == SIZE_UNSPECIFIED) {
15909f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return measureSpec;
15919f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        }
1592ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int size = MeasureSpec.getSize(measureSpec);
1593e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        final int mode = MeasureSpec.getMode(measureSpec);
1594e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        switch (mode) {
1595e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.EXACTLY:
1596ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                return measureSpec;
1597e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.AT_MOST:
1598698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov                return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY);
1599e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.UNSPECIFIED:
1600698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov                return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY);
1601e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            default:
1602ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                throw new IllegalArgumentException("Unknown measure mode: " + mode);
1603e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        }
1604e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
1605e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
1606e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
1607d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Utility to reconcile a desired size and state, with constraints imposed
1608d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * by a MeasureSpec. Tries to respect the min size, unless a different size
1609d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * is imposed by the constraints.
16109f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     *
16119f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param minSize The minimal desired size.
16129f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param measuredSize The currently measured size.
16139f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param measureSpec The current measure spec.
16149f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @return The resolved size and state.
16159f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     */
1616d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int resolveSizeAndStateRespectingMinSize(
1617d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int minSize, int measuredSize, int measureSpec) {
16189f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (minSize != SIZE_UNSPECIFIED) {
16199f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            final int desiredWidth = Math.max(minSize, measuredSize);
16209f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return resolveSizeAndState(desiredWidth, measureSpec, 0);
16219f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        } else {
16229f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return measuredSize;
16239f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        }
16249f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    }
16259f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
16269f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    /**
1627d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Resets the selector indices and clear the cached string representation of
1628d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * these indices.
1629a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     */
1630b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeSelectorWheelIndices() {
1631a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        mSelectorIndexToStringCache.clear();
1632232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
1633b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int current = getValue();
1634b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        for (int i = 0; i < mSelectorIndices.length; i++) {
1635b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
1636b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (mWrapSelectorWheel) {
1637b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                selectorIndex = getWrappedSelectorIndex(selectorIndex);
1638b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1639232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            selectorIndices[i] = selectorIndex;
1640232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            ensureCachedScrollSelectorValue(selectorIndices[i]);
1641a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
1642a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    }
1643a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov
1644a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    /**
1645d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Sets the current value of this NumberPicker.
164668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
1647d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param current The new value of the NumberPicker.
1648d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param notifyChange Whether to notify if the current value changed.
164968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1650d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void setValueInternal(int current, boolean notifyChange) {
1651e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mValue == current) {
1652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1653206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
16549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Wrap around the values if we go past the start or end
1655e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel) {
1656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            current = getWrappedSelectorIndex(current);
1657d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1658d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            current = Math.max(current, mMinValue);
1659d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            current = Math.min(current, mMaxValue);
16609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1661e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        int previous = mValue;
1662d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mValue = current;
1663d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        updateInputTextView();
1664d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (notifyChange) {
1665d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            notifyChange(previous, current);
1666d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1667fac14f9731ce7fc765de582c983af751aab697deSvetoslav Ganov        initializeSelectorWheelIndices();
1668fac14f9731ce7fc765de582c983af751aab697deSvetoslav Ganov        invalidate();
16699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1670fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
167168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1672b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Changes the current value by one which is increment or
1673b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * decrement based on the passes argument.
1674d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * decrement the current value.
1675b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1676b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param increment True to increment, false to decrement.
1677b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
1678d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     private void changeValueByOne(boolean increment) {
1679d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mHasSelectorWheel) {
1680b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mInputText.setVisibility(View.INVISIBLE);
1681d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (!moveToFinalScrollerPosition(mFlingScroller)) {
1682d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                moveToFinalScrollerPosition(mAdjustScroller);
1683d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1684b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mPreviousScrollerY = 0;
1685b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (increment) {
1686fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov                mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight, SNAP_SCROLL_DURATION);
1687b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1688fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov                mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight, SNAP_SCROLL_DURATION);
1689b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1690b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            invalidate();
1691b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        } else {
1692b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (increment) {
1693d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                setValueInternal(mValue + 1, true);
1694b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1695d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                setValueInternal(mValue - 1, true);
1696b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1697b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
1698b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1699b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1700b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeSelectorWheel() {
1701b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1702b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
17039cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        int totalTextHeight = selectorIndices.length * mTextSize;
170401fa0d7aae1a551e1e7cfb90d2aeaf2fcb3978afSvetoslav Ganov        float totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
1705d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        float textGapCount = selectorIndices.length;
1706b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
1707b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorElementHeight = mTextSize + mSelectorTextGapHeight;
1708d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // Ensure that the middle item is positioned the same as the text in
1709d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // mInputText
1710eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        int editTextTextPosition = mInputText.getBaseline() + mInputText.getTop();
1711d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mInitialScrollOffset = editTextTextPosition
1712d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                - (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
17136a19fcde07527706c2a9175e0b9f2a71549b7ffeSvetoslav Ganov        mCurrentScrollOffset = mInitialScrollOffset;
17149cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
17159cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    }
17169cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1717b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeFadingEdges() {
1718b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setVerticalFadingEdgeEnabled(true);
1719b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setFadingEdgeLength((mBottom - mTop - mTextSize) / 2);
1720b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1721b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1722206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1723206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Callback invoked upon completion of a given <code>scroller</code>.
1724206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1725206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void onScrollerFinished(Scroller scroller) {
1726206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller == mFlingScroller) {
1727d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (!ensureScrollWheelAdjusted()) {
1728b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                updateInputTextView();
1729b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1730d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
17319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1732d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
1733d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                updateInputTextView();
1734d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
17359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1737fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1738206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
17399cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Handles transition to a given <code>scrollState</code>
174050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
17419cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private void onScrollStateChange(int scrollState) {
17429cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mScrollState == scrollState) {
17439cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            return;
17449cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
17459cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mScrollState = scrollState;
17469cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mOnScrollListener != null) {
17474bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            mOnScrollListener.onScrollStateChange(this, scrollState);
174850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
174950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
175050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
175150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1752206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flings the selector with the given <code>velocityY</code>.
1753206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1754206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void fling(int velocityY) {
1755206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = 0;
1756206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1757234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov        if (velocityY > 0) {
1758234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov            mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
1759206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1760234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov            mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1762206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1763206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        invalidate();
17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1766206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1767206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The wrapped index <code>selectorIndex</code> value.
176868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1769206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getWrappedSelectorIndex(int selectorIndex) {
1770e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex > mMaxValue) {
1771e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMinValue + (selectorIndex - mMaxValue) % (mMaxValue - mMinValue) - 1;
1772e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else if (selectorIndex < mMinValue) {
1773e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMaxValue - (mMinValue - selectorIndex) % (mMaxValue - mMinValue) + 1;
1774206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1775206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return selectorIndex;
17769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1777fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1778206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1779206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Increments the <code>selectorIndices</code> whose string representations
1780206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1781206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1782b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void incrementSelectorIndices(int[] selectorIndices) {
1783206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length - 1; i++) {
1784206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i + 1];
1785206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1786206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1;
1787e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) {
1788e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMinValue;
1789206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1790206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex;
1791206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1792206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1793fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1794206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1795206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Decrements the <code>selectorIndices</code> whose string representations
1796206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1797206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1798206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void decrementSelectorIndices(int[] selectorIndices) {
1799206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = selectorIndices.length - 1; i > 0; i--) {
1800206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i - 1];
1801206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1802206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[1] - 1;
1803e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) {
1804e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMaxValue;
1805206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1806206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[0] = nextScrollSelectorIndex;
1807206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1808206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1809fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1810206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1811206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Ensures we have a cached string representation of the given <code>
1812d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * selectorIndex</code> to avoid multiple instantiations of the same string.
1813206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1814206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void ensureCachedScrollSelectorValue(int selectorIndex) {
1815206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        SparseArray<String> cache = mSelectorIndexToStringCache;
1816206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String scrollSelectorValue = cache.get(selectorIndex);
1817206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scrollSelectorValue != null) {
1818206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1819206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1820e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex < mMinValue || selectorIndex > mMaxValue) {
1821206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scrollSelectorValue = "";
1822206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1823206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues != null) {
1824e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                int displayedValueIndex = selectorIndex - mMinValue;
182550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                scrollSelectorValue = mDisplayedValues[displayedValueIndex];
1826206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1827206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollSelectorValue = formatNumber(selectorIndex);
18289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
18299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1830206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        cache.put(selectorIndex, scrollSelectorValue);
18319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1832fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1833206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private String formatNumber(int value) {
1834d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        return (mFormatter != null) ? mFormatter.format(value) : formatNumberWithLocale(value);
1835206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
18369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1837206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void validateInputTextView(View v) {
1838206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String str = String.valueOf(((TextView) v).getText());
1839206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (TextUtils.isEmpty(str)) {
1840206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Restore to the old value as we don't allow empty values
1841206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
1842206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1843206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Check the new value and ensure it's in range
1844206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int current = getSelectedPos(str.toString());
1845d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(current, true);
18469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1847206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1848fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1849206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1850206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the view of this NumberPicker. If displayValues were specified in
18519cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * the string corresponding to the index specified by the current value will
18529cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * be returned. Otherwise, the formatter specified in {@link #setFormatter}
18539cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * will be used to format the number.
1854d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
1855d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return Whether the text was updated.
1856206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1857d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean updateInputTextView() {
1858206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /*
1859206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * If we don't have displayed values then use the current number else
1860206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * find the correct value in the displayed values for the current
1861206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * number.
1862206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
1863d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        String text = (mDisplayedValues == null) ? formatNumber(mValue)
1864d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                : mDisplayedValues[mValue - mMinValue];
1865d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!TextUtils.isEmpty(text) && !text.equals(mInputText.getText().toString())) {
1866d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mInputText.setText(text);
1867d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
18689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
18693fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
1870d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
1871206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1872fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1873206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1874206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Notifies the listener, if registered, of a change of the value of this
1875206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * NumberPicker.
1876206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1877206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void notifyChange(int previous, int current) {
1878cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        if (mOnValueChangeListener != null) {
1879cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov            mOnValueChangeListener.onValueChange(this, previous, mValue);
1880206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1881206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
18829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1883206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1884b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Posts a command for changing the current value by one.
1885b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1886b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param increment Whether to increment or decrement the value.
1887206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1888d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void postChangeCurrentByOneFromLongPress(boolean increment, long delayMillis) {
1889b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand == null) {
1890b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand();
1891d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1892d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1893d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1894d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mChangeCurrentByOneFromLongPressCommand.setStep(increment);
1895d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        postDelayed(mChangeCurrentByOneFromLongPressCommand, delayMillis);
1896d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1897d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1898d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1899d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Removes the command for changing the current value by one.
1900d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1901d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void removeChangeCurrentByOneFromLongPress() {
1902d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand != null) {
1903d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1904d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1905d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1906d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1907d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1908d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Posts a command for beginning an edit of the current value via IME on
1909d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * long press.
1910d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1911d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void postBeginSoftInputOnLongPressCommand() {
1912d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand == null) {
1913d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mBeginSoftInputOnLongPressCommand = new BeginSoftInputOnLongPressCommand();
1914d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1915d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1916d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1917d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        postDelayed(mBeginSoftInputOnLongPressCommand, ViewConfiguration.getLongPressTimeout());
1918d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1919d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1920d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1921d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Removes the command for beginning an edit of the current value via IME.
1922d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1923d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void removeBeginSoftInputCommand() {
1924d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand != null) {
1925d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1926206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1927206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
19289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1930206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Removes all pending callback from the message queue.
1931206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1932206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void removeAllCallbacks() {
1933b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand != null) {
1934b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1935206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1936206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand != null) {
1937206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
19389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1939d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand != null) {
1940d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1941d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1942232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mPressedStateHelper.cancel();
19439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1945206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1946206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The selected index given its displayed <code>value</code>.
1947206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1948206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getSelectedPos(String value) {
19499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisplayedValues == null) {
1950aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            try {
1951206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
1952aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            } catch (NumberFormatException e) {
1953206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
1954aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            }
19559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
19569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < mDisplayedValues.length; i++) {
1957206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Don't force the user to type in jan when ja will do
1958206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                value = value.toLowerCase();
1959206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
1960e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    return mMinValue + i;
19619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
19629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1963fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1964206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            /*
1965206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov             * The user might have typed in a number into the month field i.e.
19669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             * 10 instead of OCT so support that too.
19679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             */
19689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1969206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
19709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
1971fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1972206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
19739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
19749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1975e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
19769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1979206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link SetSelectionCommand} from the given <code>selectionStart
1980d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * </code> to <code>selectionEnd</code>.
19819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1982206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
1983206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand == null) {
1984206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSetSelectionCommand = new SetSelectionCommand();
1985206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1986206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
1987206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1988206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionStart = selectionStart;
1989206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionEnd = selectionEnd;
1990206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(mSetSelectionCommand);
19919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
199268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
199368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1994d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio     * The numbers accepted by the input text's {@link Filter}
1995d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio     */
1996d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    private static final char[] DIGIT_CHARACTERS = new char[] {
1997d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            // Latin digits are the common case
1998d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1999d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            // Arabic-Indic
2000d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            '\u0660', '\u0661', '\u0662', '\u0663', '\u0664', '\u0665', '\u0666', '\u0667', '\u0668'
2001d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            , '\u0669',
2002d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            // Extended Arabic-Indic
2003d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            '\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8'
2004962dde734b7e347948a1624571e8c180260bae85Martin Olsson            , '\u06f9',
2005962dde734b7e347948a1624571e8c180260bae85Martin Olsson            // Hindi and Marathi (Devanagari script)
2006962dde734b7e347948a1624571e8c180260bae85Martin Olsson            '\u0966', '\u0967', '\u0968', '\u0969', '\u096a', '\u096b', '\u096c', '\u096d', '\u096e'
2007962dde734b7e347948a1624571e8c180260bae85Martin Olsson            , '\u096f',
2008962dde734b7e347948a1624571e8c180260bae85Martin Olsson            // Bengali
2009962dde734b7e347948a1624571e8c180260bae85Martin Olsson            '\u09e6', '\u09e7', '\u09e8', '\u09e9', '\u09ea', '\u09eb', '\u09ec', '\u09ed', '\u09ee'
2010962dde734b7e347948a1624571e8c180260bae85Martin Olsson            , '\u09ef',
2011962dde734b7e347948a1624571e8c180260bae85Martin Olsson            // Kannada
2012962dde734b7e347948a1624571e8c180260bae85Martin Olsson            '\u0ce6', '\u0ce7', '\u0ce8', '\u0ce9', '\u0cea', '\u0ceb', '\u0cec', '\u0ced', '\u0cee'
2013962dde734b7e347948a1624571e8c180260bae85Martin Olsson            , '\u0cef'
2014d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    };
2015d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
2016d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    /**
2017206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Filter for accepting only valid indices or prefixes of the string
2018206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * representation of valid indices.
201968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
2020206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class InputTextFilter extends NumberKeyListener {
2021206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2022206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // XXX This doesn't allow for range limits when controlled by a
2023206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // soft input method!
2024206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public int getInputType() {
2025206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return InputType.TYPE_CLASS_TEXT;
2026206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2027206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2028206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
2029206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        protected char[] getAcceptedChars() {
2030206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return DIGIT_CHARACTERS;
2031206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2032206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2033206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
2034d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public CharSequence filter(
2035d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
2036206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues == null) {
2037206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
2038206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (filtered == null) {
2039206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    filtered = source.subSequence(start, end);
2040206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
2041206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2042206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
2043206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
2044206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2045206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if ("".equals(result)) {
2046206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return result;
2047206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
2048206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int val = getSelectedPos(result);
2049206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2050206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                /*
2051206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * Ensure the user can't type in a value greater than the max
2052206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * allowed. We have to allow less than min as the user might
2053206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * want to delete some numbers and then type a new number.
20546d8a99fecc5dd3aec502c0419af61e8d2a3c398dSungmin Choi                 * And prevent multiple-"0" that exceeds the length of upper
20556d8a99fecc5dd3aec502c0419af61e8d2a3c398dSungmin Choi                 * bound number.
2056206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 */
20576d8a99fecc5dd3aec502c0419af61e8d2a3c398dSungmin Choi                if (val > mMaxValue || result.length() > String.valueOf(mMaxValue).length()) {
2058206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
2059206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
2060206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return filtered;
2061206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
2062206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
2063206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = String.valueOf(source.subSequence(start, end));
2064206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (TextUtils.isEmpty(filtered)) {
2065206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
2066206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
2067206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
2068206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
2069206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String str = String.valueOf(result).toLowerCase();
2070206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                for (String val : mDisplayedValues) {
2071206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    String valLowerCase = val.toLowerCase();
2072206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (valLowerCase.startsWith(str)) {
2073206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postSetSelectionCommand(result.length(), val.length());
2074206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        return val.subSequence(dstart, val.length());
2075206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
2076206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
2077206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return "";
2078206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
2079206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2080206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
2081206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2082206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
2083d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Ensures that the scroll wheel is adjusted i.e. there is no offset and the
2084d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * middle element is in the middle of the widget.
2085d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
2086d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return Whether an adjustment has been made.
2087d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
2088d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean ensureScrollWheelAdjusted() {
2089d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // adjust to the closest value
2090d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
2091d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (deltaY != 0) {
2092d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mPreviousScrollerY = 0;
2093d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
2094d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
2095d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2096d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
2097d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            invalidate();
2098d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
2099d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2100d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
2101d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
2102d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2103232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    class PressedStateHelper implements Runnable {
2104232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public static final int BUTTON_INCREMENT = 1;
2105232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public static final int BUTTON_DECREMENT = 2;
2106232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2107232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private final int MODE_PRESS = 1;
2108232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private final int MODE_TAPPED = 2;
2109232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2110232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private int mManagedButton;
2111232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private int mMode;
2112232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2113232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void cancel() {
2114232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = 0;
2115232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = 0;
2116232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.removeCallbacks(this);
2117232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mIncrementVirtualButtonPressed) {
2118232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mIncrementVirtualButtonPressed = false;
2119232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2120232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
2121232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mDecrementVirtualButtonPressed = false;
2122232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mDecrementVirtualButtonPressed) {
2123232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                invalidate(0, 0, mRight, mTopSelectionDividerTop);
2124fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov            }
2125fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov        }
2126fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov
2127232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void buttonPressDelayed(int button) {
2128232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            cancel();
2129232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = MODE_PRESS;
2130232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = button;
2131232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.postDelayed(this, ViewConfiguration.getTapTimeout());
2132232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
2133232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2134232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void buttonTapped(int button) {
2135232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            cancel();
2136232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = MODE_TAPPED;
2137232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = button;
2138232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.post(this);
2139232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
2140232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2141232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        @Override
2142232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void run() {
2143232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            switch (mMode) {
2144232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                case MODE_PRESS: {
2145232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    switch (mManagedButton) {
2146232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_INCREMENT: {
2147232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mIncrementVirtualButtonPressed = true;
2148232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2149232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        } break;
2150232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_DECREMENT: {
2151232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mDecrementVirtualButtonPressed = true;
2152232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, 0, mRight, mTopSelectionDividerTop);
2153232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        }
2154232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
2155232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } break;
2156232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                case MODE_TAPPED: {
2157232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    switch (mManagedButton) {
2158232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_INCREMENT: {
2159232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            if (!mIncrementVirtualButtonPressed) {
2160232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                NumberPicker.this.postDelayed(this,
2161232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        ViewConfiguration.getPressedStateDuration());
2162232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            }
2163232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mIncrementVirtualButtonPressed ^= true;
2164232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2165232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        } break;
2166232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_DECREMENT: {
2167232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            if (!mDecrementVirtualButtonPressed) {
2168232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                NumberPicker.this.postDelayed(this,
2169232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        ViewConfiguration.getPressedStateDuration());
2170232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            }
2171232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mDecrementVirtualButtonPressed ^= true;
2172232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, 0, mRight, mTopSelectionDividerTop);
2173232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        }
2174232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
2175232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } break;
2176fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov            }
2177fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov        }
2178fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov    }
2179fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov
2180d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
2181206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for setting the input text selection.
2182206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2183206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class SetSelectionCommand implements Runnable {
2184206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionStart;
2185206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2186206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionEnd;
2187206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2188206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
2189206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setSelection(mSelectionStart, mSelectionEnd);
2190206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2191206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
2192206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2193206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
2194b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Command for changing the current value from a long press by one.
2195206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2196b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    class ChangeCurrentByOneFromLongPressCommand implements Runnable {
2197b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        private boolean mIncrement;
2198206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2199d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void setStep(boolean increment) {
2200b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mIncrement = increment;
2201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2203d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2204206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
2205d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            changeValueByOne(mIncrement);
22064bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            postDelayed(this, mLongPressUpdateInterval);
2207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
220868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
2209a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2210a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    /**
2211a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     * @hide
2212a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     */
2213a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    public static class CustomEditText extends EditText {
2214a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2215a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        public CustomEditText(Context context, AttributeSet attrs) {
2216a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            super(context, attrs);
2217a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        }
2218a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2219a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        @Override
2220a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        public void onEditorAction(int actionCode) {
2221a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            super.onEditorAction(actionCode);
2222a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            if (actionCode == EditorInfo.IME_ACTION_DONE) {
2223a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov                clearFocus();
2224a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            }
2225a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        }
2226a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    }
2227d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2228d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
2229d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Command for beginning soft input on long press.
2230d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
2231d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    class BeginSoftInputOnLongPressCommand implements Runnable {
2232d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2233d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2234d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public void run() {
223546a27efe9528ac3c5b37726707844e7c868815acSvetoslav            performLongClick();
2236d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2237d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
2238d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2239791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov    /**
2240791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov     * Class for managing virtual view tree rooted at this picker.
2241791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov     */
2242d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    class AccessibilityNodeProviderImpl extends AccessibilityNodeProvider {
2243791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private static final int UNDEFINED = Integer.MIN_VALUE;
2244791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2245d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_INCREMENT = 1;
2246d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2247d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_INPUT = 2;
2248d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2249d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_DECREMENT = 3;
2250d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2251d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private final Rect mTempRect = new Rect();
2252d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2253d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private final int[] mTempArray = new int[2];
2254d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2255791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private int mAccessibilityFocusedView = UNDEFINED;
2256791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2257d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2258d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
2259d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2260d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case View.NO_ID:
2261d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForNumberPicker( mScrollX, mScrollY,
2262d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft), mScrollY + (mBottom - mTop));
2263d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT:
2264d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_DECREMENT,
2265d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            getVirtualDecrementButtonText(), mScrollX, mScrollY,
2266d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft),
2267d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mTopSelectionDividerTop + mSelectionDividerHeight);
2268d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT:
22690e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette                    return createAccessibiltyNodeInfoForInputText(mScrollX,
22700e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette                            mTopSelectionDividerTop + mSelectionDividerHeight,
22710e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette                            mScrollX + (mRight - mLeft),
22720e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette                            mBottomSelectionDividerBottom - mSelectionDividerHeight);
2273d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT:
2274d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_INCREMENT,
2275d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            getVirtualIncrementButtonText(), mScrollX,
2276d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mBottomSelectionDividerBottom - mSelectionDividerHeight,
2277d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft), mScrollY + (mBottom - mTop));
2278d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2279d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.createAccessibilityNodeInfo(virtualViewId);
2280d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2281d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2282d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2283d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String searched,
2284d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int virtualViewId) {
2285d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (TextUtils.isEmpty(searched)) {
2286d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return Collections.emptyList();
2287d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2288d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            String searchedLowerCase = searched.toLowerCase();
2289d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            List<AccessibilityNodeInfo> result = new ArrayList<AccessibilityNodeInfo>();
2290d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2291d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case View.NO_ID: {
2292d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2293d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_DECREMENT, result);
2294d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2295d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_INPUT, result);
2296d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2297d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_INCREMENT, result);
2298d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return result;
2299d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
2300d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT:
2301d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT:
2302d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2303d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase, virtualViewId,
2304d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            result);
2305d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return result;
2306d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
2307d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2308d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.findAccessibilityNodeInfosByText(searched, virtualViewId);
2309d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2310d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2311d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2312aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
2313d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2314791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case View.NO_ID: {
2315791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2316791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2317791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2318791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2319791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                requestAccessibilityFocus();
2320791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2321791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2322791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2323791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2324791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2325791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2326791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                clearAccessibilityFocus();
2327791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2328791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2329791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return false;
2330791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
233148d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
2332fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()
2333fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                    && (getWrapSelectorWheel() || getValue() < getMaxValue())) {
233448d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                changeValueByOne(true);
233548d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                return true;
233648d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                            }
233748d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        } return false;
233848d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
2339fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()
2340fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                    && (getWrapSelectorWheel() || getValue() > getMinValue())) {
234148d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                changeValueByOne(false);
234248d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                return true;
234348d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                            }
234448d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        } return false;
2345791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2346791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } break;
2347d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2348d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    switch (action) {
2349d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_FOCUS: {
2350fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled() && !mInputText.isFocused()) {
2351d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                return mInputText.requestFocus();
2352d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
2353d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        } break;
2354d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
2355fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled() && mInputText.isFocused()) {
2356d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                mInputText.clearFocus();
2357d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                return true;
2358d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
2359791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return false;
2360791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2361791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2362fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()) {
236346a27efe9528ac3c5b37726707844e7c868815acSvetoslav                                performClick();
236446a27efe9528ac3c5b37726707844e7c868815acSvetoslav                                return true;
236546a27efe9528ac3c5b37726707844e7c868815acSvetoslav                            }
236646a27efe9528ac3c5b37726707844e7c868815acSvetoslav                            return false;
236746a27efe9528ac3c5b37726707844e7c868815acSvetoslav                        }
236846a27efe9528ac3c5b37726707844e7c868815acSvetoslav                        case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
236946a27efe9528ac3c5b37726707844e7c868815acSvetoslav                            if (NumberPicker.this.isEnabled()) {
237046a27efe9528ac3c5b37726707844e7c868815acSvetoslav                                performLongClick();
2371fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                return true;
2372fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            }
2373fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            return false;
2374791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2375791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2376791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2377791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2378791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2379791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2380791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mInputText.invalidate();
2381791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2382791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2383791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2384791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2385791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2386791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2387791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2388791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2389791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mInputText.invalidate();
2390791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2391791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2392791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2393791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        default: {
2394791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return mInputText.performAccessibilityAction(action, arguments);
2395791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2396d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2397791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2398791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2399791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2400791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2401fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()) {
2402fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                NumberPicker.this.changeValueByOne(true);
2403fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2404fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_CLICKED);
2405fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                return true;
2406fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            }
2407fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                        } return false;
2408791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2409791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2410791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2411791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2412791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2413791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2414791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2415791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2416791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2417791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2418791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2419791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2420791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2421791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2422791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2423791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2424791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2425791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2426791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2427791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2428791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2429791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2430791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2431fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()) {
2432fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                final boolean increment = (virtualViewId == VIRTUAL_VIEW_ID_INCREMENT);
2433fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                NumberPicker.this.changeValueByOne(increment);
2434fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2435fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_CLICKED);
2436fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                return true;
2437fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            }
2438fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                        } return false;
2439791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2440791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2441791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2442791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2443791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2444791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, 0, mRight, mTopSelectionDividerTop);
2445791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2446791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2447791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2448791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2449791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2450791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2451791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2452791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2453791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, 0, mRight, mTopSelectionDividerTop);
2454791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2455791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2456791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2457791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2458791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2459d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2460aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov            return super.performAction(virtualViewId, action, arguments);
2461d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2462d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2463d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
2464d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2465d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2466791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    if (hasVirtualDecrementButton()) {
2467791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
2468791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                getVirtualDecrementButtonText());
2469791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2470d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2471d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2472d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    sendAccessibilityEventForVirtualText(eventType);
2473d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2474d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2475791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    if (hasVirtualIncrementButton()) {
2476791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
2477791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                getVirtualIncrementButtonText());
2478791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2479d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2480d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2481d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2482d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2483d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void sendAccessibilityEventForVirtualText(int eventType) {
2484a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
2485a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
2486a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                mInputText.onInitializeAccessibilityEvent(event);
2487a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                mInputText.onPopulateAccessibilityEvent(event);
2488a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2489a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                requestSendAccessibilityEvent(NumberPicker.this, event);
2490a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov            }
2491d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2492d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2493d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
2494d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                String text) {
2495a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
2496a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
2497a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setClassName(Button.class.getName());
2498a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setPackageName(mContext.getPackageName());
2499a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.getText().add(text);
2500a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setEnabled(NumberPicker.this.isEnabled());
2501a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setSource(NumberPicker.this, virtualViewId);
2502a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                requestSendAccessibilityEvent(NumberPicker.this, event);
2503a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov            }
2504d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2505d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2506d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void findAccessibilityNodeInfosByTextInChild(String searchedLowerCase,
2507d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int virtualViewId, List<AccessibilityNodeInfo> outResult) {
2508d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2509d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2510d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    String text = getVirtualDecrementButtonText();
2511d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text)
2512d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && text.toString().toLowerCase().contains(searchedLowerCase)) {
2513d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT));
2514d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2515d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } return;
2516d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2517d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    CharSequence text = mInputText.getText();
2518d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text) &&
2519d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            text.toString().toLowerCase().contains(searchedLowerCase)) {
2520d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
2521d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        return;
2522d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2523d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    CharSequence contentDesc = mInputText.getText();
2524d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(contentDesc) &&
2525d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            contentDesc.toString().toLowerCase().contains(searchedLowerCase)) {
2526d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
2527d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        return;
2528d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2529d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2530d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2531d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    String text = getVirtualIncrementButtonText();
2532d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text)
2533d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && text.toString().toLowerCase().contains(searchedLowerCase)) {
2534d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT));
2535d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2536d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } return;
2537d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2538d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2539d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
25400e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette        private AccessibilityNodeInfo createAccessibiltyNodeInfoForInputText(
25410e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette                int left, int top, int right, int bottom) {
2542d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo();
2543d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2544791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) {
2545791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2546791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2547791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT) {
2548791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2549791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
25500e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            Rect boundsInParent = mTempRect;
25510e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            boundsInParent.set(left, top, right, bottom);
25520e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            info.setVisibleToUser(isVisibleToUser(boundsInParent));
25530e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            info.setBoundsInParent(boundsInParent);
25540e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            Rect boundsInScreen = boundsInParent;
25550e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            int[] locationOnScreen = mTempArray;
25560e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            getLocationOnScreen(locationOnScreen);
25570e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
25580e2d281083e375d3b0bd44d61dbbef5c6d93921eAlan Viverette            info.setBoundsInScreen(boundsInScreen);
2559d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2560d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2561d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2562d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private AccessibilityNodeInfo createAccessibilityNodeInfoForVirtualButton(int virtualViewId,
2563d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                String text, int left, int top, int right, int bottom) {
2564d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
2565d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setClassName(Button.class.getName());
2566d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setPackageName(mContext.getPackageName());
2567d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this, virtualViewId);
2568d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setParent(NumberPicker.this);
2569d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setText(text);
2570d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setClickable(true);
2571d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setLongClickable(true);
2572d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setEnabled(NumberPicker.this.isEnabled());
2573d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInParent = mTempRect;
2574d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInParent.set(left, top, right, bottom);
25750d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setVisibleToUser(isVisibleToUser(boundsInParent));
2576d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInParent(boundsInParent);
2577d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInScreen = boundsInParent;
2578d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int[] locationOnScreen = mTempArray;
2579d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            getLocationOnScreen(locationOnScreen);
2580d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
2581d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInScreen(boundsInScreen);
2582791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2583791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != virtualViewId) {
2584791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2585791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2586791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == virtualViewId) {
2587791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2588791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2589fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov            if (NumberPicker.this.isEnabled()) {
2590fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
2591fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov            }
2592791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2593d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2594d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2595d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2596d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private AccessibilityNodeInfo createAccessibilityNodeInfoForNumberPicker(int left, int top,
2597d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int right, int bottom) {
2598d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
25990d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setClassName(NumberPicker.class.getName());
2600d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setPackageName(mContext.getPackageName());
2601d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this);
2602791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2603791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (hasVirtualDecrementButton()) {
2604791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
2605791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
26060d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2607791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (hasVirtualIncrementButton()) {
2608791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
2609791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2610791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
26114528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            info.setParent((View) getParentForAccessibility());
2612d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setEnabled(NumberPicker.this.isEnabled());
2613d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setScrollable(true);
2614983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov
2615983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov            final float applicationScale =
2616983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov                getContext().getResources().getCompatibilityInfo().applicationScale;
2617983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov
2618d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInParent = mTempRect;
2619d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInParent.set(left, top, right, bottom);
2620983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov            boundsInParent.scale(applicationScale);
2621d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInParent(boundsInParent);
2622983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov
26230d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setVisibleToUser(isVisibleToUser());
2624983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov
2625d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInScreen = boundsInParent;
2626d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int[] locationOnScreen = mTempArray;
2627d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            getLocationOnScreen(locationOnScreen);
2628d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
2629983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov            boundsInScreen.scale(applicationScale);
2630d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInScreen(boundsInScreen);
2631791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2632791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != View.NO_ID) {
2633791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2634791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2635791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == View.NO_ID) {
2636791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2637791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2638fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov            if (NumberPicker.this.isEnabled()) {
2639fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                if (getWrapSelectorWheel() || getValue() < getMaxValue()) {
2640fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
2641fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                }
2642fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                if (getWrapSelectorWheel() || getValue() > getMinValue()) {
2643fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
2644fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                }
264548d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov            }
2646791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2647d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2648d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2649d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2650791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private boolean hasVirtualDecrementButton() {
2651791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            return getWrapSelectorWheel() || getValue() > getMinValue();
2652791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
2653791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2654791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private boolean hasVirtualIncrementButton() {
2655791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            return getWrapSelectorWheel() || getValue() < getMaxValue();
2656791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
2657791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2658d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private String getVirtualDecrementButtonText() {
2659d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int value = mValue - 1;
2660d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mWrapSelectorWheel) {
2661d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                value = getWrappedSelectorIndex(value);
2662d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2663d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (value >= mMinValue) {
2664d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return (mDisplayedValues == null) ? formatNumber(value)
2665d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        : mDisplayedValues[value - mMinValue];
2666d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2667d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return null;
2668d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2669d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2670d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private String getVirtualIncrementButtonText() {
2671d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int value = mValue + 1;
2672d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mWrapSelectorWheel) {
2673d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                value = getWrappedSelectorIndex(value);
2674d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2675d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (value <= mMaxValue) {
2676d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return (mDisplayedValues == null) ? formatNumber(value)
2677d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        : mDisplayedValues[value - mMinValue];
2678d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2679d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return null;
2680d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2681d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
2682d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
2683d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    static private String formatNumberWithLocale(int value) {
2684d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        return String.format(Locale.getDefault(), "%d", value);
2685d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
26867762d93621f508f4410fd37b5bbdfec0071b0420Paul Westbrook}
2687