Skip to content

Commit 5dc30a7

Browse files
sganovAndroid (Google) Code Review
authored andcommitted
Merge "DatePicker crashes when going from 2036 to 2035 via ▼" into ics-mr0
2 parents 2f35281 + 6304b0d commit 5dc30a7

File tree

3 files changed

+88
-21
lines changed

3 files changed

+88
-21
lines changed

core/java/android/widget/DatePicker.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import android.view.accessibility.AccessibilityEvent;
3333
import android.view.accessibility.AccessibilityManager;
3434
import android.view.inputmethod.EditorInfo;
35+
import android.view.inputmethod.InputMethodManager;
3536
import android.widget.NumberPicker.OnValueChangeListener;
3637

3738
import com.android.internal.R;
@@ -90,6 +91,12 @@ public class DatePicker extends FrameLayout {
9091

9192
private final NumberPicker mYearSpinner;
9293

94+
private final EditText mDaySpinnerInput;
95+
96+
private final EditText mMonthSpinnerInput;
97+
98+
private final EditText mYearSpinnerInput;
99+
93100
private final CalendarView mCalendarView;
94101

95102
private Locale mCurrentLocale;
@@ -164,6 +171,7 @@ public DatePicker(Context context, AttributeSet attrs, int defStyle) {
164171

165172
OnValueChangeListener onChangeListener = new OnValueChangeListener() {
166173
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
174+
updateInputState();
167175
mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
168176
// take care of wrapping of days and months to update greater fields
169177
if (picker == mDaySpinner) {
@@ -214,6 +222,7 @@ public void onSelectedDayChange(CalendarView view, int year, int month, int mont
214222
mDaySpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
215223
mDaySpinner.setOnLongPressUpdateInterval(100);
216224
mDaySpinner.setOnValueChangedListener(onChangeListener);
225+
mDaySpinnerInput = (EditText) mDaySpinner.findViewById(R.id.numberpicker_input);
217226

218227
// month
219228
mMonthSpinner = (NumberPicker) findViewById(R.id.month);
@@ -222,11 +231,13 @@ public void onSelectedDayChange(CalendarView view, int year, int month, int mont
222231
mMonthSpinner.setDisplayedValues(mShortMonths);
223232
mMonthSpinner.setOnLongPressUpdateInterval(200);
224233
mMonthSpinner.setOnValueChangedListener(onChangeListener);
234+
mMonthSpinnerInput = (EditText) mMonthSpinner.findViewById(R.id.numberpicker_input);
225235

226236
// year
227237
mYearSpinner = (NumberPicker) findViewById(R.id.year);
228238
mYearSpinner.setOnLongPressUpdateInterval(100);
229239
mYearSpinner.setOnValueChangedListener(onChangeListener);
240+
mYearSpinnerInput = (EditText) mYearSpinner.findViewById(R.id.numberpicker_input);
230241

231242
// show only what the user required but make sure we
232243
// show something and the spinners have higher priority
@@ -709,6 +720,27 @@ private void setContentDescriptions() {
709720
mYearSpinner.findViewById(R.id.decrement).setContentDescription(text);
710721
}
711722

723+
private void updateInputState() {
724+
// Make sure that if the user changes the value and the IME is active
725+
// for one of the inputs if this widget, the IME is closed. If the user
726+
// changed the value via the IME and there is a next input the IME will
727+
// be shown, otherwise the user chose another means of changing the
728+
// value and having the IME up makes no sense.
729+
InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
730+
if (inputMethodManager != null) {
731+
if (inputMethodManager.isActive(mYearSpinnerInput)) {
732+
mYearSpinnerInput.clearFocus();
733+
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
734+
} else if (inputMethodManager.isActive(mMonthSpinnerInput)) {
735+
mMonthSpinnerInput.clearFocus();
736+
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
737+
} else if (inputMethodManager.isActive(mDaySpinnerInput)) {
738+
mDaySpinnerInput.clearFocus();
739+
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
740+
}
741+
}
742+
}
743+
712744
/**
713745
* Class for managing state storing/restoring.
714746
*/

core/java/android/widget/NumberPicker.java

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@ public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
536536

537537
OnClickListener onClickListener = new OnClickListener() {
538538
public void onClick(View v) {
539+
InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
540+
if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) {
541+
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
542+
}
539543
mInputText.clearFocus();
540544
if (v.getId() == R.id.increment) {
541545
changeCurrentByOne(true);
@@ -571,17 +575,14 @@ public boolean onLongClick(View v) {
571575
mInputText = (EditText) findViewById(R.id.numberpicker_input);
572576
mInputText.setOnFocusChangeListener(new OnFocusChangeListener() {
573577
public void onFocusChange(View v, boolean hasFocus) {
574-
InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
575578
if (hasFocus) {
576579
mInputText.selectAll();
580+
InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
577581
if (inputMethodManager != null) {
578582
inputMethodManager.showSoftInput(mInputText, 0);
579583
}
580584
} else {
581585
mInputText.setSelection(0, 0);
582-
if (inputMethodManager != null) {
583-
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
584-
}
585586
validateInputTextView(v);
586587
}
587588
}
@@ -996,17 +997,14 @@ public boolean getWrapSelectorWheel() {
996997
* enabled.
997998
* </p>
998999
*
999-
* @param wrapSelector Whether to wrap.
1000+
* @param wrapSelectorWheel Whether to wrap.
10001001
*/
1001-
public void setWrapSelectorWheel(boolean wrapSelector) {
1002-
if (wrapSelector && (mMaxValue - mMinValue) < mSelectorIndices.length) {
1002+
public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
1003+
if (wrapSelectorWheel && (mMaxValue - mMinValue) < mSelectorIndices.length) {
10031004
throw new IllegalStateException("Range less than selector items count.");
10041005
}
1005-
if (wrapSelector != mWrapSelectorWheel) {
1006-
// force the selector indices array to be reinitialized
1007-
mSelectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] = Integer.MAX_VALUE;
1008-
mWrapSelectorWheel = wrapSelector;
1009-
// force redraw since we might look different
1006+
if (wrapSelectorWheel != mWrapSelectorWheel) {
1007+
mWrapSelectorWheel = wrapSelectorWheel;
10101008
updateIncrementAndDecrementButtonsVisibilityState();
10111009
}
10121010
}
@@ -1206,7 +1204,13 @@ protected void onDraw(Canvas canvas) {
12061204
for (int i = 0; i < selectorIndices.length; i++) {
12071205
int selectorIndex = selectorIndices[i];
12081206
String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex);
1209-
canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
1207+
// Do not draw the middle item if input is visible since the input is shown only
1208+
// if the wheel is static and it covers the middle item. Otherwise, if the user
1209+
// starts editing the text via the IME he may see a dimmed version of the old
1210+
// value intermixed with the new one.
1211+
if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) {
1212+
canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
1213+
}
12101214
y += mSelectorElementHeight;
12111215
}
12121216

core/java/android/widget/TimePicker.java

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import android.view.LayoutInflater;
2828
import android.view.View;
2929
import android.view.accessibility.AccessibilityEvent;
30-
import android.view.accessibility.AccessibilityManager;
3130
import android.view.inputmethod.EditorInfo;
31+
import android.view.inputmethod.InputMethodManager;
3232
import android.widget.NumberPicker.OnValueChangeListener;
3333

3434
import com.android.internal.R;
@@ -79,6 +79,12 @@ public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
7979

8080
private final NumberPicker mAmPmSpinner;
8181

82+
private final EditText mHourSpinnerInput;
83+
84+
private final EditText mMinuteSpinnerInput;
85+
86+
private final EditText mAmPmSpinnerInput;
87+
8288
private final TextView mDivider;
8389

8490
// Note that the legacy implementation of the TimePicker is
@@ -140,6 +146,7 @@ public TimePicker(Context context, AttributeSet attrs, int defStyle) {
140146
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
141147
mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
142148
public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
149+
updateInputState();
143150
if (!is24HourView()) {
144151
if ((oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY)
145152
|| (oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) {
@@ -150,8 +157,8 @@ public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
150157
onTimeChanged();
151158
}
152159
});
153-
EditText hourInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input);
154-
hourInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
160+
mHourSpinnerInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input);
161+
mHourSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
155162

156163
// divider (only for the new widget style)
157164
mDivider = (TextView) findViewById(R.id.divider);
@@ -167,6 +174,7 @@ public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
167174
mMinuteSpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
168175
mMinuteSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
169176
public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
177+
updateInputState();
170178
int minValue = mMinuteSpinner.getMinValue();
171179
int maxValue = mMinuteSpinner.getMaxValue();
172180
if (oldVal == maxValue && newVal == minValue) {
@@ -187,8 +195,8 @@ public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
187195
onTimeChanged();
188196
}
189197
});
190-
EditText minuteInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input);
191-
minuteInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
198+
mMinuteSpinnerInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input);
199+
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
192200

193201
/* Get the localized am/pm strings and use them in the spinner */
194202
mAmPmStrings = new DateFormatSymbols().getAmPmStrings();
@@ -197,6 +205,7 @@ public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
197205
View amPmView = findViewById(R.id.amPm);
198206
if (amPmView instanceof Button) {
199207
mAmPmSpinner = null;
208+
mAmPmSpinnerInput = null;
200209
mAmPmButton = (Button) amPmView;
201210
mAmPmButton.setOnClickListener(new OnClickListener() {
202211
public void onClick(View button) {
@@ -213,13 +222,14 @@ public void onClick(View button) {
213222
mAmPmSpinner.setDisplayedValues(mAmPmStrings);
214223
mAmPmSpinner.setOnValueChangedListener(new OnValueChangeListener() {
215224
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
225+
updateInputState();
216226
picker.requestFocus();
217227
mIsAm = !mIsAm;
218228
updateAmPmControl();
219229
}
220230
});
221-
EditText amPmInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input);
222-
amPmInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
231+
mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input);
232+
mAmPmSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
223233
}
224234

225235
// update controls to initial state
@@ -319,7 +329,7 @@ public void writeToParcel(Parcel dest, int flags) {
319329
dest.writeInt(mMinute);
320330
}
321331

322-
@SuppressWarnings("unused")
332+
@SuppressWarnings({"unused", "hiding"})
323333
public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
324334
public SavedState createFromParcel(Parcel in) {
325335
return new SavedState(in);
@@ -524,4 +534,25 @@ private void setContentDescriptions() {
524534
mAmPmSpinner.findViewById(R.id.decrement).setContentDescription(text);
525535
}
526536
}
537+
538+
private void updateInputState() {
539+
// Make sure that if the user changes the value and the IME is active
540+
// for one of the inputs if this widget, the IME is closed. If the user
541+
// changed the value via the IME and there is a next input the IME will
542+
// be shown, otherwise the user chose another means of changing the
543+
// value and having the IME up makes no sense.
544+
InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
545+
if (inputMethodManager != null) {
546+
if (inputMethodManager.isActive(mHourSpinnerInput)) {
547+
mHourSpinnerInput.clearFocus();
548+
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
549+
} else if (inputMethodManager.isActive(mMinuteSpinnerInput)) {
550+
mMinuteSpinnerInput.clearFocus();
551+
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
552+
} else if (inputMethodManager.isActive(mAmPmSpinnerInput)) {
553+
mAmPmSpinnerInput.clearFocus();
554+
inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
555+
}
556+
}
557+
}
527558
}

0 commit comments

Comments
 (0)