NumberPicker.java revision 6d8a99fecc5dd3aec502c0419af61e8d2a3c398d
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookimport android.annotation.Widget;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
21206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.ColorStateList;
22206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.TypedArray;
23206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Canvas;
24206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Color;
25206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Paint;
26e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.graphics.Paint.Align;
27b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.graphics.Rect;
289cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.graphics.drawable.Drawable;
29aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganovimport android.os.Bundle;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputFilter;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputType;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.Spanned;
33206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.text.TextUtils;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.NumberKeyListener;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
36206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.util.SparseArray;
379cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.util.TypedValue;
38206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.KeyEvent;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.LayoutInflater;
40b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport android.view.LayoutInflater.Filter;
41206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.MotionEvent;
42206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.VelocityTracker;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
44206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.ViewConfiguration;
453fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
463fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganovimport android.view.accessibility.AccessibilityManager;
478a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
48d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport android.view.accessibility.AccessibilityNodeProvider;
499cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganovimport android.view.animation.DecelerateInterpolator;
50a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganovimport android.view.inputmethod.EditorInfo;
51206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.inputmethod.InputMethodManager;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
53b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganovimport com.android.internal.R;
54d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglioimport libcore.icu.LocaleData;
55b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
56d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.ArrayList;
57d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.Collections;
58d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganovimport java.util.List;
59d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglioimport java.util.Locale;
60d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
6168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook/**
624bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * A widget that enables the user to select a number form a predefined range.
63d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * There are two flavors of this widget and which one is presented to the user
64d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * depends on the current theme.
65d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <ul>
66d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <li>
67d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * If the current theme is derived from {@link android.R.style#Theme} the widget
68d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * presents the current value as an editable input field with an increment button
69d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * above and a decrement button below. Long pressing the buttons allows for a quick
70d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * change of the current value. Tapping on the input field allows to type in
71d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * a desired value.
72d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </li>
73d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * <li>
74d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * If the current theme is derived from {@link android.R.style#Theme_Holo} or
75d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * {@link android.R.style#Theme_Holo_Light} the widget presents the current
76d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * value as an editable input field with a lesser value above and a greater
77d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * value below. Tapping on the lesser or greater value selects it by animating
78d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * the number axis up or down to make the chosen value current. Flinging up
79d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * or down allows for multiple increments or decrements of the current value.
80d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * Long pressing on the lesser and greater values also allows for a quick change
81d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * of the current value. Tapping on the current value allows to type in a
82d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * desired value.
83d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </li>
84d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov * </ul>
854bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * <p>
864bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * For an example of using this widget, see {@link android.widget.TimePicker}.
874bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * </p>
8868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook */
8968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook@Widget
9068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpublic class NumberPicker extends LinearLayout {
91fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
9268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
933f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * The number of items show in the selector wheel.
943f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     */
95d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int SELECTOR_WHEEL_ITEM_COUNT = 3;
963f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov
973f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov    /**
98e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default update interval during long press.
99e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
100e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300;
101e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
102e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
103206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The index of the middle selector item.
10468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
105d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int SELECTOR_MIDDLE_ITEM_INDEX = SELECTOR_WHEEL_ITEM_COUNT / 2;
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The coefficient by which to adjust (divide) the max fling velocity.
10968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8;
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
112206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the duration for adjusting the selector wheel.
114206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
116fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
117206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * The duration of scrolling while snapping to a given position.
119b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
120fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov    private static final int SNAP_SCROLL_DURATION = 300;
121b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
122b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
123b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The strength of fading in the top and bottom while drawing the selector.
124206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
125b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f;
126206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
127206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
128b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The default unscaled height of the selection divider.
129206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
130b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2;
131206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
132206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
133d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The default unscaled distance between the selection dividers.
134b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
135d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48;
136b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
137b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
138d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The resource id for the default layout.
139b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
140d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private static final int DEFAULT_LAYOUT_RESOURCE_ID = R.layout.number_picker;
1419cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1429cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
1439f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * Constant for unspecified size.
1449f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     */
1459f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    private static final int SIZE_UNSPECIFIED = -1;
1469f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
1479f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    /**
148206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Use a custom NumberPicker formatting callback to use two-digit minutes
149206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * strings like "01". Keeping a static formatter etc. is the most efficient
150206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * way to do this; it avoids creating temporary objects on every call to
151206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * format().
152206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
153d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    private static class TwoDigitFormatter implements NumberPicker.Formatter {
154206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final StringBuilder mBuilder = new StringBuilder();
155206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
156d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        char mZeroDigit;
157d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        java.util.Formatter mFmt;
158206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
159206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final Object[] mArgs = new Object[1];
160206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
161d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        TwoDigitFormatter() {
162d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            final Locale locale = Locale.getDefault();
163d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            init(locale);
164d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        }
165d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
166d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        private void init(Locale locale) {
167d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            mFmt = createFormatter(locale);
168d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            mZeroDigit = getZeroDigit(locale);
169d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        }
170d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
171e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value) {
172d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            final Locale currentLocale = Locale.getDefault();
173d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            if (mZeroDigit != getZeroDigit(currentLocale)) {
174d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                init(currentLocale);
175d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            }
176206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mArgs[0] = value;
177206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mBuilder.delete(0, mBuilder.length());
178206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mFmt.format("%02d", mArgs);
179206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return mFmt.toString();
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
181d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
182d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        private static char getZeroDigit(Locale locale) {
183d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            return LocaleData.get(locale).zeroDigit;
184d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        }
185d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
186d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        private java.util.Formatter createFormatter(Locale locale) {
187d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            return new java.util.Formatter(mBuilder, locale);
188d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        }
189d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
190d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
191d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    private static final TwoDigitFormatter sTwoDigitFormatter = new TwoDigitFormatter();
192d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
193a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio    /**
194a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio     * @hide
195a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio     */
196a65fe884d1bba4c64b961df27bd04e33e28a7209Fabrice Di Meglio    public static final Formatter getTwoDigitFormatter() {
197d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        return sTwoDigitFormatter;
198d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
199d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
200206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The increment button.
202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
203206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mIncrementButton;
204206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
205206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
206206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The decrement button.
207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
208206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mDecrementButton;
209206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
210206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
211206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The text for showing the current value.
212206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
213206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final EditText mInputText;
214206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
215206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
216d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The distance between the two selection dividers.
217d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
218d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final int mSelectionDividersDistance;
219d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
220d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
221ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * The min height of this widget.
222ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
223ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final int mMinHeight;
224ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
225ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
226e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * The max height of this widget.
227e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
228e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    private final int mMaxHeight;
229e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
230e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
231e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * The max width of this widget.
232e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
233ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final int mMinWidth;
234ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
235ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
236ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * The max width of this widget.
237ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
238ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private int mMaxWidth;
239ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
240ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
241ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Flag whether to compute the max width.
242ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
243ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private final boolean mComputeMaxWidth;
244e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
245e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
246206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of the text.
247206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
248206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mTextSize;
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
250206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
251b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * The height of the gap between text elements if the selector wheel.
252b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
253b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private int mSelectorTextGapHeight;
254b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
255b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
256206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The values to be displayed instead the indices.
257206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String[] mDisplayedValues;
25968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
26068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
26168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Lower value of the range of numbers allowed for the NumberPicker
26268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
263e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMinValue;
26468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
26568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
26668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Upper value of the range of numbers allowed for the NumberPicker
26768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
268e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mMaxValue;
26968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
27068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
27168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Current value of this NumberPicker
27268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
273e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mValue;
27468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
27568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
276206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Listener to be notified upon current value change.
27768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
278cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    private OnValueChangeListener mOnValueChangeListener;
27950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
28050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
28150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Listener to be notified upon scroll state change.
28250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
28350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private OnScrollListener mOnScrollListener;
284206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
285206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
286206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Formatter for for displaying the current value.
287206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Formatter mFormatter;
289fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
290206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The speed for updating the value form long press.
292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
293e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL;
294fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
29568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Cache for the string representation of selector indices.
297206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
298206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final SparseArray<String> mSelectorIndexToStringCache = new SparseArray<String>();
299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
300206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The selector indices whose value are show by the selector.
302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
303d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final int[] mSelectorIndices = new int[SELECTOR_WHEEL_ITEM_COUNT];
304206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
305206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Paint} for drawing the selector.
307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
308b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private final Paint mSelectorWheelPaint;
309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
311232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * The {@link Drawable} for pressed virtual (increment/decrement) buttons.
312232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
313232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private final Drawable mVirtualButtonPressedDrawable;
314232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
315232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of a selector element (text + gap).
317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
318206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mSelectorElementHeight;
319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The initial offset of the scroll selector.
322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mInitialScrollOffset = Integer.MIN_VALUE;
324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
326206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The current offset of the scroll selector.
327206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
328206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mCurrentScrollOffset;
329206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
330206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
331206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for flinging the selector.
332206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
333206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mFlingScroller;
334206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
335206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
336206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for adjusting the selector.
337206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mAdjustScroller;
339206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
340206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
341206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The previous Y coordinate while scrolling the selector.
342206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
343206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mPreviousScrollerY;
344206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
345206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
346206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for setting the input text selection.
347206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
348206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private SetSelectionCommand mSetSelectionCommand;
349206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
351b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Handle to the reusable command for changing the current value from long
352b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * press by one.
353206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
354b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand;
355206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
356206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
357d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Command for beginning an edit of the current value via IME on long press.
358b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
359d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private BeginSoftInputOnLongPressCommand mBeginSoftInputOnLongPressCommand;
360b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
361b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
362206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last down event.
363206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
364206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastDownEventY;
365206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
367d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The time of the last down event.
368206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
369d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private long mLastDownEventTime;
370206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
371206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
372d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The Y position of the last down or move event.
373206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
374d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private float mLastDownOrMoveEventY;
375206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
376206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
377206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Determines speed during touch scrolling.
378206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
379206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private VelocityTracker mVelocityTracker;
380206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
381206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
382206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledTouchSlop()
383206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
384206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mTouchSlop;
385206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
386206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
387206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMinimumFlingVelocity()
388206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
389206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMinimumFlingVelocity;
390206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
391206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
392206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMaximumFlingVelocity()
393206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
394206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMaximumFlingVelocity;
395206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
396206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
397206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag whether the selector should wrap around.
398206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
399e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean mWrapSelectorWheel;
400206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
401206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
402206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The back ground color used to optimize scroller fading.
40368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
404206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mSolidColor;
405206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
406206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
407d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Flag whether this widget has a selector wheel.
4084243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov     */
409d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private final boolean mHasSelectorWheel;
4104243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov
4114243dc394d89a93cb207efa36e9755c2424d688bSvetoslav Ganov    /**
4129cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Divider for showing item to be selected while scrolling
4139cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
4149cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final Drawable mSelectionDivider;
4159cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4169cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
4179cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * The height of the selection divider.
4189cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
4199cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private final int mSelectionDividerHeight;
4209cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4219cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
422d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The current scroll state of the number picker.
423206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
424d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
425206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
426206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
427d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Flag whether to ignore move events - we ignore such when we show in IME
428d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * to prevent the content from scrolling.
42950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
430d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean mIngonreMoveEvents;
43150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
43250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
433d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Flag whether to show soft input on tap.
4349cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     */
435d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean mShowSoftInputOnTap;
4369cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
4379cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    /**
438d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The top of the top selection divider.
439b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
440d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mTopSelectionDividerTop;
441b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
442b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    /**
443d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The bottom of the bottom selection divider.
444a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     */
445d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mBottomSelectionDividerBottom;
446d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
447d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
448d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * The virtual id of the last hovered child.
449d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
450d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int mLastHoveredChildVirtualViewId;
451d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
452d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
453232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Whether the increment virtual button is pressed.
454232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
455232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private boolean mIncrementVirtualButtonPressed;
456232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
457232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
458232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Whether the decrement virtual button is pressed.
459232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
460232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private boolean mDecrementVirtualButtonPressed;
461232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
462232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
463d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Provider to report to clients the semantic structure of this widget.
464d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
465d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private AccessibilityNodeProviderImpl mAccessibilityNodeProvider;
466a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
467a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    /**
468232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     * Helper class for managing pressed state of the virtual buttons.
469232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov     */
470232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    private final PressedStateHelper mPressedStateHelper;
471232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
472232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    /**
4735dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov     * The keycode of the last handled DPAD down event.
4745dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov     */
4755dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov    private int mLastHandledDownDpadKeyCode = -1;
4765dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov
4775dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov    /**
478e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for changes of the current value.
479206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
480cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public interface OnValueChangeListener {
481e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
482206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /**
4834bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * Called upon a change of the current value.
4844bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
485206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param picker The NumberPicker associated with this listener.
486206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param oldVal The previous value.
487206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param newVal The new value.
488206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
489e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        void onValueChange(NumberPicker picker, int oldVal, int newVal);
490206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
491206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
492206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
493e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface to listen for the picker scroll state.
49450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
49550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public interface OnScrollListener {
49650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
49750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
49850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The view is not scrolling.
49950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
50050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_IDLE = 0;
50150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
50250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
503d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov         * The user is scrolling using touch, and his finger is still on the screen.
50450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
50550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_TOUCH_SCROLL = 1;
50650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
50750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
50850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user had previously been scrolling using touch and performed a fling.
50950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
51050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_FLING = 2;
51150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
51250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
513e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Callback invoked while the number picker scroll state has changed.
51450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
515e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param view The view whose scroll state is being reported.
516e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * @param scrollState The current scroll state. One of
517e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE},
518e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_TOUCH_SCROLL} or
519e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *            {@link #SCROLL_STATE_IDLE}.
52050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
5214bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        public void onScrollStateChange(NumberPicker view, int scrollState);
52250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
52350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
52450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
525e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Interface used to format current value into a string for presentation.
526206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
527206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public interface Formatter {
5284bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
5294bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        /**
530e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Formats a string representation of the current value.
5314bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
5324bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @param value The currently selected value.
5334bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @return A formatted string representation.
5344bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         */
535e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public String format(int value);
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
537fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
53868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
5394bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
5404bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
5414bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param context The application environment.
5424bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
5434bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public NumberPicker(Context context) {
5444bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        this(context, null);
5454bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
5464bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
5474bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
5484bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
549206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
550206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context The application environment.
551206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs A collection of attributes.
55268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public NumberPicker(Context context, AttributeSet attrs) {
554206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        this(context, attrs, R.attr.numberPickerStyle);
555206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
556206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
557206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
558206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Create a new number picker
559206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
560206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context the application environment.
561206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs a collection of attributes.
562206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param defStyle The default style to apply to this view.
563206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
564206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
565206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super(context, attrs, defStyle);
566206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
567206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // process style attributes
568d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        TypedArray attributesArray = context.obtainStyledAttributes(
569d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                attrs, R.styleable.NumberPicker, defStyle, 0);
570d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int layoutResId = attributesArray.getResourceId(
571d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalLayout, DEFAULT_LAYOUT_RESOURCE_ID);
572d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
573d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mHasSelectorWheel = (layoutResId != DEFAULT_LAYOUT_RESOURCE_ID);
574d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
575206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
576d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
5779cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
578d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
579d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int defSelectionDividerHeight = (int) TypedValue.applyDimension(
580d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
5819cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                getResources().getDisplayMetrics());
5829cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mSelectionDividerHeight = attributesArray.getDimensionPixelSize(
5839cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                R.styleable.NumberPicker_selectionDividerHeight, defSelectionDividerHeight);
584d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
585d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int defSelectionDividerDistance = (int) TypedValue.applyDimension(
586d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE,
587d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                getResources().getDisplayMetrics());
588d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mSelectionDividersDistance = attributesArray.getDimensionPixelSize(
589d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_selectionDividersDistance, defSelectionDividerDistance);
590d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
591e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov        mMinHeight = attributesArray.getDimensionPixelSize(
592e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov                R.styleable.NumberPicker_internalMinHeight, SIZE_UNSPECIFIED);
593d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
594e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov        mMaxHeight = attributesArray.getDimensionPixelSize(
595e8331bd2e7ad3d62140143cafba3ff69be028557Svetoslav Ganov                R.styleable.NumberPicker_internalMaxHeight, SIZE_UNSPECIFIED);
5969f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED
5979f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                && mMinHeight > mMaxHeight) {
598ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            throw new IllegalArgumentException("minHeight > maxHeight");
599ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
600d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
601d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mMinWidth = attributesArray.getDimensionPixelSize(
602d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalMinWidth, SIZE_UNSPECIFIED);
603d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
604d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mMaxWidth = attributesArray.getDimensionPixelSize(
605d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                R.styleable.NumberPicker_internalMaxWidth, SIZE_UNSPECIFIED);
6069f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED
6079f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                && mMinWidth > mMaxWidth) {
608ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            throw new IllegalArgumentException("minWidth > maxWidth");
609ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
610d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
611f7c83bc4d3e1996b491824902a6a0f98ac69bedeSvetoslav Ganov        mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED);
612206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
613232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mVirtualButtonPressedDrawable = attributesArray.getDrawable(
614232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                R.styleable.NumberPicker_virtualButtonPressedDrawable);
615232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
616d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        attributesArray.recycle();
6179cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
618232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mPressedStateHelper = new PressedStateHelper();
619232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
620206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // By default Linearlayout that we extend is not drawn. This is
621206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // its draw() method is not called but dispatchDraw() is called
622206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // directly (see ViewGroup.drawChild()). However, this class uses
623206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // the fading edge effect implemented by View and we need our
624206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // draw() method to be called. Therefore, we declare we will draw.
625d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        setWillNotDraw(!mHasSelectorWheel);
626206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
627206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
628206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                Context.LAYOUT_INFLATER_SERVICE);
629d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        inflater.inflate(layoutResId, this, true);
63068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
631206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnClickListener onClickListener = new OnClickListener() {
63268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            public void onClick(View v) {
633b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                hideSoftInput();
634206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
635206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
636d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    changeValueByOne(true);
637206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
638d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    changeValueByOne(false);
63968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
64068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
64168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        };
64268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
643206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnLongClickListener onLongClickListener = new OnLongClickListener() {
644206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public boolean onLongClick(View v) {
645b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                hideSoftInput();
646206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
647206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
648d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(true, 0);
649206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
650d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(false, 0);
651206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return true;
653206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
654206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        };
65568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // increment button
657d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
658d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton = (ImageButton) findViewById(R.id.increment);
659d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setOnClickListener(onClickListener);
660d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setOnLongClickListener(onLongClickListener);
661d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
662d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton = null;
663d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
664206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
665206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // decrement button
666d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
667d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton = (ImageButton) findViewById(R.id.decrement);
668d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setOnClickListener(onClickListener);
669d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setOnLongClickListener(onLongClickListener);
670d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
671d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton = null;
672d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
673206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
674206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // input text
675012dd5a461f18a2e5dad38c60282fac1c21ff7feSvetoslav Ganov        mInputText = (EditText) findViewById(R.id.numberpicker_input);
676206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setOnFocusChangeListener(new OnFocusChangeListener() {
677206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onFocusChange(View v, boolean hasFocus) {
678a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                if (hasFocus) {
679a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.selectAll();
680a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                } else {
681a53efe9923bedab4fe5d578f32eaff308e5b9e76Svetoslav Ganov                    mInputText.setSelection(0, 0);
682206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    validateInputTextView(v);
68368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
68468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
685206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
686206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setFilters(new InputFilter[] {
687206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            new InputTextFilter()
688206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
68968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
690206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
691a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        mInputText.setImeOptions(EditorInfo.IME_ACTION_DONE);
692206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
693206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // initialize constants
694206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ViewConfiguration configuration = ViewConfiguration.get(context);
695206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = configuration.getScaledTouchSlop();
696206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
697206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity()
698206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;
699206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTextSize = (int) mInputText.getTextSize();
700206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
701206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the selector wheel paint
702206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Paint paint = new Paint();
703206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setAntiAlias(true);
704206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextAlign(Align.CENTER);
705206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextSize(mTextSize);
706206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTypeface(mInputText.getTypeface());
707206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ColorStateList colors = mInputText.getTextColors();
708206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int color = colors.getColorForState(ENABLED_STATE_SET, Color.WHITE);
709206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setColor(color);
710b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorWheelPaint = paint;
711206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
712206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the fling and adjust scrollers
713bf80562d22b2bbe7944d80d0524c69d0238010cbSvetoslav Ganov        mFlingScroller = new Scroller(getContext(), null, true);
7149cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));
715fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
716206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateInputTextView();
7174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
7184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        // If not explicitly specified this view is important for accessibility.
7194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
7204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
7214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
722206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
723206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
724206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
7259cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
726d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
727d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onLayout(changed, left, top, right, bottom);
728d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return;
729d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
730ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int msrdWdth = getMeasuredWidth();
731ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int msrdHght = getMeasuredHeight();
732ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
733ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        // Input text centered horizontally.
734ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtMsrdWdth = mInputText.getMeasuredWidth();
735ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtMsrdHght = mInputText.getMeasuredHeight();
736ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
737ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
738ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
739ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
740ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
741ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
742d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (changed) {
743b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            // need to do all this when we know our size
744b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            initializeSelectorWheel();
745b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            initializeFadingEdges();
746d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mTopSelectionDividerTop = (getHeight() - mSelectionDividersDistance) / 2
747d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    - mSelectionDividerHeight;
748d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mBottomSelectionDividerBottom = mTopSelectionDividerTop + 2 * mSelectionDividerHeight
749d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    + mSelectionDividersDistance;
750b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
751206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
752206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
753206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
754e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
755d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
756d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
757d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return;
758d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
759698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        // Try greedily to fit the max width and height.
760698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth);
761698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight);
762ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
763698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        // Flag if we are measured with width or height less than the respective min.
7649f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, getMeasuredWidth(),
7659f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                widthMeasureSpec);
7669f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, getMeasuredHeight(),
7679f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov                heightMeasureSpec);
768698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov        setMeasuredDimension(widthSize, heightSize);
769e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
770e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
771d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
772d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Move to the final position of a scroller. Ensures to force finish the scroller
773d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * and if it is not at its final position a scroll of the selector wheel is
774d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * performed to fast forward to the final position.
775d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
776d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param scroller The scroller to whose final position to get.
777d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return True of the a move was performed, i.e. the scroller was not in final position.
778d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
779d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean moveToFinalScrollerPosition(Scroller scroller) {
780d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        scroller.forceFinished(true);
781d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int amountToScroll = scroller.getFinalY() - scroller.getCurrY();
782d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int futureScrollOffset = (mCurrentScrollOffset + amountToScroll) % mSelectorElementHeight;
783d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int overshootAdjustment = mInitialScrollOffset - futureScrollOffset;
784d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (overshootAdjustment != 0) {
785d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (Math.abs(overshootAdjustment) > mSelectorElementHeight / 2) {
786d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (overshootAdjustment > 0) {
787d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    overshootAdjustment -= mSelectorElementHeight;
788d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
789d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    overshootAdjustment += mSelectorElementHeight;
790d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
791d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
792d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            amountToScroll += overshootAdjustment;
793d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            scrollBy(0, amountToScroll);
794d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
795d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
796d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
797d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
798d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
799e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    @Override
800206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onInterceptTouchEvent(MotionEvent event) {
801d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel || !isEnabled()) {
80251c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
80351c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
804d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int action = event.getActionMasked();
805d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (action) {
806d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_DOWN: {
807206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                removeAllCallbacks();
808d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.INVISIBLE);
809d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownOrMoveEventY = mLastDownEventY = event.getY();
810d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownEventTime = event.getEventTime();
811d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mIngonreMoveEvents = false;
812d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mShowSoftInputOnTap = false;
813232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                // Handle pressed state before any state change.
814232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                if (mLastDownEventY < mTopSelectionDividerTop) {
815232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
816232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mPressedStateHelper.buttonPressDelayed(
817232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                PressedStateHelper.BUTTON_DECREMENT);
818232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
819232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
820232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
821232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mPressedStateHelper.buttonPressDelayed(
822232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                PressedStateHelper.BUTTON_INCREMENT);
823232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
824232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                }
825232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                // Make sure we support flinging inside scrollables.
82683dc45c65988e9b86e156d59f59ede48195ed1d5Svetoslav Ganov                getParent().requestDisallowInterceptTouchEvent(true);
827d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (!mFlingScroller.isFinished()) {
828d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mFlingScroller.forceFinished(true);
829d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mAdjustScroller.forceFinished(true);
830d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
831d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (!mAdjustScroller.isFinished()) {
832d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mFlingScroller.forceFinished(true);
833d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mAdjustScroller.forceFinished(true);
834d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (mLastDownEventY < mTopSelectionDividerTop) {
835b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                    hideSoftInput();
836d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(
837d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            false, ViewConfiguration.getLongPressTimeout());
838d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else if (mLastDownEventY > mBottomSelectionDividerBottom) {
839b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov                    hideSoftInput();
840d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postChangeCurrentByOneFromLongPress(
841d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            true, ViewConfiguration.getLongPressTimeout());
842d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
843d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mShowSoftInputOnTap = true;
844d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    postBeginSoftInputOnLongPressCommand();
845206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
846d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return true;
847d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
848206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
849206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return false;
850206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
851206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
852206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
853d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public boolean onTouchEvent(MotionEvent event) {
854d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!isEnabled() || !mHasSelectorWheel) {
85551c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return false;
85651c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        }
857206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mVelocityTracker == null) {
858206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mVelocityTracker = VelocityTracker.obtain();
859206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
860d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mVelocityTracker.addMovement(event);
861d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int action = event.getActionMasked();
862206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (action) {
863d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_MOVE: {
864d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (mIngonreMoveEvents) {
865d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    break;
866d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
867d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                float currentMoveY = event.getY();
868d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
869206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
870206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (deltaDownY > mTouchSlop) {
871d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        removeAllCallbacks();
8729cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                        onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
873206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
874d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } else {
875232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY));
876d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    scrollBy(0, deltaMoveY);
877d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    invalidate();
878206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
879d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mLastDownOrMoveEventY = currentMoveY;
880d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } break;
881d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_UP: {
882d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeBeginSoftInputCommand();
883d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeChangeCurrentByOneFromLongPress();
884232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mPressedStateHelper.cancel();
885206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                VelocityTracker velocityTracker = mVelocityTracker;
886206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
887206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int initialVelocity = (int) velocityTracker.getYVelocity();
888206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
889232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    fling(initialVelocity);
8909cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
891206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
892d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    int eventY = (int) event.getY();
893d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    int deltaMoveY = (int) Math.abs(eventY - mLastDownEventY);
894d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    long deltaTime = event.getEventTime() - mLastDownEventTime;
895d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (deltaMoveY <= mTouchSlop && deltaTime < ViewConfiguration.getTapTimeout()) {
896d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        if (mShowSoftInputOnTap) {
897d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mShowSoftInputOnTap = false;
898d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            showSoftInput();
899d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        } else {
900d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            int selectorIndexOffset = (eventY / mSelectorElementHeight)
901d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                    - SELECTOR_MIDDLE_ITEM_INDEX;
902d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            if (selectorIndexOffset > 0) {
903d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                changeValueByOne(true);
904232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                mPressedStateHelper.buttonTapped(
905232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        PressedStateHelper.BUTTON_INCREMENT);
906d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            } else if (selectorIndexOffset < 0) {
907d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                changeValueByOne(false);
908232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                mPressedStateHelper.buttonTapped(
909232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        PressedStateHelper.BUTTON_DECREMENT);
910d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
911206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        }
912206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    } else {
913d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        ensureScrollWheelAdjusted();
914206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
915d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
916206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
917206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker.recycle();
918206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker = null;
919d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } break;
920206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
921206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return true;
922206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
923206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
924206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
925206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTouchEvent(MotionEvent event) {
926b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        final int action = event.getActionMasked();
927b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        switch (action) {
928b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_CANCEL:
929b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            case MotionEvent.ACTION_UP:
930b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                removeAllCallbacks();
931b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                break;
932206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
933206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTouchEvent(event);
934206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
935206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
936206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
937206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchKeyEvent(KeyEvent event) {
938d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int keyCode = event.getKeyCode();
939d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (keyCode) {
940d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case KeyEvent.KEYCODE_DPAD_CENTER:
941d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case KeyEvent.KEYCODE_ENTER:
942d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeAllCallbacks();
943d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                break;
9445dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov            case KeyEvent.KEYCODE_DPAD_DOWN:
9455dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov            case KeyEvent.KEYCODE_DPAD_UP:
9465dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                if (!mHasSelectorWheel) {
9475dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                    break;
9485dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                }
9495dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                switch (event.getAction()) {
9505dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                    case KeyEvent.ACTION_DOWN:
95186cf0cdfe14ce9606d42671b2961081f3220a789Justin Mattson                        if (mWrapSelectorWheel || (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
9525dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                                ? getValue() < getMaxValue() : getValue() > getMinValue()) {
9535dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            requestFocus();
9545dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            mLastHandledDownDpadKeyCode = keyCode;
9555dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            removeAllCallbacks();
9565dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            if (mFlingScroller.isFinished()) {
95786cf0cdfe14ce9606d42671b2961081f3220a789Justin Mattson                                changeValueByOne(keyCode == KeyEvent.KEYCODE_DPAD_DOWN);
9585dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            }
9595dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            return true;
9605dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        }
9615dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        break;
9625dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                    case KeyEvent.ACTION_UP:
9635dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        if (mLastHandledDownDpadKeyCode == keyCode) {
9645dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            mLastHandledDownDpadKeyCode = -1;
9655dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                            return true;
9665dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        }
9675dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                        break;
9685dc21d9b340fe2c1cb4c37435d8a29429cf3f79eSvetoslav Ganov                }
969206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
970206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchKeyEvent(event);
971206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
972206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
973206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
974206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTrackballEvent(MotionEvent event) {
975d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        final int action = event.getActionMasked();
976d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        switch (action) {
977d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_CANCEL:
978d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            case MotionEvent.ACTION_UP:
979d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                removeAllCallbacks();
980d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                break;
981206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
982206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTrackballEvent(event);
983206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
985206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
986d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    protected boolean dispatchHoverEvent(MotionEvent event) {
987d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
988d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.dispatchHoverEvent(event);
989d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
990d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
991d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int eventY = (int) event.getY();
992d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int hoveredVirtualViewId;
993d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (eventY < mTopSelectionDividerTop) {
994d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_DECREMENT;
995d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } else if (eventY > mBottomSelectionDividerBottom) {
996d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INCREMENT;
997d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            } else {
998d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                hoveredVirtualViewId = AccessibilityNodeProviderImpl.VIRTUAL_VIEW_ID_INPUT;
999d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1000d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            final int action = event.getActionMasked();
1001d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeProviderImpl provider =
1002d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                (AccessibilityNodeProviderImpl) getAccessibilityNodeProvider();
1003d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (action) {
1004d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_ENTER: {
1005d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
1006d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
1007d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
1008791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    provider.performAction(hoveredVirtualViewId,
1009791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
1010d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
1011d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_MOVE: {
1012d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (mLastHoveredChildVirtualViewId != hoveredVirtualViewId
1013d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && mLastHoveredChildVirtualViewId != View.NO_ID) {
1014d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        provider.sendAccessibilityEventForVirtualView(
1015d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                mLastHoveredChildVirtualViewId,
1016d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
1017d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
1018d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
1019d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        mLastHoveredChildVirtualViewId = hoveredVirtualViewId;
1020791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        provider.performAction(hoveredVirtualViewId,
1021791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
1022d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
1023d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
1024d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case MotionEvent.ACTION_HOVER_EXIT: {
1025d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId,
1026d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
1027d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    mLastHoveredChildVirtualViewId = View.NO_ID;
1028d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
1029d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1030206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1031d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
1032d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1033d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1034d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    @Override
1035d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public void computeScroll() {
1036206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Scroller scroller = mFlingScroller;
1037206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
1038206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scroller = mAdjustScroller;
1039206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (scroller.isFinished()) {
1040206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return;
1041206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1042206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1043206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scroller.computeScrollOffset();
1044206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int currentScrollerY = scroller.getCurrY();
1045206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mPreviousScrollerY == 0) {
1046206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = scroller.getStartY();
1047206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1048206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scrollBy(0, currentScrollerY - mPreviousScrollerY);
1049206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = currentScrollerY;
1050206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
1051206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            onScrollerFinished(scroller);
1052206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1053206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1056fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setEnabled(boolean enabled) {
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.setEnabled(enabled);
1060d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1061d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIncrementButton.setEnabled(enabled);
1062d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1063d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1064d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mDecrementButton.setEnabled(enabled);
1065d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1066206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setEnabled(enabled);
1067206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1068206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1069206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1070206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void scrollBy(int x, int y) {
1071b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
107234c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y > 0
107334c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
1074206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
1075206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1076206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
107734c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov        if (!mWrapSelectorWheel && y < 0
107834c0688ceafbeef2648bd2287b3b3c3801679448Svetoslav Ganov                && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
1079206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
1080206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1081206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1082206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mCurrentScrollOffset += y;
1083b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) {
1084206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset -= mSelectorElementHeight;
1085206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            decrementSelectorIndices(selectorIndices);
1086d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
1087b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
1088206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
1089206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1090206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1091b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) {
1092206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset += mSelectorElementHeight;
1093b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            incrementSelectorIndices(selectorIndices);
1094d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
1095b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
1096206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
1097206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1098206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1100fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
11014bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    @Override
11024bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public int getSolidColor() {
11034bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return mSolidColor;
11044bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
11054bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
110668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
11074bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the listener to be notified on change of the current value.
1108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1109e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onValueChangedListener The listener.
111068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1111cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov    public void setOnValueChangedListener(OnValueChangeListener onValueChangedListener) {
1112cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        mOnValueChangeListener = onValueChangedListener;
111350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
111450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
111550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
11164bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set listener to be notified for scroll state changes.
111750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
1118e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param onScrollListener The listener.
111950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
112050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public void setOnScrollListener(OnScrollListener onScrollListener) {
112150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mOnScrollListener = onScrollListener;
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1123fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
112468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
11254bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set the formatter to be used for formatting the current value.
11264bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
1127e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Note: If you have provided alternative values for the values this
1128e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * formatter is never invoked.
11294bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
1130206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1131e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param formatter The formatter object. If formatter is <code>null</code>,
1132e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            {@link String#valueOf(int)} will be used.
1133d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *@see #setDisplayedValues(String[])
113468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFormatter(Formatter formatter) {
1136e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (formatter == mFormatter) {
1137e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1138e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFormatter = formatter;
1140b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
11419cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
1142206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1143206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1144206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1145e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Set the current value for the number picker.
11464bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
1147e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
1148e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
1149e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
1150e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1151e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1152e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMinValue()} and
1153e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
1154e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
1155e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1156e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1157e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
1158e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
1159e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMaxValue()} value.
1160e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
1161e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1162e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
1163e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
1164e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * current value is set to the {@link NumberPicker#getMinValue()} value.
11654bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
1166206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
1167e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param value The current value.
11684bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @see #setWrapSelectorWheel(boolean)
1169e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMinValue(int)
1170e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setMaxValue(int)
1171206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1172e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setValue(int value) {
1173d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        setValueInternal(value, false);
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1175fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
117668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1177d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Shows the soft input for its input text.
1178d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1179d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void showSoftInput() {
1180d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
1181d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (inputMethodManager != null) {
1182d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mHasSelectorWheel) {
1183d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.VISIBLE);
1184d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1185d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mInputText.requestFocus();
1186d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            inputMethodManager.showSoftInput(mInputText, 0);
1187d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1188d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1189d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1190d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1191d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Hides the soft input if it is active for the input text.
1192b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov     */
1193b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    private void hideSoftInput() {
1194b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
1195b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
1196b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov            inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
1197d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mHasSelectorWheel) {
1198d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                mInputText.setVisibility(View.INVISIBLE);
1199d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1200b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov        }
1201b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    }
1202b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov
1203b52d9729bfb2ef7ad50d9a5125ebf3a8322429a9Svetoslav Ganov    /**
1204ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Computes the max width if no such specified as an attribute.
1205ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     */
1206ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    private void tryComputeMaxWidth() {
1207ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (!mComputeMaxWidth) {
1208ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            return;
1209ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1210ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        int maxTextWidth = 0;
1211ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (mDisplayedValues == null) {
1212ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            float maxDigitWidth = 0;
1213ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            for (int i = 0; i <= 9; i++) {
1214d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                final float digitWidth = mSelectorWheelPaint.measureText(formatNumberWithLocale(i));
1215ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                if (digitWidth > maxDigitWidth) {
1216ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                    maxDigitWidth = digitWidth;
1217ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                }
1218ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1219ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            int numberOfDigits = 0;
1220ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            int current = mMaxValue;
1221ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            while (current > 0) {
1222ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                numberOfDigits++;
1223ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                current = current / 10;
1224ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1225ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            maxTextWidth = (int) (numberOfDigits * maxDigitWidth);
1226ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        } else {
1227ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            final int valueCount = mDisplayedValues.length;
1228ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            for (int i = 0; i < valueCount; i++) {
1229ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                final float textWidth = mSelectorWheelPaint.measureText(mDisplayedValues[i]);
1230ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                if (textWidth > maxTextWidth) {
1231ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                    maxTextWidth = (int) textWidth;
1232ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                }
1233ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1234ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1235ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        maxTextWidth += mInputText.getPaddingLeft() + mInputText.getPaddingRight();
1236ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        if (mMaxWidth != maxTextWidth) {
1237ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            if (maxTextWidth > mMinWidth) {
1238ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                mMaxWidth = maxTextWidth;
1239ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            } else {
1240ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                mMaxWidth = mMinWidth;
1241ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            }
1242ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov            invalidate();
1243ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        }
1244ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    }
1245ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov
1246ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov    /**
1247e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets whether the selector wheel wraps when reaching the min/max value.
124868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
1249e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return True if the selector wheel wraps.
12504bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1251e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMinValue()
1252e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #getMaxValue()
125368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1254e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public boolean getWrapSelectorWheel() {
1255e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mWrapSelectorWheel;
1256206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1257206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1258206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1259e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets whether the selector wheel shown during flinging/scrolling should
1260e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * wrap around the {@link NumberPicker#getMinValue()} and
1261e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link NumberPicker#getMaxValue()} values.
1262e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1263d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * By default if the range (max - min) is more than the number of items shown
1264d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * on the selector wheel the selector wheel wrapping is enabled.
1265e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
12663f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * <p>
1267d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * <strong>Note:</strong> If the number of items, i.e. the range (
1268d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()}) is less than
1269d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * the number of items shown on the selector wheel, the selector wheel will
1270d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * not wrap. Hence, in such a case calling this method is a NOP.
12713f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov     * </p>
1272d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
12736304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov     * @param wrapSelectorWheel Whether to wrap.
1274206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
12756304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov    public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
12763f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov        final boolean wrappingAllowed = (mMaxValue - mMinValue) >= mSelectorIndices.length;
12773f9c9eab7b41c409c3c7b503b89ad340e408b6a0Svetoslav Ganov        if ((!wrapSelectorWheel || wrappingAllowed) && wrapSelectorWheel != mWrapSelectorWheel) {
12786304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            mWrapSelectorWheel = wrapSelectorWheel;
1279206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12834bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the speed at which the numbers be incremented and decremented when
12844bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * the up and down buttons are long pressed respectively.
1285e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
1286e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default value is 300 ms.
1287e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * </p>
128868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
12894bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param intervalMillis The speed (in milliseconds) at which the numbers
1290e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            will be incremented and decremented.
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12924bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setOnLongPressUpdateInterval(long intervalMillis) {
12934bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        mLongPressUpdateInterval = intervalMillis;
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1295fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1297e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the value of the picker.
1298e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1299e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The value.
1300e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1301e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getValue() {
1302e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mValue;
1303e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1304e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1305e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1306e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the min value of the picker.
1307e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1308e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The min value
1309e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1310e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMinValue() {
1311e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
1312e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1313e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1314e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1315e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the min value of the picker.
1316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
13177018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * @param minValue The min value inclusive.
13187018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     *
13197018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * <strong>Note:</strong> The length of the displayed values array
13207018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * set via {@link #setDisplayedValues(String[])} must be equal to the
13217018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * range of selectable numbers which is equal to
13227018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
1323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1324e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMinValue(int minValue) {
1325e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue == minValue) {
1326e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1327e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1328e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (minValue < 0) {
1329e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("minValue must be >= 0");
1330e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1331e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMinValue = minValue;
1332e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMinValue > mValue) {
1333e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMinValue;
1334e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1335e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1336e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1337b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1338e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1339ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        tryComputeMaxWidth();
1340d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        invalidate();
1341206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1342206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
13434bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
1344e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Returns the max value of the picker.
13454bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
1346e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The max value.
13474bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1348e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getMaxValue() {
1349e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMaxValue;
13504bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
13514bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
13524bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
1353e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the max value of the picker.
13544bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
13557018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * @param maxValue The max value inclusive.
13567018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     *
13577018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * <strong>Note:</strong> The length of the displayed values array
13587018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * set via {@link #setDisplayedValues(String[])} must be equal to the
13597018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * range of selectable numbers which is equal to
13607018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
13614bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
1362e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMaxValue(int maxValue) {
1363e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue == maxValue) {
1364e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1365e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1366e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (maxValue < 0) {
1367e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("maxValue must be >= 0");
1368e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1369e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMaxValue = maxValue;
1370e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mMaxValue < mValue) {
1371e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mValue = mMaxValue;
1372e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1373e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
1374e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
1375b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1376e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1377ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        tryComputeMaxWidth();
1378d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        invalidate();
1379e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1380e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1381e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1382e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the values to be displayed instead of string values.
1383e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1384e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The displayed values.
1385e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1386e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public String[] getDisplayedValues() {
1387e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mDisplayedValues;
1388e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1389e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1390e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1391e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the values to be displayed.
1392e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1393e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param displayedValues The displayed values.
13947018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     *
13957018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * <strong>Note:</strong> The length of the displayed values array
13967018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * must be equal to the range of selectable numbers which is equal to
13977018cfdc05dc6135949806749ff5c370dce09cedSvetoslav Ganov     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
1398e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1399e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setDisplayedValues(String[] displayedValues) {
1400e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues == displayedValues) {
1401e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
1402e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1403e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mDisplayedValues = displayedValues;
1404e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mDisplayedValues != null) {
1405e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // Allow text entry rather than strictly numeric entry.
1406e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
1407e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
1408e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else {
1409e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
1410e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1411e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        updateInputTextView();
1412b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
14139f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        tryComputeMaxWidth();
1414206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1415206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1416206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1417206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getTopFadingEdgeStrength() {
1418206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1419206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1420206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1421206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1422206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getBottomFadingEdgeStrength() {
1423206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
1424206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1425206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1426206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1427206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDetachedFromWindow() {
1428206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
1429206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1430206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1431206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1432206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDraw(Canvas canvas) {
1433d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1434d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            super.onDraw(canvas);
1435206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1436206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1437206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float x = (mRight - mLeft) / 2;
1438206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float y = mCurrentScrollOffset;
1439206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1440232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        // draw the virtual buttons pressed state if needed
1441232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        if (mVirtualButtonPressedDrawable != null
1442232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                && mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
1443232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mDecrementVirtualButtonPressed) {
1444232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
1445232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setBounds(0, 0, mRight, mTopSelectionDividerTop);
1446232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.draw(canvas);
1447232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
1448232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mIncrementVirtualButtonPressed) {
1449232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
1450232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.setBounds(0, mBottomSelectionDividerBottom, mRight,
1451232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        mBottom);
1452232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mVirtualButtonPressedDrawable.draw(canvas);
1453232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
1454232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
1455232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
14569cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        // draw the selector wheel
1457b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
1458206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length; i++) {
1459206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int selectorIndex = selectorIndices[i];
1460206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
1461d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // Do not draw the middle item if input is visible since the input
1462d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // is shown only if the wheel is static and it covers the middle
1463d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // item. Otherwise, if the user starts editing the text via the
1464d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // IME he may see a dimmed version of the old value intermixed
1465d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            // with the new one.
14666304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) {
14676304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov                canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
14686304b0d58e74509a9f21b67b5227b2fee2f1b60fSvetoslav Ganov            }
1469206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            y += mSelectorElementHeight;
1470206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
14719cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1472d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // draw the selection dividers
14739cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mSelectionDivider != null) {
14749cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the top divider
1475d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int topOfTopDivider = mTopSelectionDividerTop;
14769cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
14779cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfTopDivider, mRight, bottomOfTopDivider);
14789cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
14799cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
14809cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            // draw the bottom divider
1481d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int bottomOfBottomDivider = mBottomSelectionDividerBottom;
1482d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int topOfBottomDivider = bottomOfBottomDivider - mSelectionDividerHeight;
14839cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.setBounds(0, topOfBottomDivider, mRight, bottomOfBottomDivider);
14849cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            mSelectionDivider.draw(canvas);
14859cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1487fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
14883fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov    @Override
14898a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
14908a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
14918a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        event.setClassName(NumberPicker.class.getName());
1492d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setScrollable(true);
1493d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setScrollY((mMinValue + mValue) * mSelectorElementHeight);
1494d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        event.setMaxScrollY((mMaxValue - mMinValue) * mSelectorElementHeight);
14958a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
14968a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
14978a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
1498d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1499d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!mHasSelectorWheel) {
1500d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.getAccessibilityNodeProvider();
1501d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1502d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mAccessibilityNodeProvider == null) {
1503d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mAccessibilityNodeProvider = new AccessibilityNodeProviderImpl();
1504d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1505d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return mAccessibilityNodeProvider;
15068a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
15078a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
150868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1509ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * Makes a measure spec that tries greedily to use the max value.
1510e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     *
1511e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     * @param measureSpec The measure spec.
1512698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov     * @param maxSize The max value for the size.
1513ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov     * @return A measure spec greedily imposing the max size.
1514e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov     */
1515698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov    private int makeMeasureSpec(int measureSpec, int maxSize) {
15169f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (maxSize == SIZE_UNSPECIFIED) {
15179f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return measureSpec;
15189f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        }
1519ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov        final int size = MeasureSpec.getSize(measureSpec);
1520e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        final int mode = MeasureSpec.getMode(measureSpec);
1521e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        switch (mode) {
1522e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.EXACTLY:
1523ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                return measureSpec;
1524e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.AT_MOST:
1525698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov                return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY);
1526e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            case MeasureSpec.UNSPECIFIED:
1527698e1d5d867a7ac653fa1d6f29cef4cfb73fad38Svetoslav Ganov                return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY);
1528e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov            default:
1529ec1e06a00d26a783fb948e739ad8157c03e55302Svetoslav Ganov                throw new IllegalArgumentException("Unknown measure mode: " + mode);
1530e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov        }
1531e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    }
1532e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov
1533e0c8ab5aaa02d7b9b01fe68fb893af4d78482f6cSvetoslav Ganov    /**
1534d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Utility to reconcile a desired size and state, with constraints imposed
1535d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * by a MeasureSpec. Tries to respect the min size, unless a different size
1536d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * is imposed by the constraints.
15379f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     *
15389f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param minSize The minimal desired size.
15399f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param measuredSize The currently measured size.
15409f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @param measureSpec The current measure spec.
15419f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     * @return The resolved size and state.
15429f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov     */
1543d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private int resolveSizeAndStateRespectingMinSize(
1544d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int minSize, int measuredSize, int measureSpec) {
15459f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        if (minSize != SIZE_UNSPECIFIED) {
15469f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            final int desiredWidth = Math.max(minSize, measuredSize);
15479f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return resolveSizeAndState(desiredWidth, measureSpec, 0);
15489f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        } else {
15499f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov            return measuredSize;
15509f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov        }
15519f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    }
15529f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov
15539f086d812ff04703de4b14e02a7702ba7c8acb33Svetoslav Ganov    /**
1554d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Resets the selector indices and clear the cached string representation of
1555d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * these indices.
1556a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     */
1557b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeSelectorWheelIndices() {
1558a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        mSelectorIndexToStringCache.clear();
1559232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
1560b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int current = getValue();
1561b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        for (int i = 0; i < mSelectorIndices.length; i++) {
1562b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
1563b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (mWrapSelectorWheel) {
1564b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                selectorIndex = getWrappedSelectorIndex(selectorIndex);
1565b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1566232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            selectorIndices[i] = selectorIndex;
1567232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            ensureCachedScrollSelectorValue(selectorIndices[i]);
1568a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
1569a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    }
1570a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov
1571a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    /**
1572d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Sets the current value of this NumberPicker.
157368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
1574d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param current The new value of the NumberPicker.
1575d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @param notifyChange Whether to notify if the current value changed.
157668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1577d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void setValueInternal(int current, boolean notifyChange) {
1578e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mValue == current) {
1579206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1580206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Wrap around the values if we go past the start or end
1582e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel) {
1583206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            current = getWrappedSelectorIndex(current);
1584d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1585d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            current = Math.max(current, mMinValue);
1586d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            current = Math.min(current, mMaxValue);
15879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1588e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        int previous = mValue;
1589d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mValue = current;
1590d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        updateInputTextView();
1591d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (notifyChange) {
1592d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            notifyChange(previous, current);
1593d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1594fac14f9731ce7fc765de582c983af751aab697deSvetoslav Ganov        initializeSelectorWheelIndices();
1595fac14f9731ce7fc765de582c983af751aab697deSvetoslav Ganov        invalidate();
15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1597fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
159868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1599b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Changes the current value by one which is increment or
1600b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * decrement based on the passes argument.
1601d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * decrement the current value.
1602b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1603b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param increment True to increment, false to decrement.
1604b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     */
1605d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     private void changeValueByOne(boolean increment) {
1606d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mHasSelectorWheel) {
1607b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mInputText.setVisibility(View.INVISIBLE);
1608d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (!moveToFinalScrollerPosition(mFlingScroller)) {
1609d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                moveToFinalScrollerPosition(mAdjustScroller);
1610d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
1611b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mPreviousScrollerY = 0;
1612b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (increment) {
1613fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov                mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight, SNAP_SCROLL_DURATION);
1614b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1615fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov                mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight, SNAP_SCROLL_DURATION);
1616b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1617b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            invalidate();
1618b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        } else {
1619b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            if (increment) {
1620d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                setValueInternal(mValue + 1, true);
1621b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            } else {
1622d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                setValueInternal(mValue - 1, true);
1623b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1624b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        }
1625b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1626b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1627b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeSelectorWheel() {
1628b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        initializeSelectorWheelIndices();
1629b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        int[] selectorIndices = mSelectorIndices;
16309cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        int totalTextHeight = selectorIndices.length * mTextSize;
163101fa0d7aae1a551e1e7cfb90d2aeaf2fcb3978afSvetoslav Ganov        float totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
1632d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        float textGapCount = selectorIndices.length;
1633b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
1634b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        mSelectorElementHeight = mTextSize + mSelectorTextGapHeight;
1635d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // Ensure that the middle item is positioned the same as the text in
1636d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // mInputText
1637eeafd4296b7cf4c2ad9c985562f6d70e2a3ffc03Chet Haase        int editTextTextPosition = mInputText.getBaseline() + mInputText.getTop();
1638d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mInitialScrollOffset = editTextTextPosition
1639d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                - (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
16406a19fcde07527706c2a9175e0b9f2a71549b7ffeSvetoslav Ganov        mCurrentScrollOffset = mInitialScrollOffset;
16419cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        updateInputTextView();
16429cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    }
16439cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov
1644b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void initializeFadingEdges() {
1645b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setVerticalFadingEdgeEnabled(true);
1646b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        setFadingEdgeLength((mBottom - mTop - mTextSize) / 2);
1647b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    }
1648b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov
1649206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1650206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Callback invoked upon completion of a given <code>scroller</code>.
1651206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void onScrollerFinished(Scroller scroller) {
1653206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller == mFlingScroller) {
1654d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (!ensureScrollWheelAdjusted()) {
1655b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov                updateInputTextView();
1656b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            }
1657d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
16589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1659d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
1660d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                updateInputTextView();
1661d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
16629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1664fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1665206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
16669cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * Handles transition to a given <code>scrollState</code>
166750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
16689cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov    private void onScrollStateChange(int scrollState) {
16699cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mScrollState == scrollState) {
16709cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov            return;
16719cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        }
16729cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        mScrollState = scrollState;
16739cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov        if (mOnScrollListener != null) {
16744bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            mOnScrollListener.onScrollStateChange(this, scrollState);
167550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
167650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
167750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
167850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1679206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flings the selector with the given <code>velocityY</code>.
1680206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1681206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void fling(int velocityY) {
1682206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = 0;
1683206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1684234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov        if (velocityY > 0) {
1685234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov            mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
1686206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1687234484a9fb53786aa8b7ed2a3df7e24d41817689Svetoslav Ganov            mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
16889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1689206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1690206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        invalidate();
16919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1693206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1694206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The wrapped index <code>selectorIndex</code> value.
169568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1696206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getWrappedSelectorIndex(int selectorIndex) {
1697e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex > mMaxValue) {
1698e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMinValue + (selectorIndex - mMaxValue) % (mMaxValue - mMinValue) - 1;
1699e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else if (selectorIndex < mMinValue) {
1700e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mMaxValue - (mMinValue - selectorIndex) % (mMaxValue - mMinValue) + 1;
1701206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1702206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return selectorIndex;
17039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1704fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1705206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1706206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Increments the <code>selectorIndices</code> whose string representations
1707206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1708206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1709b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    private void incrementSelectorIndices(int[] selectorIndices) {
1710206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length - 1; i++) {
1711206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i + 1];
1712206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1713206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1;
1714e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) {
1715e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMinValue;
1716206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1717206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex;
1718206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1719206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1720fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1721206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1722206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Decrements the <code>selectorIndices</code> whose string representations
1723206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1724206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1725206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void decrementSelectorIndices(int[] selectorIndices) {
1726206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = selectorIndices.length - 1; i > 0; i--) {
1727206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i - 1];
1728206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1729206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[1] - 1;
1730e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) {
1731e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            nextScrollSelectorIndex = mMaxValue;
1732206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1733206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[0] = nextScrollSelectorIndex;
1734206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1735206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1736fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1737206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1738206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Ensures we have a cached string representation of the given <code>
1739d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * selectorIndex</code> to avoid multiple instantiations of the same string.
1740206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1741206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void ensureCachedScrollSelectorValue(int selectorIndex) {
1742206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        SparseArray<String> cache = mSelectorIndexToStringCache;
1743206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String scrollSelectorValue = cache.get(selectorIndex);
1744206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scrollSelectorValue != null) {
1745206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1746206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1747e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (selectorIndex < mMinValue || selectorIndex > mMaxValue) {
1748206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scrollSelectorValue = "";
1749206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1750206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues != null) {
1751e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                int displayedValueIndex = selectorIndex - mMinValue;
175250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                scrollSelectorValue = mDisplayedValues[displayedValueIndex];
1753206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1754206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollSelectorValue = formatNumber(selectorIndex);
17559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1757206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        cache.put(selectorIndex, scrollSelectorValue);
17589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1759fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1760206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private String formatNumber(int value) {
1761d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        return (mFormatter != null) ? mFormatter.format(value) : formatNumberWithLocale(value);
1762206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1764206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void validateInputTextView(View v) {
1765206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String str = String.valueOf(((TextView) v).getText());
1766206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (TextUtils.isEmpty(str)) {
1767206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Restore to the old value as we don't allow empty values
1768206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
1769206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1770206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Check the new value and ensure it's in range
1771206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int current = getSelectedPos(str.toString());
1772d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            setValueInternal(current, true);
17739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1774206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1775fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1776206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1777206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the view of this NumberPicker. If displayValues were specified in
17789cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * the string corresponding to the index specified by the current value will
17799cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * be returned. Otherwise, the formatter specified in {@link #setFormatter}
17809cd5fb2f46e219ec8250383fb201b94cb2ee8c0aSvetoslav Ganov     * will be used to format the number.
1781d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
1782d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return Whether the text was updated.
1783206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1784d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean updateInputTextView() {
1785206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /*
1786206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * If we don't have displayed values then use the current number else
1787206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * find the correct value in the displayed values for the current
1788206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * number.
1789206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
1790d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        String text = (mDisplayedValues == null) ? formatNumber(mValue)
1791d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                : mDisplayedValues[mValue - mMinValue];
1792d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (!TextUtils.isEmpty(text) && !text.equals(mInputText.getText().toString())) {
1793d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mInputText.setText(text);
1794d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17963fec3fe0e3a83c5e0d1264f34bcc55b158537bc6Svetoslav Ganov
1797d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
1798206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1799fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1800206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1801206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Notifies the listener, if registered, of a change of the value of this
1802206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * NumberPicker.
1803206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1804206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void notifyChange(int previous, int current) {
1805cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov        if (mOnValueChangeListener != null) {
1806cedc446684e94c9971c38c3206f1f224314bda2aSvetoslav Ganov            mOnValueChangeListener.onValueChange(this, previous, mValue);
1807206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1808206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1810206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1811b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Posts a command for changing the current value by one.
1812b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     *
1813b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * @param increment Whether to increment or decrement the value.
1814206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1815d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void postChangeCurrentByOneFromLongPress(boolean increment, long delayMillis) {
1816b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand == null) {
1817b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand();
1818d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1819d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1820d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1821d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        mChangeCurrentByOneFromLongPressCommand.setStep(increment);
1822d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        postDelayed(mChangeCurrentByOneFromLongPressCommand, delayMillis);
1823d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1824d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1825d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1826d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Removes the command for changing the current value by one.
1827d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1828d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void removeChangeCurrentByOneFromLongPress() {
1829d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand != null) {
1830d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1831d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1832d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1833d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1834d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1835d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Posts a command for beginning an edit of the current value via IME on
1836d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * long press.
1837d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1838d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void postBeginSoftInputOnLongPressCommand() {
1839d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand == null) {
1840d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mBeginSoftInputOnLongPressCommand = new BeginSoftInputOnLongPressCommand();
1841d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        } else {
1842d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1843d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1844d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        postDelayed(mBeginSoftInputOnLongPressCommand, ViewConfiguration.getLongPressTimeout());
1845d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
1846d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
1847d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
1848d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Removes the command for beginning an edit of the current value via IME.
1849d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
1850d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private void removeBeginSoftInputCommand() {
1851d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand != null) {
1852d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1853206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1854206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
18559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1856206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1857206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Removes all pending callback from the message queue.
1858206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1859206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void removeAllCallbacks() {
1860b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        if (mChangeCurrentByOneFromLongPressCommand != null) {
1861b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            removeCallbacks(mChangeCurrentByOneFromLongPressCommand);
1862206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1863206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand != null) {
1864206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
18659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1866d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (mBeginSoftInputOnLongPressCommand != null) {
1867d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            removeCallbacks(mBeginSoftInputOnLongPressCommand);
1868d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
1869232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        mPressedStateHelper.cancel();
18709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1872206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1873206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The selected index given its displayed <code>value</code>.
1874206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1875206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getSelectedPos(String value) {
18769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisplayedValues == null) {
1877aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            try {
1878206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
1879aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            } catch (NumberFormatException e) {
1880206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
1881aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            }
18829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
18839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < mDisplayedValues.length; i++) {
1884206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Don't force the user to type in jan when ja will do
1885206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                value = value.toLowerCase();
1886206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
1887e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    return mMinValue + i;
18889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
18899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1890fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1891206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            /*
1892206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov             * The user might have typed in a number into the month field i.e.
18939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             * 10 instead of OCT so support that too.
18949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             */
18959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1896206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
18979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
1898fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1899206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
19009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
19019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1902e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinValue;
19039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
19049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1906206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link SetSelectionCommand} from the given <code>selectionStart
1907d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * </code> to <code>selectionEnd</code>.
19089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1909206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
1910206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand == null) {
1911206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSetSelectionCommand = new SetSelectionCommand();
1912206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1913206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
1914206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1915206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionStart = selectionStart;
1916206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionEnd = selectionEnd;
1917206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(mSetSelectionCommand);
19189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
191968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
192068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1921d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio     * The numbers accepted by the input text's {@link Filter}
1922d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio     */
1923d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    private static final char[] DIGIT_CHARACTERS = new char[] {
1924d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            // Latin digits are the common case
1925d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1926d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            // Arabic-Indic
1927d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            '\u0660', '\u0661', '\u0662', '\u0663', '\u0664', '\u0665', '\u0666', '\u0667', '\u0668'
1928d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            , '\u0669',
1929d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            // Extended Arabic-Indic
1930d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            '\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8'
1931d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio            , '\u06f9'
1932d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    };
1933d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
1934d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    /**
1935206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Filter for accepting only valid indices or prefixes of the string
1936206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * representation of valid indices.
193768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1938206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class InputTextFilter extends NumberKeyListener {
1939206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1940206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // XXX This doesn't allow for range limits when controlled by a
1941206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // soft input method!
1942206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public int getInputType() {
1943206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return InputType.TYPE_CLASS_TEXT;
1944206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1945206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1946206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1947206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        protected char[] getAcceptedChars() {
1948206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return DIGIT_CHARACTERS;
1949206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1950206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1951206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1952d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public CharSequence filter(
1953d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
1954206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues == null) {
1955206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
1956206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (filtered == null) {
1957206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    filtered = source.subSequence(start, end);
1958206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1959206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1960206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1961206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1962206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1963206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if ("".equals(result)) {
1964206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return result;
1965206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1966206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int val = getSelectedPos(result);
1967206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1968206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                /*
1969206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * Ensure the user can't type in a value greater than the max
1970206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * allowed. We have to allow less than min as the user might
1971206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * want to delete some numbers and then type a new number.
19726d8a99fecc5dd3aec502c0419af61e8d2a3c398dSungmin Choi                 * And prevent multiple-"0" that exceeds the length of upper
19736d8a99fecc5dd3aec502c0419af61e8d2a3c398dSungmin Choi                 * bound number.
1974206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 */
19756d8a99fecc5dd3aec502c0419af61e8d2a3c398dSungmin Choi                if (val > mMaxValue || result.length() > String.valueOf(mMaxValue).length()) {
1976206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1977206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
1978206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return filtered;
1979206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1980206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1981206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = String.valueOf(source.subSequence(start, end));
1982206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (TextUtils.isEmpty(filtered)) {
1983206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1984206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1985206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1986206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1987206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String str = String.valueOf(result).toLowerCase();
1988206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                for (String val : mDisplayedValues) {
1989206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    String valLowerCase = val.toLowerCase();
1990206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (valLowerCase.startsWith(str)) {
1991206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postSetSelectionCommand(result.length(), val.length());
1992206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        return val.subSequence(dstart, val.length());
1993206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
1994206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1995206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return "";
1996206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1997206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1998206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1999206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2000206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
2001d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Ensures that the scroll wheel is adjusted i.e. there is no offset and the
2002d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * middle element is in the middle of the widget.
2003d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     *
2004d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * @return Whether an adjustment has been made.
2005d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
2006d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    private boolean ensureScrollWheelAdjusted() {
2007d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        // adjust to the closest value
2008d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
2009d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        if (deltaY != 0) {
2010d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mPreviousScrollerY = 0;
2011d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
2012d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
2013d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2014d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
2015d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            invalidate();
2016d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return true;
2017d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2018d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        return false;
2019d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
2020d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2021232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov    class PressedStateHelper implements Runnable {
2022232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public static final int BUTTON_INCREMENT = 1;
2023232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public static final int BUTTON_DECREMENT = 2;
2024232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2025232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private final int MODE_PRESS = 1;
2026232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private final int MODE_TAPPED = 2;
2027232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2028232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private int mManagedButton;
2029232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        private int mMode;
2030232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2031232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void cancel() {
2032232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = 0;
2033232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = 0;
2034232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.removeCallbacks(this);
2035232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mIncrementVirtualButtonPressed) {
2036232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                mIncrementVirtualButtonPressed = false;
2037232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2038232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            }
2039232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mDecrementVirtualButtonPressed = false;
2040232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            if (mDecrementVirtualButtonPressed) {
2041232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                invalidate(0, 0, mRight, mTopSelectionDividerTop);
2042fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov            }
2043fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov        }
2044fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov
2045232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void buttonPressDelayed(int button) {
2046232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            cancel();
2047232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = MODE_PRESS;
2048232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = button;
2049232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.postDelayed(this, ViewConfiguration.getTapTimeout());
2050232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
2051232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2052232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void buttonTapped(int button) {
2053232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            cancel();
2054232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mMode = MODE_TAPPED;
2055232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            mManagedButton = button;
2056232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            NumberPicker.this.post(this);
2057232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        }
2058232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov
2059232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        @Override
2060232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov        public void run() {
2061232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov            switch (mMode) {
2062232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                case MODE_PRESS: {
2063232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    switch (mManagedButton) {
2064232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_INCREMENT: {
2065232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mIncrementVirtualButtonPressed = true;
2066232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2067232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        } break;
2068232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_DECREMENT: {
2069232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mDecrementVirtualButtonPressed = true;
2070232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, 0, mRight, mTopSelectionDividerTop);
2071232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        }
2072232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
2073232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } break;
2074232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                case MODE_TAPPED: {
2075232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    switch (mManagedButton) {
2076232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_INCREMENT: {
2077232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            if (!mIncrementVirtualButtonPressed) {
2078232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                NumberPicker.this.postDelayed(this,
2079232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        ViewConfiguration.getPressedStateDuration());
2080232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            }
2081232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mIncrementVirtualButtonPressed ^= true;
2082232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2083232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        } break;
2084232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        case BUTTON_DECREMENT: {
2085232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            if (!mDecrementVirtualButtonPressed) {
2086232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                NumberPicker.this.postDelayed(this,
2087232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                                        ViewConfiguration.getPressedStateDuration());
2088232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            }
2089232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            mDecrementVirtualButtonPressed ^= true;
2090232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                            invalidate(0, 0, mRight, mTopSelectionDividerTop);
2091232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                        }
2092232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                    }
2093232dd3f335b8995cd9ab220f7b3fad77076de42bSvetoslav Ganov                } break;
2094fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov            }
2095fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov        }
2096fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov    }
2097fe41ce4ef97e7da585fdbf06988da8047e72b9f6Svetoslav Ganov
2098d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
2099206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for setting the input text selection.
2100206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2101206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class SetSelectionCommand implements Runnable {
2102206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionStart;
2103206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2104206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionEnd;
2105206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2106206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
2107206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setSelection(mSelectionStart, mSelectionEnd);
2108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2109206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
2110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2111206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
2112b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov     * Command for changing the current value from a long press by one.
2113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
2114b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov    class ChangeCurrentByOneFromLongPressCommand implements Runnable {
2115b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov        private boolean mIncrement;
2116206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2117d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void setStep(boolean increment) {
2118b80a3fcad1776c1b9abe3662899660b4f88ac2abSvetoslav Ganov            mIncrement = increment;
2119206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
2120206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
2121d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2122206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
2123d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            changeValueByOne(mIncrement);
21244bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            postDelayed(this, mLongPressUpdateInterval);
2125206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
212668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
2127a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2128a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    /**
2129a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     * @hide
2130a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov     */
2131a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    public static class CustomEditText extends EditText {
2132a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2133a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        public CustomEditText(Context context, AttributeSet attrs) {
2134a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            super(context, attrs);
2135a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        }
2136a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov
2137a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        @Override
2138a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        public void onEditorAction(int actionCode) {
2139a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            super.onEditorAction(actionCode);
2140a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            if (actionCode == EditorInfo.IME_ACTION_DONE) {
2141a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov                clearFocus();
2142a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov            }
2143a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov        }
2144a2b41b438d45570867e4682c0caaf93ace5e712eSvetoslav Ganov    }
2145d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2146d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    /**
2147d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     * Command for beginning soft input on long press.
2148d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov     */
2149d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    class BeginSoftInputOnLongPressCommand implements Runnable {
2150d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2151d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2152d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public void run() {
2153d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            showSoftInput();
2154d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            mIngonreMoveEvents = true;
2155d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2156d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
2157d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2158791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov    /**
2159791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov     * Class for managing virtual view tree rooted at this picker.
2160791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov     */
2161d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    class AccessibilityNodeProviderImpl extends AccessibilityNodeProvider {
2162791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private static final int UNDEFINED = Integer.MIN_VALUE;
2163791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2164d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_INCREMENT = 1;
2165d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2166d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_INPUT = 2;
2167d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2168d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private static final int VIRTUAL_VIEW_ID_DECREMENT = 3;
2169d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2170d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private final Rect mTempRect = new Rect();
2171d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2172d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private final int[] mTempArray = new int[2];
2173d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2174791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private int mAccessibilityFocusedView = UNDEFINED;
2175791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2176d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2177d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
2178d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2179d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case View.NO_ID:
2180d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForNumberPicker( mScrollX, mScrollY,
2181d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft), mScrollY + (mBottom - mTop));
2182d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT:
2183d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_DECREMENT,
2184d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            getVirtualDecrementButtonText(), mScrollX, mScrollY,
2185d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft),
2186d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mTopSelectionDividerTop + mSelectionDividerHeight);
2187d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT:
2188d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibiltyNodeInfoForInputText();
2189d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT:
2190d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return createAccessibilityNodeInfoForVirtualButton(VIRTUAL_VIEW_ID_INCREMENT,
2191d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            getVirtualIncrementButtonText(), mScrollX,
2192d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mBottomSelectionDividerBottom - mSelectionDividerHeight,
2193d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            mScrollX + (mRight - mLeft), mScrollY + (mBottom - mTop));
2194d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2195d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.createAccessibilityNodeInfo(virtualViewId);
2196d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2197d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2198d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2199d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String searched,
2200d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int virtualViewId) {
2201d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (TextUtils.isEmpty(searched)) {
2202d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return Collections.emptyList();
2203d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2204d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            String searchedLowerCase = searched.toLowerCase();
2205d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            List<AccessibilityNodeInfo> result = new ArrayList<AccessibilityNodeInfo>();
2206d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2207d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case View.NO_ID: {
2208d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2209d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_DECREMENT, result);
2210d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2211d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_INPUT, result);
2212d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase,
2213d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            VIRTUAL_VIEW_ID_INCREMENT, result);
2214d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return result;
2215d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
2216d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT:
2217d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT:
2218d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2219d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    findAccessibilityNodeInfosByTextInChild(searchedLowerCase, virtualViewId,
2220d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            result);
2221d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    return result;
2222d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                }
2223d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2224d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return super.findAccessibilityNodeInfosByText(searched, virtualViewId);
2225d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2226d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2227d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        @Override
2228aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
2229d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2230791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case View.NO_ID: {
2231791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2232791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2233791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2234791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2235791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                requestAccessibilityFocus();
2236791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2237791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2238791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2239791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2240791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2241791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2242791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                clearAccessibilityFocus();
2243791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2244791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2245791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return false;
2246791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
224748d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
2248fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()
2249fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                    && (getWrapSelectorWheel() || getValue() < getMaxValue())) {
225048d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                changeValueByOne(true);
225148d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                return true;
225248d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                            }
225348d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        } return false;
225448d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
2255fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()
2256fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                    && (getWrapSelectorWheel() || getValue() > getMinValue())) {
225748d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                changeValueByOne(false);
225848d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                                return true;
225948d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                            }
226048d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov                        } return false;
2261791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2262791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } break;
2263d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2264d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    switch (action) {
2265d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_FOCUS: {
2266fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled() && !mInputText.isFocused()) {
2267d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                return mInputText.requestFocus();
2268d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
2269d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        } break;
2270d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
2271fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled() && mInputText.isFocused()) {
2272d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                mInputText.clearFocus();
2273d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                                return true;
2274d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            }
2275791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return false;
2276791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2277791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2278fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()) {
2279fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                showSoftInput();
2280fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                return true;
2281fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            }
2282fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            return false;
2283791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2284791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2285791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2286791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2287791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2288791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2289791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mInputText.invalidate();
2290791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2291791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2292791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2293791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2294791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2295791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2296791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2297791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2298791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mInputText.invalidate();
2299791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2300791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2301791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2302791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        default: {
2303791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            return mInputText.performAccessibilityAction(action, arguments);
2304791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        }
2305d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2306791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2307791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2308791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2309791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2310fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()) {
2311fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                NumberPicker.this.changeValueByOne(true);
2312fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2313fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_CLICKED);
2314fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                return true;
2315fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            }
2316fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                        } return false;
2317791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2318791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2319791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2320791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2321791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2322791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2323791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2324791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2325791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2326791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2327791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2328791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2329791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2330791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2331791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
2332791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2333791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2334791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2335791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2336791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2337791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2338791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    switch (action) {
2339791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_CLICK: {
2340fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            if (NumberPicker.this.isEnabled()) {
2341fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                final boolean increment = (virtualViewId == VIRTUAL_VIEW_ID_INCREMENT);
2342fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                NumberPicker.this.changeValueByOne(increment);
2343fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2344fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_CLICKED);
2345fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                                return true;
2346fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                            }
2347fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                        } return false;
2348791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
2349791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView != virtualViewId) {
2350791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = virtualViewId;
2351791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2352791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
2353791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, 0, mRight, mTopSelectionDividerTop);
2354791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2355791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2356791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2357791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        case  AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
2358791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            if (mAccessibilityFocusedView == virtualViewId) {
2359791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                mAccessibilityFocusedView = UNDEFINED;
2360791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                sendAccessibilityEventForVirtualView(virtualViewId,
2361791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
2362791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                invalidate(0, 0, mRight, mTopSelectionDividerTop);
2363791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                return true;
2364791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                            }
2365791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        } return false;
2366791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2367791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                } return false;
2368d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2369aa780c110922148a6a4ba06734bb2b0bb8c98f93Svetoslav Ganov            return super.performAction(virtualViewId, action, arguments);
2370d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2371d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2372d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) {
2373d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2374d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2375791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    if (hasVirtualDecrementButton()) {
2376791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
2377791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                getVirtualDecrementButtonText());
2378791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2379d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2380d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2381d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    sendAccessibilityEventForVirtualText(eventType);
2382d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2383d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2384791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    if (hasVirtualIncrementButton()) {
2385791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                        sendAccessibilityEventForVirtualButton(virtualViewId, eventType,
2386791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                                getVirtualIncrementButtonText());
2387791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                    }
2388d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2389d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2390d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2391d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2392d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void sendAccessibilityEventForVirtualText(int eventType) {
2393a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
2394a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
2395a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                mInputText.onInitializeAccessibilityEvent(event);
2396a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                mInputText.onPopulateAccessibilityEvent(event);
2397a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2398a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                requestSendAccessibilityEvent(NumberPicker.this, event);
2399a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov            }
2400d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2401d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2402d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
2403d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                String text) {
2404a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
2405a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
2406a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setClassName(Button.class.getName());
2407a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setPackageName(mContext.getPackageName());
2408a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.getText().add(text);
2409a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setEnabled(NumberPicker.this.isEnabled());
2410a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                event.setSource(NumberPicker.this, virtualViewId);
2411a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov                requestSendAccessibilityEvent(NumberPicker.this, event);
2412a90927629881f0faf54c1003ebd38c27a730045eSvetoslav Ganov            }
2413d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2414d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2415d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private void findAccessibilityNodeInfosByTextInChild(String searchedLowerCase,
2416d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int virtualViewId, List<AccessibilityNodeInfo> outResult) {
2417d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            switch (virtualViewId) {
2418d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_DECREMENT: {
2419d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    String text = getVirtualDecrementButtonText();
2420d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text)
2421d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && text.toString().toLowerCase().contains(searchedLowerCase)) {
2422d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT));
2423d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2424d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } return;
2425d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INPUT: {
2426d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    CharSequence text = mInputText.getText();
2427d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text) &&
2428d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            text.toString().toLowerCase().contains(searchedLowerCase)) {
2429d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
2430d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        return;
2431d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2432d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    CharSequence contentDesc = mInputText.getText();
2433d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(contentDesc) &&
2434d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            contentDesc.toString().toLowerCase().contains(searchedLowerCase)) {
2435d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT));
2436d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        return;
2437d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2438d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } break;
2439d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                case VIRTUAL_VIEW_ID_INCREMENT: {
2440d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    String text = getVirtualIncrementButtonText();
2441d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    if (!TextUtils.isEmpty(text)
2442d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                            && text.toString().toLowerCase().contains(searchedLowerCase)) {
2443d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        outResult.add(createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT));
2444d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                    }
2445d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                } return;
2446d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2447d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2448d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2449d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private AccessibilityNodeInfo createAccessibiltyNodeInfoForInputText() {
2450d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo();
2451d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2452791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) {
2453791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2454791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2455791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT) {
2456791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2457791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2458d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2459d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2460d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2461d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private AccessibilityNodeInfo createAccessibilityNodeInfoForVirtualButton(int virtualViewId,
2462d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                String text, int left, int top, int right, int bottom) {
2463d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
2464d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setClassName(Button.class.getName());
2465d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setPackageName(mContext.getPackageName());
2466d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this, virtualViewId);
2467d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setParent(NumberPicker.this);
2468d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setText(text);
2469d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setClickable(true);
2470d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setLongClickable(true);
2471d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setEnabled(NumberPicker.this.isEnabled());
2472d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInParent = mTempRect;
2473d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInParent.set(left, top, right, bottom);
24740d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setVisibleToUser(isVisibleToUser(boundsInParent));
2475d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInParent(boundsInParent);
2476d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInScreen = boundsInParent;
2477d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int[] locationOnScreen = mTempArray;
2478d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            getLocationOnScreen(locationOnScreen);
2479d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
2480d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInScreen(boundsInScreen);
2481791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2482791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != virtualViewId) {
2483791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2484791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2485791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == virtualViewId) {
2486791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2487791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2488fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov            if (NumberPicker.this.isEnabled()) {
2489fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
2490fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov            }
2491791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2492d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2493d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2494d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2495d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private AccessibilityNodeInfo createAccessibilityNodeInfoForNumberPicker(int left, int top,
2496d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                int right, int bottom) {
2497d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
24980d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setClassName(NumberPicker.class.getName());
2499d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setPackageName(mContext.getPackageName());
2500d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setSource(NumberPicker.this);
2501791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2502791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (hasVirtualDecrementButton()) {
2503791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT);
2504791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
25050d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
2506791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (hasVirtualIncrementButton()) {
2507791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
2508791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2509791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
25104528b4e882584745f48263fa6626987e63832a2aSvetoslav Ganov            info.setParent((View) getParentForAccessibility());
2511d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setEnabled(NumberPicker.this.isEnabled());
2512d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setScrollable(true);
2513983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov
2514983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov            final float applicationScale =
2515983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov                getContext().getResources().getCompatibilityInfo().applicationScale;
2516983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov
2517d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInParent = mTempRect;
2518d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInParent.set(left, top, right, bottom);
2519983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov            boundsInParent.scale(applicationScale);
2520d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInParent(boundsInParent);
2521983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov
25220d607fbe546ac943de38dad33ae681b09efec6eaGuang Zhu            info.setVisibleToUser(isVisibleToUser());
2523983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov
2524d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            Rect boundsInScreen = boundsInParent;
2525d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int[] locationOnScreen = mTempArray;
2526d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            getLocationOnScreen(locationOnScreen);
2527d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]);
2528983119ab22a18c743e4084dff27f35e3f490dd34Svetoslav Ganov            boundsInScreen.scale(applicationScale);
2529d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            info.setBoundsInScreen(boundsInScreen);
2530791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2531791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView != View.NO_ID) {
2532791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
2533791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2534791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            if (mAccessibilityFocusedView == View.NO_ID) {
2535791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
2536791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            }
2537fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov            if (NumberPicker.this.isEnabled()) {
2538fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                if (getWrapSelectorWheel() || getValue() < getMaxValue()) {
2539fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
2540fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                }
2541fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                if (getWrapSelectorWheel() || getValue() > getMinValue()) {
2542fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
2543fb1e80a247221ee7e8f5c5deba04812021d9d07eSvetoslav Ganov                }
254448d1586f4065fc9ab97a679da1e4f7c327c943f2Svetoslav Ganov            }
2545791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2546d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return info;
2547d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2548d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2549791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private boolean hasVirtualDecrementButton() {
2550791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            return getWrapSelectorWheel() || getValue() > getMinValue();
2551791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
2552791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2553791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        private boolean hasVirtualIncrementButton() {
2554791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov            return getWrapSelectorWheel() || getValue() < getMaxValue();
2555791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov        }
2556791fd31a68c59395952005886ba799169f80a29aSvetoslav Ganov
2557d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private String getVirtualDecrementButtonText() {
2558d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int value = mValue - 1;
2559d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mWrapSelectorWheel) {
2560d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                value = getWrappedSelectorIndex(value);
2561d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2562d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (value >= mMinValue) {
2563d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return (mDisplayedValues == null) ? formatNumber(value)
2564d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        : mDisplayedValues[value - mMinValue];
2565d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2566d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return null;
2567d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2568d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov
2569d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        private String getVirtualIncrementButtonText() {
2570d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            int value = mValue + 1;
2571d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (mWrapSelectorWheel) {
2572d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                value = getWrappedSelectorIndex(value);
2573d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2574d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            if (value <= mMaxValue) {
2575d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                return (mDisplayedValues == null) ? formatNumber(value)
2576d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov                        : mDisplayedValues[value - mMinValue];
2577d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            }
2578d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov            return null;
2579d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov        }
2580d11e6151fe88314505fa7adca6278de2e772b11cSvetoslav Ganov    }
2581d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio
2582d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    static private String formatNumberWithLocale(int value) {
2583d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio        return String.format(Locale.getDefault(), "%d", value);
2584d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio    }
25857762d93621f508f4410fd37b5bbdfec0071b0420Paul Westbrook}
2586