SimpleMonthView.java revision a7f827bab3df423d2a3b527853de3e9ac9e378fd
1bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio/*
2bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Copyright (C) 2014 The Android Open Source Project
3bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio *
4bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Licensed under the Apache License, Version 2.0 (the "License");
5bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * you may not use this file except in compliance with the License.
6bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * You may obtain a copy of the License at
7bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio *
8bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio *      http://www.apache.org/licenses/LICENSE-2.0
9bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio *
10bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Unless required by applicable law or agreed to in writing, software
11bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * distributed under the License is distributed on an "AS IS" BASIS,
12bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * See the License for the specific language governing permissions and
14bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * limitations under the License.
15bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */
16bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
17bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Megliopackage android.widget;
18bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
19bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.Context;
20bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.res.ColorStateList;
21bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.res.Resources;
220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viveretteimport android.content.res.TypedArray;
23bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Canvas;
24bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint;
25bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint.Align;
26bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint.Style;
27bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Rect;
28bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Typeface;
29a7f827bab3df423d2a3b527853de3e9ac9e378fdDeepanshu Guptaimport android.icu.text.SimpleDateFormat;
30bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.os.Bundle;
315dc973cb03889c58988703b58aefbd2397fb02c3Alan Viveretteimport android.text.TextPaint;
32bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.text.format.DateFormat;
33bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.util.AttributeSet;
34ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport android.util.IntArray;
355c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viveretteimport android.util.MathUtils;
36c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viveretteimport android.util.StateSet;
37bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.MotionEvent;
38bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.View;
39bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityEvent;
40bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo;
410ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viveretteimport android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
42bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
43bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport com.android.internal.R;
44bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport com.android.internal.widget.ExploreByTouchHelper;
45bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
461ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haaseimport java.text.NumberFormat;
47bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Calendar;
48bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Locale;
49bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
50bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio/**
51bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * A calendar-like view displaying a specified month and the appropriate selectable day numbers
52bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * within the specified month.
53bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */
54bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioclass SimpleMonthView extends View {
550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private static final int DAYS_IN_WEEK = 7;
560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private static final int MAX_WEEKS_IN_MONTH = 6;
57bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
58bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final int DEFAULT_SELECTED_DAY = -1;
59bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
60bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
620ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
63c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette
645dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette    private final TextPaint mMonthPaint = new TextPaint();
655dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette    private final TextPaint mDayOfWeekPaint = new TextPaint();
665dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette    private final TextPaint mDayPaint = new TextPaint();
670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private final Paint mDaySelectorPaint = new Paint();
680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private final Paint mDayHighlightPaint = new Paint();
695dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette
700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private final Calendar mCalendar = Calendar.getInstance();
7160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance();
72bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private final MonthViewTouchHelper mTouchHelper;
74fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette
750ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private final SimpleDateFormat mTitleFormatter;
760ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private final SimpleDateFormat mDayOfWeekFormatter;
771ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haase    private final NumberFormat mDayFormatter;
78bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
79f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    // Desired dimensions.
80f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private final int mDesiredMonthHeight;
81f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private final int mDesiredDayOfWeekHeight;
82f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private final int mDesiredDayHeight;
83f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private final int mDesiredCellWidth;
84f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private final int mDesiredDaySelectorRadius;
8560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
860ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private CharSequence mTitle;
87bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
88bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mMonth;
89bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mYear;
90bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
91f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    // Dimensions as laid out.
92f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private int mMonthHeight;
93f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private int mDayOfWeekHeight;
94f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private int mDayHeight;
95f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private int mCellWidth;
96f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    private int mDaySelectorRadius;
97f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette
980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private int mPaddedWidth;
990ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private int mPaddedHeight;
100bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1010ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /** The day of month for the selected day, or -1 if no day is selected. */
102c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    private int mActivatedDay = -1;
103bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /**
1050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * The day of month for today, or -1 if the today is not in the current
1060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * month.
1070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     */
108bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mToday = DEFAULT_SELECTED_DAY;
109bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /** The first day of the week (ex. Calendar.SUNDAY). */
111bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mWeekStart = DEFAULT_WEEK_START;
112bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /** The number of days (ex. 28) in the current month. */
1140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private int mDaysInMonth;
115bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /**
1170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * The day of week (ex. Calendar.SUNDAY) for the first day of the current
1180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * month.
1190ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     */
1200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private int mDayOfWeekStart;
121bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /** The day of month for the first (inclusive) enabled day. */
123bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mEnabledDayStart = 1;
124bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /** The day of month for the last (inclusive) enabled day. */
126bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mEnabledDayEnd = 31;
127bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /** Optional listener for handling day click actions. */
129bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private OnDayClickListener mOnDayClickListener;
130bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
131c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    private ColorStateList mDayTextColor;
132c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette
13360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    private int mTouchedItem = -1;
13460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
135bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public SimpleMonthView(Context context) {
136bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        this(context, null);
137bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
138bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
139bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public SimpleMonthView(Context context, AttributeSet attrs) {
140bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        this(context, attrs, R.attr.datePickerStyle);
141bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
142bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
14350eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette    public SimpleMonthView(Context context, AttributeSet attrs, int defStyleAttr) {
14450eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette        this(context, attrs, defStyleAttr, 0);
14550eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette    }
146bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
14750eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette    public SimpleMonthView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
14850eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette        super(context, attrs, defStyleAttr, defStyleRes);
149bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
15050eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette        final Resources res = context.getResources();
151f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mDesiredMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height);
152f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
153f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
154f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
15578bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette        mDesiredDaySelectorRadius = res.getDimensionPixelSize(
15678bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette                R.dimen.date_picker_day_selector_radius);
15760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
158bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Set up accessibility components.
159bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper = new MonthViewTouchHelper(this);
160bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        setAccessibilityDelegate(mTouchHelper);
161bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
162bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
1630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final Locale locale = res.getConfiguration().locale;
1640ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final String titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT);
1650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
1660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
1671ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haase        mDayFormatter = NumberFormat.getIntegerInstance(locale);
1680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
1690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        initPaints(res);
1700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
1710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
1720ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /**
1730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * Applies the specified text appearance resource to a paint, returning the
1740ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * text color if one is set in the text appearance.
1750ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     *
1760ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param p the paint to modify
1770ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param resId the resource ID of the text appearance
1780ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @return the text color, if available
1790ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     */
1800ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private ColorStateList applyTextAppearance(Paint p, int resId) {
1810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final TypedArray ta = mContext.obtainStyledAttributes(null,
1820ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                R.styleable.TextAppearance, 0, resId);
1830ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
1840ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
1850ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        if (fontFamily != null) {
1860ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            p.setTypeface(Typeface.create(fontFamily, 0));
1870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
1880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
1890ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        p.setTextSize(ta.getDimensionPixelSize(
1900ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                R.styleable.TextAppearance_textSize, (int) p.getTextSize()));
1910ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
1920ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
1930ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        if (textColor != null) {
1940ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0);
1950ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            p.setColor(enabledColor);
1960ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
1970ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
1980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        ta.recycle();
1990ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
2000ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        return textColor;
2010ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
2020ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
20378bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette    public int getMonthHeight() {
20478bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette        return mMonthHeight;
20578bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette    }
20678bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette
20778bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette    public int getCellWidth() {
20878bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette        return mCellWidth;
20978bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette    }
21078bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette
2110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    public void setMonthTextAppearance(int resId) {
21278bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette        applyTextAppearance(mMonthPaint, resId);
21360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
2140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        invalidate();
2150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
2160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
2170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    public void setDayOfWeekTextAppearance(int resId) {
2180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        applyTextAppearance(mDayOfWeekPaint, resId);
2190ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        invalidate();
2200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
2210ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
2220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    public void setDayTextAppearance(int resId) {
2230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final ColorStateList textColor = applyTextAppearance(mDayPaint, resId);
2240ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        if (textColor != null) {
2250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            mDayTextColor = textColor;
2260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
2270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
2280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        invalidate();
2290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
2300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
2310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    public CharSequence getTitle() {
2320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        if (mTitle == null) {
2330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            mTitle = mTitleFormatter.format(mCalendar.getTime());
2340ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
2350ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        return mTitle;
236c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    }
237c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette
238c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    /**
239c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette     * Sets up the text and style properties for painting.
240c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette     */
2410ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private void initPaints(Resources res) {
2420ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final String monthTypeface = res.getString(R.string.date_picker_month_typeface);
2430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final String dayOfWeekTypeface = res.getString(R.string.date_picker_day_of_week_typeface);
2440ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final String dayTypeface = res.getString(R.string.date_picker_day_typeface);
2450ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
2460ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int monthTextSize = res.getDimensionPixelSize(
2470ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                R.dimen.date_picker_month_text_size);
2480ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int dayOfWeekTextSize = res.getDimensionPixelSize(
2490ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                R.dimen.date_picker_day_of_week_text_size);
2500ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int dayTextSize = res.getDimensionPixelSize(
2510ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                R.dimen.date_picker_day_text_size);
2520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
253c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mMonthPaint.setAntiAlias(true);
2540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mMonthPaint.setTextSize(monthTextSize);
2550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0));
256c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mMonthPaint.setTextAlign(Align.CENTER);
257c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mMonthPaint.setStyle(Style.FILL);
258c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette
259c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mDayOfWeekPaint.setAntiAlias(true);
2600ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDayOfWeekPaint.setTextSize(dayOfWeekTextSize);
2610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0));
262c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mDayOfWeekPaint.setTextAlign(Align.CENTER);
263c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mDayOfWeekPaint.setStyle(Style.FILL);
264c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette
2650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDaySelectorPaint.setAntiAlias(true);
2660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDaySelectorPaint.setStyle(Style.FILL);
2670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
2680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDayHighlightPaint.setAntiAlias(true);
2690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDayHighlightPaint.setStyle(Style.FILL);
270c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette
271c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mDayPaint.setAntiAlias(true);
2720ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDayPaint.setTextSize(dayTextSize);
2730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDayPaint.setTypeface(Typeface.create(dayTypeface, 0));
274c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mDayPaint.setTextAlign(Align.CENTER);
275c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mDayPaint.setStyle(Style.FILL);
276bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
277bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
278c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    void setMonthTextColor(ColorStateList monthTextColor) {
279c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0);
280c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mMonthPaint.setColor(enabledColor);
281c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        invalidate();
282c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    }
283bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
284c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    void setDayOfWeekTextColor(ColorStateList dayOfWeekTextColor) {
285c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        final int enabledColor = dayOfWeekTextColor.getColorForState(ENABLED_STATE_SET, 0);
286c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mDayOfWeekPaint.setColor(enabledColor);
287c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        invalidate();
288c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    }
289bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
290c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    void setDayTextColor(ColorStateList dayTextColor) {
291c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mDayTextColor = dayTextColor;
292c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        invalidate();
293c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette    }
294bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
2950ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    void setDaySelectorColor(ColorStateList dayBackgroundColor) {
2965dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette        final int activatedColor = dayBackgroundColor.getColorForState(
297c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette                StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
2980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDaySelectorPaint.setColor(activatedColor);
299c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        invalidate();
300bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
301bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
3020ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    void setDayHighlightColor(ColorStateList dayHighlightColor) {
3030ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int pressedColor = dayHighlightColor.getColorForState(
3040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED), 0);
3050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDayHighlightPaint.setColor(pressedColor);
3060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        invalidate();
307bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
308bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
309bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public void setOnDayClickListener(OnDayClickListener listener) {
310bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mOnDayClickListener = listener;
311bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
312bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
313bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
314bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public boolean dispatchHoverEvent(MotionEvent event) {
315bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // First right-of-refusal goes the touch exploration helper.
3160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
317bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
318bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
319bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
320bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public boolean onTouchEvent(MotionEvent event) {
32160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int x = (int) (event.getX() + 0.5f);
32260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int y = (int) (event.getY() + 0.5f);
32360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
324ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        final int action = event.getAction();
325ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        switch (action) {
3260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            case MotionEvent.ACTION_DOWN:
3270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            case MotionEvent.ACTION_MOVE:
32878bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette                final int touchedItem = getDayAtLocation(x, y);
32960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                if (mTouchedItem != touchedItem) {
33060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                    mTouchedItem = touchedItem;
3310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                    invalidate();
332bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                }
333ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                if (action == MotionEvent.ACTION_DOWN && touchedItem < 0) {
334ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                    // Touch something that's not an item, reject event.
335ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                    return false;
336ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                }
337bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                break;
3380ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
3390ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            case MotionEvent.ACTION_UP:
34078bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette                final int clickedDay = getDayAtLocation(x, y);
34178bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette                onDayClicked(clickedDay);
3420ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                // Fall through.
3430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            case MotionEvent.ACTION_CANCEL:
3440ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                // Reset touched day on stream end.
34560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                mTouchedItem = -1;
3460ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                invalidate();
3470ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                break;
348bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
349bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return true;
350bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
351bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
352bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
353bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    protected void onDraw(Canvas canvas) {
3540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int paddingLeft = getPaddingLeft();
3550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int paddingTop = getPaddingTop();
3560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        canvas.translate(paddingLeft, paddingTop);
3570ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
3580ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        drawMonth(canvas);
3590ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        drawDaysOfWeek(canvas);
360bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        drawDays(canvas);
3610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
3620ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        canvas.translate(-paddingLeft, -paddingTop);
3630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
3640ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
3650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private void drawMonth(Canvas canvas) {
3660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final float x = mPaddedWidth / 2f;
3670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
3680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        // Vertically centered within the month header height.
3690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
3700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final float y = (mMonthHeight - lineHeight) / 2f;
3710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
3720ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        canvas.drawText(getTitle().toString(), x, y, mMonthPaint);
3730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
3740ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
3750ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private void drawDaysOfWeek(Canvas canvas) {
37660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final TextPaint p = mDayOfWeekPaint;
37760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int headerHeight = mMonthHeight;
37860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int rowHeight = mDayOfWeekHeight;
379f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int colWidth = mCellWidth;
38060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
38160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        // Text is vertically centered within the day of week height.
38260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
38360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int rowCenter = headerHeight + rowHeight / 2;
38460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
38560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        for (int col = 0; col < DAYS_IN_WEEK; col++) {
38660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            final int colCenter = colWidth * col + colWidth / 2;
387ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            final int colCenterRtl;
388ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            if (isLayoutRtl()) {
389ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                colCenterRtl = mPaddedWidth - colCenter;
390ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            } else {
391ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                colCenterRtl = colCenter;
392ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            }
393ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette
39460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
39560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            final String label = getDayOfWeekLabel(dayOfWeek);
396ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p);
3970ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
3980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
3990ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
40060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    private String getDayOfWeekLabel(int dayOfWeek) {
40160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek);
40260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        return mDayOfWeekFormatter.format(mDayOfWeekLabelCalendar.getTime());
40360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    }
40460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
4050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /**
4060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * Draws the month days.
4070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     */
4080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private void drawDays(Canvas canvas) {
40960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final TextPaint p = mDayPaint;
41060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int headerHeight = mMonthHeight + mDayOfWeekHeight;
41160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int rowHeight = mDayHeight;
412f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int colWidth = mCellWidth;
4130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
41460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        // Text is vertically centered within the row height.
41560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
41660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        int rowCenter = headerHeight + rowHeight / 2;
4170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
41860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) {
41960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            final int colCenter = colWidth * col + colWidth / 2;
420ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            final int colCenterRtl;
421ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            if (isLayoutRtl()) {
422ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                colCenterRtl = mPaddedWidth - colCenter;
423ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            } else {
424ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                colCenterRtl = colCenter;
425ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            }
426ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette
4270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            int stateMask = 0;
4280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
4295c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            final boolean isDayEnabled = isDayEnabled(day);
4305c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            if (isDayEnabled) {
4310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                stateMask |= StateSet.VIEW_STATE_ENABLED;
4320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            }
4330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
4340ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            final boolean isDayActivated = mActivatedDay == day;
4350ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            if (isDayActivated) {
4360ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                stateMask |= StateSet.VIEW_STATE_ACTIVATED;
4370ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
4380ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                // Adjust the circle to be centered on the row.
439ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette                canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
44060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            } else if (mTouchedItem == day) {
4410ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                stateMask |= StateSet.VIEW_STATE_PRESSED;
4420ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
4435c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette                if (isDayEnabled) {
4445c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette                    // Adjust the circle to be centered on the row.
4455c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette                    canvas.drawCircle(colCenterRtl, rowCenter,
4465c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette                            mDaySelectorRadius, mDayHighlightPaint);
4475c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette                }
4480ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            }
4490ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
4500ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            final boolean isDayToday = mToday == day;
4510ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            final int dayTextColor;
4520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            if (isDayToday && !isDayActivated) {
4530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                dayTextColor = mDaySelectorPaint.getColor();
4540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            } else {
4550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                final int[] stateSet = StateSet.get(stateMask);
4560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
4570ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            }
45860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            p.setColor(dayTextColor);
4590ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
4601ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haase            canvas.drawText(mDayFormatter.format(day), colCenterRtl, rowCenter - halfLineHeight, p);
4610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
46260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            col++;
4630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
46460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            if (col == DAYS_IN_WEEK) {
46560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                col = 0;
46660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                rowCenter += rowHeight;
4670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            }
4680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
469bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
470bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
4715c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette    private boolean isDayEnabled(int day) {
4725c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette        return day >= mEnabledDayStart && day <= mEnabledDayEnd;
4735c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette    }
4745c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette
4755c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette    private boolean isValidDayOfMonth(int day) {
4765c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette        return day >= 1 && day <= mDaysInMonth;
4775c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette    }
4785c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette
479518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private static boolean isValidDayOfWeek(int day) {
4805878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette        return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY;
4815878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette    }
4825878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette
4835878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette    private static boolean isValidMonth(int month) {
4845878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette        return month >= Calendar.JANUARY && month <= Calendar.DECEMBER;
48575b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio    }
48675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio
487bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
4880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * Sets the selected day.
489bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *
4900ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param dayOfMonth the selected day of the month, or {@code -1} to clear
4910ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     *                   the selection
4920ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     */
4930ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    public void setSelectedDay(int dayOfMonth) {
4940ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mActivatedDay = dayOfMonth;
4950ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
4960ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        // Invalidate cached accessibility information.
4970ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mTouchHelper.invalidateRoot();
4980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        invalidate();
4990ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
5000ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
5010ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /**
5020ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * Sets the first day of the week.
5030ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     *
5040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param weekStart which day the week should start on, valid values are
5050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     *                  {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
5060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     */
5070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    public void setFirstDayOfWeek(int weekStart) {
5080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        if (isValidDayOfWeek(weekStart)) {
5090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            mWeekStart = weekStart;
5100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        } else {
5110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            mWeekStart = mCalendar.getFirstDayOfWeek();
5120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
5130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
5140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        // Invalidate cached accessibility information.
5150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mTouchHelper.invalidateRoot();
5160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        invalidate();
5170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    }
5180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
5190ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    /**
5200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * Sets all the parameters for displaying this week.
5210ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * <p>
5220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * Parameters have a default value and will only update if a new value is
5230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * included, except for focus month, which will always default to no focus
5240ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * month if no value is passed in. The only required parameter is the week
5250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * start.
5260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     *
5270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param selectedDay the selected day of the month, or -1 for no selection
5280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param month the month
5290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param year the year
5300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param weekStart which day the week should start on, valid values are
5310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     *                  {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
5320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param enabledDayStart the first enabled day
5330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param enabledDayEnd the last enabled day
534bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
53575b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio    void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart,
53675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            int enabledDayEnd) {
537c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette        mActivatedDay = selectedDay;
538bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
5395878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette        if (isValidMonth(month)) {
54075b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            mMonth = month;
54175b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio        }
54275b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio        mYear = year;
543bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
544bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mCalendar.set(Calendar.MONTH, mMonth);
545bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mCalendar.set(Calendar.YEAR, mYear);
546bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mCalendar.set(Calendar.DAY_OF_MONTH, 1);
547bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK);
548bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
549518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette        if (isValidDayOfWeek(weekStart)) {
55075b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            mWeekStart = weekStart;
551bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        } else {
552bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            mWeekStart = mCalendar.getFirstDayOfWeek();
553bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
554bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
5550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        // Figure out what day today is.
5560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final Calendar today = Calendar.getInstance();
5570ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mToday = -1;
5580ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mDaysInMonth = getDaysInMonth(mMonth, mYear);
5590ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        for (int i = 0; i < mDaysInMonth; i++) {
560bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int day = i + 1;
561bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (sameDay(day, today)) {
562bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                mToday = day;
563bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
564bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
5650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
5665c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette        mEnabledDayStart = MathUtils.constrain(enabledDayStart, 1, mDaysInMonth);
5675c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette        mEnabledDayEnd = MathUtils.constrain(enabledDayEnd, mEnabledDayStart, mDaysInMonth);
5685c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette
5690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        // Invalidate the old title.
5700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        mTitle = null;
571bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
572bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Invalidate cached accessibility information.
573bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper.invalidateRoot();
574bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
575bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
576bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static int getDaysInMonth(int month, int year) {
577bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        switch (month) {
578bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.JANUARY:
579bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.MARCH:
580bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.MAY:
581bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.JULY:
582bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.AUGUST:
583bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.OCTOBER:
584bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.DECEMBER:
585bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return 31;
586bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.APRIL:
587bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.JUNE:
588bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.SEPTEMBER:
589bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.NOVEMBER:
590bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return 30;
591bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.FEBRUARY:
592bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return (year % 4 == 0) ? 29 : 28;
593bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            default:
594bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                throw new IllegalArgumentException("Invalid Month");
595bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
596bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
597bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
5980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private boolean sameDay(int day, Calendar today) {
5990ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH)
6000ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                && day == today.get(Calendar.DAY_OF_MONTH);
601bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
602bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
603bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
604bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
605816aa142023c75cf427401c21831193998bdf233Alan Viverette        final int preferredHeight = mDesiredDayHeight * MAX_WEEKS_IN_MONTH
606816aa142023c75cf427401c21831193998bdf233Alan Viverette                + mDesiredDayOfWeekHeight + mDesiredMonthHeight
607816aa142023c75cf427401c21831193998bdf233Alan Viverette                + getPaddingTop() + getPaddingBottom();
608f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK
6090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                + getPaddingStart() + getPaddingEnd();
6100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec);
6110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec);
6120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        setMeasuredDimension(resolvedWidth, resolvedHeight);
613bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
614bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
615bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
616ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette    public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
617ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        super.onRtlPropertiesChanged(layoutDirection);
618ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette
619ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        requestLayout();
620ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette    }
621ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette
622ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette    @Override
623f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
624f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        if (!changed) {
625f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette            return;
626f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        }
627f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette
628f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        // Let's initialize a completely reasonable number of variables.
629f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int w = right - left;
630f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int h = bottom - top;
631f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int paddingLeft = getPaddingLeft();
632f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int paddingTop = getPaddingTop();
633f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int paddingRight = getPaddingRight();
634f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int paddingBottom = getPaddingBottom();
635f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int paddedRight = w - paddingRight;
636f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int paddedBottom = h - paddingBottom;
637f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int paddedWidth = paddedRight - paddingLeft;
638f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int paddedHeight = paddedBottom - paddingTop;
639f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        if (paddedWidth == mPaddedWidth || paddedHeight == mPaddedHeight) {
640f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette            return;
641f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        }
642f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette
643f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mPaddedWidth = paddedWidth;
644f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mPaddedHeight = paddedHeight;
645f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette
646f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        // We may have been laid out smaller than our preferred size. If so,
647f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        // scale all dimensions to fit.
648f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int measuredPaddedHeight = getMeasuredHeight() - paddingTop - paddingBottom;
649f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final float scaleH = paddedHeight / (float) measuredPaddedHeight;
650f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int monthHeight = (int) (mDesiredMonthHeight * scaleH);
65160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
652f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mMonthHeight = monthHeight;
653f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mDayOfWeekHeight = (int) (mDesiredDayOfWeekHeight * scaleH);
654f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mDayHeight = (int) (mDesiredDayHeight * scaleH);
655f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mCellWidth = cellWidth;
656f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette
657f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        // Compute the largest day selector radius that's still within the clip
658f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        // bounds and desired selector radius.
659f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int maxSelectorWidth = cellWidth / 2 + Math.min(paddingLeft, paddingRight);
660f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int maxSelectorHeight = mDayHeight / 2 + paddingBottom;
661f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius,
662f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette                Math.min(maxSelectorWidth, maxSelectorHeight));
66360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
664bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Invalidate cached accessibility information.
665bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper.invalidateRoot();
666bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
667bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
6680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette    private int findDayOffset() {
6690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int offset = mDayOfWeekStart - mWeekStart;
6700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        if (mDayOfWeekStart < mWeekStart) {
6710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            return offset + DAYS_IN_WEEK;
672bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
6730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        return offset;
674bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
675bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
676bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
67778bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette     * Calculates the day of the month at the specified touch position. Returns
67878bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette     * the day of the month or -1 if the position wasn't in a valid day.
6790ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     *
6800ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param x the x position of the touch event
6810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param y the y position of the touch event
68278bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette     * @return the day of the month at (x, y), or -1 if the position wasn't in
68378bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette     *         a valid day
684bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
68578bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette    private int getDayAtLocation(int x, int y) {
68660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int paddedX = x - getPaddingLeft();
6870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        if (paddedX < 0 || paddedX >= mPaddedWidth) {
6880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            return -1;
6890ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
690c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette
6910ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int headerHeight = mMonthHeight + mDayOfWeekHeight;
69260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int paddedY = y - getPaddingTop();
6930ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        if (paddedY < headerHeight || paddedY >= mPaddedHeight) {
6940ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            return -1;
6950ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        }
696c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette
697ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        // Adjust for RTL after applying padding.
698ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        final int paddedXRtl;
699ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        if (isLayoutRtl()) {
700ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            paddedXRtl = mPaddedWidth - paddedX;
701ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        } else {
702ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            paddedXRtl = paddedX;
703ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        }
704ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette
7050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int row = (paddedY - headerHeight) / mDayHeight;
706ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        final int col = (paddedXRtl * DAYS_IN_WEEK) / mPaddedWidth;
7070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int index = col + row * DAYS_IN_WEEK;
7080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int day = index + 1 - findDayOffset();
7095c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette        if (!isValidDayOfMonth(day)) {
7100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            return -1;
711bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
712bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
7130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        return day;
714bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
715bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
716bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
7170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * Calculates the bounds of the specified day.
718bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *
71978bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette     * @param id the day of the month
7200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param outBounds the rect to populate with bounds
721bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
72278bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette    private boolean getBoundsForDay(int id, Rect outBounds) {
7235c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette        if (!isValidDayOfMonth(id)) {
7240ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            return false;
725bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
726bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
72760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int index = id - 1 + findDayOffset();
72860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
729ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        // Compute left edge, taking into account RTL.
7300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int col = index % DAYS_IN_WEEK;
731f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette        final int colWidth = mCellWidth;
732ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        final int left;
733ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        if (isLayoutRtl()) {
734ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            left = getWidth() - getPaddingRight() - (col + 1) * colWidth;
735ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        } else {
736ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette            left = getPaddingLeft() + col * colWidth;
737ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette        }
7380ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
73960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        // Compute top edge.
74060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int row = index / DAYS_IN_WEEK;
74160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int rowHeight = mDayHeight;
7420ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette        final int headerHeight = mMonthHeight + mDayOfWeekHeight;
74360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        final int top = getPaddingTop() + headerHeight + row * rowHeight;
7440ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
74560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        outBounds.set(left, top, left + colWidth, top + rowHeight);
746bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
74778bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette        return true;
74860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    }
74960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
75060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    /**
751bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Called when the user clicks on a day. Handles callbacks to the
752bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * {@link OnDayClickListener} if one is set.
753bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *
7540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette     * @param day the day that was clicked
755bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
75660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    private boolean onDayClicked(int day) {
7575c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette        if (!isValidDayOfMonth(day) || !isDayEnabled(day)) {
75860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            return false;
75960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        }
76060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
761bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        if (mOnDayClickListener != null) {
76260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            final Calendar date = Calendar.getInstance();
763bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            date.set(mYear, mMonth, day);
764bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            mOnDayClickListener.onDayClick(this, date);
765bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
766bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
767bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // This is a no-op if accessibility is turned off.
768bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED);
76960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        return true;
77060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    }
77160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
77260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette    /**
773bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Provides a virtual view hierarchy for interfacing with an accessibility
774bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * service.
775bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
776bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private class MonthViewTouchHelper extends ExploreByTouchHelper {
777bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        private static final String DATE_FORMAT = "dd MMMM yyyy";
778bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
779bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        private final Rect mTempRect = new Rect();
780bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        private final Calendar mTempCalendar = Calendar.getInstance();
781bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
782bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        public MonthViewTouchHelper(View host) {
783bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            super(host);
784bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
785bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
786bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
787bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        protected int getVirtualViewAt(float x, float y) {
78878bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette            final int day = getDayAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
7895c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            if (day != -1) {
790bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return day;
791bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
792bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return ExploreByTouchHelper.INVALID_ID;
793bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
794bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
795bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
796ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette        protected void getVisibleVirtualViews(IntArray virtualViewIds) {
7970ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            for (int day = 1; day <= mDaysInMonth; day++) {
798bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                virtualViewIds.add(day);
799bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
800bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
801bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
802bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
803bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
80478bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette            event.setContentDescription(getDayDescription(virtualViewId));
805bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
806bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
807bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
808bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
80978bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette            final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
8100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette
8110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            if (!hasBounds) {
8120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                // The day is invalid, kill the node.
8130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                mTempRect.setEmpty();
8140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                node.setContentDescription("");
8150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                node.setBoundsInParent(mTempRect);
8160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                node.setVisibleToUser(false);
8170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette                return;
8180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette            }
819bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
82078bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette            node.setText(getDayText(virtualViewId));
82178bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette            node.setContentDescription(getDayDescription(virtualViewId));
822bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            node.setBoundsInParent(mTempRect);
8235c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette
8245c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            final boolean isDayEnabled = isDayEnabled(virtualViewId);
8255c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            if (isDayEnabled) {
8265c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette                node.addAction(AccessibilityAction.ACTION_CLICK);
8275c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            }
8285c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette
8295c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            node.setEnabled(isDayEnabled);
830bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
831c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette            if (virtualViewId == mActivatedDay) {
83260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                // TODO: This should use activated once that's supported.
83360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                node.setChecked(true);
834bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
835bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
836bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
837bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
838bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
839bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
840bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                Bundle arguments) {
841bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            switch (action) {
842bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                case AccessibilityNodeInfo.ACTION_CLICK:
84378bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette                    return onDayClicked(virtualViewId);
844bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
845bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
846bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return false;
847bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
848bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
849bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        /**
85060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette         * Generates a description for a given virtual view.
851bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         *
85278bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette         * @param id the day to generate a description for
85360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette         * @return a description of the virtual view
854bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         */
85578bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette        private CharSequence getDayDescription(int id) {
8565c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            if (isValidDayOfMonth(id)) {
85760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                mTempCalendar.set(mYear, mMonth, id);
85860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette                return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis());
85960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            }
860bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
86160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            return "";
86260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        }
86360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette
86460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette        /**
86560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette         * Generates displayed text for a given virtual view.
86660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette         *
86778bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette         * @param id the day to generate text for
86860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette         * @return the visible text of the virtual view
86960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette         */
87078bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette        private CharSequence getDayText(int id) {
8715c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette            if (isValidDayOfMonth(id)) {
8721ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haase                return mDayFormatter.format(id);
873bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
874bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
87560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette            return null;
876bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
877bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
878bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
879bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
880bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Handles callbacks when the user clicks on a time object.
881bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
882bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public interface OnDayClickListener {
88378bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette        void onDayClick(SimpleMonthView view, Calendar day);
884bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
885bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio}
886