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; 34fc0e80c929547bba62f93d609c83fe6d6bfbfd34Rubenimport android.util.AttributeSet; 35e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.MotionEvent; 36e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.View; 37e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.accessibility.AccessibilityEvent; 38e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.accessibility.AccessibilityNodeInfo; 39e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 40e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport com.android.datetimepicker.R; 41e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport com.android.datetimepicker.Utils; 42e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport com.android.datetimepicker.date.MonthAdapter.CalendarDay; 43e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 44e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.security.InvalidParameterException; 45e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.Calendar; 46e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.Formatter; 47e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.HashMap; 48e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.List; 49e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.Locale; 50e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 51e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung/** 52e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * A calendar-like view displaying a specified month and the appropriate selectable day numbers 53e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * within the specified month. 54e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 55e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungpublic abstract class MonthView extends View { 56e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private static final String TAG = "MonthView"; 57e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 58e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 59e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * These params can be passed into the view to control how it appears. 60e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #VIEW_PARAMS_WEEK} is the only required field, though the default 61e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * values are unlikely to fit most layouts correctly. 62e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 63e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 64e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This sets the height of this week in pixels 65e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 66e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_HEIGHT = "height"; 67e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 68e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This specifies the position (or weeks since the epoch) of this week, 69e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * calculated using {@link Utils#getWeeksSinceEpochFromJulianDay} 70e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 71e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_MONTH = "month"; 72e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 73e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This specifies the position (or weeks since the epoch) of this week, 74e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * calculated using {@link Utils#getWeeksSinceEpochFromJulianDay} 75e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 76e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_YEAR = "year"; 77e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 78e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This sets one of the days in this view as selected {@link Time#SUNDAY} 79e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * through {@link Time#SATURDAY}. 80e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 81e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day"; 82e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 83e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Which day the week should start on. {@link Time#SUNDAY} through 84e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link Time#SATURDAY}. 85e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 86e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_WEEK_START = "week_start"; 87e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 88e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * How many days to display at a time. Days will be displayed starting with 89e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #mWeekStart}. 90e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 91e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_NUM_DAYS = "num_days"; 92e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 93e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Which month is currently in focus, as defined by {@link Time#month} 94e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * [0-11]. 95e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 96e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month"; 97e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 98e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * If this month should display week numbers. false if 0, true otherwise. 99e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 100e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num"; 101e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 102e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int DEFAULT_HEIGHT = 32; 103e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MIN_HEIGHT = 10; 104e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_SELECTED_DAY = -1; 105e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_WEEK_START = Calendar.SUNDAY; 106e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_NUM_DAYS = 7; 107e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_SHOW_WK_NUM = 0; 108e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_FOCUS_MONTH = -1; 109e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int DEFAULT_NUM_ROWS = 6; 110e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static final int MAX_NUM_ROWS = 6; 111e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 112e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private static final int SELECTED_CIRCLE_ALPHA = 60; 113e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 114e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int DAY_SEPARATOR_WIDTH = 1; 115e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MINI_DAY_NUMBER_TEXT_SIZE; 116e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MONTH_LABEL_TEXT_SIZE; 117e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MONTH_DAY_LABEL_TEXT_SIZE; 118e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int MONTH_HEADER_SIZE; 119e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static int DAY_SELECTED_CIRCLE_SIZE; 120e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 121e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // used for scaling to the device density 122e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected static float mScale = 0; 123e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 124fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben protected DatePickerController mController; 1257c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 126e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // affects the padding on the sides of this view 127d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein protected int mEdgePadding = 0; 128e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 129e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String mDayOfWeekTypeface; 130e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String mMonthTitleTypeface; 131e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 132e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthNumPaint; 133e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthTitlePaint; 134e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthTitleBGPaint; 135e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mSelectedCirclePaint; 136e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthDayLabelPaint; 137e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 138e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Formatter mFormatter; 139e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final StringBuilder mStringBuilder; 140e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 141e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The Julian day of the first day displayed by this item 142e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mFirstJulianDay = -1; 143e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The month of the first day in this week 144e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mFirstMonth = -1; 145e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The month of the last day in this week 146e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mLastMonth = -1; 147e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 148e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonth; 149e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 150e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mYear; 151e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Quick reference to the width of this view, matches parent 152e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mWidth; 153e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The height this view should draw at in pixels, set by height param 154e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mRowHeight = DEFAULT_HEIGHT; 155e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // If this view contains the today 156e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected boolean mHasToday = false; 157e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day is selected [0-6] or -1 if no day is selected 158e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedDay = -1; 159e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day is today [0-6] or -1 if no day is today 160e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mToday = DEFAULT_SELECTED_DAY; 161e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day of the week to start on [0-6] 162e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mWeekStart = DEFAULT_WEEK_START; 163e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // How many days to display 164e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mNumDays = DEFAULT_NUM_DAYS; 165e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The number of days + a spot for week number if it is displayed 166e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mNumCells = mNumDays; 167e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The left edge of the selected day 168e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedLeft = -1; 169e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The right edge of the selected day 170e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedRight = -1; 171e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 172e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Calendar mCalendar; 1736286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected final Calendar mDayLabelCalendar; 174e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final MonthViewTouchHelper mTouchHelper; 175e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 176d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein protected int mNumRows = DEFAULT_NUM_ROWS; 177e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 178e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Optional listener for handling day click actions 17959d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben protected OnDayClickListener mOnDayClickListener; 18059d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben 181e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Whether to prevent setting the accessibility delegate 182e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private boolean mLockAccessibilityDelegate; 183e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 184e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mDayTextColor; 185e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mTodayNumberColor; 1867c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein protected int mDisabledDayTextColor; 187e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonthTitleColor; 188e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonthTitleBGColor; 189e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 190e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public MonthView(Context context) { 1917c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein this(context, null); 1927c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 1937c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 194fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben public MonthView(Context context, AttributeSet attr) { 195fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben super(context, attr); 196e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Resources res = context.getResources(); 197e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 198e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayLabelCalendar = Calendar.getInstance(); 199e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar = Calendar.getInstance(); 200e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 201e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface); 202e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleTypeface = res.getString(R.string.sans_serif); 203e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 204e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayTextColor = res.getColor(R.color.date_picker_text_normal); 205e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTodayNumberColor = res.getColor(R.color.blue); 2067c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein mDisabledDayTextColor = res.getColor(R.color.date_picker_text_disabled); 207a09b3c940e98b8606a16a94b48b6d0121d9d3635Scott Kennedy mMonthTitleColor = res.getColor(android.R.color.white); 208e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGColor = res.getColor(R.color.circle_background); 209e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 210e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mStringBuilder = new StringBuilder(50); 211e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); 212e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 213e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MINI_DAY_NUMBER_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.day_number_size); 214e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_LABEL_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.month_label_size); 215e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_DAY_LABEL_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.month_day_label_text_size); 216e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_HEADER_SIZE = res.getDimensionPixelOffset(R.dimen.month_list_item_header_height); 217e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung DAY_SELECTED_CIRCLE_SIZE = res 218e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung .getDimensionPixelSize(R.dimen.day_number_select_circle_radius); 219e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 220e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = (res.getDimensionPixelOffset(R.dimen.date_picker_view_animator_height) 221fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben - getMonthHeaderSize()) / MAX_NUM_ROWS; 222e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 223e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Set up accessibility components. 224529542558544a0e6d78424e33c60934adc143804Ruben mTouchHelper = getMonthViewTouchHelper(); 225e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung ViewCompat.setAccessibilityDelegate(this, mTouchHelper); 226e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); 227e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mLockAccessibilityDelegate = true; 228e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 229e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Sets up any standard paints that will be used 230e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung initView(); 231e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 232e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 233fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben public void setDatePickerController(DatePickerController controller) { 234fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben mController = controller; 235fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben } 236fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben 237529542558544a0e6d78424e33c60934adc143804Ruben protected MonthViewTouchHelper getMonthViewTouchHelper() { 238529542558544a0e6d78424e33c60934adc143804Ruben return new MonthViewTouchHelper(this); 239529542558544a0e6d78424e33c60934adc143804Ruben } 240529542558544a0e6d78424e33c60934adc143804Ruben 241e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 242e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setAccessibilityDelegate(AccessibilityDelegate delegate) { 243e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Workaround for a JB MR1 issue where accessibility delegates on 244e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // top-level ListView items are overwritten. 245e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (!mLockAccessibilityDelegate) { 246e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung super.setAccessibilityDelegate(delegate); 247e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 248e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 249e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 250e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setOnDayClickListener(OnDayClickListener listener) { 251e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mOnDayClickListener = listener; 252e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 253e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 254e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 255e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean dispatchHoverEvent(MotionEvent event) { 256e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // First right-of-refusal goes the touch exploration helper. 257e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mTouchHelper.dispatchHoverEvent(event)) { 258e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 259e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 260e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return super.dispatchHoverEvent(event); 261e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 262e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 263e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 264e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean onTouchEvent(MotionEvent event) { 265e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung switch (event.getAction()) { 266e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung case MotionEvent.ACTION_UP: 267e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = getDayFromLocation(event.getX(), event.getY()); 268e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 269e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung onDayClick(day); 270e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 271e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung break; 272e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 273e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 274e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 275e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 276e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 277e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Sets up the text and style properties for painting. Override this if you 278e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * want to use a different paint. 279e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 280e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void initView() { 281e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint = new Paint(); 282e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setFakeBoldText(true); 283e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setAntiAlias(true); 284e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTextSize(MONTH_LABEL_TEXT_SIZE); 285e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface, Typeface.BOLD)); 286e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setColor(mDayTextColor); 287e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTextAlign(Align.CENTER); 288e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setStyle(Style.FILL); 289e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 290e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint = new Paint(); 291e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setFakeBoldText(true); 292e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setAntiAlias(true); 293e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setColor(mMonthTitleBGColor); 294e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setTextAlign(Align.CENTER); 295e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setStyle(Style.FILL); 296e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 297e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint = new Paint(); 298e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setFakeBoldText(true); 299e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setAntiAlias(true); 300e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setColor(mTodayNumberColor); 301e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setTextAlign(Align.CENTER); 302e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setStyle(Style.FILL); 303e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setAlpha(SELECTED_CIRCLE_ALPHA); 304e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 305e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint = new Paint(); 306e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setAntiAlias(true); 307e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTextSize(MONTH_DAY_LABEL_TEXT_SIZE); 308e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setColor(mDayTextColor); 309e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.NORMAL)); 310e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setStyle(Style.FILL); 311e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTextAlign(Align.CENTER); 312e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setFakeBoldText(true); 313e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 314e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint = new Paint(); 315e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setAntiAlias(true); 316e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); 317e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setStyle(Style.FILL); 318e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setTextAlign(Align.CENTER); 319e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setFakeBoldText(false); 320e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 321e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 322e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 323e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onDraw(Canvas canvas) { 324e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthTitle(canvas); 325e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthDayLabels(canvas); 326e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthNums(canvas); 327e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 328e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 329e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int mDayOfWeekStart = 0; 330e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 331e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 332e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Sets all the parameters for displaying this week. The only required 333e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * parameter is the week number. Other parameters have a default value and 334e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * will only update if a new value is included, except for focus month, 335e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * which will always default to no focus month if no value is passed in. See 336e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters. 337e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 338e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param params A map of the new parameters, see 339e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #VIEW_PARAMS_HEIGHT} 340e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 341e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setMonthParams(HashMap<String, Integer> params) { 342e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (!params.containsKey(VIEW_PARAMS_MONTH) && !params.containsKey(VIEW_PARAMS_YEAR)) { 343e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung throw new InvalidParameterException("You must specify month and year for this view"); 344e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 345e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung setTag(params); 346e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // We keep the current value for any params not present 347e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_HEIGHT)) { 348e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = params.get(VIEW_PARAMS_HEIGHT); 349e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mRowHeight < MIN_HEIGHT) { 350e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = MIN_HEIGHT; 351e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 352e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 353e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { 354e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY); 355e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 356e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 357e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Allocate space for caching the day numbers and focus values 358e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonth = params.get(VIEW_PARAMS_MONTH); 359e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mYear = params.get(VIEW_PARAMS_YEAR); 360e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 361e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Figure out what day today is 362e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final Time today = new Time(Time.getCurrentTimezone()); 363e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung today.setToNow(); 364e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mHasToday = false; 365e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mToday = -1; 366e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 367e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.MONTH, mMonth); 368e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.YEAR, mYear); 369e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.DAY_OF_MONTH, 1); 370e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK); 371e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 372e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_WEEK_START)) { 373e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWeekStart = params.get(VIEW_PARAMS_WEEK_START); 374e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } else { 375e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWeekStart = mCalendar.getFirstDayOfWeek(); 376e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 377e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 378e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumCells = Utils.getDaysInMonth(mMonth, mYear); 379e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int i = 0; i < mNumCells; i++) { 380e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = i + 1; 381e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (sameDay(day, today)) { 382e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mHasToday = true; 383e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mToday = day; 384e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 385e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 386e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumRows = calculateNumRows(); 387e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 388e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Invalidate cached accessibility information. 389e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.invalidateRoot(); 390e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 391e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 3925cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein public void setSelectedDay(int day) { 3935cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein mSelectedDay = day; 3945cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein } 3955cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein 396e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void reuse() { 397e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumRows = DEFAULT_NUM_ROWS; 398e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung requestLayout(); 399e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 400e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 401e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int calculateNumRows() { 402e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int offset = findDayOffset(); 403e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dividend = (offset + mNumCells) / mNumDays; 404e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int remainder = (offset + mNumCells) % mNumDays; 405e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return (dividend + (remainder > 0 ? 1 : 0)); 406e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 407e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 408e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private boolean sameDay(int day, Time today) { 409e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return mYear == today.year && 410e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonth == today.month && 411e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung day == today.monthDay; 412e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 413e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 414e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 415e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 416e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows 417fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben + getMonthHeaderSize()); 418e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 419e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 420e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 421e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onSizeChanged(int w, int h, int oldw, int oldh) { 422e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWidth = w; 423e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 424e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Invalidate cached accessibility information. 425e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.invalidateRoot(); 426e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 427e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 4285cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein public int getMonth() { 4295cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein return mMonth; 4305cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein } 4315cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein 4325cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein public int getYear() { 4335cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein return mYear; 4345cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein } 4355cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein 436fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben /** 437fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben * A wrapper to the MonthHeaderSize to allow override it in children 438fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben */ 439fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben protected int getMonthHeaderSize() { 440fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben return MONTH_HEADER_SIZE; 441fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben } 442fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben 443e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String getMonthAndYearString() { 444e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR 445e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung | DateUtils.FORMAT_NO_MONTH_DAY; 446e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mStringBuilder.setLength(0); 447e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung long millis = mCalendar.getTimeInMillis(); 448e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags, 449e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Time.getCurrentTimezone()).toString(); 450e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 451e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 4526286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected void drawMonthTitle(Canvas canvas) { 453d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein int x = (mWidth + 2 * mEdgePadding) / 2; 454fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben int y = (getMonthHeaderSize() - MONTH_DAY_LABEL_TEXT_SIZE) / 2 + (MONTH_LABEL_TEXT_SIZE / 3); 455e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung canvas.drawText(getMonthAndYearString(), x, y, mMonthTitlePaint); 456e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 457e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 4586286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected void drawMonthDayLabels(Canvas canvas) { 459fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben int y = getMonthHeaderSize() - (MONTH_DAY_LABEL_TEXT_SIZE / 2); 460d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein int dayWidthHalf = (mWidth - mEdgePadding * 2) / (mNumDays * 2); 461e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 462e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int i = 0; i < mNumDays; i++) { 463e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int calendarDay = (i + mWeekStart) % mNumDays; 464d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein int x = (2 * i + 1) * dayWidthHalf + mEdgePadding; 465e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay); 466e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung canvas.drawText(mDayLabelCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, 467e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Locale.getDefault()).toUpperCase(Locale.getDefault()), x, y, 468e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint); 469e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 470e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 471e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 472e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 473e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Draws the week and month day numbers for this week. Override this method 474e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * if you need different placement. 475e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 476e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param canvas The canvas to draw on 477e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 478e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void drawMonthNums(Canvas canvas) { 479e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int y = (((mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2) - DAY_SEPARATOR_WIDTH) 480fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben + getMonthHeaderSize(); 48148bf1debfe72dee562d95f796e4fff3fe78c0bd4Ruben final float dayWidthHalf = (mWidth - mEdgePadding * 2) / (mNumDays * 2.0f); 482e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int j = findDayOffset(); 483e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int dayNumber = 1; dayNumber <= mNumCells; dayNumber++) { 48448bf1debfe72dee562d95f796e4fff3fe78c0bd4Ruben final int x = (int)((2 * j + 1) * dayWidthHalf + mEdgePadding); 485e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 486e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int yRelativeToDay = (mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH; 487e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 48848bf1debfe72dee562d95f796e4fff3fe78c0bd4Ruben final int startX = (int)(x - dayWidthHalf); 48948bf1debfe72dee562d95f796e4fff3fe78c0bd4Ruben final int stopX = (int)(x + dayWidthHalf); 49048bf1debfe72dee562d95f796e4fff3fe78c0bd4Ruben final int startY = (int)(y - yRelativeToDay); 49148bf1debfe72dee562d95f796e4fff3fe78c0bd4Ruben final int stopY = (int)(startY + mRowHeight); 492e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 493e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthDay(canvas, mYear, mMonth, dayNumber, x, y, startX, stopX, startY, stopY); 494e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 495e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung j++; 496e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (j == mNumDays) { 497e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung j = 0; 498e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung y += mRowHeight; 499e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 500e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 501e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 502e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 503e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 504e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This method should draw the month day. Implemented by sub-classes to allow customization. 505e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 506e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param canvas The canvas to draw on 507e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param year The year of this month day 508e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param month The month of this month day 509e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day number of this month day 510e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param x The default x position to draw the day number 511e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param y The default y position to draw the day number 512e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param startX The left boundary of the day number rect 513e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param stopX The right boundary of the day number rect 514e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param startY The top boundary of the day number rect 515e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param stopY The bottom boundary of the day number rect 516e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 517e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public abstract void drawMonthDay(Canvas canvas, int year, int month, int day, 518e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x, int y, int startX, int stopX, int startY, int stopY); 519e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 5206286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected int findDayOffset() { 521e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart) 522e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung - mWeekStart; 523e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 524e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 525e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 526e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 527e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Calculates the day that the given x position is in, accounting for week 528e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * number. Returns the day or -1 if the position wasn't in a day. 529e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 530e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param x The x position of the touch event 531e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return The day number, or -1 if the position wasn't in a day 532e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 533e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public int getDayFromLocation(float x, float y) { 53459d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben final int day = getInternalDayFromLocation(x, y); 53559d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben if (day < 1 || day > mNumCells) { 53659d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben return -1; 53759d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben } 53859d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben return day; 53959d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben } 54059d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben 54159d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben /** 54259d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben * Calculates the day that the given x position is in, accounting for week 54359d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben * number. 54459d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben * 54559d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben * @param x The x position of the touch event 54659d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben * @return The day number 54759d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben */ 54859d94e4e4bd797430acd57c5288ec0e20bf93ba8Ruben protected int getInternalDayFromLocation(float x, float y) { 549d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein int dayStart = mEdgePadding; 550d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein if (x < dayStart || x > mWidth - mEdgePadding) { 551e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return -1; 552e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 553e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels 554fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben int row = (int) (y - getMonthHeaderSize()) / mRowHeight; 555d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mEdgePadding)); 556e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 557e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int day = column - findDayOffset() + 1; 558e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung day += row * mNumDays; 559e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return day; 560e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 561e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 562e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 563e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Called when the user clicks on a day. Handles callbacks to the 564e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link OnDayClickListener} if one is set. 5657c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * <p/> 5667c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * If the day is out of the range set by minDate and/or maxDate, this is a no-op. 567e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 568e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day that was clicked 569e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 570e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private void onDayClick(int day) { 5717c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein // If the min / max date are set, only process the click if it's a valid selection. 5727c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (isOutOfRange(mYear, mMonth, day)) { 5737c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return; 5747c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5757c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 5767c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 577e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mOnDayClickListener != null) { 578e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mOnDayClickListener.onDayClick(this, new CalendarDay(mYear, mMonth, day)); 579e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 580e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 581e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // This is a no-op if accessibility is turned off. 582e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED); 583e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 584e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 585e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 5867c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * @return true if the specified year/month/day are within the range set by minDate and maxDate. 5877c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * If one or either have not been set, they are considered as Integer.MIN_VALUE and 5887c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein * Integer.MAX_VALUE. 5897c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein */ 5907c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein protected boolean isOutOfRange(int year, int month, int day) { 5917c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (isBeforeMin(year, month, day)) { 5927c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 5937c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (isAfterMax(year, month, day)) { 5947c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 5957c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5967c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 5977c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 5987c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 5997c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6007c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein private boolean isBeforeMin(int year, int month, int day) { 601e0a0cb288106e3a25441ea57a123a812929ec79cRuben if (mController == null) { 602e0a0cb288106e3a25441ea57a123a812929ec79cRuben return false; 603e0a0cb288106e3a25441ea57a123a812929ec79cRuben } 6047c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein Calendar minDate = mController.getMinDate(); 6057c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (minDate == null) { 6067c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6077c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6087c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6097c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (year < minDate.get(Calendar.YEAR)) { 6107c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6117c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (year > minDate.get(Calendar.YEAR)) { 6127c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6137c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6147c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6157c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (month < minDate.get(Calendar.MONTH)) { 6167c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6177c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (month > minDate.get(Calendar.MONTH)) { 6187c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6197c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6207c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6217c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (day < minDate.get(Calendar.DAY_OF_MONTH)) { 6227c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6237c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else { 6247c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6257c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6267c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6277c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6287c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein private boolean isAfterMax(int year, int month, int day) { 629e0a0cb288106e3a25441ea57a123a812929ec79cRuben if (mController == null) { 630e0a0cb288106e3a25441ea57a123a812929ec79cRuben return false; 631e0a0cb288106e3a25441ea57a123a812929ec79cRuben } 6327c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein Calendar maxDate = mController.getMaxDate(); 6337c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (maxDate == null) { 6347c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6357c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6367c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6377c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (year > maxDate.get(Calendar.YEAR)) { 6387c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6397c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (year < maxDate.get(Calendar.YEAR)) { 6407c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6417c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6427c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6437c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (month > maxDate.get(Calendar.MONTH)) { 6447c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6457c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else if (month < maxDate.get(Calendar.MONTH)) { 6467c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6477c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6487c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6497c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein if (day > maxDate.get(Calendar.DAY_OF_MONTH)) { 6507c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return true; 6517c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } else { 6527c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein return false; 6537c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6547c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein } 6557c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein 6567c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein /** 657e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return The date that has accessibility focus, or {@code null} if no date 658e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * has focus 659e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 660e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public CalendarDay getAccessibilityFocus() { 661e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = mTouchHelper.getFocusedVirtualView(); 662e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 663e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return new CalendarDay(mYear, mMonth, day); 664e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 665e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return null; 666e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 667e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 668e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 669e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Clears accessibility focus within the view. No-op if the view does not 670e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * contain accessibility focus. 671e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 672e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void clearAccessibilityFocus() { 673e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.clearFocusedVirtualView(); 674e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 675e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 676e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 677e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Attempts to restore accessibility focus to the specified date. 678e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 679e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The date which should receive focus 680e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return {@code false} if the date is not valid for this month view, or 681e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@code true} if the date received focus 682e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 683e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean restoreAccessibilityFocus(CalendarDay day) { 684e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if ((day.year != mYear) || (day.month != mMonth) || (day.day > mNumCells)) { 685e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return false; 686e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 687e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.setFocusedVirtualView(day.day); 688e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 689e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 690e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 691e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 692e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Provides a virtual view hierarchy for interfacing with an accessibility 693e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * service. 694e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 695529542558544a0e6d78424e33c60934adc143804Ruben protected class MonthViewTouchHelper extends ExploreByTouchHelper { 696e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private static final String DATE_FORMAT = "dd MMMM yyyy"; 697e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 698e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Rect mTempRect = new Rect(); 699e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Calendar mTempCalendar = Calendar.getInstance(); 700e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 701e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public MonthViewTouchHelper(View host) { 702e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung super(host); 703e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 704e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 705e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setFocusedVirtualView(int virtualViewId) { 706e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getAccessibilityNodeProvider(MonthView.this).performAction( 707e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung virtualViewId, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null); 708e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 709e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 710e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void clearFocusedVirtualView() { 711e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int focusedVirtualView = getFocusedVirtualView(); 712e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) { 713e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getAccessibilityNodeProvider(MonthView.this).performAction( 714e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung focusedVirtualView, 715e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, 716e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung null); 717e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 718e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 719e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 720e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 721e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int getVirtualViewAt(float x, float y) { 722e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = getDayFromLocation(x, y); 723e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 724e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return day; 725e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 726e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return ExploreByTouchHelper.INVALID_ID; 727e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 728e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 729e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 730e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { 731e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int day = 1; day <= mNumCells; day++) { 732e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung virtualViewIds.add(day); 733e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 734e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 735e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 736e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 737e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { 738e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung event.setContentDescription(getItemDescription(virtualViewId)); 739e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 740e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 741e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 742e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onPopulateNodeForVirtualView(int virtualViewId, 743e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung AccessibilityNodeInfoCompat node) { 744e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getItemBounds(virtualViewId, mTempRect); 745e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 746e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setContentDescription(getItemDescription(virtualViewId)); 747e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setBoundsInParent(mTempRect); 748e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.addAction(AccessibilityNodeInfo.ACTION_CLICK); 749e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 750e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (virtualViewId == mSelectedDay) { 751e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setSelected(true); 752e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 753e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 754e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 755e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 756e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 757e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected boolean onPerformActionForVirtualView(int virtualViewId, int action, 758e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Bundle arguments) { 759e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung switch (action) { 760e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung case AccessibilityNodeInfo.ACTION_CLICK: 761e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung onDayClick(virtualViewId); 762e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 763e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 764e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 765e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return false; 766e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 767e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 768e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 769e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Calculates the bounding rectangle of a given time object. 770e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 771e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day to calculate bounds for 772e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param rect The rectangle in which to store the bounds 773e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 7746286fbddd99eaf87daf5c887b09fe51011712d80Ruben protected void getItemBounds(int day, Rect rect) { 775d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein final int offsetX = mEdgePadding; 776fc0e80c929547bba62f93d609c83fe6d6bfbfd34Ruben final int offsetY = getMonthHeaderSize(); 777e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int cellHeight = mRowHeight; 778d89e3fa6558283b3d9c83ca5fdded49e4c4feb14Sam Blitzstein final int cellWidth = ((mWidth - (2 * mEdgePadding)) / mNumDays); 779e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int index = ((day - 1) + findDayOffset()); 780e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int row = (index / mNumDays); 781e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int column = (index % mNumDays); 782e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int x = (offsetX + (column * cellWidth)); 783e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int y = (offsetY + (row * cellHeight)); 784e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 785e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung rect.set(x, y, (x + cellWidth), (y + cellHeight)); 786e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 787e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 788e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 789e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Generates a description for a given time object. Since this 790e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * description will be spoken, the components are ordered by descending 791e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * specificity as DAY MONTH YEAR. 792e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 793e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day to generate a description for 794e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return A description of the time object 795e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 796529542558544a0e6d78424e33c60934adc143804Ruben protected CharSequence getItemDescription(int day) { 797e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTempCalendar.set(mYear, mMonth, day); 798e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final CharSequence date = DateFormat.format(DATE_FORMAT, 799e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTempCalendar.getTimeInMillis()); 800e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 801e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day == mSelectedDay) { 802e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return getContext().getString(R.string.item_is_selected, date); 803e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 804e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 805e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return date; 806e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 807e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 808e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 809e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 810e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Handles callbacks when the user clicks on a time object. 811e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 812e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public interface OnDayClickListener { 813e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void onDayClick(MonthView view, CalendarDay day); 814e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 815e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung} 816