1/* 2 * Copyright (C) 2016 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 android.widget; 18 19import android.content.Context; 20import android.content.res.Configuration; 21import android.content.res.TypedArray; 22import android.icu.util.Calendar; 23import android.os.Parcelable; 24import android.text.InputType; 25import android.text.TextUtils; 26import android.text.format.DateFormat; 27import android.util.AttributeSet; 28import android.view.LayoutInflater; 29import android.view.View; 30import android.view.accessibility.AccessibilityEvent; 31import android.view.inputmethod.EditorInfo; 32import android.view.inputmethod.InputMethodManager; 33import android.widget.DatePicker.AbstractDatePickerDelegate; 34import android.widget.NumberPicker.OnValueChangeListener; 35 36import libcore.icu.ICU; 37 38import java.text.DateFormatSymbols; 39import java.text.ParseException; 40import java.text.SimpleDateFormat; 41import java.util.Arrays; 42import java.util.Locale; 43 44/** 45 * A delegate implementing the basic DatePicker 46 */ 47class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate { 48 49 private static final String DATE_FORMAT = "MM/dd/yyyy"; 50 51 private static final int DEFAULT_START_YEAR = 1900; 52 53 private static final int DEFAULT_END_YEAR = 2100; 54 55 private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true; 56 57 private static final boolean DEFAULT_SPINNERS_SHOWN = true; 58 59 private static final boolean DEFAULT_ENABLED_STATE = true; 60 61 private final LinearLayout mSpinners; 62 63 private final NumberPicker mDaySpinner; 64 65 private final NumberPicker mMonthSpinner; 66 67 private final NumberPicker mYearSpinner; 68 69 private final EditText mDaySpinnerInput; 70 71 private final EditText mMonthSpinnerInput; 72 73 private final EditText mYearSpinnerInput; 74 75 private final CalendarView mCalendarView; 76 77 private String[] mShortMonths; 78 79 private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT); 80 81 private int mNumberOfMonths; 82 83 private Calendar mTempDate; 84 85 private Calendar mMinDate; 86 87 private Calendar mMaxDate; 88 89 private boolean mIsEnabled = DEFAULT_ENABLED_STATE; 90 91 DatePickerSpinnerDelegate(DatePicker delegator, Context context, AttributeSet attrs, 92 int defStyleAttr, int defStyleRes) { 93 super(delegator, context); 94 95 mDelegator = delegator; 96 mContext = context; 97 98 // initialization based on locale 99 setCurrentLocale(Locale.getDefault()); 100 101 final TypedArray attributesArray = context.obtainStyledAttributes(attrs, 102 com.android.internal.R.styleable.DatePicker, defStyleAttr, defStyleRes); 103 boolean spinnersShown = attributesArray.getBoolean(com.android.internal.R.styleable.DatePicker_spinnersShown, 104 DEFAULT_SPINNERS_SHOWN); 105 boolean calendarViewShown = attributesArray.getBoolean( 106 com.android.internal.R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN); 107 int startYear = attributesArray.getInt(com.android.internal.R.styleable.DatePicker_startYear, 108 DEFAULT_START_YEAR); 109 int endYear = attributesArray.getInt(com.android.internal.R.styleable.DatePicker_endYear, DEFAULT_END_YEAR); 110 String minDate = attributesArray.getString(com.android.internal.R.styleable.DatePicker_minDate); 111 String maxDate = attributesArray.getString(com.android.internal.R.styleable.DatePicker_maxDate); 112 int layoutResourceId = attributesArray.getResourceId( 113 com.android.internal.R.styleable.DatePicker_legacyLayout, com.android.internal.R.layout.date_picker_legacy); 114 attributesArray.recycle(); 115 116 LayoutInflater inflater = (LayoutInflater) context 117 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 118 final View view = inflater.inflate(layoutResourceId, mDelegator, true); 119 view.setSaveFromParentEnabled(false); 120 121 OnValueChangeListener onChangeListener = new OnValueChangeListener() { 122 public void onValueChange(NumberPicker picker, int oldVal, int newVal) { 123 updateInputState(); 124 mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis()); 125 // take care of wrapping of days and months to update greater fields 126 if (picker == mDaySpinner) { 127 int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH); 128 if (oldVal == maxDayOfMonth && newVal == 1) { 129 mTempDate.add(Calendar.DAY_OF_MONTH, 1); 130 } else if (oldVal == 1 && newVal == maxDayOfMonth) { 131 mTempDate.add(Calendar.DAY_OF_MONTH, -1); 132 } else { 133 mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal); 134 } 135 } else if (picker == mMonthSpinner) { 136 if (oldVal == 11 && newVal == 0) { 137 mTempDate.add(Calendar.MONTH, 1); 138 } else if (oldVal == 0 && newVal == 11) { 139 mTempDate.add(Calendar.MONTH, -1); 140 } else { 141 mTempDate.add(Calendar.MONTH, newVal - oldVal); 142 } 143 } else if (picker == mYearSpinner) { 144 mTempDate.set(Calendar.YEAR, newVal); 145 } else { 146 throw new IllegalArgumentException(); 147 } 148 // now set the date to the adjusted one 149 setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH), 150 mTempDate.get(Calendar.DAY_OF_MONTH)); 151 updateSpinners(); 152 updateCalendarView(); 153 notifyDateChanged(); 154 } 155 }; 156 157 mSpinners = (LinearLayout) mDelegator.findViewById(com.android.internal.R.id.pickers); 158 159 // calendar view day-picker 160 mCalendarView = (CalendarView) mDelegator.findViewById(com.android.internal.R.id.calendar_view); 161 mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() { 162 public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) { 163 setDate(year, month, monthDay); 164 updateSpinners(); 165 notifyDateChanged(); 166 } 167 }); 168 169 // day 170 mDaySpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.day); 171 mDaySpinner.setFormatter(NumberPicker.getTwoDigitFormatter()); 172 mDaySpinner.setOnLongPressUpdateInterval(100); 173 mDaySpinner.setOnValueChangedListener(onChangeListener); 174 mDaySpinnerInput = (EditText) mDaySpinner.findViewById(com.android.internal.R.id.numberpicker_input); 175 176 // month 177 mMonthSpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.month); 178 mMonthSpinner.setMinValue(0); 179 mMonthSpinner.setMaxValue(mNumberOfMonths - 1); 180 mMonthSpinner.setDisplayedValues(mShortMonths); 181 mMonthSpinner.setOnLongPressUpdateInterval(200); 182 mMonthSpinner.setOnValueChangedListener(onChangeListener); 183 mMonthSpinnerInput = (EditText) mMonthSpinner.findViewById(com.android.internal.R.id.numberpicker_input); 184 185 // year 186 mYearSpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.year); 187 mYearSpinner.setOnLongPressUpdateInterval(100); 188 mYearSpinner.setOnValueChangedListener(onChangeListener); 189 mYearSpinnerInput = (EditText) mYearSpinner.findViewById(com.android.internal.R.id.numberpicker_input); 190 191 // show only what the user required but make sure we 192 // show something and the spinners have higher priority 193 if (!spinnersShown && !calendarViewShown) { 194 setSpinnersShown(true); 195 } else { 196 setSpinnersShown(spinnersShown); 197 setCalendarViewShown(calendarViewShown); 198 } 199 200 // set the min date giving priority of the minDate over startYear 201 mTempDate.clear(); 202 if (!TextUtils.isEmpty(minDate)) { 203 if (!parseDate(minDate, mTempDate)) { 204 mTempDate.set(startYear, 0, 1); 205 } 206 } else { 207 mTempDate.set(startYear, 0, 1); 208 } 209 setMinDate(mTempDate.getTimeInMillis()); 210 211 // set the max date giving priority of the maxDate over endYear 212 mTempDate.clear(); 213 if (!TextUtils.isEmpty(maxDate)) { 214 if (!parseDate(maxDate, mTempDate)) { 215 mTempDate.set(endYear, 11, 31); 216 } 217 } else { 218 mTempDate.set(endYear, 11, 31); 219 } 220 setMaxDate(mTempDate.getTimeInMillis()); 221 222 // initialize to current date 223 mCurrentDate.setTimeInMillis(System.currentTimeMillis()); 224 init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate 225 .get(Calendar.DAY_OF_MONTH), null); 226 227 // re-order the number spinners to match the current date format 228 reorderSpinners(); 229 230 // accessibility 231 setContentDescriptions(); 232 233 // If not explicitly specified this view is important for accessibility. 234 if (mDelegator.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 235 mDelegator.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 236 } 237 } 238 239 @Override 240 public void init(int year, int monthOfYear, int dayOfMonth, 241 DatePicker.OnDateChangedListener onDateChangedListener) { 242 setDate(year, monthOfYear, dayOfMonth); 243 updateSpinners(); 244 updateCalendarView(); 245 246 mOnDateChangedListener = onDateChangedListener; 247 } 248 249 @Override 250 public void updateDate(int year, int month, int dayOfMonth) { 251 if (!isNewDate(year, month, dayOfMonth)) { 252 return; 253 } 254 setDate(year, month, dayOfMonth); 255 updateSpinners(); 256 updateCalendarView(); 257 notifyDateChanged(); 258 } 259 260 @Override 261 public int getYear() { 262 return mCurrentDate.get(Calendar.YEAR); 263 } 264 265 @Override 266 public int getMonth() { 267 return mCurrentDate.get(Calendar.MONTH); 268 } 269 270 @Override 271 public int getDayOfMonth() { 272 return mCurrentDate.get(Calendar.DAY_OF_MONTH); 273 } 274 275 @Override 276 public void setFirstDayOfWeek(int firstDayOfWeek) { 277 mCalendarView.setFirstDayOfWeek(firstDayOfWeek); 278 } 279 280 @Override 281 public int getFirstDayOfWeek() { 282 return mCalendarView.getFirstDayOfWeek(); 283 } 284 285 @Override 286 public void setMinDate(long minDate) { 287 mTempDate.setTimeInMillis(minDate); 288 if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR) 289 && mTempDate.get(Calendar.DAY_OF_YEAR) == mMinDate.get(Calendar.DAY_OF_YEAR)) { 290 // Same day, no-op. 291 return; 292 } 293 mMinDate.setTimeInMillis(minDate); 294 mCalendarView.setMinDate(minDate); 295 if (mCurrentDate.before(mMinDate)) { 296 mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis()); 297 updateCalendarView(); 298 } 299 updateSpinners(); 300 } 301 302 @Override 303 public Calendar getMinDate() { 304 final Calendar minDate = Calendar.getInstance(); 305 minDate.setTimeInMillis(mCalendarView.getMinDate()); 306 return minDate; 307 } 308 309 @Override 310 public void setMaxDate(long maxDate) { 311 mTempDate.setTimeInMillis(maxDate); 312 if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR) 313 && mTempDate.get(Calendar.DAY_OF_YEAR) == mMaxDate.get(Calendar.DAY_OF_YEAR)) { 314 // Same day, no-op. 315 return; 316 } 317 mMaxDate.setTimeInMillis(maxDate); 318 mCalendarView.setMaxDate(maxDate); 319 if (mCurrentDate.after(mMaxDate)) { 320 mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis()); 321 updateCalendarView(); 322 } 323 updateSpinners(); 324 } 325 326 @Override 327 public Calendar getMaxDate() { 328 final Calendar maxDate = Calendar.getInstance(); 329 maxDate.setTimeInMillis(mCalendarView.getMaxDate()); 330 return maxDate; 331 } 332 333 @Override 334 public void setEnabled(boolean enabled) { 335 mDaySpinner.setEnabled(enabled); 336 mMonthSpinner.setEnabled(enabled); 337 mYearSpinner.setEnabled(enabled); 338 mCalendarView.setEnabled(enabled); 339 mIsEnabled = enabled; 340 } 341 342 @Override 343 public boolean isEnabled() { 344 return mIsEnabled; 345 } 346 347 @Override 348 public CalendarView getCalendarView() { 349 return mCalendarView; 350 } 351 352 @Override 353 public void setCalendarViewShown(boolean shown) { 354 mCalendarView.setVisibility(shown ? View.VISIBLE : View.GONE); 355 } 356 357 @Override 358 public boolean getCalendarViewShown() { 359 return (mCalendarView.getVisibility() == View.VISIBLE); 360 } 361 362 @Override 363 public void setSpinnersShown(boolean shown) { 364 mSpinners.setVisibility(shown ? View.VISIBLE : View.GONE); 365 } 366 367 @Override 368 public boolean getSpinnersShown() { 369 return mSpinners.isShown(); 370 } 371 372 @Override 373 public void onConfigurationChanged(Configuration newConfig) { 374 setCurrentLocale(newConfig.locale); 375 } 376 377 @Override 378 public Parcelable onSaveInstanceState(Parcelable superState) { 379 return new SavedState(superState, getYear(), getMonth(), getDayOfMonth(), 380 getMinDate().getTimeInMillis(), getMaxDate().getTimeInMillis()); 381 } 382 383 @Override 384 public void onRestoreInstanceState(Parcelable state) { 385 if (state instanceof SavedState) { 386 final SavedState ss = (SavedState) state; 387 setDate(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay()); 388 updateSpinners(); 389 updateCalendarView(); 390 } 391 } 392 393 @Override 394 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 395 onPopulateAccessibilityEvent(event); 396 return true; 397 } 398 399 /** 400 * Sets the current locale. 401 * 402 * @param locale The current locale. 403 */ 404 @Override 405 protected void setCurrentLocale(Locale locale) { 406 super.setCurrentLocale(locale); 407 408 mTempDate = getCalendarForLocale(mTempDate, locale); 409 mMinDate = getCalendarForLocale(mMinDate, locale); 410 mMaxDate = getCalendarForLocale(mMaxDate, locale); 411 mCurrentDate = getCalendarForLocale(mCurrentDate, locale); 412 413 mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1; 414 mShortMonths = new DateFormatSymbols().getShortMonths(); 415 416 if (usingNumericMonths()) { 417 // We're in a locale where a date should either be all-numeric, or all-text. 418 // All-text would require custom NumberPicker formatters for day and year. 419 mShortMonths = new String[mNumberOfMonths]; 420 for (int i = 0; i < mNumberOfMonths; ++i) { 421 mShortMonths[i] = String.format("%d", i + 1); 422 } 423 } 424 } 425 426 /** 427 * Tests whether the current locale is one where there are no real month names, 428 * such as Chinese, Japanese, or Korean locales. 429 */ 430 private boolean usingNumericMonths() { 431 return Character.isDigit(mShortMonths[Calendar.JANUARY].charAt(0)); 432 } 433 434 /** 435 * Gets a calendar for locale bootstrapped with the value of a given calendar. 436 * 437 * @param oldCalendar The old calendar. 438 * @param locale The locale. 439 */ 440 private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) { 441 if (oldCalendar == null) { 442 return Calendar.getInstance(locale); 443 } else { 444 final long currentTimeMillis = oldCalendar.getTimeInMillis(); 445 Calendar newCalendar = Calendar.getInstance(locale); 446 newCalendar.setTimeInMillis(currentTimeMillis); 447 return newCalendar; 448 } 449 } 450 451 /** 452 * Reorders the spinners according to the date format that is 453 * explicitly set by the user and if no such is set fall back 454 * to the current locale's default format. 455 */ 456 private void reorderSpinners() { 457 mSpinners.removeAllViews(); 458 // We use numeric spinners for year and day, but textual months. Ask icu4c what 459 // order the user's locale uses for that combination. http://b/7207103. 460 String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd"); 461 char[] order = ICU.getDateFormatOrder(pattern); 462 final int spinnerCount = order.length; 463 for (int i = 0; i < spinnerCount; i++) { 464 switch (order[i]) { 465 case 'd': 466 mSpinners.addView(mDaySpinner); 467 setImeOptions(mDaySpinner, spinnerCount, i); 468 break; 469 case 'M': 470 mSpinners.addView(mMonthSpinner); 471 setImeOptions(mMonthSpinner, spinnerCount, i); 472 break; 473 case 'y': 474 mSpinners.addView(mYearSpinner); 475 setImeOptions(mYearSpinner, spinnerCount, i); 476 break; 477 default: 478 throw new IllegalArgumentException(Arrays.toString(order)); 479 } 480 } 481 } 482 483 /** 484 * Parses the given <code>date</code> and in case of success sets the result 485 * to the <code>outDate</code>. 486 * 487 * @return True if the date was parsed. 488 */ 489 private boolean parseDate(String date, Calendar outDate) { 490 try { 491 outDate.setTime(mDateFormat.parse(date)); 492 return true; 493 } catch (ParseException e) { 494 e.printStackTrace(); 495 return false; 496 } 497 } 498 499 private boolean isNewDate(int year, int month, int dayOfMonth) { 500 return (mCurrentDate.get(Calendar.YEAR) != year 501 || mCurrentDate.get(Calendar.MONTH) != month 502 || mCurrentDate.get(Calendar.DAY_OF_MONTH) != dayOfMonth); 503 } 504 505 private void setDate(int year, int month, int dayOfMonth) { 506 mCurrentDate.set(year, month, dayOfMonth); 507 resetAutofilledValue(); 508 if (mCurrentDate.before(mMinDate)) { 509 mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis()); 510 } else if (mCurrentDate.after(mMaxDate)) { 511 mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis()); 512 } 513 } 514 515 private void updateSpinners() { 516 // set the spinner ranges respecting the min and max dates 517 if (mCurrentDate.equals(mMinDate)) { 518 mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH)); 519 mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH)); 520 mDaySpinner.setWrapSelectorWheel(false); 521 mMonthSpinner.setDisplayedValues(null); 522 mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH)); 523 mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH)); 524 mMonthSpinner.setWrapSelectorWheel(false); 525 } else if (mCurrentDate.equals(mMaxDate)) { 526 mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH)); 527 mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH)); 528 mDaySpinner.setWrapSelectorWheel(false); 529 mMonthSpinner.setDisplayedValues(null); 530 mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH)); 531 mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH)); 532 mMonthSpinner.setWrapSelectorWheel(false); 533 } else { 534 mDaySpinner.setMinValue(1); 535 mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH)); 536 mDaySpinner.setWrapSelectorWheel(true); 537 mMonthSpinner.setDisplayedValues(null); 538 mMonthSpinner.setMinValue(0); 539 mMonthSpinner.setMaxValue(11); 540 mMonthSpinner.setWrapSelectorWheel(true); 541 } 542 543 // make sure the month names are a zero based array 544 // with the months in the month spinner 545 String[] displayedValues = Arrays.copyOfRange(mShortMonths, 546 mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1); 547 mMonthSpinner.setDisplayedValues(displayedValues); 548 549 // year spinner range does not change based on the current date 550 mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR)); 551 mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR)); 552 mYearSpinner.setWrapSelectorWheel(false); 553 554 // set the spinner values 555 mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR)); 556 mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH)); 557 mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH)); 558 559 if (usingNumericMonths()) { 560 mMonthSpinnerInput.setRawInputType(InputType.TYPE_CLASS_NUMBER); 561 } 562 } 563 564 /** 565 * Updates the calendar view with the current date. 566 */ 567 private void updateCalendarView() { 568 mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false); 569 } 570 571 572 /** 573 * Notifies the listener, if such, for a change in the selected date. 574 */ 575 private void notifyDateChanged() { 576 mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 577 if (mOnDateChangedListener != null) { 578 mOnDateChangedListener.onDateChanged(mDelegator, getYear(), getMonth(), 579 getDayOfMonth()); 580 } 581 if (mAutoFillChangeListener != null) { 582 mAutoFillChangeListener.onDateChanged(mDelegator, getYear(), getMonth(), 583 getDayOfMonth()); 584 } 585 } 586 587 /** 588 * Sets the IME options for a spinner based on its ordering. 589 * 590 * @param spinner The spinner. 591 * @param spinnerCount The total spinner count. 592 * @param spinnerIndex The index of the given spinner. 593 */ 594 private void setImeOptions(NumberPicker spinner, int spinnerCount, int spinnerIndex) { 595 final int imeOptions; 596 if (spinnerIndex < spinnerCount - 1) { 597 imeOptions = EditorInfo.IME_ACTION_NEXT; 598 } else { 599 imeOptions = EditorInfo.IME_ACTION_DONE; 600 } 601 TextView input = (TextView) spinner.findViewById(com.android.internal.R.id.numberpicker_input); 602 input.setImeOptions(imeOptions); 603 } 604 605 private void setContentDescriptions() { 606 // Day 607 trySetContentDescription(mDaySpinner, com.android.internal.R.id.increment, 608 com.android.internal.R.string.date_picker_increment_day_button); 609 trySetContentDescription(mDaySpinner, com.android.internal.R.id.decrement, 610 com.android.internal.R.string.date_picker_decrement_day_button); 611 // Month 612 trySetContentDescription(mMonthSpinner, com.android.internal.R.id.increment, 613 com.android.internal.R.string.date_picker_increment_month_button); 614 trySetContentDescription(mMonthSpinner, com.android.internal.R.id.decrement, 615 com.android.internal.R.string.date_picker_decrement_month_button); 616 // Year 617 trySetContentDescription(mYearSpinner, com.android.internal.R.id.increment, 618 com.android.internal.R.string.date_picker_increment_year_button); 619 trySetContentDescription(mYearSpinner, com.android.internal.R.id.decrement, 620 com.android.internal.R.string.date_picker_decrement_year_button); 621 } 622 623 private void trySetContentDescription(View root, int viewId, int contDescResId) { 624 View target = root.findViewById(viewId); 625 if (target != null) { 626 target.setContentDescription(mContext.getString(contDescResId)); 627 } 628 } 629 630 private void updateInputState() { 631 // Make sure that if the user changes the value and the IME is active 632 // for one of the inputs if this widget, the IME is closed. If the user 633 // changed the value via the IME and there is a next input the IME will 634 // be shown, otherwise the user chose another means of changing the 635 // value and having the IME up makes no sense. 636 InputMethodManager inputMethodManager = InputMethodManager.peekInstance(); 637 if (inputMethodManager != null) { 638 if (inputMethodManager.isActive(mYearSpinnerInput)) { 639 mYearSpinnerInput.clearFocus(); 640 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0); 641 } else if (inputMethodManager.isActive(mMonthSpinnerInput)) { 642 mMonthSpinnerInput.clearFocus(); 643 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0); 644 } else if (inputMethodManager.isActive(mDaySpinnerInput)) { 645 mDaySpinnerInput.clearFocus(); 646 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0); 647 } 648 } 649 } 650} 651