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