MonthView.java revision e668d6b1b77ac4b127f961150e0d0a8a088143d9
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 123e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // affects the padding on the sides of this view 124e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mPadding = 0; 125e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 126e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String mDayOfWeekTypeface; 127e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String mMonthTitleTypeface; 128e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 129e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthNumPaint; 130e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthTitlePaint; 131e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthTitleBGPaint; 132e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mSelectedCirclePaint; 133e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected Paint mMonthDayLabelPaint; 134e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 135e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Formatter mFormatter; 136e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final StringBuilder mStringBuilder; 137e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 138e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The Julian day of the first day displayed by this item 139e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mFirstJulianDay = -1; 140e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The month of the first day in this week 141e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mFirstMonth = -1; 142e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The month of the last day in this week 143e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mLastMonth = -1; 144e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 145e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonth; 146e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 147e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mYear; 148e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Quick reference to the width of this view, matches parent 149e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mWidth; 150e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The height this view should draw at in pixels, set by height param 151e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mRowHeight = DEFAULT_HEIGHT; 152e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // If this view contains the today 153e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected boolean mHasToday = false; 154e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day is selected [0-6] or -1 if no day is selected 155e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedDay = -1; 156e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day is today [0-6] or -1 if no day is today 157e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mToday = DEFAULT_SELECTED_DAY; 158e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Which day of the week to start on [0-6] 159e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mWeekStart = DEFAULT_WEEK_START; 160e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // How many days to display 161e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mNumDays = DEFAULT_NUM_DAYS; 162e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The number of days + a spot for week number if it is displayed 163e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mNumCells = mNumDays; 164e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The left edge of the selected day 165e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedLeft = -1; 166e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // The right edge of the selected day 167e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mSelectedRight = -1; 168e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 169e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Calendar mCalendar; 170e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Calendar mDayLabelCalendar; 171e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final MonthViewTouchHelper mTouchHelper; 172e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 173e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int mNumRows = DEFAULT_NUM_ROWS; 174e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 175e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Optional listener for handling day click actions 176e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private OnDayClickListener mOnDayClickListener; 177e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Whether to prevent setting the accessibility delegate 178e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private boolean mLockAccessibilityDelegate; 179e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 180e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mDayTextColor; 181e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mTodayNumberColor; 182e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonthTitleColor; 183e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int mMonthTitleBGColor; 184e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 185e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public MonthView(Context context) { 186e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung super(context); 187e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 188e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Resources res = context.getResources(); 189e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 190e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayLabelCalendar = Calendar.getInstance(); 191e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar = Calendar.getInstance(); 192e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 193e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface); 194e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleTypeface = res.getString(R.string.sans_serif); 195e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 196e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayTextColor = res.getColor(R.color.date_picker_text_normal); 197e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTodayNumberColor = res.getColor(R.color.blue); 198e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleColor = res.getColor(R.color.white); 199e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGColor = res.getColor(R.color.circle_background); 200e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 201e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mStringBuilder = new StringBuilder(50); 202e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); 203e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 204e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MINI_DAY_NUMBER_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.day_number_size); 205e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_LABEL_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.month_label_size); 206e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_DAY_LABEL_TEXT_SIZE = res.getDimensionPixelSize(R.dimen.month_day_label_text_size); 207e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung MONTH_HEADER_SIZE = res.getDimensionPixelOffset(R.dimen.month_list_item_header_height); 208e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung DAY_SELECTED_CIRCLE_SIZE = res 209e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung .getDimensionPixelSize(R.dimen.day_number_select_circle_radius); 210e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 211e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = (res.getDimensionPixelOffset(R.dimen.date_picker_view_animator_height) 212e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung - MONTH_HEADER_SIZE) / MAX_NUM_ROWS; 213e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 214e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Set up accessibility components. 215e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper = new MonthViewTouchHelper(this); 216e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung ViewCompat.setAccessibilityDelegate(this, mTouchHelper); 217e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); 218e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mLockAccessibilityDelegate = true; 219e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 220e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Sets up any standard paints that will be used 221e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung initView(); 222e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 223e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 224e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 225e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setAccessibilityDelegate(AccessibilityDelegate delegate) { 226e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Workaround for a JB MR1 issue where accessibility delegates on 227e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // top-level ListView items are overwritten. 228e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (!mLockAccessibilityDelegate) { 229e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung super.setAccessibilityDelegate(delegate); 230e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 231e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 232e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 233e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setOnDayClickListener(OnDayClickListener listener) { 234e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mOnDayClickListener = listener; 235e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 236e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 237e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 238e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean dispatchHoverEvent(MotionEvent event) { 239e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // First right-of-refusal goes the touch exploration helper. 240e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mTouchHelper.dispatchHoverEvent(event)) { 241e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 242e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 243e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return super.dispatchHoverEvent(event); 244e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 245e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 246e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 247e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean onTouchEvent(MotionEvent event) { 248e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung switch (event.getAction()) { 249e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung case MotionEvent.ACTION_UP: 250e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = getDayFromLocation(event.getX(), event.getY()); 251e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 252e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung onDayClick(day); 253e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 254e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung break; 255e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 256e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 257e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 258e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 259e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 260e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Sets up the text and style properties for painting. Override this if you 261e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * want to use a different paint. 262e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 263e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void initView() { 264e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint = new Paint(); 265e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setFakeBoldText(true); 266e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setAntiAlias(true); 267e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTextSize(MONTH_LABEL_TEXT_SIZE); 268e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface, Typeface.BOLD)); 269e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setColor(mDayTextColor); 270e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setTextAlign(Align.CENTER); 271e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitlePaint.setStyle(Style.FILL); 272e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 273e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint = new Paint(); 274e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setFakeBoldText(true); 275e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setAntiAlias(true); 276e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setColor(mMonthTitleBGColor); 277e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setTextAlign(Align.CENTER); 278e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthTitleBGPaint.setStyle(Style.FILL); 279e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 280e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint = new Paint(); 281e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setFakeBoldText(true); 282e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setAntiAlias(true); 283e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setColor(mTodayNumberColor); 284e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setTextAlign(Align.CENTER); 285e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setStyle(Style.FILL); 286e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedCirclePaint.setAlpha(SELECTED_CIRCLE_ALPHA); 287e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 288e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint = new Paint(); 289e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setAntiAlias(true); 290e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTextSize(MONTH_DAY_LABEL_TEXT_SIZE); 291e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setColor(mDayTextColor); 292e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.NORMAL)); 293e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setStyle(Style.FILL); 294e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setTextAlign(Align.CENTER); 295e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint.setFakeBoldText(true); 296e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 297e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint = new Paint(); 298e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setAntiAlias(true); 299e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); 300e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setStyle(Style.FILL); 301e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setTextAlign(Align.CENTER); 302e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthNumPaint.setFakeBoldText(false); 303e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 304e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 305e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 306e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onDraw(Canvas canvas) { 307e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthTitle(canvas); 308e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthDayLabels(canvas); 309e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthNums(canvas); 310e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 311e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 312e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int mDayOfWeekStart = 0; 313e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 314e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 315e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Sets all the parameters for displaying this week. The only required 316e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * parameter is the week number. Other parameters have a default value and 317e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * will only update if a new value is included, except for focus month, 318e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * which will always default to no focus month if no value is passed in. See 319e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters. 320e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 321e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param params A map of the new parameters, see 322e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link #VIEW_PARAMS_HEIGHT} 323e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 324e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setMonthParams(HashMap<String, Integer> params) { 325e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (!params.containsKey(VIEW_PARAMS_MONTH) && !params.containsKey(VIEW_PARAMS_YEAR)) { 326e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung throw new InvalidParameterException("You must specify month and year for this view"); 327e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 328e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung setTag(params); 329e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // We keep the current value for any params not present 330e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_HEIGHT)) { 331e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = params.get(VIEW_PARAMS_HEIGHT); 332e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mRowHeight < MIN_HEIGHT) { 333e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mRowHeight = MIN_HEIGHT; 334e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 335e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 336e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { 337e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY); 338e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 339e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 340e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Allocate space for caching the day numbers and focus values 341e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonth = params.get(VIEW_PARAMS_MONTH); 342e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mYear = params.get(VIEW_PARAMS_YEAR); 343e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 344e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Figure out what day today is 345e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final Time today = new Time(Time.getCurrentTimezone()); 346e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung today.setToNow(); 347e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mHasToday = false; 348e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mToday = -1; 349e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 350e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.MONTH, mMonth); 351e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.YEAR, mYear); 352e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mCalendar.set(Calendar.DAY_OF_MONTH, 1); 353e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK); 354e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 355e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (params.containsKey(VIEW_PARAMS_WEEK_START)) { 356e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWeekStart = params.get(VIEW_PARAMS_WEEK_START); 357e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } else { 358e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWeekStart = mCalendar.getFirstDayOfWeek(); 359e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 360e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 361e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumCells = Utils.getDaysInMonth(mMonth, mYear); 362e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int i = 0; i < mNumCells; i++) { 363e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = i + 1; 364e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (sameDay(day, today)) { 365e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mHasToday = true; 366e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mToday = day; 367e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 368e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 369e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumRows = calculateNumRows(); 370e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 371e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Invalidate cached accessibility information. 372e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.invalidateRoot(); 373e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 374e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 375e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void reuse() { 376e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mNumRows = DEFAULT_NUM_ROWS; 377e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung requestLayout(); 378e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 379e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 380e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int calculateNumRows() { 381e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int offset = findDayOffset(); 382e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dividend = (offset + mNumCells) / mNumDays; 383e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int remainder = (offset + mNumCells) % mNumDays; 384e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return (dividend + (remainder > 0 ? 1 : 0)); 385e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 386e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 387e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private boolean sameDay(int day, Time today) { 388e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return mYear == today.year && 389e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonth == today.month && 390e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung day == today.monthDay; 391e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 392e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 393e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 394e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 395e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows 396e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung + MONTH_HEADER_SIZE); 397e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 398e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 399e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 400e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onSizeChanged(int w, int h, int oldw, int oldh) { 401e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mWidth = w; 402e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 403e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Invalidate cached accessibility information. 404e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.invalidateRoot(); 405e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 406e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 407e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private String getMonthAndYearString() { 408e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR 409e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung | DateUtils.FORMAT_NO_MONTH_DAY; 410e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mStringBuilder.setLength(0); 411e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung long millis = mCalendar.getTimeInMillis(); 412e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags, 413e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Time.getCurrentTimezone()).toString(); 414e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 415e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 416e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private void drawMonthTitle(Canvas canvas) { 417e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x = (mWidth + 2 * mPadding) / 2; 418e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int y = (MONTH_HEADER_SIZE - MONTH_DAY_LABEL_TEXT_SIZE) / 2 + (MONTH_LABEL_TEXT_SIZE / 3); 419e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung canvas.drawText(getMonthAndYearString(), x, y, mMonthTitlePaint); 420e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 421e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 422e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private void drawMonthDayLabels(Canvas canvas) { 423e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int y = MONTH_HEADER_SIZE - (MONTH_DAY_LABEL_TEXT_SIZE / 2); 424e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2); 425e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 426e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int i = 0; i < mNumDays; i++) { 427e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int calendarDay = (i + mWeekStart) % mNumDays; 428e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x = (2 * i + 1) * dayWidthHalf + mPadding; 429e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay); 430e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung canvas.drawText(mDayLabelCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, 431e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Locale.getDefault()).toUpperCase(Locale.getDefault()), x, y, 432e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mMonthDayLabelPaint); 433e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 434e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 435e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 436e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 437e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Draws the week and month day numbers for this week. Override this method 438e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * if you need different placement. 439e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 440e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param canvas The canvas to draw on 441e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 442e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void drawMonthNums(Canvas canvas) { 443e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int y = (((mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2) - DAY_SEPARATOR_WIDTH) 444e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung + MONTH_HEADER_SIZE; 445e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2); 446e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int j = findDayOffset(); 447e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int dayNumber = 1; dayNumber <= mNumCells; dayNumber++) { 448e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x = (2 * j + 1) * dayWidthHalf + mPadding; 449e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 450e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int yRelativeToDay = (mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH; 451e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 452e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int startX = x - dayWidthHalf; 453e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int stopX = x + dayWidthHalf; 454e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int startY = y - yRelativeToDay; 455e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int stopY = startY + mRowHeight; 456e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 457e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung drawMonthDay(canvas, mYear, mMonth, dayNumber, x, y, startX, stopX, startY, stopY); 458e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 459e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung j++; 460e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (j == mNumDays) { 461e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung j = 0; 462e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung y += mRowHeight; 463e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 464e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 465e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 466e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 467e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 468e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * This method should draw the month day. Implemented by sub-classes to allow customization. 469e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 470e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param canvas The canvas to draw on 471e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param year The year of this month day 472e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param month The month of this month day 473e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day number of this month day 474e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param x The default x position to draw the day number 475e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param y The default y position to draw the day number 476e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param startX The left boundary of the day number rect 477e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param stopX The right boundary of the day number rect 478e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param startY The top boundary of the day number rect 479e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param stopY The bottom boundary of the day number rect 480e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 481e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public abstract void drawMonthDay(Canvas canvas, int year, int month, int day, 482e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int x, int y, int startX, int stopX, int startY, int stopY); 483e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 484e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private int findDayOffset() { 485e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart) 486e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung - mWeekStart; 487e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 488e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 489e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 490e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 491e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Calculates the day that the given x position is in, accounting for week 492e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * number. Returns the day or -1 if the position wasn't in a day. 493e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 494e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param x The x position of the touch event 495e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return The day number, or -1 if the position wasn't in a day 496e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 497e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public int getDayFromLocation(float x, float y) { 498e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int dayStart = mPadding; 499e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (x < dayStart || x > mWidth - mPadding) { 500e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return -1; 501e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 502e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels 503e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int row = (int) (y - MONTH_HEADER_SIZE) / mRowHeight; 504e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)); 505e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 506e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung int day = column - findDayOffset() + 1; 507e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung day += row * mNumDays; 508e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day < 1 || day > mNumCells) { 509e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return -1; 510e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 511e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return day; 512e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 513e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 514e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 515e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Called when the user clicks on a day. Handles callbacks to the 516e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@link OnDayClickListener} if one is set. 517e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 518e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day that was clicked 519e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 520e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private void onDayClick(int day) { 521e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (mOnDayClickListener != null) { 522e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mOnDayClickListener.onDayClick(this, new CalendarDay(mYear, mMonth, day)); 523e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 524e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 525e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung // This is a no-op if accessibility is turned off. 526e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED); 527e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 528e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 529e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 530e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return The date that has accessibility focus, or {@code null} if no date 531e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * has focus 532e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 533e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public CalendarDay getAccessibilityFocus() { 534e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = mTouchHelper.getFocusedVirtualView(); 535e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 536e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return new CalendarDay(mYear, mMonth, day); 537e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 538e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return null; 539e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 540e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 541e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 542e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Clears accessibility focus within the view. No-op if the view does not 543e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * contain accessibility focus. 544e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 545e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void clearAccessibilityFocus() { 546e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.clearFocusedVirtualView(); 547e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 548e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 549e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 550e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Attempts to restore accessibility focus to the specified date. 551e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 552e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The date which should receive focus 553e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return {@code false} if the date is not valid for this month view, or 554e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * {@code true} if the date received focus 555e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 556e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public boolean restoreAccessibilityFocus(CalendarDay day) { 557e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if ((day.year != mYear) || (day.month != mMonth) || (day.day > mNumCells)) { 558e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return false; 559e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 560e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTouchHelper.setFocusedVirtualView(day.day); 561e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 562e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 563e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 564e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 565e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Provides a virtual view hierarchy for interfacing with an accessibility 566e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * service. 567e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 568e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private class MonthViewTouchHelper extends ExploreByTouchHelper { 569e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private static final String DATE_FORMAT = "dd MMMM yyyy"; 570e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 571e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Rect mTempRect = new Rect(); 572e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private final Calendar mTempCalendar = Calendar.getInstance(); 573e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 574e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public MonthViewTouchHelper(View host) { 575e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung super(host); 576e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 577e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 578e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void setFocusedVirtualView(int virtualViewId) { 579e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getAccessibilityNodeProvider(MonthView.this).performAction( 580e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung virtualViewId, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null); 581e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 582e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 583e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void clearFocusedVirtualView() { 584e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int focusedVirtualView = getFocusedVirtualView(); 585e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) { 586e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getAccessibilityNodeProvider(MonthView.this).performAction( 587e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung focusedVirtualView, 588e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, 589e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung null); 590e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 591e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 592e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 593e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 594e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected int getVirtualViewAt(float x, float y) { 595e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int day = getDayFromLocation(x, y); 596e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day >= 0) { 597e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return day; 598e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 599e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return ExploreByTouchHelper.INVALID_ID; 600e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 601e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 602e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 603e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { 604e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung for (int day = 1; day <= mNumCells; day++) { 605e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung virtualViewIds.add(day); 606e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 607e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 608e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 609e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 610e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { 611e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung event.setContentDescription(getItemDescription(virtualViewId)); 612e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 613e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 614e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 615e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected void onPopulateNodeForVirtualView(int virtualViewId, 616e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung AccessibilityNodeInfoCompat node) { 617e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung getItemBounds(virtualViewId, mTempRect); 618e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 619e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setContentDescription(getItemDescription(virtualViewId)); 620e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setBoundsInParent(mTempRect); 621e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.addAction(AccessibilityNodeInfo.ACTION_CLICK); 622e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 623e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (virtualViewId == mSelectedDay) { 624e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung node.setSelected(true); 625e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 626e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 627e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 628e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 629e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung @Override 630e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung protected boolean onPerformActionForVirtualView(int virtualViewId, int action, 631e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung Bundle arguments) { 632e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung switch (action) { 633e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung case AccessibilityNodeInfo.ACTION_CLICK: 634e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung onDayClick(virtualViewId); 635e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return true; 636e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 637e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 638e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return false; 639e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 640e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 641e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 642e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Calculates the bounding rectangle of a given time object. 643e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 644e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day to calculate bounds for 645e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param rect The rectangle in which to store the bounds 646e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 647e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private void getItemBounds(int day, Rect rect) { 648e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int offsetX = mPadding; 649e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int offsetY = MONTH_HEADER_SIZE; 650e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int cellHeight = mRowHeight; 651e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays); 652e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int index = ((day - 1) + findDayOffset()); 653e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int row = (index / mNumDays); 654e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int column = (index % mNumDays); 655e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int x = (offsetX + (column * cellWidth)); 656e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final int y = (offsetY + (row * cellHeight)); 657e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 658e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung rect.set(x, y, (x + cellWidth), (y + cellHeight)); 659e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 660e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 661e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 662e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Generates a description for a given time object. Since this 663e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * description will be spoken, the components are ordered by descending 664e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * specificity as DAY MONTH YEAR. 665e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * 666e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @param day The day to generate a description for 667e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * @return A description of the time object 668e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 669e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung private CharSequence getItemDescription(int day) { 670e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTempCalendar.set(mYear, mMonth, day); 671e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung final CharSequence date = DateFormat.format(DATE_FORMAT, 672e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung mTempCalendar.getTimeInMillis()); 673e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 674e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung if (day == mSelectedDay) { 675e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return getContext().getString(R.string.item_is_selected, date); 676e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 677e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 678e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung return date; 679e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 680e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 681e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung 682e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung /** 683e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Handles callbacks when the user clicks on a time object. 684e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */ 685e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public interface OnDayClickListener { 686e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung public void onDayClick(MonthView view, CalendarDay day); 687e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung } 688e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung} 689