EMMA Coverage Report (generated Thu Apr 06 20:30:03 CEST 2017)
[all classes][net.mandaria.tippytipper.widgets]

COVERAGE SUMMARY FOR SOURCE FILE [NumberPicker.java]

nameclass, %method, %block, %line, %
NumberPicker.java100% (6/6)74%  (35/47)66%  (558/843)68%  (119.5/175)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class NumberPicker$3100% (1/1)50%  (1/2)12%  (6/50)12%  (1/8)
run (): void 0%   (0/1)0%   (0/44)0%   (0/7)
NumberPicker$3 (NumberPicker): void 100% (1/1)100% (6/6)100% (1/1)
     
class NumberPicker$NumberPickerInputFilter100% (1/1)100% (3/3)30%  (25/83)27%  (3/11)
filter (CharSequence, int, int, Spanned, int, int): CharSequence 100% (1/1)21%  (15/73)20%  (2/10)
NumberPicker$NumberPickerInputFilter (NumberPicker): void 100% (1/1)100% (6/6)100% (1/1)
NumberPicker$NumberPickerInputFilter (NumberPicker, NumberPicker$1): void 100% (1/1)100% (4/4)100% (1/1)
     
class NumberPicker$1100% (1/1)50%  (1/2)43%  (19/44)50%  (4/8)
toString (int): String 0%   (0/1)0%   (0/25)0%   (0/4)
NumberPicker$1 (): void 100% (1/1)100% (19/19)100% (4/4)
     
class NumberPicker100% (1/1)73%  (24/33)72%  (397/553)72%  (92.5/128)
access$000 (NumberPicker): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
access$100 (NumberPicker): long 0%   (0/1)0%   (0/3)0%   (0/1)
access$200 (NumberPicker): Handler 0%   (0/1)0%   (0/3)0%   (0/1)
access$300 (NumberPicker): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
onLongClick (View): boolean 0%   (0/1)0%   (0/32)0%   (0/8)
setEnabled (boolean): void 0%   (0/1)0%   (0/16)0%   (0/5)
setOnChangeListener (NumberPicker$OnChangedListener): void 0%   (0/1)0%   (0/4)0%   (0/2)
setRange (int, int, String []): void 0%   (0/1)0%   (0/15)0%   (0/6)
setSpeed (long): void 0%   (0/1)0%   (0/4)0%   (0/2)
getSelectedPos (String): int 100% (1/1)16%  (6/38)22%  (2/9)
notifyChange (): void 100% (1/1)33%  (4/12)67%  (2/3)
validateInput (View): void 100% (1/1)63%  (32/51)78%  (7/9)
updateView (): void 100% (1/1)63%  (19/30)80%  (4/5)
NumberPicker (Context, AttributeSet, int): void 100% (1/1)97%  (114/117)96%  (25/26)
<static initializer> 100% (1/1)100% (52/52)100% (3/3)
NumberPicker (Context): void 100% (1/1)100% (5/5)100% (2/2)
NumberPicker (Context, AttributeSet): void 100% (1/1)100% (6/6)100% (2/2)
access$600 (NumberPicker): String [] 100% (1/1)100% (3/3)100% (1/1)
access$700 (NumberPicker): InputFilter 100% (1/1)100% (3/3)100% (1/1)
access$800 (): char [] 100% (1/1)100% (2/2)100% (1/1)
access$900 (NumberPicker, String): int 100% (1/1)100% (4/4)100% (1/1)
cancelDecrement (): void 100% (1/1)100% (4/4)100% (2/2)
cancelIncrement (): void 100% (1/1)100% (4/4)100% (2/2)
changeCurrent (int): void 100% (1/1)100% (27/27)100% (9/9)
formatNumber (int): String 100% (1/1)100% (11/11)100% (1/1)
getCurrent (): int 100% (1/1)100% (3/3)100% (1/1)
getCurrentFormatted (): String 100% (1/1)100% (5/5)100% (1/1)
onClick (View): void 100% (1/1)100% (36/36)100% (8/8)
onFocusChange (View, boolean): void 100% (1/1)100% (6/6)100% (3/3)
setCurrent (int): void 100% (1/1)100% (6/6)100% (3/3)
setFormatter (NumberPicker$Formatter): void 100% (1/1)100% (4/4)100% (2/2)
setRange (int, int): void 100% (1/1)100% (12/12)100% (5/5)
validateCurrentView (CharSequence): void 100% (1/1)100% (29/29)100% (8/8)
     
class NumberPicker$NumberRangeKeyListener100% (1/1)80%  (4/5)97%  (67/69)92%  (12/13)
getInputType (): int 0%   (0/1)0%   (0/2)0%   (0/1)
NumberPicker$NumberRangeKeyListener (NumberPicker): void 100% (1/1)100% (6/6)100% (1/1)
NumberPicker$NumberRangeKeyListener (NumberPicker, NumberPicker$1): void 100% (1/1)100% (4/4)100% (1/1)
filter (CharSequence, int, int, Spanned, int, int): CharSequence 100% (1/1)100% (55/55)100% (10/10)
getAcceptedChars (): char [] 100% (1/1)100% (2/2)100% (1/1)
     
class NumberPicker$2100% (1/1)100% (2/2)100% (44/44)100% (8/8)
NumberPicker$2 (): void 100% (1/1)100% (19/19)100% (4/4)
toString (int): String 100% (1/1)100% (25/25)100% (4/4)

1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 
17package net.mandaria.tippytipper.widgets;
18 
19import net.mandaria.tippytipper.R;
20import android.content.Context;
21import android.os.Handler;
22import android.text.InputFilter;
23import android.text.InputType;
24import android.text.Spanned;
25import android.text.method.NumberKeyListener;
26import android.util.AttributeSet;
27import android.view.LayoutInflater;
28import android.view.View;
29import android.view.View.OnClickListener;
30import android.view.View.OnFocusChangeListener;
31import android.view.View.OnLongClickListener;
32import android.widget.TextView;
33import android.widget.LinearLayout;
34import android.widget.EditText;
35 
36/**
37 * This class has been pulled from the Android platform source code, its an internal widget that hasn't been
38 * made public so its included in the project in this fashion for use with the preferences screen; I have made
39 * a few slight modifications to the code here, I simply put a MAX and MIN default in the code but these values
40 * can still be set publically by calling code.
41 *
42 * @author Google
43 */
44public class NumberPicker extends LinearLayout implements OnClickListener,
45        OnFocusChangeListener, OnLongClickListener {
46 
47    private static final String TAG = "NumberPicker";
48    private static final int DEFAULT_MAX = 999;
49    private static final int DEFAULT_MIN = 0;
50 
51    public interface OnChangedListener {
52        void onChanged(NumberPicker picker, int oldVal, int newVal);
53    }
54 
55    public interface Formatter {
56        String toString(int value);
57    }
58 
59    /*
60     * Use a custom NumberPicker formatting callback to use two-digit
61     * minutes strings like "01".  Keeping a static formatter etc. is the
62     * most efficient way to do this; it avoids creating temporary objects
63     * on every call to format().
64     */
65    public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER =
66            new NumberPicker.Formatter() {
67                final StringBuilder mBuilder = new StringBuilder();
68                final java.util.Formatter mFmt = new java.util.Formatter(mBuilder);
69                final Object[] mArgs = new Object[1];
70                public String toString(int value) {
71                    mArgs[0] = value;
72                    mBuilder.delete(0, mBuilder.length());
73                    mFmt.format("%02d", mArgs);
74                    return mFmt.toString();
75                }
76        };
77        
78    public static final NumberPicker.Formatter THREE_DIGIT_FORMATTER =
79            new NumberPicker.Formatter() {
80                final StringBuilder mBuilder = new StringBuilder();
81                final java.util.Formatter mFmt = new java.util.Formatter(mBuilder);
82                final Object[] mArgs = new Object[1];
83                public String toString(int value) {
84                    mArgs[0] = value;
85                    mBuilder.delete(0, mBuilder.length());
86                    mFmt.format("%03d", mArgs);
87                    return mFmt.toString();
88                }
89        };
90 
91    private final Handler mHandler;
92    private final Runnable mRunnable = new Runnable() {
93        public void run() {
94            if (mIncrement) {
95                changeCurrent(mCurrent + 1);
96                mHandler.postDelayed(this, mSpeed);
97            } else if (mDecrement) {
98                changeCurrent(mCurrent - 1);
99                mHandler.postDelayed(this, mSpeed);
100            }
101        }
102    };
103 
104    private final EditText mText;
105    private final InputFilter mNumberInputFilter;
106 
107    private String[] mDisplayedValues;
108    protected int mStart;
109    protected int mEnd;
110    protected int mCurrent;
111    protected int mPrevious;
112    private OnChangedListener mListener;
113    private Formatter mFormatter;
114    private long mSpeed = 300;
115 
116    private boolean mIncrement;
117    private boolean mDecrement;
118 
119    public NumberPicker(Context context) {
120        this(context, null);
121    }
122 
123    public NumberPicker(Context context, AttributeSet attrs) {
124        this(context, attrs, 0);
125    }
126 
127    @SuppressWarnings({"UnusedDeclaration"})
128    public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
129        super(context, attrs);
130        setOrientation(VERTICAL);
131        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
132        inflater.inflate(R.layout.number_picker, this, true);
133        mHandler = new Handler();
134        InputFilter inputFilter = new NumberPickerInputFilter();
135        mNumberInputFilter = new NumberRangeKeyListener();
136        mIncrementButton = (NumberPickerButton) findViewById(R.id.increment);
137        mIncrementButton.setOnClickListener(this);
138        mIncrementButton.setOnLongClickListener(this);
139        mIncrementButton.setNumberPicker(this);
140        mDecrementButton = (NumberPickerButton) findViewById(R.id.decrement);
141        mDecrementButton.setOnClickListener(this);
142        mDecrementButton.setOnLongClickListener(this);
143        mDecrementButton.setNumberPicker(this);
144 
145        mText = (EditText) findViewById(R.id.timepicker_input);
146        mText.setOnFocusChangeListener(this);
147        mText.setFilters(new InputFilter[] {inputFilter});
148        mText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
149 
150        if (!isEnabled()) {
151            setEnabled(false);
152        }
153 
154        mStart = DEFAULT_MIN;
155        mEnd = DEFAULT_MAX;
156    }
157 
158    @Override
159    public void setEnabled(boolean enabled) {
160        super.setEnabled(enabled);
161        mIncrementButton.setEnabled(enabled);
162        mDecrementButton.setEnabled(enabled);
163        mText.setEnabled(enabled);
164    }
165 
166    public void setOnChangeListener(OnChangedListener listener) {
167        mListener = listener;
168    }
169 
170    public void setFormatter(Formatter formatter) {
171        mFormatter = formatter;
172    }
173 
174    /**
175     * Set the range of numbers allowed for the number picker. The current
176     * value will be automatically set to the start.
177     *
178     * @param start the start of the range (inclusive)
179     * @param end the end of the range (inclusive)
180     */
181    public void setRange(int start, int end) {
182        mStart = start;
183        mEnd = end;
184        mCurrent = start;
185        updateView();
186    }
187 
188    /**
189     * Set the range of numbers allowed for the number picker. The current
190     * value will be automatically set to the start. Also provide a mapping
191     * for values used to display to the user.
192     *
193     * @param start the start of the range (inclusive)
194     * @param end the end of the range (inclusive)
195     * @param displayedValues the values displayed to the user.
196     */
197    public void setRange(int start, int end, String[] displayedValues) {
198        mDisplayedValues = displayedValues;
199        mStart = start;
200        mEnd = end;
201        mCurrent = start;
202        updateView();
203    }
204 
205    public void setCurrent(int current) {
206        mCurrent = current;
207        updateView();
208    }
209 
210    /**
211     * The speed (in milliseconds) at which the numbers will scroll
212     * when the the +/- buttons are longpressed. Default is 300ms.
213     */
214    public void setSpeed(long speed) {
215        mSpeed = speed;
216    }
217 
218    public void onClick(View v) {
219        validateInput(mText);
220        if (!mText.hasFocus()) mText.requestFocus();
221 
222        if(v != null)
223        {
224                // now perform the increment/decrement
225                if (R.id.increment == v.getId()) {
226                    changeCurrent(mCurrent + 1);
227                } else if (R.id.decrement == v.getId()) {
228                    changeCurrent(mCurrent - 1);
229                }
230        }
231    }
232 
233    private String formatNumber(int value) {
234        return (mFormatter != null)
235                ? mFormatter.toString(value)
236                : String.valueOf(value);
237    }
238 
239    protected void changeCurrent(int current) {
240 
241        // Wrap around the values if we go past the start or end
242        if (current > mEnd) {
243            current = mStart;
244        } else if (current < mStart) {
245            current = mEnd;
246        }
247        mPrevious = mCurrent;
248        mCurrent = current;
249 
250        notifyChange();
251        updateView();
252    }
253 
254    protected void notifyChange() {
255        if (mListener != null) {
256            mListener.onChanged(this, mPrevious, mCurrent);
257        }
258    }
259 
260    protected void updateView() {
261 
262        /* If we don't have displayed values then use the
263         * current number else find the correct value in the
264         * displayed values for the current number.
265         */
266        if (mDisplayedValues == null) {
267            mText.setText(formatNumber(mCurrent));
268        } else {
269            mText.setText(mDisplayedValues[mCurrent - mStart]);
270        }
271        mText.setSelection(mText.getText().length());
272    }
273 
274    private void validateCurrentView(CharSequence str) {
275        int val = getSelectedPos(str.toString());
276        if ((val >= mStart) && (val <= mEnd)) {
277            if (mCurrent != val) {
278                mPrevious = mCurrent;
279                mCurrent = val;
280                notifyChange();
281            }
282        }
283        updateView();
284    }
285 
286    public void onFocusChange(View v, boolean hasFocus) {
287 
288        /* When focus is lost check that the text field
289         * has valid values.
290         */
291        if (!hasFocus) {
292            validateInput(v);
293        }
294    }
295 
296    private void validateInput(View v) {
297        String str = String.valueOf(((TextView) v).getText());
298        if(str.length() == 2 && mFormatter == THREE_DIGIT_FORMATTER)
299                str = str + "0";
300        else if(str.length() == 1 && mFormatter == THREE_DIGIT_FORMATTER)
301                str = str + "00";
302        
303        if ("".equals(str)) {
304 
305            // Restore to the old value as we don't allow empty values
306            updateView();
307        } else {
308 
309            // Check the new value and ensure it's in range
310            validateCurrentView(str);
311        }
312    }
313 
314    /**
315     * We start the long click here but rely on the {@link NumberPickerButton}
316     * to inform us when the long click has ended.
317     */
318    public boolean onLongClick(View v) {
319 
320        /* The text view may still have focus so clear it's focus which will
321         * trigger the on focus changed and any typed values to be pulled.
322         */
323        mText.clearFocus();
324 
325        if (R.id.increment == v.getId()) {
326            mIncrement = true;
327            mHandler.post(mRunnable);
328        } else if (R.id.decrement == v.getId()) {
329            mDecrement = true;
330            mHandler.post(mRunnable);
331        }
332        return true;
333    }
334 
335    public void cancelIncrement() {
336        mIncrement = false;
337    }
338 
339    public void cancelDecrement() {
340        mDecrement = false;
341    }
342 
343    private static final char[] DIGIT_CHARACTERS = new char[] {
344        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
345    };
346 
347    private NumberPickerButton mIncrementButton;
348    private NumberPickerButton mDecrementButton;
349 
350    private class NumberPickerInputFilter implements InputFilter {
351        public CharSequence filter(CharSequence source, int start, int end,
352                Spanned dest, int dstart, int dend) {
353            if (mDisplayedValues == null) {
354                return mNumberInputFilter.filter(source, start, end, dest, dstart, dend);
355            }
356            CharSequence filtered = String.valueOf(source.subSequence(start, end));
357            String result = String.valueOf(dest.subSequence(0, dstart))
358                    + filtered
359                    + dest.subSequence(dend, dest.length());
360            String str = String.valueOf(result).toLowerCase();
361            for (String val : mDisplayedValues) {
362                val = val.toLowerCase();
363                if (val.startsWith(str)) {
364                    return filtered;
365                }
366            }
367            return "";
368        }
369    }
370 
371    private class NumberRangeKeyListener extends NumberKeyListener {
372 
373        // XXX This doesn't allow for range limits when controlled by a
374        // soft input method!
375        public int getInputType() {
376            return InputType.TYPE_CLASS_NUMBER;
377        }
378 
379        @Override
380        protected char[] getAcceptedChars() {
381            return DIGIT_CHARACTERS;
382        }
383 
384        @Override
385        public CharSequence filter(CharSequence source, int start, int end,
386                Spanned dest, int dstart, int dend) {
387 
388            CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
389            if (filtered == null) {
390                filtered = source.subSequence(start, end);
391            }
392 
393            String result = String.valueOf(dest.subSequence(0, dstart))
394                    + filtered
395                    + dest.subSequence(dend, dest.length());
396 
397            if ("".equals(result)) {
398                return result;
399            }
400            int val = getSelectedPos(result);
401 
402            /* Ensure the user can't type in a value greater
403             * than the max allowed. We have to allow less than min
404             * as the user might want to delete some numbers
405             * and then type a new number.
406             */
407            if (val > mEnd) {
408                return "";
409            } else {
410                return filtered;
411            }
412        }
413    }
414 
415    private int getSelectedPos(String str) {
416        if (mDisplayedValues == null) {
417            return Integer.parseInt(str);
418        } else {
419            for (int i = 0; i < mDisplayedValues.length; i++) {
420 
421                /* Don't force the user to type in jan when ja will do */
422                str = str.toLowerCase();
423                if (mDisplayedValues[i].toLowerCase().startsWith(str)) {
424                    return mStart + i;
425                }
426            }
427 
428            /* The user might have typed in a number into the month field i.e.
429             * 10 instead of OCT so support that too.
430             */
431            try {
432                return Integer.parseInt(str);
433            } catch (NumberFormatException e) {
434 
435                /* Ignore as if it's not a number we don't care */
436            }
437        }
438        return mStart;
439    }
440 
441    /**
442     * @return the current value.
443     */
444    public int getCurrent() {
445        return mCurrent;
446    }
447    
448    public String getCurrentFormatted() {
449            return formatNumber(mCurrent);
450    }
451}

[all classes][net.mandaria.tippytipper.widgets]
EMMA 2.0.5312 (C) Vladimir Roubtsov