/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import android.annotation.AttrRes; import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StyleRes; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.annotation.Widget; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.icu.util.Calendar; import android.icu.util.TimeZone; import android.util.AttributeSet; import android.util.Log; import android.view.inspector.InspectableProperty; import com.android.internal.R; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; /** * This class is a calendar widget for displaying and selecting dates. The * range of dates supported by this calendar is configurable. *
* The exact appearance and interaction model of this widget may vary between * OS versions and themes (e.g. Holo versus Material), but in general a user * can select a date by tapping on it and can scroll or fling the calendar to a * desired date. * * @attr ref android.R.styleable#CalendarView_showWeekNumber * @attr ref android.R.styleable#CalendarView_firstDayOfWeek * @attr ref android.R.styleable#CalendarView_minDate * @attr ref android.R.styleable#CalendarView_maxDate * @attr ref android.R.styleable#CalendarView_shownWeekCount * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor * @attr ref android.R.styleable#CalendarView_weekNumberColor * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance * @attr ref android.R.styleable#CalendarView_dateTextAppearance */ @Widget public class CalendarView extends FrameLayout { private static final String LOG_TAG = "CalendarView"; private static final int MODE_HOLO = 0; private static final int MODE_MATERIAL = 1; @UnsupportedAppUsage private final CalendarViewDelegate mDelegate; /** * The callback used to indicate the user changes the date. */ public interface OnDateChangeListener { /** * Called upon change of the selected day. * * @param view The view associated with this listener. * @param year The year that was set. * @param month The month that was set [0-11]. * @param dayOfMonth The day of the month that was set. */ void onSelectedDayChange(@NonNull CalendarView view, int year, int month, int dayOfMonth); } public CalendarView(@NonNull Context context) { this(context, null); } public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, R.attr.calendarViewStyle); } public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.CalendarView, defStyleAttr, defStyleRes); saveAttributeDataForStyleable(context, R.styleable.CalendarView, attrs, a, defStyleAttr, defStyleRes); final int mode = a.getInt(R.styleable.CalendarView_calendarViewMode, MODE_HOLO); a.recycle(); switch (mode) { case MODE_HOLO: mDelegate = new CalendarViewLegacyDelegate( this, context, attrs, defStyleAttr, defStyleRes); break; case MODE_MATERIAL: mDelegate = new CalendarViewMaterialDelegate( this, context, attrs, defStyleAttr, defStyleRes); break; default: throw new IllegalArgumentException("invalid calendarViewMode attribute"); } } /** * Sets the number of weeks to be shown. * * @param count The shown week count. * * @attr ref android.R.styleable#CalendarView_shownWeekCount * @deprecated No longer used by Material-style CalendarView. */ @Deprecated public void setShownWeekCount(int count) { mDelegate.setShownWeekCount(count); } /** * Gets the number of weeks to be shown. * * @return The shown week count. * * @attr ref android.R.styleable#CalendarView_shownWeekCount * @deprecated No longer used by Material-style CalendarView. */ @InspectableProperty @Deprecated public int getShownWeekCount() { return mDelegate.getShownWeekCount(); } /** * Sets the background color for the selected week. * * @param color The week background color. * * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor * @deprecated No longer used by Material-style CalendarView. */ @Deprecated public void setSelectedWeekBackgroundColor(@ColorInt int color) { mDelegate.setSelectedWeekBackgroundColor(color); } /** * Gets the background color for the selected week. * * @return The week background color. * * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor * @deprecated No longer used by Material-style CalendarView. */ @InspectableProperty @ColorInt @Deprecated public int getSelectedWeekBackgroundColor() { return mDelegate.getSelectedWeekBackgroundColor(); } /** * Sets the color for the dates of the focused month. * * @param color The focused month date color. * * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor * @deprecated No longer used by Material-style CalendarView. */ @Deprecated public void setFocusedMonthDateColor(@ColorInt int color) { mDelegate.setFocusedMonthDateColor(color); } /** * Gets the color for the dates in the focused month. * * @return The focused month date color. * * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor * @deprecated No longer used by Material-style CalendarView. */ @InspectableProperty @ColorInt @Deprecated public int getFocusedMonthDateColor() { return mDelegate.getFocusedMonthDateColor(); } /** * Sets the color for the dates of a not focused month. * * @param color A not focused month date color. * * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor * @deprecated No longer used by Material-style CalendarView. */ @Deprecated public void setUnfocusedMonthDateColor(@ColorInt int color) { mDelegate.setUnfocusedMonthDateColor(color); } /** * Gets the color for the dates in a not focused month. * * @return A not focused month date color. * * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor * @deprecated No longer used by Material-style CalendarView. */ @InspectableProperty @ColorInt @Deprecated public int getUnfocusedMonthDateColor() { return mDelegate.getUnfocusedMonthDateColor(); } /** * Sets the color for the week numbers. * * @param color The week number color. * * @attr ref android.R.styleable#CalendarView_weekNumberColor * @deprecated No longer used by Material-style CalendarView. */ @Deprecated public void setWeekNumberColor(@ColorInt int color) { mDelegate.setWeekNumberColor(color); } /** * Gets the color for the week numbers. * * @return The week number color. * * @attr ref android.R.styleable#CalendarView_weekNumberColor * @deprecated No longer used by Material-style CalendarView. */ @InspectableProperty @ColorInt @Deprecated public int getWeekNumberColor() { return mDelegate.getWeekNumberColor(); } /** * Sets the color for the separator line between weeks. * * @param color The week separator color. * * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor * @deprecated No longer used by Material-style CalendarView. */ @Deprecated public void setWeekSeparatorLineColor(@ColorInt int color) { mDelegate.setWeekSeparatorLineColor(color); } /** * Gets the color for the separator line between weeks. * * @return The week separator color. * * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor * @deprecated No longer used by Material-style CalendarView. */ @ColorInt @Deprecated @InspectableProperty public int getWeekSeparatorLineColor() { return mDelegate.getWeekSeparatorLineColor(); } /** * Sets the drawable for the vertical bar shown at the beginning and at * the end of the selected date. * * @param resourceId The vertical bar drawable resource id. * * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar * @deprecated No longer used by Material-style CalendarView. */ @Deprecated public void setSelectedDateVerticalBar(@DrawableRes int resourceId) { mDelegate.setSelectedDateVerticalBar(resourceId); } /** * Sets the drawable for the vertical bar shown at the beginning and at * the end of the selected date. * * @param drawable The vertical bar drawable. * * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar * @deprecated No longer used by Material-style CalendarView. */ @Deprecated public void setSelectedDateVerticalBar(Drawable drawable) { mDelegate.setSelectedDateVerticalBar(drawable); } /** * Gets the drawable for the vertical bar shown at the beginning and at * the end of the selected date. * * @return The vertical bar drawable. * @deprecated No longer used by Material-style CalendarView. */ @InspectableProperty @Deprecated public Drawable getSelectedDateVerticalBar() { return mDelegate.getSelectedDateVerticalBar(); } /** * Sets the text appearance for the week day abbreviation of the calendar header. * * @param resourceId The text appearance resource id. * * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance */ public void setWeekDayTextAppearance(@StyleRes int resourceId) { mDelegate.setWeekDayTextAppearance(resourceId); } /** * Gets the text appearance for the week day abbreviation of the calendar header. * * @return The text appearance resource id. * * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance */ @InspectableProperty public @StyleRes int getWeekDayTextAppearance() { return mDelegate.getWeekDayTextAppearance(); } /** * Sets the text appearance for the calendar dates. * * @param resourceId The text appearance resource id. * * @attr ref android.R.styleable#CalendarView_dateTextAppearance */ public void setDateTextAppearance(@StyleRes int resourceId) { mDelegate.setDateTextAppearance(resourceId); } /** * Gets the text appearance for the calendar dates. * * @return The text appearance resource id. * * @attr ref android.R.styleable#CalendarView_dateTextAppearance */ @InspectableProperty public @StyleRes int getDateTextAppearance() { return mDelegate.getDateTextAppearance(); } /** * Gets the minimal date supported by this {@link CalendarView} in milliseconds * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time * zone. *
* Note: The default minimal date is 01/01/1900. *
* * @return The minimal supported date. * * @attr ref android.R.styleable#CalendarView_minDate */ @InspectableProperty public long getMinDate() { return mDelegate.getMinDate(); } /** * Sets the minimal date supported by this {@link CalendarView} in milliseconds * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time * zone. * * @param minDate The minimal supported date. * * @attr ref android.R.styleable#CalendarView_minDate */ public void setMinDate(long minDate) { mDelegate.setMinDate(minDate); } /** * Gets the maximal date supported by this {@link CalendarView} in milliseconds * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time * zone. *
* Note: The default maximal date is 01/01/2100. *
* * @return The maximal supported date. * * @attr ref android.R.styleable#CalendarView_maxDate */ @InspectableProperty public long getMaxDate() { return mDelegate.getMaxDate(); } /** * Sets the maximal date supported by this {@link CalendarView} in milliseconds * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time * zone. * * @param maxDate The maximal supported date. * * @attr ref android.R.styleable#CalendarView_maxDate */ public void setMaxDate(long maxDate) { mDelegate.setMaxDate(maxDate); } /** * Sets whether to show the week number. * * @param showWeekNumber True to show the week number. * @deprecated No longer used by Material-style CalendarView. * * @attr ref android.R.styleable#CalendarView_showWeekNumber */ @Deprecated public void setShowWeekNumber(boolean showWeekNumber) { mDelegate.setShowWeekNumber(showWeekNumber); } /** * Gets whether to show the week number. * * @return True if showing the week number. * @deprecated No longer used by Material-style CalendarView. * * @attr ref android.R.styleable#CalendarView_showWeekNumber */ @InspectableProperty @Deprecated public boolean getShowWeekNumber() { return mDelegate.getShowWeekNumber(); } /** * Gets the first day of week. * * @return The first day of the week conforming to the {@link CalendarView} * APIs. * @see Calendar#MONDAY * @see Calendar#TUESDAY * @see Calendar#WEDNESDAY * @see Calendar#THURSDAY * @see Calendar#FRIDAY * @see Calendar#SATURDAY * @see Calendar#SUNDAY * * @attr ref android.R.styleable#CalendarView_firstDayOfWeek */ @InspectableProperty public int getFirstDayOfWeek() { return mDelegate.getFirstDayOfWeek(); } /** * Sets the first day of week. * * @param firstDayOfWeek The first day of the week conforming to the * {@link CalendarView} APIs. * @see Calendar#MONDAY * @see Calendar#TUESDAY * @see Calendar#WEDNESDAY * @see Calendar#THURSDAY * @see Calendar#FRIDAY * @see Calendar#SATURDAY * @see Calendar#SUNDAY * * @attr ref android.R.styleable#CalendarView_firstDayOfWeek */ public void setFirstDayOfWeek(int firstDayOfWeek) { mDelegate.setFirstDayOfWeek(firstDayOfWeek); } /** * Sets the listener to be notified upon selected date change. * * @param listener The listener to be notified. */ public void setOnDateChangeListener(OnDateChangeListener listener) { mDelegate.setOnDateChangeListener(listener); } /** * Gets the selected date in milliseconds since January 1, 1970 00:00:00 in * {@link TimeZone#getDefault()} time zone. * * @return The selected date. */ public long getDate() { return mDelegate.getDate(); } /** * Sets the selected date in milliseconds since January 1, 1970 00:00:00 in * {@link TimeZone#getDefault()} time zone. * * @param date The selected date. * * @throws IllegalArgumentException of the provided date is before the * minimal or after the maximal date. * * @see #setDate(long, boolean, boolean) * @see #setMinDate(long) * @see #setMaxDate(long) */ public void setDate(long date) { mDelegate.setDate(date); } /** * Sets the selected date in milliseconds since January 1, 1970 00:00:00 in * {@link TimeZone#getDefault()} time zone. * * @param date The date. * @param animate Whether to animate the scroll to the current date. * @param center Whether to center the current date even if it is already visible. * * @throws IllegalArgumentException of the provided date is before the * minimal or after the maximal date. * * @see #setMinDate(long) * @see #setMaxDate(long) */ public void setDate(long date, boolean animate, boolean center) { mDelegate.setDate(date, animate, center); } /** * Retrieves the screen bounds for the specific date in the coordinate system of this * view. If the passed date is being currently displayed, this method returns true and * the caller can query the fields of the passed {@link Rect} object. Otherwise the * method returns false and does not touch the passed {@link Rect} object. * * @hide */ @TestApi public boolean getBoundsForDate(long date, Rect outBounds) { return mDelegate.getBoundsForDate(date, outBounds); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDelegate.onConfigurationChanged(newConfig); } @Override public CharSequence getAccessibilityClassName() { return CalendarView.class.getName(); } /** * A delegate interface that defined the public API of the CalendarView. Allows different * CalendarView implementations. This would need to be implemented by the CalendarView delegates * for the real behavior. */ private interface CalendarViewDelegate { void setShownWeekCount(int count); int getShownWeekCount(); void setSelectedWeekBackgroundColor(@ColorInt int color); @ColorInt int getSelectedWeekBackgroundColor(); void setFocusedMonthDateColor(@ColorInt int color); @ColorInt int getFocusedMonthDateColor(); void setUnfocusedMonthDateColor(@ColorInt int color); @ColorInt int getUnfocusedMonthDateColor(); void setWeekNumberColor(@ColorInt int color); @ColorInt int getWeekNumberColor(); void setWeekSeparatorLineColor(@ColorInt int color); @ColorInt int getWeekSeparatorLineColor(); void setSelectedDateVerticalBar(@DrawableRes int resourceId); void setSelectedDateVerticalBar(Drawable drawable); Drawable getSelectedDateVerticalBar(); void setWeekDayTextAppearance(@StyleRes int resourceId); @StyleRes int getWeekDayTextAppearance(); void setDateTextAppearance(@StyleRes int resourceId); @StyleRes int getDateTextAppearance(); void setMinDate(long minDate); long getMinDate(); void setMaxDate(long maxDate); long getMaxDate(); void setShowWeekNumber(boolean showWeekNumber); boolean getShowWeekNumber(); void setFirstDayOfWeek(int firstDayOfWeek); int getFirstDayOfWeek(); void setDate(long date); void setDate(long date, boolean animate, boolean center); long getDate(); boolean getBoundsForDate(long date, Rect outBounds); void setOnDateChangeListener(OnDateChangeListener listener); void onConfigurationChanged(Configuration newConfig); } /** * An abstract class which can be used as a start for CalendarView implementations */ abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate { /** The default minimal date. */ protected static final String DEFAULT_MIN_DATE = "01/01/1900"; /** The default maximal date. */ protected static final String DEFAULT_MAX_DATE = "01/01/2100"; protected CalendarView mDelegator; protected Context mContext; protected Locale mCurrentLocale; AbstractCalendarViewDelegate(CalendarView delegator, Context context) { mDelegator = delegator; mContext = context; // Initialization based on locale setCurrentLocale(Locale.getDefault()); } protected void setCurrentLocale(Locale locale) { if (locale.equals(mCurrentLocale)) { return; } mCurrentLocale = locale; } @Override public void setShownWeekCount(int count) { // Deprecated. } @Override public int getShownWeekCount() { // Deprecated. return 0; } @Override public void setSelectedWeekBackgroundColor(@ColorInt int color) { // Deprecated. } @ColorInt @Override public int getSelectedWeekBackgroundColor() { return 0; } @Override public void setFocusedMonthDateColor(@ColorInt int color) { // Deprecated. } @ColorInt @Override public int getFocusedMonthDateColor() { return 0; } @Override public void setUnfocusedMonthDateColor(@ColorInt int color) { // Deprecated. } @ColorInt @Override public int getUnfocusedMonthDateColor() { return 0; } @Override public void setWeekNumberColor(@ColorInt int color) { // Deprecated. } @ColorInt @Override public int getWeekNumberColor() { // Deprecated. return 0; } @Override public void setWeekSeparatorLineColor(@ColorInt int color) { // Deprecated. } @ColorInt @Override public int getWeekSeparatorLineColor() { // Deprecated. return 0; } @Override public void setSelectedDateVerticalBar(@DrawableRes int resId) { // Deprecated. } @Override public void setSelectedDateVerticalBar(Drawable drawable) { // Deprecated. } @Override public Drawable getSelectedDateVerticalBar() { // Deprecated. return null; } @Override public void setShowWeekNumber(boolean showWeekNumber) { // Deprecated. } @Override public boolean getShowWeekNumber() { // Deprecated. return false; } @Override public void onConfigurationChanged(Configuration newConfig) { // Nothing to do here, configuration changes are already propagated // by ViewGroup. } } /** String for parsing dates. */ private static final String DATE_FORMAT = "MM/dd/yyyy"; /** Date format for parsing dates. */ private static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT); /** * Utility method for the date format used by CalendarView's min/max date. * * @hide Use only as directed. For internal use only. */ public static boolean parseDate(String date, Calendar outDate) { if (date == null || date.isEmpty()) { return false; } try { final Date parsedDate = DATE_FORMATTER.parse(date); outDate.setTime(parsedDate); return true; } catch (ParseException e) { Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT); return false; } } }