1/* 2 * Copyright (C) 2010 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 com.android.internal.R; 20 21import android.annotation.AttrRes; 22import android.annotation.ColorInt; 23import android.annotation.DrawableRes; 24import android.annotation.NonNull; 25import android.annotation.Nullable; 26import android.annotation.StyleRes; 27import android.annotation.Widget; 28import android.content.Context; 29import android.content.res.Configuration; 30import android.content.res.TypedArray; 31import android.graphics.drawable.Drawable; 32import android.icu.util.Calendar; 33import android.icu.util.TimeZone; 34import android.util.AttributeSet; 35import android.util.Log; 36 37import java.text.DateFormat; 38import java.text.ParseException; 39import java.text.SimpleDateFormat; 40import java.util.Date; 41import java.util.Locale; 42 43/** 44 * This class is a calendar widget for displaying and selecting dates. The 45 * range of dates supported by this calendar is configurable. 46 * <p> 47 * The exact appearance and interaction model of this widget may vary between 48 * OS versions and themes (e.g. Holo versus Material), but in general a user 49 * can select a date by tapping on it and can scroll or fling the calendar to a 50 * desired date. 51 * 52 * @attr ref android.R.styleable#CalendarView_showWeekNumber 53 * @attr ref android.R.styleable#CalendarView_firstDayOfWeek 54 * @attr ref android.R.styleable#CalendarView_minDate 55 * @attr ref android.R.styleable#CalendarView_maxDate 56 * @attr ref android.R.styleable#CalendarView_shownWeekCount 57 * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor 58 * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor 59 * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor 60 * @attr ref android.R.styleable#CalendarView_weekNumberColor 61 * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor 62 * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar 63 * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance 64 * @attr ref android.R.styleable#CalendarView_dateTextAppearance 65 */ 66@Widget 67public class CalendarView extends FrameLayout { 68 private static final String LOG_TAG = "CalendarView"; 69 70 private static final int MODE_HOLO = 0; 71 private static final int MODE_MATERIAL = 1; 72 73 private final CalendarViewDelegate mDelegate; 74 75 /** 76 * The callback used to indicate the user changes the date. 77 */ 78 public interface OnDateChangeListener { 79 80 /** 81 * Called upon change of the selected day. 82 * 83 * @param view The view associated with this listener. 84 * @param year The year that was set. 85 * @param month The month that was set [0-11]. 86 * @param dayOfMonth The day of the month that was set. 87 */ 88 void onSelectedDayChange(@NonNull CalendarView view, int year, int month, int dayOfMonth); 89 } 90 91 public CalendarView(@NonNull Context context) { 92 this(context, null); 93 } 94 95 public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs) { 96 this(context, attrs, R.attr.calendarViewStyle); 97 } 98 99 public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs, 100 @AttrRes int defStyleAttr) { 101 this(context, attrs, defStyleAttr, 0); 102 } 103 104 public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs, 105 @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { 106 super(context, attrs, defStyleAttr, defStyleRes); 107 108 final TypedArray a = context.obtainStyledAttributes( 109 attrs, R.styleable.CalendarView, defStyleAttr, defStyleRes); 110 final int mode = a.getInt(R.styleable.CalendarView_calendarViewMode, MODE_HOLO); 111 a.recycle(); 112 113 switch (mode) { 114 case MODE_HOLO: 115 mDelegate = new CalendarViewLegacyDelegate( 116 this, context, attrs, defStyleAttr, defStyleRes); 117 break; 118 case MODE_MATERIAL: 119 mDelegate = new CalendarViewMaterialDelegate( 120 this, context, attrs, defStyleAttr, defStyleRes); 121 break; 122 default: 123 throw new IllegalArgumentException("invalid calendarViewMode attribute"); 124 } 125 } 126 127 /** 128 * Sets the number of weeks to be shown. 129 * 130 * @param count The shown week count. 131 * 132 * @attr ref android.R.styleable#CalendarView_shownWeekCount 133 * @deprecated No longer used by Material-style CalendarView. 134 */ 135 @Deprecated 136 public void setShownWeekCount(int count) { 137 mDelegate.setShownWeekCount(count); 138 } 139 140 /** 141 * Gets the number of weeks to be shown. 142 * 143 * @return The shown week count. 144 * 145 * @attr ref android.R.styleable#CalendarView_shownWeekCount 146 * @deprecated No longer used by Material-style CalendarView. 147 */ 148 @Deprecated 149 public int getShownWeekCount() { 150 return mDelegate.getShownWeekCount(); 151 } 152 153 /** 154 * Sets the background color for the selected week. 155 * 156 * @param color The week background color. 157 * 158 * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor 159 * @deprecated No longer used by Material-style CalendarView. 160 */ 161 @Deprecated 162 public void setSelectedWeekBackgroundColor(@ColorInt int color) { 163 mDelegate.setSelectedWeekBackgroundColor(color); 164 } 165 166 /** 167 * Gets the background color for the selected week. 168 * 169 * @return The week background color. 170 * 171 * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor 172 * @deprecated No longer used by Material-style CalendarView. 173 */ 174 @ColorInt 175 @Deprecated 176 public int getSelectedWeekBackgroundColor() { 177 return mDelegate.getSelectedWeekBackgroundColor(); 178 } 179 180 /** 181 * Sets the color for the dates of the focused month. 182 * 183 * @param color The focused month date color. 184 * 185 * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor 186 * @deprecated No longer used by Material-style CalendarView. 187 */ 188 @Deprecated 189 public void setFocusedMonthDateColor(@ColorInt int color) { 190 mDelegate.setFocusedMonthDateColor(color); 191 } 192 193 /** 194 * Gets the color for the dates in the focused month. 195 * 196 * @return The focused month date color. 197 * 198 * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor 199 * @deprecated No longer used by Material-style CalendarView. 200 */ 201 @ColorInt 202 @Deprecated 203 public int getFocusedMonthDateColor() { 204 return mDelegate.getFocusedMonthDateColor(); 205 } 206 207 /** 208 * Sets the color for the dates of a not focused month. 209 * 210 * @param color A not focused month date color. 211 * 212 * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor 213 * @deprecated No longer used by Material-style CalendarView. 214 */ 215 @Deprecated 216 public void setUnfocusedMonthDateColor(@ColorInt int color) { 217 mDelegate.setUnfocusedMonthDateColor(color); 218 } 219 220 /** 221 * Gets the color for the dates in a not focused month. 222 * 223 * @return A not focused month date color. 224 * 225 * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor 226 * @deprecated No longer used by Material-style CalendarView. 227 */ 228 @ColorInt 229 @Deprecated 230 public int getUnfocusedMonthDateColor() { 231 return mDelegate.getUnfocusedMonthDateColor(); 232 } 233 234 /** 235 * Sets the color for the week numbers. 236 * 237 * @param color The week number color. 238 * 239 * @attr ref android.R.styleable#CalendarView_weekNumberColor 240 * @deprecated No longer used by Material-style CalendarView. 241 */ 242 @Deprecated 243 public void setWeekNumberColor(@ColorInt int color) { 244 mDelegate.setWeekNumberColor(color); 245 } 246 247 /** 248 * Gets the color for the week numbers. 249 * 250 * @return The week number color. 251 * 252 * @attr ref android.R.styleable#CalendarView_weekNumberColor 253 * @deprecated No longer used by Material-style CalendarView. 254 */ 255 @ColorInt 256 @Deprecated 257 public int getWeekNumberColor() { 258 return mDelegate.getWeekNumberColor(); 259 } 260 261 /** 262 * Sets the color for the separator line between weeks. 263 * 264 * @param color The week separator color. 265 * 266 * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor 267 * @deprecated No longer used by Material-style CalendarView. 268 */ 269 @Deprecated 270 public void setWeekSeparatorLineColor(@ColorInt int color) { 271 mDelegate.setWeekSeparatorLineColor(color); 272 } 273 274 /** 275 * Gets the color for the separator line between weeks. 276 * 277 * @return The week separator color. 278 * 279 * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor 280 * @deprecated No longer used by Material-style CalendarView. 281 */ 282 @ColorInt 283 @Deprecated 284 public int getWeekSeparatorLineColor() { 285 return mDelegate.getWeekSeparatorLineColor(); 286 } 287 288 /** 289 * Sets the drawable for the vertical bar shown at the beginning and at 290 * the end of the selected date. 291 * 292 * @param resourceId The vertical bar drawable resource id. 293 * 294 * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar 295 * @deprecated No longer used by Material-style CalendarView. 296 */ 297 @Deprecated 298 public void setSelectedDateVerticalBar(@DrawableRes int resourceId) { 299 mDelegate.setSelectedDateVerticalBar(resourceId); 300 } 301 302 /** 303 * Sets the drawable for the vertical bar shown at the beginning and at 304 * the end of the selected date. 305 * 306 * @param drawable The vertical bar drawable. 307 * 308 * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar 309 * @deprecated No longer used by Material-style CalendarView. 310 */ 311 @Deprecated 312 public void setSelectedDateVerticalBar(Drawable drawable) { 313 mDelegate.setSelectedDateVerticalBar(drawable); 314 } 315 316 /** 317 * Gets the drawable for the vertical bar shown at the beginning and at 318 * the end of the selected date. 319 * 320 * @return The vertical bar drawable. 321 * @deprecated No longer used by Material-style CalendarView. 322 */ 323 @Deprecated 324 public Drawable getSelectedDateVerticalBar() { 325 return mDelegate.getSelectedDateVerticalBar(); 326 } 327 328 /** 329 * Sets the text appearance for the week day abbreviation of the calendar header. 330 * 331 * @param resourceId The text appearance resource id. 332 * 333 * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance 334 */ 335 public void setWeekDayTextAppearance(@StyleRes int resourceId) { 336 mDelegate.setWeekDayTextAppearance(resourceId); 337 } 338 339 /** 340 * Gets the text appearance for the week day abbreviation of the calendar header. 341 * 342 * @return The text appearance resource id. 343 * 344 * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance 345 */ 346 public @StyleRes int getWeekDayTextAppearance() { 347 return mDelegate.getWeekDayTextAppearance(); 348 } 349 350 /** 351 * Sets the text appearance for the calendar dates. 352 * 353 * @param resourceId The text appearance resource id. 354 * 355 * @attr ref android.R.styleable#CalendarView_dateTextAppearance 356 */ 357 public void setDateTextAppearance(@StyleRes int resourceId) { 358 mDelegate.setDateTextAppearance(resourceId); 359 } 360 361 /** 362 * Gets the text appearance for the calendar dates. 363 * 364 * @return The text appearance resource id. 365 * 366 * @attr ref android.R.styleable#CalendarView_dateTextAppearance 367 */ 368 public @StyleRes int getDateTextAppearance() { 369 return mDelegate.getDateTextAppearance(); 370 } 371 372 /** 373 * Gets the minimal date supported by this {@link CalendarView} in milliseconds 374 * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time 375 * zone. 376 * <p> 377 * Note: The default minimal date is 01/01/1900. 378 * <p> 379 * 380 * @return The minimal supported date. 381 * 382 * @attr ref android.R.styleable#CalendarView_minDate 383 */ 384 public long getMinDate() { 385 return mDelegate.getMinDate(); 386 } 387 388 /** 389 * Sets the minimal date supported by this {@link CalendarView} in milliseconds 390 * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time 391 * zone. 392 * 393 * @param minDate The minimal supported date. 394 * 395 * @attr ref android.R.styleable#CalendarView_minDate 396 */ 397 public void setMinDate(long minDate) { 398 mDelegate.setMinDate(minDate); 399 } 400 401 /** 402 * Gets the maximal date supported by this {@link CalendarView} in milliseconds 403 * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time 404 * zone. 405 * <p> 406 * Note: The default maximal date is 01/01/2100. 407 * <p> 408 * 409 * @return The maximal supported date. 410 * 411 * @attr ref android.R.styleable#CalendarView_maxDate 412 */ 413 public long getMaxDate() { 414 return mDelegate.getMaxDate(); 415 } 416 417 /** 418 * Sets the maximal date supported by this {@link CalendarView} in milliseconds 419 * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time 420 * zone. 421 * 422 * @param maxDate The maximal supported date. 423 * 424 * @attr ref android.R.styleable#CalendarView_maxDate 425 */ 426 public void setMaxDate(long maxDate) { 427 mDelegate.setMaxDate(maxDate); 428 } 429 430 /** 431 * Sets whether to show the week number. 432 * 433 * @param showWeekNumber True to show the week number. 434 * @deprecated No longer used by Material-style CalendarView. 435 * 436 * @attr ref android.R.styleable#CalendarView_showWeekNumber 437 */ 438 @Deprecated 439 public void setShowWeekNumber(boolean showWeekNumber) { 440 mDelegate.setShowWeekNumber(showWeekNumber); 441 } 442 443 /** 444 * Gets whether to show the week number. 445 * 446 * @return True if showing the week number. 447 * @deprecated No longer used by Material-style CalendarView. 448 * 449 * @attr ref android.R.styleable#CalendarView_showWeekNumber 450 */ 451 @Deprecated 452 public boolean getShowWeekNumber() { 453 return mDelegate.getShowWeekNumber(); 454 } 455 456 /** 457 * Gets the first day of week. 458 * 459 * @return The first day of the week conforming to the {@link CalendarView} 460 * APIs. 461 * @see Calendar#MONDAY 462 * @see Calendar#TUESDAY 463 * @see Calendar#WEDNESDAY 464 * @see Calendar#THURSDAY 465 * @see Calendar#FRIDAY 466 * @see Calendar#SATURDAY 467 * @see Calendar#SUNDAY 468 * 469 * @attr ref android.R.styleable#CalendarView_firstDayOfWeek 470 */ 471 public int getFirstDayOfWeek() { 472 return mDelegate.getFirstDayOfWeek(); 473 } 474 475 /** 476 * Sets the first day of week. 477 * 478 * @param firstDayOfWeek The first day of the week conforming to the 479 * {@link CalendarView} APIs. 480 * @see Calendar#MONDAY 481 * @see Calendar#TUESDAY 482 * @see Calendar#WEDNESDAY 483 * @see Calendar#THURSDAY 484 * @see Calendar#FRIDAY 485 * @see Calendar#SATURDAY 486 * @see Calendar#SUNDAY 487 * 488 * @attr ref android.R.styleable#CalendarView_firstDayOfWeek 489 */ 490 public void setFirstDayOfWeek(int firstDayOfWeek) { 491 mDelegate.setFirstDayOfWeek(firstDayOfWeek); 492 } 493 494 /** 495 * Sets the listener to be notified upon selected date change. 496 * 497 * @param listener The listener to be notified. 498 */ 499 public void setOnDateChangeListener(OnDateChangeListener listener) { 500 mDelegate.setOnDateChangeListener(listener); 501 } 502 503 /** 504 * Gets the selected date in milliseconds since January 1, 1970 00:00:00 in 505 * {@link TimeZone#getDefault()} time zone. 506 * 507 * @return The selected date. 508 */ 509 public long getDate() { 510 return mDelegate.getDate(); 511 } 512 513 /** 514 * Sets the selected date in milliseconds since January 1, 1970 00:00:00 in 515 * {@link TimeZone#getDefault()} time zone. 516 * 517 * @param date The selected date. 518 * 519 * @throws IllegalArgumentException of the provided date is before the 520 * minimal or after the maximal date. 521 * 522 * @see #setDate(long, boolean, boolean) 523 * @see #setMinDate(long) 524 * @see #setMaxDate(long) 525 */ 526 public void setDate(long date) { 527 mDelegate.setDate(date); 528 } 529 530 /** 531 * Sets the selected date in milliseconds since January 1, 1970 00:00:00 in 532 * {@link TimeZone#getDefault()} time zone. 533 * 534 * @param date The date. 535 * @param animate Whether to animate the scroll to the current date. 536 * @param center Whether to center the current date even if it is already visible. 537 * 538 * @throws IllegalArgumentException of the provided date is before the 539 * minimal or after the maximal date. 540 * 541 * @see #setMinDate(long) 542 * @see #setMaxDate(long) 543 */ 544 public void setDate(long date, boolean animate, boolean center) { 545 mDelegate.setDate(date, animate, center); 546 } 547 548 @Override 549 protected void onConfigurationChanged(Configuration newConfig) { 550 super.onConfigurationChanged(newConfig); 551 mDelegate.onConfigurationChanged(newConfig); 552 } 553 554 @Override 555 public CharSequence getAccessibilityClassName() { 556 return CalendarView.class.getName(); 557 } 558 559 /** 560 * A delegate interface that defined the public API of the CalendarView. Allows different 561 * CalendarView implementations. This would need to be implemented by the CalendarView delegates 562 * for the real behavior. 563 */ 564 private interface CalendarViewDelegate { 565 void setShownWeekCount(int count); 566 int getShownWeekCount(); 567 568 void setSelectedWeekBackgroundColor(@ColorInt int color); 569 @ColorInt int getSelectedWeekBackgroundColor(); 570 571 void setFocusedMonthDateColor(@ColorInt int color); 572 @ColorInt int getFocusedMonthDateColor(); 573 574 void setUnfocusedMonthDateColor(@ColorInt int color); 575 @ColorInt int getUnfocusedMonthDateColor(); 576 577 void setWeekNumberColor(@ColorInt int color); 578 @ColorInt int getWeekNumberColor(); 579 580 void setWeekSeparatorLineColor(@ColorInt int color); 581 @ColorInt int getWeekSeparatorLineColor(); 582 583 void setSelectedDateVerticalBar(@DrawableRes int resourceId); 584 void setSelectedDateVerticalBar(Drawable drawable); 585 Drawable getSelectedDateVerticalBar(); 586 587 void setWeekDayTextAppearance(@StyleRes int resourceId); 588 @StyleRes int getWeekDayTextAppearance(); 589 590 void setDateTextAppearance(@StyleRes int resourceId); 591 @StyleRes int getDateTextAppearance(); 592 593 void setMinDate(long minDate); 594 long getMinDate(); 595 596 void setMaxDate(long maxDate); 597 long getMaxDate(); 598 599 void setShowWeekNumber(boolean showWeekNumber); 600 boolean getShowWeekNumber(); 601 602 void setFirstDayOfWeek(int firstDayOfWeek); 603 int getFirstDayOfWeek(); 604 605 void setDate(long date); 606 void setDate(long date, boolean animate, boolean center); 607 long getDate(); 608 609 void setOnDateChangeListener(OnDateChangeListener listener); 610 611 void onConfigurationChanged(Configuration newConfig); 612 } 613 614 /** 615 * An abstract class which can be used as a start for CalendarView implementations 616 */ 617 abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate { 618 /** The default minimal date. */ 619 protected static final String DEFAULT_MIN_DATE = "01/01/1900"; 620 621 /** The default maximal date. */ 622 protected static final String DEFAULT_MAX_DATE = "01/01/2100"; 623 624 protected CalendarView mDelegator; 625 protected Context mContext; 626 protected Locale mCurrentLocale; 627 628 AbstractCalendarViewDelegate(CalendarView delegator, Context context) { 629 mDelegator = delegator; 630 mContext = context; 631 632 // Initialization based on locale 633 setCurrentLocale(Locale.getDefault()); 634 } 635 636 protected void setCurrentLocale(Locale locale) { 637 if (locale.equals(mCurrentLocale)) { 638 return; 639 } 640 mCurrentLocale = locale; 641 } 642 643 @Override 644 public void setShownWeekCount(int count) { 645 // Deprecated. 646 } 647 648 @Override 649 public int getShownWeekCount() { 650 // Deprecated. 651 return 0; 652 } 653 654 @Override 655 public void setSelectedWeekBackgroundColor(@ColorInt int color) { 656 // Deprecated. 657 } 658 659 @ColorInt 660 @Override 661 public int getSelectedWeekBackgroundColor() { 662 return 0; 663 } 664 665 @Override 666 public void setFocusedMonthDateColor(@ColorInt int color) { 667 // Deprecated. 668 } 669 670 @ColorInt 671 @Override 672 public int getFocusedMonthDateColor() { 673 return 0; 674 } 675 676 @Override 677 public void setUnfocusedMonthDateColor(@ColorInt int color) { 678 // Deprecated. 679 } 680 681 @ColorInt 682 @Override 683 public int getUnfocusedMonthDateColor() { 684 return 0; 685 } 686 687 @Override 688 public void setWeekNumberColor(@ColorInt int color) { 689 // Deprecated. 690 } 691 692 @ColorInt 693 @Override 694 public int getWeekNumberColor() { 695 // Deprecated. 696 return 0; 697 } 698 699 @Override 700 public void setWeekSeparatorLineColor(@ColorInt int color) { 701 // Deprecated. 702 } 703 704 @ColorInt 705 @Override 706 public int getWeekSeparatorLineColor() { 707 // Deprecated. 708 return 0; 709 } 710 711 @Override 712 public void setSelectedDateVerticalBar(@DrawableRes int resId) { 713 // Deprecated. 714 } 715 716 @Override 717 public void setSelectedDateVerticalBar(Drawable drawable) { 718 // Deprecated. 719 } 720 721 @Override 722 public Drawable getSelectedDateVerticalBar() { 723 // Deprecated. 724 return null; 725 } 726 727 @Override 728 public void setShowWeekNumber(boolean showWeekNumber) { 729 // Deprecated. 730 } 731 732 @Override 733 public boolean getShowWeekNumber() { 734 // Deprecated. 735 return false; 736 } 737 738 @Override 739 public void onConfigurationChanged(Configuration newConfig) { 740 // Nothing to do here, configuration changes are already propagated 741 // by ViewGroup. 742 } 743 } 744 745 /** String for parsing dates. */ 746 private static final String DATE_FORMAT = "MM/dd/yyyy"; 747 748 /** Date format for parsing dates. */ 749 private static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT); 750 751 /** 752 * Utility method for the date format used by CalendarView's min/max date. 753 * 754 * @hide Use only as directed. For internal use only. 755 */ 756 public static boolean parseDate(String date, Calendar outDate) { 757 if (date == null || date.isEmpty()) { 758 return false; 759 } 760 761 try { 762 final Date parsedDate = DATE_FORMATTER.parse(date); 763 outDate.setTime(parsedDate); 764 return true; 765 } catch (ParseException e) { 766 Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT); 767 return false; 768 } 769 } 770} 771