NumberPicker.java revision bf80562d22b2bbe7944d80d0524c69d0238010cb
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
198bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powellimport com.android.internal.R;
208bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powell
21206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.Animator;
22206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.AnimatorListenerAdapter;
23206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.AnimatorSet;
24206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.ObjectAnimator;
25206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.animation.ValueAnimator;
2668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookimport android.annotation.Widget;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
28206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.ColorStateList;
29206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.content.res.TypedArray;
30206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Canvas;
31206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Color;
32206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Paint;
33206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.graphics.Paint.Align;
3450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.graphics.Rect;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputFilter;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputType;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.Spanned;
38206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.text.TextUtils;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.NumberKeyListener;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
41206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.util.SparseArray;
42206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.KeyEvent;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.LayoutInflater;
4450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.view.LayoutInflater.Filter;
45206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.MotionEvent;
46206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.VelocityTracker;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
48206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.ViewConfiguration;
49206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.animation.OvershootInterpolator;
50206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganovimport android.view.inputmethod.InputMethodManager;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook/**
534bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * A widget that enables the user to select a number form a predefined range.
544bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * The widget presents an input filed and up and down buttons for selecting the
554bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * current value. Pressing/long pressing the up and down buttons increments and
564bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * decrements the current value respectively. Touching the input filed shows a
574bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * scroll wheel, tapping on which while shown and not moving allows direct edit
584bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * of the current value. Sliding motions up or down hide the buttons and the
594bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * input filed, show the scroll wheel, and rotate the latter. Flinging is
604bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * also supported. The widget enables mapping from positions to strings such
614bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * that instead the position index the corresponding string is displayed.
624bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * <p>
634bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * For an example of using this widget, see {@link android.widget.TimePicker}.
644bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov * </p>
6568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook */
6668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook@Widget
6768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrookpublic class NumberPicker extends LinearLayout {
68fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
6968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
70206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The index of the middle selector item.
7168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
72206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MIDDLE_ITEM_INDEX = 2;
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
75206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The coefficient by which to adjust (divide) the max fling velocity.
7668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
77206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8;
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
79206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
80206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the duration for adjusting the selector wheel.
81206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
82206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
83fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
84206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
85206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The the delay for showing the input controls after a single tap on the
86206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * input text.
87206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
88206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int SHOW_INPUT_CONTROLS_DELAY_MILLIS = ViewConfiguration
89206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            .getDoubleTapTimeout();
90206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
91206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
92206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The update step for incrementing the current value.
93206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
94206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int UPDATE_STEP_INCREMENT = 1;
95206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
96206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
97206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The update step for decrementing the current value.
98206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
99206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final int UPDATE_STEP_DECREMENT = -1;
100206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
101206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
102206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The strength of fading in the top and bottom while drawing the selector.
103206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
104206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f;
105206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
106206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
107206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The numbers accepted by the input text's {@link Filter}
108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
109206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private static final char[] DIGIT_CHARACTERS = new char[] {
110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
111206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    };
112206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
114206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Use a custom NumberPicker formatting callback to use two-digit minutes
115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * strings like "01". Keeping a static formatter etc. is the most efficient
116206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * way to do this; it avoids creating temporary objects on every call to
117206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * format().
118206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
119206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER = new NumberPicker.Formatter() {
120206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final StringBuilder mBuilder = new StringBuilder();
121206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
122f0ef665299d795df7905897e1c337e37891dafefJean-Baptiste Queru        final java.util.Formatter mFmt = new java.util.Formatter(mBuilder, java.util.Locale.US);
123206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
124206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final Object[] mArgs = new Object[1];
125206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
126206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public String toString(int value) {
127206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mArgs[0] = value;
128206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mBuilder.delete(0, mBuilder.length());
129206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mFmt.format("%02d", mArgs);
130206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return mFmt.toString();
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
134206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
135206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The increment button.
136206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
137206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mIncrementButton;
138206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
139206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
140206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The decrement button.
141206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
142206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final ImageButton mDecrementButton;
143206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
144206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
145206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The text for showing the current value.
146206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
147206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final EditText mInputText;
148206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
149206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
150206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of the text.
151206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
152206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mTextSize;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
154206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
155206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The values to be displayed instead the indices.
156206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String[] mDisplayedValues;
15868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
15968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
16068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Lower value of the range of numbers allowed for the NumberPicker
16168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
16268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    private int mStart;
16368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
16468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
16568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Upper value of the range of numbers allowed for the NumberPicker
16668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
16768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    private int mEnd;
16868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
16968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
17068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Current value of this NumberPicker
17168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
17268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    private int mCurrent;
17368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
17468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
175206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Listener to be notified upon current value change.
17668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1774bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    private OnChangeListener mOnChangeListener;
17850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
17950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
18050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Listener to be notified upon scroll state change.
18150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
18250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private OnScrollListener mOnScrollListener;
183206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
184206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
185206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Formatter for for displaying the current value.
186206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Formatter mFormatter;
188fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
189206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
190206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The speed for updating the value form long press.
191206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1924bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    private long mLongPressUpdateInterval = 300;
193fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
19468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
195206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Cache for the string representation of selector indices.
196206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
197206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final SparseArray<String> mSelectorIndexToStringCache = new SparseArray<String>();
198206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
199206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
200206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The selector indices whose value are show by the selector.
201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int[] mSelectorIndices = new int[] {
203206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE,
204206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            Integer.MIN_VALUE
205206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    };
206206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
208206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Paint} for drawing the selector.
209206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
210206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Paint mSelectorPaint;
211206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
212206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
213206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The height of a selector element (text + gap).
214206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
215206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mSelectorElementHeight;
216206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
217206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
218206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The initial offset of the scroll selector.
219206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
220206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mInitialScrollOffset = Integer.MIN_VALUE;
221206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
222206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
223206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The current offset of the scroll selector.
224206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
225206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mCurrentScrollOffset;
226206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
227206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
228206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for flinging the selector.
229206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
230206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mFlingScroller;
231206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
232206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
233206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The {@link Scroller} responsible for adjusting the selector.
234206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
235206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Scroller mAdjustScroller;
236206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
237206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
238206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The previous Y coordinate while scrolling the selector.
239206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
240206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mPreviousScrollerY;
241206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
242206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
243206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for setting the input text selection.
244206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
245206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private SetSelectionCommand mSetSelectionCommand;
246206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
247206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
248206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for adjusting the scroller.
249206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
250206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private AdjustScrollerCommand mAdjustScrollerCommand;
251206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
252206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
253206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Handle to the reusable command for updating the current value from long
254206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * press.
255206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
256206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private UpdateValueFromLongPressCommand mUpdateFromLongPressCommand;
257206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
258206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
259206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * {@link Animator} for showing the up/down arrows.
260206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
261206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final AnimatorSet mShowInputControlsAnimator;
262206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
263206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
264206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last down event.
265206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
266206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastDownEventY;
267206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
268206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
269206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The Y position of the last motion event.
270206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
271206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private float mLastMotionEventY;
272206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
273206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
274206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag if to begin edit on next up event.
275206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
276206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mBeginEditOnUpEvent;
277206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
278206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
279206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag if to adjust the selector wheel on next up event.
280206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
281206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mAdjustScrollerOnUpEvent;
282206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
283206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
284206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag if to draw the selector wheel.
285206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
286206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mDrawSelectorWheel;
287206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
288206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
289206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Determines speed during touch scrolling.
290206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private VelocityTracker mVelocityTracker;
292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
293206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
294206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledTouchSlop()
295206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
296206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mTouchSlop;
297206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
298206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMinimumFlingVelocity()
300206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMinimumFlingVelocity;
302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
303206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
304206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @see ViewConfiguration#getScaledMaximumFlingVelocity()
305206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int mMaximumFlingVelocity;
307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
308206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flag whether the selector should wrap around.
310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
311206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean mWrapSelector;
312206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
313206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
314206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The back ground color used to optimize scroller fading.
31568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final int mSolidColor;
317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
318206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Reusable {@link Rect} instance.
320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private final Rect mTempRect = new Rect();
322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
32450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The current scroll state of the number picker.
32550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
32650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
32750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
32850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
329206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * The callback interface used to indicate the number value has changed.
330206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
3314bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public interface OnChangeListener {
332206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /**
3334bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * Called upon a change of the current value.
3344bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
335206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param picker The NumberPicker associated with this listener.
336206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param oldVal The previous value.
337206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * @param newVal The new value.
338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
3394bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        void onChange(NumberPicker picker, int oldVal, int newVal);
340206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
341206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
342206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
34350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Interface for listening to the picker scroll state.
34450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
34550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public interface OnScrollListener {
34650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
34750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
34850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The view is not scrolling.
34950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
35050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_IDLE = 0;
35150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
35250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
35350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user is scrolling using touch, and their finger is still on the screen.
35450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
35550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_TOUCH_SCROLL = 1;
35650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
35750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
35850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * The user had previously been scrolling using touch and performed a fling.
35950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
36050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public static int SCROLL_STATE_FLING = 2;
36150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
36250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
36350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Callback method to be invoked while the number picker is being scrolled.
36450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
36550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param view The view whose scroll state is being reported
36650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param scrollState The current scroll state. One of {@link #SCROLL_STATE_IDLE},
36750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}.
36850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
3694bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        public void onScrollStateChange(NumberPicker view, int scrollState);
37050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
37150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
37250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
3734bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Interface used to format the number into a string for presentation.
374206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
375206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public interface Formatter {
3764bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
3774bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        /**
3784bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * Formats a string representation of the current index.
3794bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         *
3804bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @param value The currently selected value.
3814bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         * @return A formatted string representation.
3824bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov         */
3834bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        public String toString(int value);
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
385fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
38668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
3874bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
3884bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
3894bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param context The application environment.
3904bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
3914bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public NumberPicker(Context context) {
3924bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        this(context, null);
3934bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
3944bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
3954bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
3964bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Create a new number picker.
397206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
398206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context The application environment.
399206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs A collection of attributes.
40068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public NumberPicker(Context context, AttributeSet attrs) {
402206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        this(context, attrs, R.attr.numberPickerStyle);
403206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
404206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
405206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
406206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Create a new number picker
407206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
408206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param context the application environment.
409206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param attrs a collection of attributes.
410206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param defStyle The default style to apply to this view.
411206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
412206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
413206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super(context, attrs, defStyle);
414206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
415206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // process style attributes
416206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        TypedArray attributesArray = context.obtainStyledAttributes(attrs,
417206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                R.styleable.NumberPicker, defStyle, 0);
418206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int orientation = attributesArray.getInt(R.styleable.NumberPicker_orientation, VERTICAL);
419206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setOrientation(orientation);
420206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
421206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        attributesArray.recycle();
422206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
423206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // By default Linearlayout that we extend is not drawn. This is
424206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // its draw() method is not called but dispatchDraw() is called
425206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // directly (see ViewGroup.drawChild()). However, this class uses
426206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // the fading edge effect implemented by View and we need our
427206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // draw() method to be called. Therefore, we declare we will draw.
428206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setWillNotDraw(false);
429206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setDrawSelectorWheel(false);
430206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
431206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
432206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                Context.LAYOUT_INFLATER_SERVICE);
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        inflater.inflate(R.layout.number_picker, this, true);
43468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
435206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnClickListener onClickListener = new OnClickListener() {
43668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            public void onClick(View v) {
437206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
438206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
43968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                    changeCurrent(mCurrent + 1);
440206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
44168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                    changeCurrent(mCurrent - 1);
44268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
44368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
44468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        };
44568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
446206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        OnLongClickListener onLongClickListener = new OnLongClickListener() {
447206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public boolean onLongClick(View v) {
448206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mInputText.clearFocus();
449206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (v.getId() == R.id.increment) {
450206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    postUpdateValueFromLongPress(UPDATE_STEP_INCREMENT);
451206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
452206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    postUpdateValueFromLongPress(UPDATE_STEP_DECREMENT);
453206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
454206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return true;
455206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
456206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        };
45768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
458206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // increment button
459206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton = (ImageButton) findViewById(R.id.increment);
460206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setOnClickListener(onClickListener);
461206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setOnLongClickListener(onLongClickListener);
462206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
463206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // decrement button
464206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton = (ImageButton) findViewById(R.id.decrement);
465206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setOnClickListener(onClickListener);
466206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setOnLongClickListener(onLongClickListener);
467206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
468206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // input text
469206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText = (EditText) findViewById(R.id.timepicker_input);
470206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setOnFocusChangeListener(new OnFocusChangeListener() {
471206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onFocusChange(View v, boolean hasFocus) {
47268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                if (!hasFocus) {
473206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    validateInputTextView(v);
47468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
47568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
476206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
477206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setFilters(new InputFilter[] {
478206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            new InputTextFilter()
479206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
48068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
481206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
482206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
483206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // initialize constants
484206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = ViewConfiguration.getTapTimeout();
485206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ViewConfiguration configuration = ViewConfiguration.get(context);
486206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTouchSlop = configuration.getScaledTouchSlop();
487206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
488206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity()
489206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;
490206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mTextSize = (int) mInputText.getTextSize();
491206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
492206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the selector wheel paint
493206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Paint paint = new Paint();
494206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setAntiAlias(true);
495206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextAlign(Align.CENTER);
496206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTextSize(mTextSize);
497206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setTypeface(mInputText.getTypeface());
498206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ColorStateList colors = mInputText.getTextColors();
499206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int color = colors.getColorForState(ENABLED_STATE_SET, Color.WHITE);
500206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        paint.setColor(color);
501206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSelectorPaint = paint;
502206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
503206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the animator for showing the input controls
504206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final ValueAnimator fadeScroller = ObjectAnimator.ofInt(this, "selectorPaintAlpha", 255, 0);
505206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final ObjectAnimator showIncrementButton = ObjectAnimator.ofFloat(mIncrementButton,
506206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                "alpha", 0, 1);
507206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        final ObjectAnimator showDecrementButton = ObjectAnimator.ofFloat(mDecrementButton,
508206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                "alpha", 0, 1);
509206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator = new AnimatorSet();
510206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.playTogether(fadeScroller, showIncrementButton,
511206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                showDecrementButton);
512206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.setDuration(getResources().getInteger(
513206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                R.integer.config_longAnimTime));
514206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.addListener(new AnimatorListenerAdapter() {
515206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            private boolean mCanceled = false;
516206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
517206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            @Override
518206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onAnimationEnd(Animator animation) {
519206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (!mCanceled) {
520206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    // if canceled => we still want the wheel drawn
521206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    setDrawSelectorWheel(false);
52268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook                }
523206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCanceled = false;
524206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mSelectorPaint.setAlpha(255);
525206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                invalidate();
52668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook            }
52768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
528206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            @Override
529206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            public void onAnimationCancel(Animator animation) {
530206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mShowInputControlsAnimator.isRunning()) {
531206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mCanceled = true;
532206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
533206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
534206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        });
53568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
536206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // create the fling and adjust scrollers
537bf80562d22b2bbe7944d80d0524c69d0238010cbSvetoslav Ganov        mFlingScroller = new Scroller(getContext(), null, true);
538206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mAdjustScroller = new Scroller(getContext(), new OvershootInterpolator());
539fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
540206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateInputTextView();
541206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
542206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
543206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
544206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
545206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void onWindowFocusChanged(boolean hasWindowFocus) {
546206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        super.onWindowFocusChanged(hasWindowFocus);
547206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (!hasWindowFocus) {
548206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
549206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
550206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
551206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
552206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
553206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onInterceptTouchEvent(MotionEvent event) {
554206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (event.getActionMasked()) {
555206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_DOWN:
556206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mLastMotionEventY = mLastDownEventY = event.getY();
557206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                removeAllCallbacks();
558206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mBeginEditOnUpEvent = false;
559206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mAdjustScrollerOnUpEvent = true;
560206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mDrawSelectorWheel) {
56150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    boolean scrollersFinished = mFlingScroller.isFinished()
562206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                            && mAdjustScroller.isFinished();
56350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    if (!scrollersFinished) {
56450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        mFlingScroller.forceFinished(true);
56550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        mAdjustScroller.forceFinished(true);
56650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        tryNotifyScrollListener(OnScrollListener.SCROLL_STATE_IDLE);
56750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    }
56850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    mBeginEditOnUpEvent = scrollersFinished;
569206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mAdjustScrollerOnUpEvent = true;
570206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    hideInputControls();
571206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
572206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
573206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (isEventInInputText(event)) {
574206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mAdjustScrollerOnUpEvent = false;
575206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    setDrawSelectorWheel(true);
576206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    hideInputControls();
577206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
578206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
579206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
580206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_MOVE:
581206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                float currentMoveY = event.getY();
582206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
583206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (deltaDownY > mTouchSlop) {
584206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mBeginEditOnUpEvent = false;
58550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    tryNotifyScrollListener(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
586206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    setDrawSelectorWheel(true);
587206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    hideInputControls();
588206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
589206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
590206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
591206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
592206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return false;
593206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
594206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
595206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
596206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean onTouchEvent(MotionEvent ev) {
597206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mVelocityTracker == null) {
598206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mVelocityTracker = VelocityTracker.obtain();
599206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
600206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mVelocityTracker.addMovement(ev);
601206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int action = ev.getActionMasked();
602206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        switch (action) {
603206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_MOVE:
604206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                float currentMoveY = ev.getY();
60550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                if (mBeginEditOnUpEvent
60650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
607206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
608206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (deltaDownY > mTouchSlop) {
609206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        mBeginEditOnUpEvent = false;
61050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        tryNotifyScrollListener(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
611206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
612206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
613206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int deltaMoveY = (int) (currentMoveY - mLastMotionEventY);
614206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollBy(0, deltaMoveY);
615206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                invalidate();
616206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mLastMotionEventY = currentMoveY;
617206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
618206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            case MotionEvent.ACTION_UP:
619206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mBeginEditOnUpEvent) {
620206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    setDrawSelectorWheel(false);
621206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    showInputControls();
622206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    mInputText.requestFocus();
623206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
624206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                            Context.INPUT_METHOD_SERVICE);
625206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    imm.showSoftInput(mInputText, 0);
62650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    mInputText.setSelection(0, mInputText.getText().length());
627206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return true;
628206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
629206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                VelocityTracker velocityTracker = mVelocityTracker;
630206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
631206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int initialVelocity = (int) velocityTracker.getYVelocity();
632206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
633206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    fling(initialVelocity);
63450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    tryNotifyScrollListener(OnScrollListener.SCROLL_STATE_FLING);
635206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
636206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (mAdjustScrollerOnUpEvent) {
637206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        if (mFlingScroller.isFinished() && mAdjustScroller.isFinished()) {
638206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                            postAdjustScrollerCommand(0);
639206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        }
640206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    } else {
641206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postAdjustScrollerCommand(SHOW_INPUT_CONTROLS_DELAY_MILLIS);
642206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
643206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
644206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker.recycle();
645206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mVelocityTracker = null;
646206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                break;
647206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
648206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return true;
649206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
650206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
651206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
652206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTouchEvent(MotionEvent event) {
653206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int action = event.getActionMasked();
654206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if ((action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP)
655206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                && !isEventInInputText(event)) {
656206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
657206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
658206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTouchEvent(event);
659206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
660206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
661206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
662206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchKeyEvent(KeyEvent event) {
663206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int keyCode = event.getKeyCode();
664206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
665206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
666206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
667206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchKeyEvent(event);
668206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
669206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
670206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
671206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public boolean dispatchTrackballEvent(MotionEvent event) {
672206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int action = event.getActionMasked();
673206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
674206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeAllCallbacks();
675206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
676206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return super.dispatchTrackballEvent(event);
677206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
679206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
680206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void computeScroll() {
681206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (!mDrawSelectorWheel) {
682206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
683206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
684206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Scroller scroller = mFlingScroller;
685206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
686206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scroller = mAdjustScroller;
687206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (scroller.isFinished()) {
688206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return;
689206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
690206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
691206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scroller.computeScrollOffset();
692206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int currentScrollerY = scroller.getCurrY();
693206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mPreviousScrollerY == 0) {
694206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = scroller.getStartY();
695206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
696206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        scrollBy(0, currentScrollerY - mPreviousScrollerY);
697206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = currentScrollerY;
698206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller.isFinished()) {
699206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            onScrollerFinished(scroller);
700206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
701206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
704fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
70568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
70668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Set the enabled state of this view. The interpretation of the enabled
70768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * state varies by subclass.
70868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
70968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * @param enabled True if this view is enabled, false otherwise.
71068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setEnabled(boolean enabled) {
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.setEnabled(enabled);
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIncrementButton.setEnabled(enabled);
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDecrementButton.setEnabled(enabled);
716206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setEnabled(enabled);
717206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
718206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
719206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
720206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Scrolls the selector with the given <code>vertical offset</code>.
721206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
722206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
723206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void scrollBy(int x, int y) {
724206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int[] selectorIndices = getSelectorIndices();
725206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mInitialScrollOffset == Integer.MIN_VALUE) {
726206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int totalTextHeight = selectorIndices.length * mTextSize;
727206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
728206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int textGapCount = selectorIndices.length - 1;
729206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int selectorTextGapHeight = totalTextGapHeight / textGapCount;
730206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // compensate for integer division loss of the components used to
731206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // calculate the text gap
732206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int integerDivisionLoss = (mTextSize + mBottom - mTop) % textGapCount;
733206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInitialScrollOffset = mCurrentScrollOffset = mTextSize - integerDivisionLoss / 2;
734206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSelectorElementHeight = mTextSize + selectorTextGapHeight;
735206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
736206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
737206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (!mWrapSelector && y > 0 && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mStart) {
738206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
739206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
740206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
741206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (!mWrapSelector && y < 0 && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mEnd) {
742206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset = mInitialScrollOffset;
743206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
744206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
745206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mCurrentScrollOffset += y;
746206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorElementHeight) {
747206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset -= mSelectorElementHeight;
748206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            decrementSelectorIndices(selectorIndices);
749206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
750206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mStart) {
751206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
752206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
753206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
754206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorElementHeight) {
755206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mCurrentScrollOffset += mSelectorElementHeight;
756206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            incrementScrollSelectorIndices(selectorIndices);
757206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX]);
758206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mEnd) {
759206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mCurrentScrollOffset = mInitialScrollOffset;
760206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
761206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
763fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
7644bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    @Override
7654bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public int getSolidColor() {
7664bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return mSolidColor;
7674bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
7684bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
76968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
7704bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the listener to be notified on change of the current value.
771206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
7724bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param onChangeListener The listener.
77368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
7744bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setOnChangeListener(OnChangeListener onChangeListener) {
7754bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        mOnChangeListener = onChangeListener;
77650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
77750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
77850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
7794bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set listener to be notified for scroll state changes.
78050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
78150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * @param onScrollListener the callback, should not be null.
78250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
78350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    public void setOnScrollListener(OnScrollListener onScrollListener) {
78450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mOnScrollListener = onScrollListener;
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
786fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
78768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
7884bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Set the formatter to be used for formatting the current value.
7894bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
7904bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Note: If you have provided alternative values for the selected positons
7914bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *       this formatter is never invoked.
7924bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
793206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
794206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param formatter the formatter object. If formatter is null,
7954bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *            String.valueOf() will be used.
7964bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
7974bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @see #setRange(int, int, String[])
79868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setFormatter(Formatter formatter) {
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFormatter = formatter;
8012b9484f453ef307880a93240bb050b39af72bee2Svetoslav Ganov        resetSelectorIndices();
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
803fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
805206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Set the range of numbers allowed for the number picker. The current value
806206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be automatically set to the start.
807fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor     *
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param start the start of the range (inclusive)
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param end the end of the range (inclusive)
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setRange(int start, int end) {
812206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setRange(start, end, null);
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
814fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
816206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Set the range of numbers allowed for the number picker. The current value
817206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be automatically set to the start. Also provide a mapping for values
8184bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * used to display to the user instead of the numbers in the range.
819fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor     *
8204bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param start The start of the range (inclusive).
8214bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param end The end of the range (inclusive).
8224bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param displayedValues The values displayed to the user.
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setRange(int start, int end, String[] displayedValues) {
825206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        boolean wrapSelector = (end - start) >= mSelectorIndices.length;
826206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setRange(start, end, displayedValues, wrapSelector);
827206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
828206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
829206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
830206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Set the range of numbers allowed for the number picker. The current value
831206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be automatically set to the start. Also provide a mapping for values
832206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * used to display to the user.
8334bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * <p>
8344bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Note: The <code>wrapSelectorWheel</code> argument is ignored if the range
8354bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * (difference between <code>start</code> and <code>end</code>) us less than
8364bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * five since this is the number of values shown by the selector wheel.
8374bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * </p>
838206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
839206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param start the start of the range (inclusive)
840206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param end the end of the range (inclusive)
841206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param displayedValues the values displayed to the user.
8424bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param wrapSelectorWheel Whether to wrap the selector wheel.
8434bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
8444bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @see #setWrapSelectorWheel(boolean)
845206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
8464bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setRange(int start, int end, String[] displayedValues, boolean wrapSelectorWheel) {
847a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        if (start == mStart && end == mEnd) {
848a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov            return;
849a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
850a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov
851206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (start < 0 || end < 0) {
852206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            throw new IllegalArgumentException("start and end must be > 0");
853206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
854a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDisplayedValues = displayedValues;
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mStart = start;
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mEnd = end;
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCurrent = start;
859206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
8604bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        setWrapSelectorWheel(wrapSelectorWheel);
861206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateInputTextView();
8628bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powell
8638bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powell        if (displayedValues != null) {
8648bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powell            // Allow text entry rather than strictly numeric entry.
865206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
866206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
867206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
868206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
8698bcbebd4178b2e9aca9ee3bd9e1e12c42e74c8dbAdam Powell        }
870206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
871a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        resetSelectorIndices();
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
873fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
87468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
87568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * Set the current value for the number picker.
87668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
87768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * @param current the current value the start of the range (inclusive)
8784bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
879206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @throws IllegalArgumentException when current is not within the range of
8804bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *             of the number picker.
88168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setCurrent(int current) {
883a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        if (mCurrent == current) {
884a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov            return;
885a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
88668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        if (current < mStart || current > mEnd) {
887206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            throw new IllegalArgumentException("current should be >= start and <= end");
88868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook        }
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCurrent = current;
890206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateInputTextView();
891206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
892206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
893206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
894206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
8954bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets whether the selector wheel shown during flinging/scrolling should wrap
8964bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * around the beginning and end values. By default if the range is more than
8974bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * five (the number of items shown on the selector wheel) the selector wheel
8984bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * wrapping is enabled.
899206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
900206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @param wrapSelector Whether to wrap.
901206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
9024bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setWrapSelectorWheel(boolean wrapSelector) {
903206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (wrapSelector && (mEnd - mStart) < mSelectorIndices.length) {
904206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            throw new IllegalStateException("Range less than selector items count.");
905206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
906206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (wrapSelector != mWrapSelector) {
907206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // force the selector indices array to be reinitialized
908206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] = Integer.MAX_VALUE;
909206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mWrapSelector = wrapSelector;
910206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9144bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Sets the speed at which the numbers be incremented and decremented when
9154bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * the up and down buttons are long pressed respectively.
91668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
9174bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @param intervalMillis The speed (in milliseconds) at which the numbers
9184bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *            will be incremented and decremented (default 300ms).
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9204bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public void setOnLongPressUpdateInterval(long intervalMillis) {
9214bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        mLongPressUpdateInterval = intervalMillis;
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
923fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
924206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
9254bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Returns the current value of the NumberPicker.
926206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *
927206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return the current value.
928206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
929206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public int getCurrent() {
930206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return mCurrent;
931206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
932206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
9334bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
9344bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Returns the range lower value of the NumberPicker.
9354bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
9364bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @return The lower number of the range.
9374bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
9384bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public int getRangeStart() {
9394bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return mStart;
9404bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    }
9414bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov
9424bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    /**
9434bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * Returns the range end value of the NumberPicker.
9444bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     *
9454bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     * @return The upper number of the range.
9464bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov     */
9474bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov    public int getRangeEnd() {
9484bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        return mEnd;
949206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
950206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
951206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
952206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getTopFadingEdgeStrength() {
953206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
954206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
955206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
956206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
957206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected float getBottomFadingEdgeStrength() {
958206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
959206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
960206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
961206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
962206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDetachedFromWindow() {
963206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
964206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
965206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
966206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
967206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void dispatchDraw(Canvas canvas) {
968206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // There is a good reason for doing this. See comments in draw().
969206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
970206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
971206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
972206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    public void draw(Canvas canvas) {
973206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Dispatch draw to our children only if we are not currently running
974206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // the animation for simultaneously fading out the scroll wheel and
975206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // showing in the buttons. This class takes advantage of the View
976206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // implementation of fading edges effect to draw the selector wheel.
977206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // However, in View.draw(), the fading is applied after all the children
978206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // have been drawn and we do not want this fading to be applied to the
979206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // buttons which are currently showing in. Therefore, we draw our
980206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // children
981206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // after we have completed drawing ourselves.
982206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
983c75ec3324e9e3e6500148cc2978caab90ea962e4Chet Haase        super.draw(canvas);
984206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
985206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // Draw our children if we are not showing the selector wheel of fading
986206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // it out
987206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mShowInputControlsAnimator.isRunning() || !mDrawSelectorWheel) {
988206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            long drawTime = getDrawingTime();
989206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            for (int i = 0, count = getChildCount(); i < count; i++) {
990206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                View child = getChildAt(i);
991206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (!child.isShown()) {
992206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    continue;
993206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
994206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                drawChild(canvas, getChildAt(i), drawTime);
995206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
996206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
997206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
998206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
999206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @Override
1000206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    protected void onDraw(Canvas canvas) {
1001206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // we only draw the selector wheel
1002206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (!mDrawSelectorWheel) {
1003206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1004206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1005206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float x = (mRight - mLeft) / 2;
1006206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        float y = mCurrentScrollOffset;
1007206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1008206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int[] selectorIndices = getSelectorIndices();
1009206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length; i++) {
1010206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int selectorIndex = selectorIndices[i];
1011206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
1012206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            canvas.drawText(scrollSelectorValue, x, y, mSelectorPaint);
1013206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            y += mSelectorElementHeight;
1014206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1016fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
101768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1018a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     * Resets the selector indices and clear the cached
1019a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     * string representation of these indices.
1020a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     */
1021a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    private void resetSelectorIndices() {
1022a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        mSelectorIndexToStringCache.clear();
1023a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        int[] selectorIdices = getSelectorIndices();
1024a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        for (int i = 0; i < selectorIdices.length; i++) {
1025a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov            selectorIdices[i] = Integer.MIN_VALUE;
1026a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov        }
1027a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    }
1028a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov
1029a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov    /**
1030206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Sets the current value of this NumberPicker, and sets mPrevious to the
1031206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * previous value. If current is greater than mEnd less than mStart, the
1032206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * value of mCurrent is wrapped around. Subclasses can override this to
1033206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * change the wrapping behavior
103468f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     *
103568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     * @param current the new value of the NumberPicker
103668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1037206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void changeCurrent(int current) {
1038206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mCurrent == current) {
1039206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1040206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Wrap around the values if we go past the start or end
1042206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mWrapSelector) {
1043206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            current = getWrappedSelectorIndex(current);
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1045206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int previous = mCurrent;
1046206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setCurrent(current);
1047206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        notifyChange(previous, current);
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1049fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
105068f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1051206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Sets the <code>alpha</code> of the {@link Paint} for drawing the selector
1052206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * wheel.
105368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1054206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    @SuppressWarnings("unused")
1055206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    // Called by ShowInputControlsAnimator via reflection
1056206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void setSelectorPaintAlpha(int alpha) {
1057206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSelectorPaint.setAlpha(alpha);
1058206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mDrawSelectorWheel) {
1059206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
106368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1064206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return If the <code>event</code> is in the input text.
106568f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1066206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private boolean isEventInInputText(MotionEvent event) {
1067206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.getHitRect(mTempRect);
1068206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return mTempRect.contains((int) event.getX(), (int) event.getY());
1069206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1070206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1071206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1072206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Sets if to <code>drawSelectionWheel</code>.
1073206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1074206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void setDrawSelectorWheel(boolean drawSelectorWheel) {
1075206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDrawSelectorWheel = drawSelectorWheel;
1076206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // do not fade if the selector wheel not shown
1077206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        setVerticalFadingEdgeEnabled(drawSelectorWheel);
1078206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1079206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1080206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1081206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Callback invoked upon completion of a given <code>scroller</code>.
1082206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1083206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void onScrollerFinished(Scroller scroller) {
1084206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scroller == mFlingScroller) {
1085206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            postAdjustScrollerCommand(0);
108650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            tryNotifyScrollListener(OnScrollListener.SCROLL_STATE_IDLE);
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1088206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            showInputControls();
1089206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1092fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1093206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
109450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Notifies the scroll listener for the given <code>scrollState</code>
109550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * if the scroll state differs from the current scroll state.
109650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
109750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    private void tryNotifyScrollListener(int scrollState) {
109850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (mOnScrollListener != null && mScrollState != scrollState) {
109950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mScrollState = scrollState;
11004bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            mOnScrollListener.onScrollStateChange(this, scrollState);
110150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
110250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
110350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
110450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1105206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Flings the selector with the given <code>velocityY</code>.
1106206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1107206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void fling(int velocityY) {
1108206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mPreviousScrollerY = 0;
1109206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        Scroller flingScroller = mFlingScroller;
1110206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1111206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mWrapSelector) {
1112206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (velocityY > 0) {
1113206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
1114206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1115206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                flingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
1116206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1117206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1118206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (velocityY > 0) {
1119206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int maxY = mTextSize * (mCurrent - mStart);
1120206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, maxY);
1121206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1122206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int startY = mTextSize * (mEnd - mCurrent);
1123206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int maxY = startY;
1124206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                flingScroller.fling(0, startY, 0, velocityY, 0, 0, 0, maxY);
11252bf761c20253222a763bce9a63d14c48ab08a556Suchi Amalapurapu            }
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1127206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1128206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        postAdjustScrollerCommand(flingScroller.getDuration());
1129206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        invalidate();
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1132206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1133206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Hides the input controls which is the up/down arrows and the text field.
1134206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1135206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void hideInputControls() {
1136206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.cancel();
1137206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mIncrementButton.setVisibility(INVISIBLE);
1138206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mDecrementButton.setVisibility(INVISIBLE);
1139206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setVisibility(INVISIBLE);
1140206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1141c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1142206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1143206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Show the input controls by making them visible and animating the alpha
1144206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * property up/down arrows.
1145206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1146206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void showInputControls() {
1147206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        updateIncrementAndDecrementButtonsVisibilityState();
1148206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setVisibility(VISIBLE);
1149206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mShowInputControlsAnimator.start();
1150206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1151c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1152206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1153206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the visibility state of the increment and decrement buttons.
1154206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1155206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void updateIncrementAndDecrementButtonsVisibilityState() {
1156206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mWrapSelector || mCurrent < mEnd) {
1157206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mIncrementButton.setVisibility(VISIBLE);
1158206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1159206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mIncrementButton.setVisibility(INVISIBLE);
1160206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1161206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mWrapSelector || mCurrent > mStart) {
1162206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mDecrementButton.setVisibility(VISIBLE);
1163206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1164206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mDecrementButton.setVisibility(INVISIBLE);
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1169206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The selector indices array with proper values with the current as
1170206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     *         the middle one.
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1172206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int[] getSelectorIndices() {
1173206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int current = getCurrent();
1174206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] != current) {
1175206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            for (int i = 0; i < mSelectorIndices.length; i++) {
1176206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
1177206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mWrapSelector) {
1178206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    selectorIndex = getWrappedSelectorIndex(selectorIndex);
1179206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1180206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                mSelectorIndices[i] = selectorIndex;
1181206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                ensureCachedScrollSelectorValue(mSelectorIndices[i]);
1182206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1183206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1184206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return mSelectorIndices;
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1186fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
118768f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1188206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The wrapped index <code>selectorIndex</code> value.
118968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1190206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getWrappedSelectorIndex(int selectorIndex) {
1191206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (selectorIndex > mEnd) {
119299b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            return mStart + (selectorIndex - mEnd) % (mEnd - mStart);
1193206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else if (selectorIndex < mStart) {
119499b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            return mEnd - (mStart - selectorIndex) % (mEnd - mStart);
1195206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1196206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return selectorIndex;
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1198fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1199206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1200206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Increments the <code>selectorIndices</code> whose string representations
1201206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1202206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1203206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void incrementScrollSelectorIndices(int[] selectorIndices) {
1204206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = 0; i < selectorIndices.length - 1; i++) {
1205206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i + 1];
1206206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1207206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1;
1208206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mWrapSelector && nextScrollSelectorIndex > mEnd) {
1209206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            nextScrollSelectorIndex = mStart;
1210206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1211206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex;
1212206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1213206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1214fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1215206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1216206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Decrements the <code>selectorIndices</code> whose string representations
1217206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * will be displayed in the selector.
1218206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1219206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void decrementSelectorIndices(int[] selectorIndices) {
1220206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        for (int i = selectorIndices.length - 1; i > 0; i--) {
1221206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            selectorIndices[i] = selectorIndices[i - 1];
1222206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1223206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        int nextScrollSelectorIndex = selectorIndices[1] - 1;
1224206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mWrapSelector && nextScrollSelectorIndex < mStart) {
1225206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            nextScrollSelectorIndex = mEnd;
1226206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1227206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        selectorIndices[0] = nextScrollSelectorIndex;
1228206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
1229206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1230fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1231206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1232206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Ensures we have a cached string representation of the given <code>
1233206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * selectorIndex</code>
1234206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * to avoid multiple instantiations of the same string.
1235206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1236206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void ensureCachedScrollSelectorValue(int selectorIndex) {
1237206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        SparseArray<String> cache = mSelectorIndexToStringCache;
1238206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String scrollSelectorValue = cache.get(selectorIndex);
1239206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (scrollSelectorValue != null) {
1240206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return;
1241206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1242206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (selectorIndex < mStart || selectorIndex > mEnd) {
1243206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            scrollSelectorValue = "";
1244206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1245206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues != null) {
124650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                int displayedValueIndex = selectorIndex - mStart;
124750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                scrollSelectorValue = mDisplayedValues[displayedValueIndex];
1248206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1249206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                scrollSelectorValue = formatNumber(selectorIndex);
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1252206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        cache.put(selectorIndex, scrollSelectorValue);
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1254fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1255206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private String formatNumber(int value) {
1256206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        return (mFormatter != null) ? mFormatter.toString(value) : String.valueOf(value);
1257206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1259206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void validateInputTextView(View v) {
1260206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        String str = String.valueOf(((TextView) v).getText());
1261206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (TextUtils.isEmpty(str)) {
1262206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Restore to the old value as we don't allow empty values
1263206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            updateInputTextView();
1264206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1265206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            // Check the new value and ensure it's in range
1266206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int current = getSelectedPos(str.toString());
1267206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(current);
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1269206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1270fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1271206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1272206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Updates the view of this NumberPicker. If displayValues were specified in
1273206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * {@link #setRange}, the string corresponding to the index specified by the
1274206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * current value will be returned. Otherwise, the formatter specified in
1275206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * {@link #setFormatter} will be used to format the number.
1276206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1277206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void updateInputTextView() {
1278206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        /*
1279206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * If we don't have displayed values then use the current number else
1280206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * find the correct value in the displayed values for the current
1281206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         * number.
1282206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov         */
1283206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mDisplayedValues == null) {
1284206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setText(formatNumber(mCurrent));
1285206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1286206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setText(mDisplayedValues[mCurrent - mStart]);
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1288206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.setSelection(mInputText.getText().length());
1289206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1290fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1291206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1292206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Notifies the listener, if registered, of a change of the value of this
1293206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * NumberPicker.
1294206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1295206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void notifyChange(int previous, int current) {
12964bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov        if (mOnChangeListener != null) {
12974bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            mOnChangeListener.onChange(this, previous, mCurrent);
1298206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1299206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1301206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1302206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts a command for updating the current value every <code>updateMillis
1303206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * </code>.
1304206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1305206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postUpdateValueFromLongPress(int updateMillis) {
1306206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mInputText.clearFocus();
1307206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        removeAllCallbacks();
1308206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mUpdateFromLongPressCommand == null) {
1309206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mUpdateFromLongPressCommand = new UpdateValueFromLongPressCommand();
1310206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1311206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mUpdateFromLongPressCommand.setUpdateStep(updateMillis);
1312206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(mUpdateFromLongPressCommand);
1313206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1315206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1316206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Removes all pending callback from the message queue.
1317206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1318206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void removeAllCallbacks() {
1319206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mUpdateFromLongPressCommand != null) {
1320206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mUpdateFromLongPressCommand);
1321206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1322206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mAdjustScrollerCommand != null) {
1323206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mAdjustScrollerCommand);
1324206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1325206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand != null) {
1326206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1330206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1331206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * @return The selected index given its displayed <code>value</code>.
1332206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1333206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private int getSelectedPos(String value) {
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisplayedValues == null) {
1335aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            try {
1336206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
1337aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            } catch (NumberFormatException e) {
1338206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
1339aa516beb79693ceffbd81a7c9d14c940e412cccbRomain Guy            }
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < mDisplayedValues.length; i++) {
1342206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Don't force the user to type in jan when ja will do
1343206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                value = value.toLowerCase();
1344206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (mDisplayedValues[i].toLowerCase().startsWith(value)) {
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return mStart + i;
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1348fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1349206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            /*
1350206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov             * The user might have typed in a number into the month field i.e.
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             * 10 instead of OCT so support that too.
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             */
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1354206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return Integer.parseInt(value);
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
1356fdf6db6748b32f620a7f68b13a67c38b4e59e5edTom Taylor
1357206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                // Ignore as if it's not a number we don't care
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mStart;
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1364206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link SetSelectionCommand} from the given <code>selectionStart
1365206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * </code> to
1366206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * <code>selectionEnd</code>.
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1368206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postSetSelectionCommand(int selectionStart, int selectionEnd) {
1369206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mSetSelectionCommand == null) {
1370206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mSetSelectionCommand = new SetSelectionCommand();
1371206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1372206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mSetSelectionCommand);
1373206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1374206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionStart = selectionStart;
1375206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        mSetSelectionCommand.mSelectionEnd = selectionEnd;
1376206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        post(mSetSelectionCommand);
13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
137868f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
137968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1380206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Posts an {@link AdjustScrollerCommand} within the given <code>
1381206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * delayMillis</code>
1382206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * .
138368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1384206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    private void postAdjustScrollerCommand(int delayMillis) {
1385206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        if (mAdjustScrollerCommand == null) {
1386206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mAdjustScrollerCommand = new AdjustScrollerCommand();
1387206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        } else {
1388206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            removeCallbacks(mAdjustScrollerCommand);
1389206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1390206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        postDelayed(mAdjustScrollerCommand, delayMillis);
139168f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
139268f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook
139368f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    /**
1394206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Filter for accepting only valid indices or prefixes of the string
1395206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * representation of valid indices.
139668f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook     */
1397206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class InputTextFilter extends NumberKeyListener {
1398206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1399206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // XXX This doesn't allow for range limits when controlled by a
1400206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        // soft input method!
1401206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public int getInputType() {
1402206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return InputType.TYPE_CLASS_TEXT;
1403206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1404206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1405206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1406206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        protected char[] getAcceptedChars() {
1407206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            return DIGIT_CHARACTERS;
1408206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1409206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1410206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        @Override
1411206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
1412206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int dstart, int dend) {
1413206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            if (mDisplayedValues == null) {
1414206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
1415206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (filtered == null) {
1416206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    filtered = source.subSequence(start, end);
1417206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1418206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1419206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1420206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1421206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1422206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if ("".equals(result)) {
1423206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return result;
1424206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1425206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                int val = getSelectedPos(result);
1426206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1427206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                /*
1428206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * Ensure the user can't type in a value greater than the max
1429206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * allowed. We have to allow less than min as the user might
1430206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 * want to delete some numbers and then type a new number.
1431206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                 */
1432206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (val > mEnd) {
1433206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1434206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                } else {
1435206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return filtered;
1436206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1437206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            } else {
1438206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                CharSequence filtered = String.valueOf(source.subSequence(start, end));
1439206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                if (TextUtils.isEmpty(filtered)) {
1440206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    return "";
1441206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1442206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
1443206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        + dest.subSequence(dend, dest.length());
1444206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                String str = String.valueOf(result).toLowerCase();
1445206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                for (String val : mDisplayedValues) {
1446206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    String valLowerCase = val.toLowerCase();
1447206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    if (valLowerCase.startsWith(str)) {
1448206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        postSetSelectionCommand(result.length(), val.length());
1449206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                        return val.subSequence(dstart, val.length());
1450206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                    }
1451206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                }
1452206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov                return "";
1453206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            }
1454206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1455206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1456206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1457206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1458206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for setting the input text selection.
1459206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1460206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class SetSelectionCommand implements Runnable {
1461206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionStart;
1462206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1463206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mSelectionEnd;
1464206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1465206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1466206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mInputText.setSelection(mSelectionStart, mSelectionEnd);
1467206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1468206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1469206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1470206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1471206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for adjusting the scroller to show in its center the closest of
1472206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * the displayed items.
1473206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1474206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class AdjustScrollerCommand implements Runnable {
1475206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1476206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mPreviousScrollerY = 0;
1477206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
1478206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            float delayCoef = (float) Math.abs(deltaY) / (float) mTextSize;
1479206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            int duration = (int) (delayCoef * SELECTOR_ADJUSTMENT_DURATION_MILLIS);
1480206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mAdjustScroller.startScroll(0, 0, 0, deltaY, duration);
1481206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            invalidate();
1482206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1483206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    }
1484206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1485206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    /**
1486206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     * Command for updating the current value from a long press.
1487206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov     */
1488206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov    class UpdateValueFromLongPressCommand implements Runnable {
1489206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private int mUpdateStep = 0;
1490206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1491206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        private void setUpdateStep(int updateStep) {
1492206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            mUpdateStep = updateStep;
1493206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
1494206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov
1495206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        public void run() {
1496206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov            changeCurrent(mCurrent + mUpdateStep);
14974bfd794475e6fb34c9dfa83d4302e9db365a3709Svetoslav Ganov            postDelayed(this, mLongPressUpdateInterval);
1498206316a61f904ea0a6b106137dd7715a2c246d4cSvetoslav Ganov        }
149968f2f547f56f239b60d13b2b62a08a65874f6662Paul Westbrook    }
15007762d93621f508f4410fd37b5bbdfec0071b0420Paul Westbrook}
1501