diff options
author | Svetoslav Ganov <svetoslavganov@google.com> | 2011-10-19 19:55:44 -0700 |
---|---|---|
committer | Svetoslav Ganov <svetoslavganov@google.com> | 2011-10-19 20:43:35 -0700 |
commit | 6304b0d58e74509a9f21b67b5227b2fee2f1b60f (patch) | |
tree | 11f863cd30398a80a89351ab44d7b5dc593becb2 | |
parent | 47f8367ab79247501d5d204632a7797fa8a888e5 (diff) | |
download | frameworks_base-6304b0d58e74509a9f21b67b5227b2fee2f1b60f.zip frameworks_base-6304b0d58e74509a9f21b67b5227b2fee2f1b60f.tar.gz frameworks_base-6304b0d58e74509a9f21b67b5227b2fee2f1b60f.tar.bz2 |
DatePicker crashes when going from 2036 to 2035 via ▼
1. Some obsolte logic was placing invalid index in the array of
scroll wheel items which was resulting in failure to look its
string representation from the cache causing a NPE.
2. While editing the current value via the IME the middle item
of the scroll wheel was partially visible i.e. the user was
able to see a dimmed version of the old value intermixed with
the new one.
3. The logic for hiding the IME while poking a button i.e. starting
to use another way of changing the current value, was incorrect.
bug:5480205
Change-Id: I1c2c96402bd38bac1dec64ccc6b550410332b9d7
-rw-r--r-- | core/java/android/widget/DatePicker.java | 32 | ||||
-rw-r--r-- | core/java/android/widget/NumberPicker.java | 30 | ||||
-rw-r--r-- | core/java/android/widget/TimePicker.java | 47 |
3 files changed, 88 insertions, 21 deletions
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 5077be6..0f462ff 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -32,6 +32,7 @@ import android.view.LayoutInflater; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.widget.NumberPicker.OnValueChangeListener; import com.android.internal.R; @@ -90,6 +91,12 @@ public class DatePicker extends FrameLayout { private final NumberPicker mYearSpinner; + private final EditText mDaySpinnerInput; + + private final EditText mMonthSpinnerInput; + + private final EditText mYearSpinnerInput; + private final CalendarView mCalendarView; private Locale mCurrentLocale; @@ -164,6 +171,7 @@ public class DatePicker extends FrameLayout { OnValueChangeListener onChangeListener = new OnValueChangeListener() { public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + updateInputState(); mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis()); // take care of wrapping of days and months to update greater fields if (picker == mDaySpinner) { @@ -214,6 +222,7 @@ public class DatePicker extends FrameLayout { mDaySpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); mDaySpinner.setOnLongPressUpdateInterval(100); mDaySpinner.setOnValueChangedListener(onChangeListener); + mDaySpinnerInput = (EditText) mDaySpinner.findViewById(R.id.numberpicker_input); // month mMonthSpinner = (NumberPicker) findViewById(R.id.month); @@ -222,11 +231,13 @@ public class DatePicker extends FrameLayout { mMonthSpinner.setDisplayedValues(mShortMonths); mMonthSpinner.setOnLongPressUpdateInterval(200); mMonthSpinner.setOnValueChangedListener(onChangeListener); + mMonthSpinnerInput = (EditText) mMonthSpinner.findViewById(R.id.numberpicker_input); // year mYearSpinner = (NumberPicker) findViewById(R.id.year); mYearSpinner.setOnLongPressUpdateInterval(100); mYearSpinner.setOnValueChangedListener(onChangeListener); + mYearSpinnerInput = (EditText) mYearSpinner.findViewById(R.id.numberpicker_input); // show only what the user required but make sure we // show something and the spinners have higher priority @@ -709,6 +720,27 @@ public class DatePicker extends FrameLayout { mYearSpinner.findViewById(R.id.decrement).setContentDescription(text); } + private void updateInputState() { + // Make sure that if the user changes the value and the IME is active + // for one of the inputs if this widget, the IME is closed. If the user + // changed the value via the IME and there is a next input the IME will + // be shown, otherwise the user chose another means of changing the + // value and having the IME up makes no sense. + InputMethodManager inputMethodManager = InputMethodManager.peekInstance(); + if (inputMethodManager != null) { + if (inputMethodManager.isActive(mYearSpinnerInput)) { + mYearSpinnerInput.clearFocus(); + inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } else if (inputMethodManager.isActive(mMonthSpinnerInput)) { + mMonthSpinnerInput.clearFocus(); + inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } else if (inputMethodManager.isActive(mDaySpinnerInput)) { + mDaySpinnerInput.clearFocus(); + inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } + } + } + /** * Class for managing state storing/restoring. */ diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index b4c844b..cf015c4 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -536,6 +536,10 @@ public class NumberPicker extends LinearLayout { OnClickListener onClickListener = new OnClickListener() { public void onClick(View v) { + InputMethodManager inputMethodManager = InputMethodManager.peekInstance(); + if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) { + inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } mInputText.clearFocus(); if (v.getId() == R.id.increment) { changeCurrentByOne(true); @@ -571,17 +575,14 @@ public class NumberPicker extends LinearLayout { mInputText = (EditText) findViewById(R.id.numberpicker_input); mInputText.setOnFocusChangeListener(new OnFocusChangeListener() { public void onFocusChange(View v, boolean hasFocus) { - InputMethodManager inputMethodManager = InputMethodManager.peekInstance(); if (hasFocus) { mInputText.selectAll(); + InputMethodManager inputMethodManager = InputMethodManager.peekInstance(); if (inputMethodManager != null) { inputMethodManager.showSoftInput(mInputText, 0); } } else { mInputText.setSelection(0, 0); - if (inputMethodManager != null) { - inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); - } validateInputTextView(v); } } @@ -996,17 +997,14 @@ public class NumberPicker extends LinearLayout { * enabled. * </p> * - * @param wrapSelector Whether to wrap. + * @param wrapSelectorWheel Whether to wrap. */ - public void setWrapSelectorWheel(boolean wrapSelector) { - if (wrapSelector && (mMaxValue - mMinValue) < mSelectorIndices.length) { + public void setWrapSelectorWheel(boolean wrapSelectorWheel) { + if (wrapSelectorWheel && (mMaxValue - mMinValue) < mSelectorIndices.length) { throw new IllegalStateException("Range less than selector items count."); } - if (wrapSelector != mWrapSelectorWheel) { - // force the selector indices array to be reinitialized - mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] = Integer.MAX_VALUE; - mWrapSelectorWheel = wrapSelector; - // force redraw since we might look different + if (wrapSelectorWheel != mWrapSelectorWheel) { + mWrapSelectorWheel = wrapSelectorWheel; updateIncrementAndDecrementButtonsVisibilityState(); } } @@ -1206,7 +1204,13 @@ public class NumberPicker extends LinearLayout { for (int i = 0; i < selectorIndices.length; i++) { int selectorIndex = selectorIndices[i]; String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex); - canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint); + // Do not draw the middle item if input is visible since the input is shown only + // if the wheel is static and it covers the middle item. Otherwise, if the user + // starts editing the text via the IME he may see a dimmed version of the old + // value intermixed with the new one. + if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) { + canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint); + } y += mSelectorElementHeight; } diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index f52e773..afca2db 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -27,8 +27,8 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.widget.NumberPicker.OnValueChangeListener; import com.android.internal.R; @@ -79,6 +79,12 @@ public class TimePicker extends FrameLayout { private final NumberPicker mAmPmSpinner; + private final EditText mHourSpinnerInput; + + private final EditText mMinuteSpinnerInput; + + private final EditText mAmPmSpinnerInput; + private final TextView mDivider; // Note that the legacy implementation of the TimePicker is @@ -140,6 +146,7 @@ public class TimePicker extends FrameLayout { mHourSpinner = (NumberPicker) findViewById(R.id.hour); mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { + updateInputState(); if (!is24HourView()) { if ((oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) || (oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) { @@ -150,8 +157,8 @@ public class TimePicker extends FrameLayout { onTimeChanged(); } }); - EditText hourInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input); - hourInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); + mHourSpinnerInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input); + mHourSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); // divider (only for the new widget style) mDivider = (TextView) findViewById(R.id.divider); @@ -167,6 +174,7 @@ public class TimePicker extends FrameLayout { mMinuteSpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); mMinuteSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { + updateInputState(); int minValue = mMinuteSpinner.getMinValue(); int maxValue = mMinuteSpinner.getMaxValue(); if (oldVal == maxValue && newVal == minValue) { @@ -187,8 +195,8 @@ public class TimePicker extends FrameLayout { onTimeChanged(); } }); - EditText minuteInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input); - minuteInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); + mMinuteSpinnerInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input); + mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); /* Get the localized am/pm strings and use them in the spinner */ mAmPmStrings = new DateFormatSymbols().getAmPmStrings(); @@ -197,6 +205,7 @@ public class TimePicker extends FrameLayout { View amPmView = findViewById(R.id.amPm); if (amPmView instanceof Button) { mAmPmSpinner = null; + mAmPmSpinnerInput = null; mAmPmButton = (Button) amPmView; mAmPmButton.setOnClickListener(new OnClickListener() { public void onClick(View button) { @@ -213,13 +222,14 @@ public class TimePicker extends FrameLayout { mAmPmSpinner.setDisplayedValues(mAmPmStrings); mAmPmSpinner.setOnValueChangedListener(new OnValueChangeListener() { public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + updateInputState(); picker.requestFocus(); mIsAm = !mIsAm; updateAmPmControl(); } }); - EditText amPmInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input); - amPmInput.setImeOptions(EditorInfo.IME_ACTION_DONE); + mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input); + mAmPmSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE); } // update controls to initial state @@ -319,7 +329,7 @@ public class TimePicker extends FrameLayout { dest.writeInt(mMinute); } - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "hiding"}) public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); @@ -524,4 +534,25 @@ public class TimePicker extends FrameLayout { mAmPmSpinner.findViewById(R.id.decrement).setContentDescription(text); } } + + private void updateInputState() { + // Make sure that if the user changes the value and the IME is active + // for one of the inputs if this widget, the IME is closed. If the user + // changed the value via the IME and there is a next input the IME will + // be shown, otherwise the user chose another means of changing the + // value and having the IME up makes no sense. + InputMethodManager inputMethodManager = InputMethodManager.peekInstance(); + if (inputMethodManager != null) { + if (inputMethodManager.isActive(mHourSpinnerInput)) { + mHourSpinnerInput.clearFocus(); + inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } else if (inputMethodManager.isActive(mMinuteSpinnerInput)) { + mMinuteSpinnerInput.clearFocus(); + inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } else if (inputMethodManager.isActive(mAmPmSpinnerInput)) { + mAmPmSpinnerInput.clearFocus(); + inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } + } + } } |