content: /*
   * 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.
   * <p>
   * 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.
       * <p>
       * Note: The default minimal date is 01/01/1900.
       * <p>
       *
       * @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.
       * <p>
       * Note: The default maximal date is 01/01/2100.
       * <p>
       *
       * @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;
          }
      }
  }
  