summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSvetoslav Ganov <svetoslavganov@google.com>2011-10-19 19:55:44 -0700
committerSvetoslav Ganov <svetoslavganov@google.com>2011-10-19 20:43:35 -0700
commit6304b0d58e74509a9f21b67b5227b2fee2f1b60f (patch)
tree11f863cd30398a80a89351ab44d7b5dc593becb2
parent47f8367ab79247501d5d204632a7797fa8a888e5 (diff)
downloadframeworks_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.java32
-rw-r--r--core/java/android/widget/NumberPicker.java30
-rw-r--r--core/java/android/widget/TimePicker.java47
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);
+ }
+ }
+ }
}