SimpleMonthView.java revision ffb46bf2956d89e3190007ccf2ef3ce3eed005fe
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;
21fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viveretteimport android.content.res.Configuration;
22bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.res.Resources;
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;
29bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.os.Bundle;
30bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.text.format.DateFormat;
31bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.text.format.DateUtils;
32bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.text.format.Time;
33bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.util.AttributeSet;
34ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport android.util.IntArray;
355878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viveretteimport android.util.MathUtils;
36bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.MotionEvent;
37bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.View;
38bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityEvent;
39bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo;
40bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
41bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport com.android.internal.R;
42bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport com.android.internal.widget.ExploreByTouchHelper;
43bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
44fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viveretteimport java.text.SimpleDateFormat;
45bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Calendar;
46bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Formatter;
47bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.List;
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 {
55bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final String TAG = "SimpleMonthView";
56bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
57518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private static final int DEFAULT_HEIGHT = 32;
58518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private static final int MIN_HEIGHT = 10;
59bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
60bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final int DEFAULT_SELECTED_DAY = -1;
61bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
62bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final int DEFAULT_NUM_DAYS = 7;
63bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final int DEFAULT_NUM_ROWS = 6;
64bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final int MAX_NUM_ROWS = 6;
65bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
66bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static final int SELECTED_CIRCLE_ALPHA = 60;
67bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
68518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private static final int DAY_SEPARATOR_WIDTH = 1;
69bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
70518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private final int mMiniDayNumberTextSize;
71518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private final int mMonthLabelTextSize;
72518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private final int mMonthDayLabelTextSize;
73518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private final int mMonthHeaderSize;
74518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private final int mDaySelectedCircleSize;
75bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
76bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // used for scaling to the device density
77bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static float mScale = 0;
78bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
79fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette    /** Single-letter (when available) formatter for the day of week label. */
80fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette    private SimpleDateFormat mDayFormatter = new SimpleDateFormat("EEEEE", Locale.getDefault());
81fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette
82bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // affects the padding on the sides of this view
83bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mPadding = 0;
84bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
85bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private String mDayOfWeekTypeface;
86bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private String mMonthTitleTypeface;
87bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
88bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private Paint mDayNumberPaint;
89bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private Paint mDayNumberDisabledPaint;
90bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private Paint mDayNumberSelectedPaint;
91bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
92bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private Paint mMonthTitlePaint;
93bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private Paint mMonthDayLabelPaint;
94bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
95bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private final Formatter mFormatter;
96bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private final StringBuilder mStringBuilder;
97bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
98bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mMonth;
99bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mYear;
100bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
101bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // Quick reference to the width of this view, matches parent
102bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mWidth;
103bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
104bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // The height this view should draw at in pixels, set by height param
105bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mRowHeight = DEFAULT_HEIGHT;
106bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
107bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // If this view contains the today
108bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private boolean mHasToday = false;
109bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
110bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // Which day is selected [0-6] or -1 if no day is selected
111bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mSelectedDay = -1;
112bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
113bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // Which day is today [0-6] or -1 if no day is today
114bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mToday = DEFAULT_SELECTED_DAY;
115bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
116bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // Which day of the week to start on [0-6]
117bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mWeekStart = DEFAULT_WEEK_START;
118bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
119bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // How many days to display
120bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mNumDays = DEFAULT_NUM_DAYS;
121bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
122bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // The number of days + a spot for week number if it is displayed
123bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mNumCells = mNumDays;
124bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
125bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mDayOfWeekStart = 0;
126bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
127bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // First enabled day
128bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mEnabledDayStart = 1;
129bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
130bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // Last enabled day
131bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mEnabledDayEnd = 31;
132bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
133bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private final Calendar mCalendar = Calendar.getInstance();
134bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private final Calendar mDayLabelCalendar = Calendar.getInstance();
135bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
136bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private final MonthViewTouchHelper mTouchHelper;
137bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
138bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mNumRows = DEFAULT_NUM_ROWS;
139bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
140bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // Optional listener for handling day click actions
141bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private OnDayClickListener mOnDayClickListener;
142bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
143bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    // Whether to prevent setting the accessibility delegate
144bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private boolean mLockAccessibilityDelegate;
145bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
146bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mNormalTextColor;
147bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mDisabledTextColor;
148bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int mSelectedDayColor;
149bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
150bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public SimpleMonthView(Context context) {
151bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        this(context, null);
152bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
153bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
154bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public SimpleMonthView(Context context, AttributeSet attrs) {
155bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        this(context, attrs, R.attr.datePickerStyle);
156bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
157bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
158bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public SimpleMonthView(Context context, AttributeSet attrs, int defStyle) {
159bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        super(context, attrs);
160bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
161bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        final Resources res = context.getResources();
162bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
163bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface);
164bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitleTypeface = res.getString(R.string.sans_serif);
165bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
166bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mStringBuilder = new StringBuilder(50);
167bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
168bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
169bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMiniDayNumberTextSize = res.getDimensionPixelSize(R.dimen.datepicker_day_number_size);
170bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthLabelTextSize = res.getDimensionPixelSize(R.dimen.datepicker_month_label_size);
171bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelTextSize = res.getDimensionPixelSize(
172bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                R.dimen.datepicker_month_day_label_text_size);
173bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthHeaderSize = res.getDimensionPixelOffset(
174bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                R.dimen.datepicker_month_list_item_header_height);
175bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDaySelectedCircleSize = res.getDimensionPixelSize(
176bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                R.dimen.datepicker_day_number_select_circle_radius);
177bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
178bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mRowHeight = (res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height)
179bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                - mMonthHeaderSize) / MAX_NUM_ROWS;
180bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
181bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Set up accessibility components.
182bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper = new MonthViewTouchHelper(this);
183bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        setAccessibilityDelegate(mTouchHelper);
184bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
185bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mLockAccessibilityDelegate = true;
186bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
187bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Sets up any standard paints that will be used
188bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        initView();
189bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
190bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
191fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette    @Override
192fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette    protected void onConfigurationChanged(Configuration newConfig) {
193fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette        super.onConfigurationChanged(newConfig);
194fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette
195fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette        mDayFormatter = new SimpleDateFormat("EEEEE", newConfig.locale);
196fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette    }
197fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette
198bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    void setTextColor(ColorStateList colors) {
199bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        final Resources res = getContext().getResources();
200bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
201bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mNormalTextColor = colors.getColorForState(ENABLED_STATE_SET,
202bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                res.getColor(R.color.datepicker_default_normal_text_color_holo_light));
203bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint.setColor(mNormalTextColor);
204bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint.setColor(mNormalTextColor);
205bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
206bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDisabledTextColor = colors.getColorForState(EMPTY_STATE_SET,
207bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                res.getColor(R.color.datepicker_default_disabled_text_color_holo_light));
208bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberDisabledPaint.setColor(mDisabledTextColor);
209bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
210bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mSelectedDayColor = colors.getColorForState(ENABLED_SELECTED_STATE_SET,
211bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                res.getColor(R.color.holo_blue_light));
212bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint.setColor(mSelectedDayColor);
213bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint.setAlpha(SELECTED_CIRCLE_ALPHA);
214bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
215bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
216bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
217bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
218bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Workaround for a JB MR1 issue where accessibility delegates on
219bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // top-level ListView items are overwritten.
220bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        if (!mLockAccessibilityDelegate) {
221bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            super.setAccessibilityDelegate(delegate);
222bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
223bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
224bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
225bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public void setOnDayClickListener(OnDayClickListener listener) {
226bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mOnDayClickListener = listener;
227bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
228bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
229bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
230bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public boolean dispatchHoverEvent(MotionEvent event) {
231bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // First right-of-refusal goes the touch exploration helper.
232bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        if (mTouchHelper.dispatchHoverEvent(event)) {
233bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return true;
234bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
235bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return super.dispatchHoverEvent(event);
236bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
237bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
238bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
239bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public boolean onTouchEvent(MotionEvent event) {
240bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        switch (event.getAction()) {
241bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case MotionEvent.ACTION_UP:
242bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                final int day = getDayFromLocation(event.getX(), event.getY());
243bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                if (day >= 0) {
244bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                    onDayClick(day);
245bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                }
246bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                break;
247bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
248bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return true;
249bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
250bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
251bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
252bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Sets up the text and style properties for painting.
253bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
254bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private void initView() {
255bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint = new Paint();
256bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint.setAntiAlias(true);
257bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint.setColor(mNormalTextColor);
258bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint.setTextSize(mMonthLabelTextSize);
259bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface, Typeface.BOLD));
260bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint.setTextAlign(Align.CENTER);
261bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint.setStyle(Style.FILL);
262bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthTitlePaint.setFakeBoldText(true);
263bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
264bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint = new Paint();
265bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint.setAntiAlias(true);
266bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint.setColor(mNormalTextColor);
267bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint.setTextSize(mMonthDayLabelTextSize);
268bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.NORMAL));
269bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint.setTextAlign(Align.CENTER);
270bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint.setStyle(Style.FILL);
271bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mMonthDayLabelPaint.setFakeBoldText(true);
272bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
273bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint = new Paint();
274bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint.setAntiAlias(true);
275bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint.setColor(mSelectedDayColor);
276bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint.setAlpha(SELECTED_CIRCLE_ALPHA);
277bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint.setTextAlign(Align.CENTER);
278bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint.setStyle(Style.FILL);
279bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberSelectedPaint.setFakeBoldText(true);
280bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
281bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberPaint = new Paint();
282bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberPaint.setAntiAlias(true);
283bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberPaint.setTextSize(mMiniDayNumberTextSize);
284bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberPaint.setTextAlign(Align.CENTER);
285bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberPaint.setStyle(Style.FILL);
286bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberPaint.setFakeBoldText(false);
287bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
288bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberDisabledPaint = new Paint();
289bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberDisabledPaint.setAntiAlias(true);
290bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberDisabledPaint.setColor(mDisabledTextColor);
291bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberDisabledPaint.setTextSize(mMiniDayNumberTextSize);
292bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberDisabledPaint.setTextAlign(Align.CENTER);
293bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberDisabledPaint.setStyle(Style.FILL);
294bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayNumberDisabledPaint.setFakeBoldText(false);
295bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
296bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
297bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
298bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    protected void onDraw(Canvas canvas) {
299bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        drawMonthTitle(canvas);
300bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        drawWeekDayLabels(canvas);
301bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        drawDays(canvas);
302bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
303bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
304518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private static boolean isValidDayOfWeek(int day) {
3055878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette        return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY;
3065878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette    }
3075878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette
3085878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette    private static boolean isValidMonth(int month) {
3095878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette        return month >= Calendar.JANUARY && month <= Calendar.DECEMBER;
31075b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio    }
31175b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio
312bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
31375b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio     * Sets all the parameters for displaying this week. Parameters have a default value and
31475b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio     * will only update if a new value is included, except for focus month, which will always
31575b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio     * default to no focus month if no value is passed in. The only required parameter is the
31675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio     * week start.
317bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *
318518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette     * @param selectedDay the selected day of the month, or -1 for no selection.
31975b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio     * @param month the month.
32075b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio     * @param year the year.
3215878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette     * @param weekStart which day the week should start on. {@link Calendar#SUNDAY} through
3225878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette     *        {@link Calendar#SATURDAY}.
32375b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio     * @param enabledDayStart the first enabled day.
32475b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio     * @param enabledDayEnd the last enabled day.
325bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
32675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio    void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart,
32775b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            int enabledDayEnd) {
32875b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio        if (mRowHeight < MIN_HEIGHT) {
32975b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            mRowHeight = MIN_HEIGHT;
330bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
33175b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio
332518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette        mSelectedDay = selectedDay;
333bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
3345878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette        if (isValidMonth(month)) {
33575b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            mMonth = month;
33675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio        }
33775b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio        mYear = year;
338bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
339bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Figure out what day today is
340bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        final Time today = new Time(Time.getCurrentTimezone());
341bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        today.setToNow();
342bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mHasToday = false;
343bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mToday = -1;
344bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
345bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mCalendar.set(Calendar.MONTH, mMonth);
346bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mCalendar.set(Calendar.YEAR, mYear);
347bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mCalendar.set(Calendar.DAY_OF_MONTH, 1);
348bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK);
349bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
350518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette        if (isValidDayOfWeek(weekStart)) {
35175b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            mWeekStart = weekStart;
352bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        } else {
353bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            mWeekStart = mCalendar.getFirstDayOfWeek();
354bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
355bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
35675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio        if (enabledDayStart > 0 && enabledDayEnd < 32) {
35775b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            mEnabledDayStart = enabledDayStart;
358bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
35975b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio        if (enabledDayEnd > 0 && enabledDayEnd < 32 && enabledDayEnd >= enabledDayStart) {
36075b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio            mEnabledDayEnd = enabledDayEnd;
361bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
362bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
363bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mNumCells = getDaysInMonth(mMonth, mYear);
364bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        for (int i = 0; i < mNumCells; i++) {
365bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int day = i + 1;
366bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (sameDay(day, today)) {
367bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                mHasToday = true;
368bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                mToday = day;
369bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
370bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
371bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mNumRows = calculateNumRows();
372bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
373bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Invalidate cached accessibility information.
374bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper.invalidateRoot();
375bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
376bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
377bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private static int getDaysInMonth(int month, int year) {
378bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        switch (month) {
379bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.JANUARY:
380bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.MARCH:
381bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.MAY:
382bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.JULY:
383bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.AUGUST:
384bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.OCTOBER:
385bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.DECEMBER:
386bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return 31;
387bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.APRIL:
388bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.JUNE:
389bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.SEPTEMBER:
390bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.NOVEMBER:
391bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return 30;
392bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            case Calendar.FEBRUARY:
393bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return (year % 4 == 0) ? 29 : 28;
394bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            default:
395bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                throw new IllegalArgumentException("Invalid Month");
396bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
397bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
398bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
399bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public void reuse() {
400bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mNumRows = DEFAULT_NUM_ROWS;
401bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        requestLayout();
402bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
403bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
404bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int calculateNumRows() {
405bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int offset = findDayOffset();
406bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int dividend = (offset + mNumCells) / mNumDays;
407bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int remainder = (offset + mNumCells) % mNumDays;
408bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return (dividend + (remainder > 0 ? 1 : 0));
409bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
410bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
411bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private boolean sameDay(int day, Time today) {
412bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return mYear == today.year &&
413bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                mMonth == today.month &&
414bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                day == today.monthDay;
415bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
416bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
417bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
418bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
419bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows
420bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                + mMonthHeaderSize);
421bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
422bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
423bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    @Override
424bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
425bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mWidth = w;
426bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
427bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Invalidate cached accessibility information.
428bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper.invalidateRoot();
429bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
430bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
431bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private String getMonthAndYearString() {
432bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
433bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                | DateUtils.FORMAT_NO_MONTH_DAY;
434bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mStringBuilder.setLength(0);
435bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        long millis = mCalendar.getTimeInMillis();
436bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
437bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                Time.getCurrentTimezone()).toString();
438bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
439bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
440bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private void drawMonthTitle(Canvas canvas) {
441518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette        final float x = (mWidth + 2 * mPadding) / 2f;
442518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette        final float y = (mMonthHeaderSize - mMonthDayLabelTextSize) / 2f;
443bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        canvas.drawText(getMonthAndYearString(), x, y, mMonthTitlePaint);
444bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
445bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
446bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private void drawWeekDayLabels(Canvas canvas) {
447fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette        final int y = mMonthHeaderSize - (mMonthDayLabelTextSize / 2);
448fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette        final int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
449bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
450bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        for (int i = 0; i < mNumDays; i++) {
451fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette            final int calendarDay = (i + mWeekStart) % mNumDays;
452bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
453fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette
454fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette            final String dayLabel = mDayFormatter.format(mDayLabelCalendar.getTime());
455fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette            final int x = (2 * i + 1) * dayWidthHalf + mPadding;
456fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette            canvas.drawText(dayLabel, x, y, mMonthDayLabelPaint);
457bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
458bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
459bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
460bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
461bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Draws the month days.
462bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
463bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private void drawDays(Canvas canvas) {
464bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int y = (((mRowHeight + mMiniDayNumberTextSize) / 2) - DAY_SEPARATOR_WIDTH)
465bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                + mMonthHeaderSize;
466bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
467bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int j = findDayOffset();
468bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        for (int day = 1; day <= mNumCells; day++) {
469bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            int x = (2 * j + 1) * dayWidthHalf + mPadding;
470bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (mSelectedDay == day) {
471bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                canvas.drawCircle(x, y - (mMiniDayNumberTextSize / 3), mDaySelectedCircleSize,
472bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                        mDayNumberSelectedPaint);
473bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
474bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
475bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (mHasToday && mToday == day) {
476bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                mDayNumberPaint.setColor(mSelectedDayColor);
477bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            } else {
478bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                mDayNumberPaint.setColor(mNormalTextColor);
479bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
480bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final Paint paint = (day < mEnabledDayStart || day > mEnabledDayEnd) ?
481bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                    mDayNumberDisabledPaint : mDayNumberPaint;
482bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            canvas.drawText(String.format("%d", day), x, y, paint);
483bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            j++;
484bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (j == mNumDays) {
485bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                j = 0;
486bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                y += mRowHeight;
487bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
488bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
489bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
490bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
491bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int findDayOffset() {
492bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
493bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                - mWeekStart;
494bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
495bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
496bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
497bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Calculates the day that the given x position is in, accounting for week
498bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * number. Returns the day or -1 if the position wasn't in a day.
499bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *
500bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * @param x The x position of the touch event
501bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * @return The day number, or -1 if the position wasn't in a day
502bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
503bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private int getDayFromLocation(float x, float y) {
504bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int dayStart = mPadding;
505bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        if (x < dayStart || x > mWidth - mPadding) {
506bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return -1;
507bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
508bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
509bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int row = (int) (y - mMonthHeaderSize) / mRowHeight;
510bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding));
511bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
512bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        int day = column - findDayOffset() + 1;
513bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        day += row * mNumDays;
514bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        if (day < 1 || day > mNumCells) {
515bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return -1;
516bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
517bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return day;
518bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
519bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
520bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
521bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Called when the user clicks on a day. Handles callbacks to the
522bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * {@link OnDayClickListener} if one is set.
523bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *
524bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * @param day The day that was clicked
525bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
526bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private void onDayClick(int day) {
527bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        if (mOnDayClickListener != null) {
528bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            Calendar date = Calendar.getInstance();
529bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            date.set(mYear, mMonth, day);
530bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            mOnDayClickListener.onDayClick(this, date);
531bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
532bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
533bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        // This is a no-op if accessibility is turned off.
534bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED);
535bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
536bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
537bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
538bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * @return The date that has accessibility focus, or {@code null} if no date
539bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *         has focus
540bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
541bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    Calendar getAccessibilityFocus() {
542bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        final int day = mTouchHelper.getFocusedVirtualView();
543bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        Calendar date = null;
544bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        if (day >= 0) {
545bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            date = Calendar.getInstance();
546bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            date.set(mYear, mMonth, day);
547bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
548bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return date;
549bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
550bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
551bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
552bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Clears accessibility focus within the view. No-op if the view does not
553bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * contain accessibility focus.
554bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
555bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public void clearAccessibilityFocus() {
556bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper.clearFocusedVirtualView();
557bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
558bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
559bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
560bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Attempts to restore accessibility focus to the specified date.
561bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *
562bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * @param day The date which should receive focus
563bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * @return {@code false} if the date is not valid for this month view, or
564bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     *         {@code true} if the date received focus
565bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
566bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    boolean restoreAccessibilityFocus(Calendar day) {
567bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        if ((day.get(Calendar.YEAR) != mYear) || (day.get(Calendar.MONTH) != mMonth) ||
568bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                (day.get(Calendar.DAY_OF_MONTH) > mNumCells)) {
569bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return false;
570bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
571bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        mTouchHelper.setFocusedVirtualView(day.get(Calendar.DAY_OF_MONTH));
572bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        return true;
573bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
574bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
575bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
576bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Provides a virtual view hierarchy for interfacing with an accessibility
577bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * service.
578bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
579bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    private class MonthViewTouchHelper extends ExploreByTouchHelper {
580bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        private static final String DATE_FORMAT = "dd MMMM yyyy";
581bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
582bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        private final Rect mTempRect = new Rect();
583bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        private final Calendar mTempCalendar = Calendar.getInstance();
584bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
585bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        public MonthViewTouchHelper(View host) {
586bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            super(host);
587bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
588bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
589bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        public void setFocusedVirtualView(int virtualViewId) {
590bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
591bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                    virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
592bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
593bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
594bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        public void clearFocusedVirtualView() {
595bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int focusedVirtualView = getFocusedVirtualView();
596bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) {
597bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
598bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                        focusedVirtualView,
599bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                        AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
600bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                        null);
601bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
602bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
603bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
604bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
605bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        protected int getVirtualViewAt(float x, float y) {
606bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int day = getDayFromLocation(x, y);
607bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (day >= 0) {
608bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return day;
609bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
610bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return ExploreByTouchHelper.INVALID_ID;
611bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
612bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
613bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
614ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette        protected void getVisibleVirtualViews(IntArray virtualViewIds) {
615bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            for (int day = 1; day <= mNumCells; day++) {
616bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                virtualViewIds.add(day);
617bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
618bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
619bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
620bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
621bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
622bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            event.setContentDescription(getItemDescription(virtualViewId));
623bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
624bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
625bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
626bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
627bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            getItemBounds(virtualViewId, mTempRect);
628bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
629bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            node.setContentDescription(getItemDescription(virtualViewId));
630bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            node.setBoundsInParent(mTempRect);
631bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
632bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
633bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (virtualViewId == mSelectedDay) {
634bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                node.setSelected(true);
635bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
636bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
637bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
638bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
639bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        @Override
640bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
641bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                Bundle arguments) {
642bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            switch (action) {
643bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                case AccessibilityNodeInfo.ACTION_CLICK:
644bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                    onDayClick(virtualViewId);
645bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                    return true;
646bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
647bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
648bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return false;
649bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
650bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
651bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        /**
652bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         * Calculates the bounding rectangle of a given time object.
653bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         *
654bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         * @param day The day to calculate bounds for
655bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         * @param rect The rectangle in which to store the bounds
656bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         */
657bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        private void getItemBounds(int day, Rect rect) {
658bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int offsetX = mPadding;
659bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int offsetY = mMonthHeaderSize;
660bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int cellHeight = mRowHeight;
661bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays);
662bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int index = ((day - 1) + findDayOffset());
663bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int row = (index / mNumDays);
664bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int column = (index % mNumDays);
665bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int x = (offsetX + (column * cellWidth));
666bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final int y = (offsetY + (row * cellHeight));
667bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
668bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            rect.set(x, y, (x + cellWidth), (y + cellHeight));
669bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
670bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
671bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        /**
672bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         * Generates a description for a given time object. Since this
673bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         * description will be spoken, the components are ordered by descending
674bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         * specificity as DAY MONTH YEAR.
675bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         *
676bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         * @param day The day to generate a description for
677bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         * @return A description of the time object
678bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio         */
679bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        private CharSequence getItemDescription(int day) {
680bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            mTempCalendar.set(mYear, mMonth, day);
681bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            final CharSequence date = DateFormat.format(DATE_FORMAT,
682bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                    mTempCalendar.getTimeInMillis());
683bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
684bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            if (day == mSelectedDay) {
685bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio                return getContext().getString(R.string.item_is_selected, date);
686bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            }
687bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
688bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio            return date;
689bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        }
690bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
691bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio
692bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    /**
693bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     * Handles callbacks when the user clicks on a time object.
694bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio     */
695bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    public interface OnDayClickListener {
696bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        public void onDayClick(SimpleMonthView view, Calendar day);
697bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio    }
698bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio}
699