MonthView.java revision e0a0cb288106e3a25441ea57a123a812929ec79c
1e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung/* 2e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Copyright (C) 2013 The Android Open Source Project 3e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 4e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Licensed under the Apache License, Version 2.0 (the "License"); 5e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * you may not use this file except in compliance with the License. 6e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * You may obtain a copy of the License at 7e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 8e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * http://www.apache.org/licenses/LICENSE-2.0 9e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 10e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Unless required by applicable law or agreed to in writing, software 11e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * distributed under the License is distributed on an "AS IS" BASIS, 12e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * See the License for the specific language governing permissions and 14e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * limitations under the License. 15e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 16e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 17e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungpackage com.android.datetimepicker.date; 18e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 19e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.content.Context; 20e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.content.res.Resources; 21e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.graphics.Canvas; 22e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.graphics.Paint; 23e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.graphics.Paint.Align; 24e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.graphics.Paint.Style; 25e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.graphics.Rect; 26e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.graphics.Typeface; 27e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.os.Bundle; 28e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.support.v4.view.ViewCompat; 29e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 30e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.support.v4.widget.ExploreByTouchHelper; 31e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.text.format.DateFormat; 32e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.text.format.DateUtils; 33e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.text.format.Time; 34e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.MotionEvent; 35e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.View; 36e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.accessibility.AccessibilityEvent; 37e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.accessibility.AccessibilityNodeInfo; 38e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 39e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport com.android.datetimepicker.R; 40e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport com.android.datetimepicker.Utils; 41e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport com.android.datetimepicker.date.MonthAdapter.CalendarDay; 42e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 43e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.security.InvalidParameterException; 44e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.Calendar; 45e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.Formatter; 46e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.HashMap; 47e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.List; 48e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.Locale; 49e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 50e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung/** 51e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * A calendar-like view displaying a specified month and the appropriate selectable day numbers 52e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * within the specified month. 53e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 54e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungpublic abstract class MonthView extends View { 55e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private static final String TAG = "MonthView"; 56e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 57e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 58e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * These params can be passed into the view to control how it appears. 59e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #VIEW_PARAMS_WEEK} is the only required field, though the default 60e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * values are unlikely to fit most layouts correctly. 61e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 62e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 63e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This sets the height of this week in pixels 64e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 65e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_HEIGHT = "height"; 66e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 67e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This specifies the position (or weeks since the epoch) of this week, 68e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * calculated using {@link Utils#getWeeksSinceEpochFromJulianDay} 69e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 70e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_MONTH = "month"; 71e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 72e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This specifies the position (or weeks since the epoch) of this week, 73e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * calculated using {@link Utils#getWeeksSinceEpochFromJulianDay} 74e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 75e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_YEAR = "year"; 76e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 77e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This sets one of the days in this view as selected {@link Time#SUNDAY} 78e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * through {@link Time#SATURDAY}. 79e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 80e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day"; 81e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 82e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Which day the week should start on. {@link Time#SUNDAY} through 83e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link Time#SATURDAY}. 84e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 85e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_WEEK_START = "week_start"; 86e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 87e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * How many days to display at a time. Days will be displayed starting with 88e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #mWeekStart}. 89e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 90e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_NUM_DAYS = "num_days"; 91e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 92e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Which month is currently in focus, as defined by {@link Time#month} 93e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * [0-11]. 94e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 95e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month"; 96e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 97e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * If this month should display week numbers. false if 0, true otherwise. 98e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 99e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num"; 100e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 101e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int DEFAULT_HEIGHT = 32; 102e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MIN_HEIGHT = 10; 103e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_SELECTED_DAY = -1; 104e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_WEEK_START = Calendar.SUNDAY; 105e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_NUM_DAYS = 7; 106e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_SHOW_WK_NUM = 0; 107e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_FOCUS_MONTH = -1; 108e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_NUM_ROWS = 6; 109e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int MAX_NUM_ROWS = 6; 110e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 111e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private static final int SELECTED_CIRCLE_ALPHA = 60; 112e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 113e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int DAY_SEPARATOR_WIDTH = 1; 114e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MINI_DAY_NUMBER_TEXT_SIZE; 115e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MONTH_LABEL_TEXT_SIZE; 116e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MONTH_DAY_LABEL_TEXT_SIZE; 117e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MONTH_HEADER_SIZE; 118e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int DAY_SELECTED_CIRCLE_SIZE; 119e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 120e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // used for scaling to the device density 121e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static float mScale = 0; 122e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 1237c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein protected final DatePickerController mController; 1247c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 125e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // affects the padding on the sides of this view 126e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mPadding = 0; 127e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 128e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String mDayOfWeekTypeface; 129e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String mMonthTitleTypeface; 130e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 131e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthNumPaint; 132e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthTitlePaint; 133e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthTitleBGPaint; 134e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mSelectedCirclePaint; 135e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthDayLabelPaint; 136e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 137e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Formatter mFormatter; 138e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final StringBuilder mStringBuilder; 139e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 140e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The Julian day of the first day displayed by this item 141e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mFirstJulianDay = -1; 142e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The month of the first day in this week 143e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mFirstMonth = -1; 144e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The month of the last day in this week 145e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mLastMonth = -1; 146e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 147e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonth; 148e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 149e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mYear; 150e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Quick reference to the width of this view, matches parent 151e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mWidth; 152e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The height this view should draw at in pixels, set by height param 153e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mRowHeight = DEFAULT_HEIGHT; 154e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // If this view contains the today 155e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected boolean mHasToday = false; 156e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day is selected [0-6] or -1 if no day is selected 157e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedDay = -1; 158e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day is today [0-6] or -1 if no day is today 159e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mToday = DEFAULT_SELECTED_DAY; 160e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day of the week to start on [0-6] 161e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mWeekStart = DEFAULT_WEEK_START; 162e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // How many days to display 163e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mNumDays = DEFAULT_NUM_DAYS; 164e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The number of days + a spot for week number if it is displayed 165e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mNumCells = mNumDays; 166e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The left edge of the selected day 167e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedLeft = -1; 168e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The right edge of the selected day 169e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedRight = -1; 170e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 171e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Calendar mCalendar; 1726286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected final Calendar mDayLabelCalendar; 173e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final MonthViewTouchHelper mTouchHelper; 174e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 175e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int mNumRows = DEFAULT_NUM_ROWS; 176e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 177e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Optional listener for handling day click actions 178e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private OnDayClickListener mOnDayClickListener; 179e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Whether to prevent setting the accessibility delegate 180e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private boolean mLockAccessibilityDelegate; 181e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 182e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mDayTextColor; 183e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mTodayNumberColor; 1847c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein protected int mDisabledDayTextColor; 185e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonthTitleColor; 186e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonthTitleBGColor; 187e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 188e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public MonthView(Context context) { 1897c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein this(context, null); 1907c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 1917c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 1927c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein public MonthView(Context context, DatePickerController controller) { 193e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung super(context); 194e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 1957c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein mController = controller; 1967c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 197e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Resources res = context.getResources(); 198e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 199e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayLabelCalendar = Calendar.getInstance(); 200e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar = Calendar.getInstance(); 201e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 202e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface); 203e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleTypeface = res.getString(R.string.sans_serif); 204e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 205e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayTextColor = res.getColor(R.color.date_picker_text_normal); 206e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTodayNumberColor = res.getColor(R.color.blue); 2077c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein mDisabledDayTextColor = res.getColor(R.color.date_picker_text_disabled); 208e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleColor = res.getColor(R.color.white); 209e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGColor = res.getColor(R.color.circle_background); 210e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 211e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mStringBuilder = new StringBuilder(50); 212e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); 213e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 214e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MINI_DAY_NUMBER_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.day_number_size); 215e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_LABEL_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.month_label_size); 216e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_DAY_LABEL_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.month_day_label_text_size); 217e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_HEADER_SIZE = res.getDimensionPixelOffset(R.dimen.month_list_item_header_height); 218e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung DAY_SELECTED_CIRCLE_SIZE = res 219e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung .getDimensionPixelSize(R.dimen.day_number_select_circle_radius); 220e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 221e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = (res.getDimensionPixelOffset(R.dimen.date_picker_view_animator_height) 222e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung - MONTH_HEADER_SIZE) / MAX_NUM_ROWS; 223e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 224e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Set up accessibility components. 225529542558544a0e6d78424e33c60934adc143804Ruben mTouchHelper = getMonthViewTouchHelper(); 226e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung ViewCompat.setAccessibilityDelegate(this, mTouchHelper); 227e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); 228e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mLockAccessibilityDelegate = true; 229e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 230e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Sets up any standard paints that will be used 231e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung initView(); 232e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 233e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 234529542558544a0e6d78424e33c60934adc143804Ruben protected MonthViewTouchHelper getMonthViewTouchHelper() { 235529542558544a0e6d78424e33c60934adc143804Ruben return new MonthViewTouchHelper(this); 236529542558544a0e6d78424e33c60934adc143804Ruben } 237529542558544a0e6d78424e33c60934adc143804Ruben 238e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 239e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setAccessibilityDelegate(AccessibilityDelegate delegate) { 240e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Workaround for a JB MR1 issue where accessibility delegates on 241e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // top-level ListView items are overwritten. 242e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (!mLockAccessibilityDelegate) { 243e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung super.setAccessibilityDelegate(delegate); 244e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 245e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 246e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 247e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setOnDayClickListener(OnDayClickListener listener) { 248e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mOnDayClickListener = listener; 249e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 250e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 251e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 252e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean dispatchHoverEvent(MotionEvent event) { 253e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // First right-of-refusal goes the touch exploration helper. 254e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mTouchHelper.dispatchHoverEvent(event)) { 255e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 256e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 257e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return super.dispatchHoverEvent(event); 258e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 259e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 260e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 261e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean onTouchEvent(MotionEvent event) { 262e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung switch (event.getAction()) { 263e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung case MotionEvent.ACTION_UP: 264e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = getDayFromLocation(event.getX(), event.getY()); 265e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 266e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung onDayClick(day); 267e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 268e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung break; 269e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 270e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 271e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 272e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 273e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 274e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Sets up the text and style properties for painting. Override this if you 275e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * want to use a different paint. 276e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 277e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void initView() { 278e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint = new Paint(); 279e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setFakeBoldText(true); 280e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setAntiAlias(true); 281e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTextSize(MONTH_LABEL_TEXT_SIZE); 282e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface, Typeface.BOLD)); 283e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setColor(mDayTextColor); 284e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTextAlign(Align.CENTER); 285e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setStyle(Style.FILL); 286e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 287e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint = new Paint(); 288e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setFakeBoldText(true); 289e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setAntiAlias(true); 290e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setColor(mMonthTitleBGColor); 291e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setTextAlign(Align.CENTER); 292e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setStyle(Style.FILL); 293e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 294e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint = new Paint(); 295e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setFakeBoldText(true); 296e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setAntiAlias(true); 297e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setColor(mTodayNumberColor); 298e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setTextAlign(Align.CENTER); 299e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setStyle(Style.FILL); 300e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setAlpha(SELECTED_CIRCLE_ALPHA); 301e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 302e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint = new Paint(); 303e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setAntiAlias(true); 304e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTextSize(MONTH_DAY_LABEL_TEXT_SIZE); 305e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setColor(mDayTextColor); 306e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.NORMAL)); 307e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setStyle(Style.FILL); 308e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTextAlign(Align.CENTER); 309e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setFakeBoldText(true); 310e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 311e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint = new Paint(); 312e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setAntiAlias(true); 313e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); 314e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setStyle(Style.FILL); 315e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setTextAlign(Align.CENTER); 316e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setFakeBoldText(false); 317e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 318e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 319e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 320e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onDraw(Canvas canvas) { 321e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthTitle(canvas); 322e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthDayLabels(canvas); 323e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthNums(canvas); 324e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 325e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 326e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int mDayOfWeekStart = 0; 327e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 328e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 329e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Sets all the parameters for displaying this week. The only required 330e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * parameter is the week number. Other parameters have a default value and 331e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * will only update if a new value is included, except for focus month, 332e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * which will always default to no focus month if no value is passed in. See 333e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters. 334e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 335e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param params A map of the new parameters, see 336e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #VIEW_PARAMS_HEIGHT} 337e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 338e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setMonthParams(HashMap<String, Integer> params) { 339e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (!params.containsKey(VIEW_PARAMS_MONTH) && !params.containsKey(VIEW_PARAMS_YEAR)) { 340e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung throw new InvalidParameterException("You must specify month and year for this view"); 341e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 342e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung setTag(params); 343e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // We keep the current value for any params not present 344e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_HEIGHT)) { 345e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = params.get(VIEW_PARAMS_HEIGHT); 346e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mRowHeight < MIN_HEIGHT) { 347e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = MIN_HEIGHT; 348e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 349e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 350e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { 351e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY); 352e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 353e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 354e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Allocate space for caching the day numbers and focus values 355e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonth = params.get(VIEW_PARAMS_MONTH); 356e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mYear = params.get(VIEW_PARAMS_YEAR); 357e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 358e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Figure out what day today is 359e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final Time today = new Time(Time.getCurrentTimezone()); 360e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung today.setToNow(); 361e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mHasToday = false; 362e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mToday = -1; 363e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 364e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.MONTH, mMonth); 365e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.YEAR, mYear); 366e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.DAY_OF_MONTH, 1); 367e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK); 368e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 369e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_WEEK_START)) { 370e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWeekStart = params.get(VIEW_PARAMS_WEEK_START); 371e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } else { 372e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWeekStart = mCalendar.getFirstDayOfWeek(); 373e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 374e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 375e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumCells = Utils.getDaysInMonth(mMonth, mYear); 376e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int i = 0; i < mNumCells; i++) { 377e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = i + 1; 378e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (sameDay(day, today)) { 379e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mHasToday = true; 380e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mToday = day; 381e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 382e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 383e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumRows = calculateNumRows(); 384e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 385e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Invalidate cached accessibility information. 386e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.invalidateRoot(); 387e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 388e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 3895cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein public void setSelectedDay(int day) { 3905cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein mSelectedDay = day; 3915cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein } 3925cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein 393e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void reuse() { 394e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumRows = DEFAULT_NUM_ROWS; 395e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung requestLayout(); 396e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 397e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 398e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int calculateNumRows() { 399e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int offset = findDayOffset(); 400e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dividend = (offset + mNumCells) / mNumDays; 401e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int remainder = (offset + mNumCells) % mNumDays; 402e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return (dividend + (remainder > 0 ? 1 : 0)); 403e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 404e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 405e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private boolean sameDay(int day, Time today) { 406e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return mYear == today.year && 407e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonth == today.month && 408e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung day == today.monthDay; 409e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 410e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 411e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 412e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 413e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows 414e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung + MONTH_HEADER_SIZE); 415e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 416e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 417e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 418e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onSizeChanged(int w, int h, int oldw, int oldh) { 419e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWidth = w; 420e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 421e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Invalidate cached accessibility information. 422e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.invalidateRoot(); 423e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 424e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 4255cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein public int getMonth() { 4265cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein return mMonth; 4275cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein } 4285cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein 4295cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein public int getYear() { 4305cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein return mYear; 4315cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein } 4325cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein 433e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String getMonthAndYearString() { 434e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR 435e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung | DateUtils.FORMAT_NO_MONTH_DAY; 436e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mStringBuilder.setLength(0); 437e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung long millis = mCalendar.getTimeInMillis(); 438e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags, 439e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Time.getCurrentTimezone()).toString(); 440e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 441e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 4426286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected void drawMonthTitle(Canvas canvas) { 443e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x = (mWidth + 2 * mPadding) / 2; 444e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int y = (MONTH_HEADER_SIZE - MONTH_DAY_LABEL_TEXT_SIZE) / 2 + (MONTH_LABEL_TEXT_SIZE / 3); 445e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung canvas.drawText(getMonthAndYearString(), x, y, mMonthTitlePaint); 446e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 447e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 4486286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected void drawMonthDayLabels(Canvas canvas) { 449e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int y = MONTH_HEADER_SIZE - (MONTH_DAY_LABEL_TEXT_SIZE / 2); 450e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2); 451e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 452e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int i = 0; i < mNumDays; i++) { 453e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int calendarDay = (i + mWeekStart) % mNumDays; 454e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x = (2 * i + 1) * dayWidthHalf + mPadding; 455e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay); 456e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung canvas.drawText(mDayLabelCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, 457e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Locale.getDefault()).toUpperCase(Locale.getDefault()), x, y, 458e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint); 459e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 460e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 461e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 462e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 463e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Draws the week and month day numbers for this week. Override this method 464e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * if you need different placement. 465e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 466e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param canvas The canvas to draw on 467e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 468e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void drawMonthNums(Canvas canvas) { 469e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int y = (((mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2) - DAY_SEPARATOR_WIDTH) 470e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung + MONTH_HEADER_SIZE; 471e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2); 472e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int j = findDayOffset(); 473e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int dayNumber = 1; dayNumber <= mNumCells; dayNumber++) { 474e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x = (2 * j + 1) * dayWidthHalf + mPadding; 475e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 476e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int yRelativeToDay = (mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH; 477e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 478e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int startX = x - dayWidthHalf; 479e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int stopX = x + dayWidthHalf; 480e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int startY = y - yRelativeToDay; 481e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int stopY = startY + mRowHeight; 482e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 483e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthDay(canvas, mYear, mMonth, dayNumber, x, y, startX, stopX, startY, stopY); 484e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 485e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung j++; 486e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (j == mNumDays) { 487e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung j = 0; 488e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung y += mRowHeight; 489e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 490e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 491e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 492e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 493e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 494e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This method should draw the month day. Implemented by sub-classes to allow customization. 495e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 496e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param canvas The canvas to draw on 497e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param year The year of this month day 498e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param month The month of this month day 499e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day number of this month day 500e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param x The default x position to draw the day number 501e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param y The default y position to draw the day number 502e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param startX The left boundary of the day number rect 503e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param stopX The right boundary of the day number rect 504e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param startY The top boundary of the day number rect 505e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param stopY The bottom boundary of the day number rect 506e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 507e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public abstract void drawMonthDay(Canvas canvas, int year, int month, int day, 508e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x, int y, int startX, int stopX, int startY, int stopY); 509e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 5106286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected int findDayOffset() { 511e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart) 512e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung - mWeekStart; 513e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 514e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 515e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 516e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 517e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Calculates the day that the given x position is in, accounting for week 518e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * number. Returns the day or -1 if the position wasn't in a day. 519e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 520e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param x The x position of the touch event 521e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return The day number, or -1 if the position wasn't in a day 522e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 523e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public int getDayFromLocation(float x, float y) { 524e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dayStart = mPadding; 525e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (x < dayStart || x > mWidth - mPadding) { 526e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return -1; 527e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 528e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels 529e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int row = (int) (y - MONTH_HEADER_SIZE) / mRowHeight; 530e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)); 531e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 532e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int day = column - findDayOffset() + 1; 533e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung day += row * mNumDays; 534e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day < 1 || day > mNumCells) { 535e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return -1; 536e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 537e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return day; 538e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 539e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 540e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 541e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Called when the user clicks on a day. Handles callbacks to the 542e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link OnDayClickListener} if one is set. 5437c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * <p/> 5447c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * If the day is out of the range set by minDate and/or maxDate, this is a no-op. 545e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 546e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day that was clicked 547e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 548e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private void onDayClick(int day) { 5497c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein // If the min / max date are set, only process the click if it's a valid selection. 5507c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (isOutOfRange(mYear, mMonth, day)) { 5517c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return; 5527c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5537c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 5547c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 555e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mOnDayClickListener != null) { 556e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mOnDayClickListener.onDayClick(this, new CalendarDay(mYear, mMonth, day)); 557e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 558e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 559e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // This is a no-op if accessibility is turned off. 560e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED); 561e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 562e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 563e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 5647c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * @return true if the specified year/month/day are within the range set by minDate and maxDate. 5657c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * If one or either have not been set, they are considered as Integer.MIN_VALUE and 5667c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * Integer.MAX_VALUE. 5677c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein */ 5687c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein protected boolean isOutOfRange(int year, int month, int day) { 5697c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (isBeforeMin(year, month, day)) { 5707c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 5717c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (isAfterMax(year, month, day)) { 5727c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 5737c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5747c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 5757c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 5767c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5777c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 5787c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein private boolean isBeforeMin(int year, int month, int day) { 579e0a0cb288106e3a25441ea57a123a812929ec79cRuben if (mController == null) { 580e0a0cb288106e3a25441ea57a123a812929ec79cRuben return false; 581e0a0cb288106e3a25441ea57a123a812929ec79cRuben } 5827c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein Calendar minDate = mController.getMinDate(); 5837c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (minDate == null) { 5847c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 5857c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5867c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 5877c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (year < minDate.get(Calendar.YEAR)) { 5887c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 5897c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (year > minDate.get(Calendar.YEAR)) { 5907c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 5917c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5927c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 5937c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (month < minDate.get(Calendar.MONTH)) { 5947c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 5957c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (month > minDate.get(Calendar.MONTH)) { 5967c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 5977c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5987c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 5997c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (day < minDate.get(Calendar.DAY_OF_MONTH)) { 6007c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6017c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else { 6027c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6037c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6047c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6057c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6067c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein private boolean isAfterMax(int year, int month, int day) { 607e0a0cb288106e3a25441ea57a123a812929ec79cRuben if (mController == null) { 608e0a0cb288106e3a25441ea57a123a812929ec79cRuben return false; 609e0a0cb288106e3a25441ea57a123a812929ec79cRuben } 6107c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein Calendar maxDate = mController.getMaxDate(); 6117c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (maxDate == null) { 6127c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6137c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6147c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6157c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (year > maxDate.get(Calendar.YEAR)) { 6167c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6177c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (year < maxDate.get(Calendar.YEAR)) { 6187c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6197c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6207c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6217c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (month > maxDate.get(Calendar.MONTH)) { 6227c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6237c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (month < maxDate.get(Calendar.MONTH)) { 6247c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6257c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6267c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6277c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (day > maxDate.get(Calendar.DAY_OF_MONTH)) { 6287c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6297c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else { 6307c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6317c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6327c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6337c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6347c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein /** 635e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return The date that has accessibility focus, or {@code null} if no date 636e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * has focus 637e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 638e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public CalendarDay getAccessibilityFocus() { 639e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = mTouchHelper.getFocusedVirtualView(); 640e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 641e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return new CalendarDay(mYear, mMonth, day); 642e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 643e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return null; 644e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 645e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 646e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 647e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Clears accessibility focus within the view. No-op if the view does not 648e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * contain accessibility focus. 649e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 650e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void clearAccessibilityFocus() { 651e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.clearFocusedVirtualView(); 652e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 653e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 654e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 655e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Attempts to restore accessibility focus to the specified date. 656e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 657e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The date which should receive focus 658e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return {@code false} if the date is not valid for this month view, or 659e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@code true} if the date received focus 660e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 661e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean restoreAccessibilityFocus(CalendarDay day) { 662e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if ((day.year != mYear) || (day.month != mMonth) || (day.day > mNumCells)) { 663e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return false; 664e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 665e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.setFocusedVirtualView(day.day); 666e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 667e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 668e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 669e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 670e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Provides a virtual view hierarchy for interfacing with an accessibility 671e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * service. 672e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 673529542558544a0e6d78424e33c60934adc143804Ruben protected class MonthViewTouchHelper extends ExploreByTouchHelper { 674e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private static final String DATE_FORMAT = "dd MMMM yyyy"; 675e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 676e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Rect mTempRect = new Rect(); 677e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Calendar mTempCalendar = Calendar.getInstance(); 678e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 679e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public MonthViewTouchHelper(View host) { 680e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung super(host); 681e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 682e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 683e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setFocusedVirtualView(int virtualViewId) { 684e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getAccessibilityNodeProvider(MonthView.this).performAction( 685e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung virtualViewId, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null); 686e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 687e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 688e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void clearFocusedVirtualView() { 689e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int focusedVirtualView = getFocusedVirtualView(); 690e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) { 691e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getAccessibilityNodeProvider(MonthView.this).performAction( 692e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung focusedVirtualView, 693e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, 694e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung null); 695e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 696e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 697e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 698e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 699e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int getVirtualViewAt(float x, float y) { 700e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = getDayFromLocation(x, y); 701e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 702e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return day; 703e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 704e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return ExploreByTouchHelper.INVALID_ID; 705e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 706e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 707e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 708e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { 709e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int day = 1; day <= mNumCells; day++) { 710e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung virtualViewIds.add(day); 711e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 712e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 713e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 714e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 715e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { 716e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung event.setContentDescription(getItemDescription(virtualViewId)); 717e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 718e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 719e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 720e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onPopulateNodeForVirtualView(int virtualViewId, 721e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung AccessibilityNodeInfoCompat node) { 722e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getItemBounds(virtualViewId, mTempRect); 723e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 724e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setContentDescription(getItemDescription(virtualViewId)); 725e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setBoundsInParent(mTempRect); 726e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.addAction(AccessibilityNodeInfo.ACTION_CLICK); 727e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 728e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (virtualViewId == mSelectedDay) { 729e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setSelected(true); 730e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 731e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 732e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 733e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 734e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 735e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected boolean onPerformActionForVirtualView(int virtualViewId, int action, 736e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Bundle arguments) { 737e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung switch (action) { 738e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung case AccessibilityNodeInfo.ACTION_CLICK: 739e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung onDayClick(virtualViewId); 740e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 741e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 742e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 743e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return false; 744e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 745e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 746e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 747e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Calculates the bounding rectangle of a given time object. 748e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 749e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day to calculate bounds for 750e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param rect The rectangle in which to store the bounds 751e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 7526286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected void getItemBounds(int day, Rect rect) { 753e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int offsetX = mPadding; 754e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int offsetY = MONTH_HEADER_SIZE; 755e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int cellHeight = mRowHeight; 756e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays); 757e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int index = ((day - 1) + findDayOffset()); 758e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int row = (index / mNumDays); 759e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int column = (index % mNumDays); 760e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int x = (offsetX + (column * cellWidth)); 761e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int y = (offsetY + (row * cellHeight)); 762e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 763e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung rect.set(x, y, (x + cellWidth), (y + cellHeight)); 764e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 765e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 766e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 767e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Generates a description for a given time object. Since this 768e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * description will be spoken, the components are ordered by descending 769e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * specificity as DAY MONTH YEAR. 770e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 771e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day to generate a description for 772e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return A description of the time object 773e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 774529542558544a0e6d78424e33c60934adc143804Ruben protected CharSequence getItemDescription(int day) { 775e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTempCalendar.set(mYear, mMonth, day); 776e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final CharSequence date = DateFormat.format(DATE_FORMAT, 777e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTempCalendar.getTimeInMillis()); 778e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 779e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day == mSelectedDay) { 780e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return getContext().getString(R.string.item_is_selected, date); 781e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 782e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 783e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return date; 784e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 785e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 786e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 787e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 788e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Handles callbacks when the user clicks on a time object. 789e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 790e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public interface OnDayClickListener { 791e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void onDayClick(MonthView view, CalendarDay day); 792e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 793e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung} 794