1bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio/* 2bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Copyright (C) 2014 The Android Open Source Project 3bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 4bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Licensed under the Apache License, Version 2.0 (the "License"); 5bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * you may not use this file except in compliance with the License. 6bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * You may obtain a copy of the License at 7bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 8bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * http://www.apache.org/licenses/LICENSE-2.0 9bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 10bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Unless required by applicable law or agreed to in writing, software 11bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * distributed under the License is distributed on an "AS IS" BASIS, 12bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * See the License for the specific language governing permissions and 14bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * limitations under the License. 15bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 16bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 17bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Megliopackage android.widget; 18bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 19d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viveretteimport com.android.internal.R; 20d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viveretteimport com.android.internal.widget.ExploreByTouchHelper; 21d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette 22e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mountimport android.annotation.Nullable; 23bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.Context; 24bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.res.ColorStateList; 25bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.res.Resources; 260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viveretteimport android.content.res.TypedArray; 27bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Canvas; 28bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint; 29bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint.Align; 30bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint.Style; 31bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Rect; 32bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Typeface; 33c3e5a82691ac9094189a000430eab939e9020b2bAlan Viveretteimport android.icu.text.DisplayContext; 34a7f827bab3df423d2a3b527853de3e9ac9e378fdDeepanshu Guptaimport android.icu.text.SimpleDateFormat; 3568763be41cffbcb4883a0a6293fc1aa9e17a3957Alan Viveretteimport android.icu.util.Calendar; 36bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.os.Bundle; 375dc973cb03889c58988703b58aefbd2397fb02c3Alan Viveretteimport android.text.TextPaint; 38bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.text.format.DateFormat; 39bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.util.AttributeSet; 40ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport android.util.IntArray; 415c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viveretteimport android.util.MathUtils; 42c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viveretteimport android.util.StateSet; 43e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mountimport android.view.KeyEvent; 44bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.MotionEvent; 45bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.View; 46e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mountimport android.view.ViewParent; 47bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityEvent; 48bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo; 490ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viveretteimport android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 50bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 511ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haaseimport java.text.NumberFormat; 52bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Locale; 53bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 54d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viveretteimport libcore.icu.LocaleData; 55d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette 56bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio/** 57bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * A calendar-like view displaying a specified month and the appropriate selectable day numbers 58bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * within the specified month. 59bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 60bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioclass SimpleMonthView extends View { 610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private static final int DAYS_IN_WEEK = 7; 620ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private static final int MAX_WEEKS_IN_MONTH = 6; 63bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 64bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final int DEFAULT_SELECTED_DAY = -1; 65bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final int DEFAULT_WEEK_START = Calendar.SUNDAY; 66bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 67e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette private static final String MONTH_YEAR_FORMAT = "MMMMy"; 68c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 69e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private static final int SELECTED_HIGHLIGHT_ALPHA = 0xB0; 70e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 715dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette private final TextPaint mMonthPaint = new TextPaint(); 725dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette private final TextPaint mDayOfWeekPaint = new TextPaint(); 735dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette private final TextPaint mDayPaint = new TextPaint(); 740ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final Paint mDaySelectorPaint = new Paint(); 750ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final Paint mDayHighlightPaint = new Paint(); 76e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private final Paint mDayHighlightSelectorPaint = new Paint(); 775dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette 78d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette /** Array of single-character weekday labels ordered by column index. */ 79e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette private final String[] mDayOfWeekLabels = new String[7]; 80e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette 81e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette private final Calendar mCalendar; 82e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette private final Locale mLocale; 83bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 840ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final MonthViewTouchHelper mTouchHelper; 85fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette 861ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haase private final NumberFormat mDayFormatter; 87bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 88f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // Desired dimensions. 89f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredMonthHeight; 90f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredDayOfWeekHeight; 91f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredDayHeight; 92f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredCellWidth; 93f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredDaySelectorRadius; 9460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 95e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette private String mMonthYearLabel; 96bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 97bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mMonth; 98bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mYear; 99bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 100f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // Dimensions as laid out. 101f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mMonthHeight; 102f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mDayOfWeekHeight; 103f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mDayHeight; 104f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mCellWidth; 105f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mDaySelectorRadius; 106f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 1070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mPaddedWidth; 1080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mPaddedHeight; 109bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The day of month for the selected day, or -1 if no day is selected. */ 111c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette private int mActivatedDay = -1; 112bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 1140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * The day of month for today, or -1 if the today is not in the current 1150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * month. 1160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 117bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mToday = DEFAULT_SELECTED_DAY; 118bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 119d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette /** The first day of the week (ex. Calendar.SUNDAY) indexed from one. */ 120bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mWeekStart = DEFAULT_WEEK_START; 121bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The number of days (ex. 28) in the current month. */ 1230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mDaysInMonth; 124bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 1260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * The day of week (ex. Calendar.SUNDAY) for the first day of the current 1270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * month. 1280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 1290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mDayOfWeekStart; 130bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The day of month for the first (inclusive) enabled day. */ 132bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mEnabledDayStart = 1; 133bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1340ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The day of month for the last (inclusive) enabled day. */ 135bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mEnabledDayEnd = 31; 136bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1370ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** Optional listener for handling day click actions. */ 138bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private OnDayClickListener mOnDayClickListener; 139bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 140c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette private ColorStateList mDayTextColor; 141c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 142e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private int mHighlightedDay = -1; 143e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private int mPreviouslyHighlightedDay = -1; 144e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private boolean mIsTouchHighlighted = false; 14560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 146bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public SimpleMonthView(Context context) { 147bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio this(context, null); 148bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 149bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 150bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public SimpleMonthView(Context context, AttributeSet attrs) { 151bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio this(context, attrs, R.attr.datePickerStyle); 152bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 153bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 15450eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette public SimpleMonthView(Context context, AttributeSet attrs, int defStyleAttr) { 15550eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette this(context, attrs, defStyleAttr, 0); 15650eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette } 157bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 15850eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette public SimpleMonthView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 15950eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette super(context, attrs, defStyleAttr, defStyleRes); 160bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 16150eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette final Resources res = context.getResources(); 162f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height); 163f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height); 164f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height); 165f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width); 16678bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette mDesiredDaySelectorRadius = res.getDimensionPixelSize( 16778bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette R.dimen.date_picker_day_selector_radius); 16860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 169bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Set up accessibility components. 170bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mTouchHelper = new MonthViewTouchHelper(this); 171bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setAccessibilityDelegate(mTouchHelper); 172bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); 173bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 174e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette mLocale = res.getConfiguration().locale; 175e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette mCalendar = Calendar.getInstance(mLocale); 176e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette 177e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette mDayFormatter = NumberFormat.getIntegerInstance(mLocale); 178e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette 179e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette updateMonthYearLabel(); 180e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette updateDayOfWeekLabels(); 1810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 1820ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette initPaints(res); 1830ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 1840ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 185e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette private void updateMonthYearLabel() { 186e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette final String format = DateFormat.getBestDateTimePattern(mLocale, MONTH_YEAR_FORMAT); 187e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette final SimpleDateFormat formatter = new SimpleDateFormat(format, mLocale); 188c3e5a82691ac9094189a000430eab939e9020b2bAlan Viverette formatter.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE); 189e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette mMonthYearLabel = formatter.format(mCalendar.getTime()); 190e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette } 191e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette 192e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette private void updateDayOfWeekLabels() { 193d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette // Use tiny (e.g. single-character) weekday names from ICU. The indices 194d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette // for this list correspond to Calendar days, e.g. SUNDAY is index 1. 195d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette final String[] tinyWeekdayNames = LocaleData.get(mLocale).tinyWeekdayNames; 196d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette for (int i = 0; i < DAYS_IN_WEEK; i++) { 197d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette mDayOfWeekLabels[i] = tinyWeekdayNames[(mWeekStart + i - 1) % DAYS_IN_WEEK + 1]; 198e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette } 199e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette } 200e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette 2010ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 2020ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Applies the specified text appearance resource to a paint, returning the 2030ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * text color if one is set in the text appearance. 2040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * 2050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param p the paint to modify 2060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param resId the resource ID of the text appearance 2070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @return the text color, if available 2080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 2090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private ColorStateList applyTextAppearance(Paint p, int resId) { 2100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final TypedArray ta = mContext.obtainStyledAttributes(null, 2110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.styleable.TextAppearance, 0, resId); 2120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily); 2140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (fontFamily != null) { 2150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette p.setTypeface(Typeface.create(fontFamily, 0)); 2160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette p.setTextSize(ta.getDimensionPixelSize( 2190ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.styleable.TextAppearance_textSize, (int) p.getTextSize())); 2200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2210ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor); 2220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (textColor != null) { 2230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0); 2240ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette p.setColor(enabledColor); 2250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette ta.recycle(); 2280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return textColor; 2300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 23278bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette public int getMonthHeight() { 23378bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette return mMonthHeight; 23478bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette } 23578bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette 23678bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette public int getCellWidth() { 23778bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette return mCellWidth; 23878bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette } 23978bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette 2400ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setMonthTextAppearance(int resId) { 24178bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette applyTextAppearance(mMonthPaint, resId); 24260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 2430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 2440ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2450ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2460ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setDayOfWeekTextAppearance(int resId) { 2470ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette applyTextAppearance(mDayOfWeekPaint, resId); 2480ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 2490ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2500ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2510ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setDayTextAppearance(int resId) { 2520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final ColorStateList textColor = applyTextAppearance(mDayPaint, resId); 2530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (textColor != null) { 2540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayTextColor = textColor; 2550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2570ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 2580ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2590ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 260c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette /** 261c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette * Sets up the text and style properties for painting. 262c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette */ 2630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private void initPaints(Resources res) { 2640ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String monthTypeface = res.getString(R.string.date_picker_month_typeface); 2650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String dayOfWeekTypeface = res.getString(R.string.date_picker_day_of_week_typeface); 2660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String dayTypeface = res.getString(R.string.date_picker_day_typeface); 2670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int monthTextSize = res.getDimensionPixelSize( 2690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.dimen.date_picker_month_text_size); 2700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int dayOfWeekTextSize = res.getDimensionPixelSize( 2710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.dimen.date_picker_day_of_week_text_size); 2720ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int dayTextSize = res.getDimensionPixelSize( 2730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.dimen.date_picker_day_text_size); 2740ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 275c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mMonthPaint.setAntiAlias(true); 2760ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mMonthPaint.setTextSize(monthTextSize); 2770ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0)); 278c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mMonthPaint.setTextAlign(Align.CENTER); 279c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mMonthPaint.setStyle(Style.FILL); 280c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 281c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayOfWeekPaint.setAntiAlias(true); 2820ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayOfWeekPaint.setTextSize(dayOfWeekTextSize); 2830ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0)); 284c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayOfWeekPaint.setTextAlign(Align.CENTER); 285c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayOfWeekPaint.setStyle(Style.FILL); 286c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 2870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDaySelectorPaint.setAntiAlias(true); 2880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDaySelectorPaint.setStyle(Style.FILL); 2890ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2900ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayHighlightPaint.setAntiAlias(true); 2910ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayHighlightPaint.setStyle(Style.FILL); 292c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 293e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mDayHighlightSelectorPaint.setAntiAlias(true); 294e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mDayHighlightSelectorPaint.setStyle(Style.FILL); 295e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 296c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayPaint.setAntiAlias(true); 2970ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayPaint.setTextSize(dayTextSize); 2980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayPaint.setTypeface(Typeface.create(dayTypeface, 0)); 299c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayPaint.setTextAlign(Align.CENTER); 300c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayPaint.setStyle(Style.FILL); 301bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 302bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 303c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette void setMonthTextColor(ColorStateList monthTextColor) { 304c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0); 305c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mMonthPaint.setColor(enabledColor); 306c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette invalidate(); 307c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette } 308bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 309c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette void setDayOfWeekTextColor(ColorStateList dayOfWeekTextColor) { 310c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette final int enabledColor = dayOfWeekTextColor.getColorForState(ENABLED_STATE_SET, 0); 311c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayOfWeekPaint.setColor(enabledColor); 312c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette invalidate(); 313c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette } 314bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 315c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette void setDayTextColor(ColorStateList dayTextColor) { 316c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayTextColor = dayTextColor; 317c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette invalidate(); 318c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette } 319bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 3200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette void setDaySelectorColor(ColorStateList dayBackgroundColor) { 3215dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette final int activatedColor = dayBackgroundColor.getColorForState( 322c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0); 3230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDaySelectorPaint.setColor(activatedColor); 324e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mDayHighlightSelectorPaint.setColor(activatedColor); 325e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mDayHighlightSelectorPaint.setAlpha(SELECTED_HIGHLIGHT_ALPHA); 326c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette invalidate(); 327bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 328bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 3290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette void setDayHighlightColor(ColorStateList dayHighlightColor) { 3300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int pressedColor = dayHighlightColor.getColorForState( 3310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED), 0); 3320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayHighlightPaint.setColor(pressedColor); 3330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 334bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 335bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 336bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void setOnDayClickListener(OnDayClickListener listener) { 337bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mOnDayClickListener = listener; 338bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 339bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 340bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 341bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public boolean dispatchHoverEvent(MotionEvent event) { 342bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // First right-of-refusal goes the touch exploration helper. 3430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event); 344bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 345bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 346bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 347bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public boolean onTouchEvent(MotionEvent event) { 34860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int x = (int) (event.getX() + 0.5f); 34960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int y = (int) (event.getY() + 0.5f); 35060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 351ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette final int action = event.getAction(); 352ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette switch (action) { 3530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette case MotionEvent.ACTION_DOWN: 3540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette case MotionEvent.ACTION_MOVE: 35578bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette final int touchedItem = getDayAtLocation(x, y); 356e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mIsTouchHighlighted = true; 357e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (mHighlightedDay != touchedItem) { 358e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = touchedItem; 359e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mPreviouslyHighlightedDay = touchedItem; 3600ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 361bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 362ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette if (action == MotionEvent.ACTION_DOWN && touchedItem < 0) { 363ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette // Touch something that's not an item, reject event. 364ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette return false; 365ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } 366bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio break; 3670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 3680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette case MotionEvent.ACTION_UP: 36978bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette final int clickedDay = getDayAtLocation(x, y); 37078bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette onDayClicked(clickedDay); 3710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Fall through. 3720ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette case MotionEvent.ACTION_CANCEL: 3730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Reset touched day on stream end. 374e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = -1; 375e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mIsTouchHighlighted = false; 3760ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 3770ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette break; 378bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 379bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return true; 380bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 381bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 382bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 383e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount public boolean onKeyDown(int keyCode, KeyEvent event) { 384e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // We need to handle focus change within the SimpleMonthView because we are simulating 385e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // multiple Views. The arrow keys will move between days until there is no space (no 386e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // day to the left, top, right, or bottom). Focus forward and back jumps out of the 387e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // SimpleMonthView, skipping over other SimpleMonthViews in the parent ViewPager 388e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // to the next focusable View in the hierarchy. 389e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount boolean focusChanged = false; 390e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount switch (event.getKeyCode()) { 391e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case KeyEvent.KEYCODE_DPAD_LEFT: 392e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (event.hasNoModifiers()) { 393e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount focusChanged = moveOneDay(isLayoutRtl()); 394e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 395e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 396e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case KeyEvent.KEYCODE_DPAD_RIGHT: 397e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (event.hasNoModifiers()) { 398e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount focusChanged = moveOneDay(!isLayoutRtl()); 399e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 400e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 401e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case KeyEvent.KEYCODE_DPAD_UP: 402e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (event.hasNoModifiers()) { 403e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount ensureFocusedDay(); 404e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (mHighlightedDay > 7) { 405e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay -= 7; 406e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount focusChanged = true; 407e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 408e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 409e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 410e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case KeyEvent.KEYCODE_DPAD_DOWN: 411e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (event.hasNoModifiers()) { 412e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount ensureFocusedDay(); 413e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (mHighlightedDay <= mDaysInMonth - 7) { 414e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay += 7; 415e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount focusChanged = true; 416e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 417e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 418e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 419e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case KeyEvent.KEYCODE_DPAD_CENTER: 420e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case KeyEvent.KEYCODE_ENTER: 421e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (mHighlightedDay != -1) { 422e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount onDayClicked(mHighlightedDay); 423e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return true; 424e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 425e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 426e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case KeyEvent.KEYCODE_TAB: { 427e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount int focusChangeDirection = 0; 428e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (event.hasNoModifiers()) { 429e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount focusChangeDirection = View.FOCUS_FORWARD; 430e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { 431e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount focusChangeDirection = View.FOCUS_BACKWARD; 432e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 433e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (focusChangeDirection != 0) { 434e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final ViewParent parent = getParent(); 435e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // move out of the ViewPager next/previous 436e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount View nextFocus = this; 437e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount do { 438e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount nextFocus = nextFocus.focusSearch(focusChangeDirection); 439e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } while (nextFocus != null && nextFocus != this && 440e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount nextFocus.getParent() == parent); 441e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (nextFocus != null) { 442e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount nextFocus.requestFocus(); 443e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return true; 444e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 445e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 446e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 447e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 448e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 449e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (focusChanged) { 450e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount invalidate(); 451e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return true; 452e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } else { 453e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return super.onKeyDown(keyCode, event); 454e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 455e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 456e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 457e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private boolean moveOneDay(boolean positive) { 458e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount ensureFocusedDay(); 459e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount boolean focusChanged = false; 460e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (positive) { 461e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (!isLastDayOfWeek(mHighlightedDay) && mHighlightedDay < mDaysInMonth) { 462e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay++; 463e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount focusChanged = true; 464e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 465e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } else { 466e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (!isFirstDayOfWeek(mHighlightedDay) && mHighlightedDay > 1) { 467e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay--; 468e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount focusChanged = true; 469e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 470e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 471e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return focusChanged; 472e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 473e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 474e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount @Override 475e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction, 476e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount @Nullable Rect previouslyFocusedRect) { 477e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (gainFocus) { 478e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // If we've gained focus through arrow keys, we should find the day closest 479e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // to the focus rect. If we've gained focus through forward/back, we should 480e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // focus on the selected day if there is one. 481e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int offset = findDayOffset(); 482e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount switch(direction) { 483e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case View.FOCUS_RIGHT: { 484e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount int row = findClosestRow(previouslyFocusedRect); 485e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = row == 0 ? 1 : (row * DAYS_IN_WEEK) - offset + 1; 486e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 487e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 488e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case View.FOCUS_LEFT: { 489e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount int row = findClosestRow(previouslyFocusedRect) + 1; 490e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = Math.min(mDaysInMonth, (row * DAYS_IN_WEEK) - offset); 491e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 492e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 493e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case View.FOCUS_DOWN: { 494e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int col = findClosestColumn(previouslyFocusedRect); 495e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int day = col - offset + 1; 496e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = day < 1 ? day + DAYS_IN_WEEK : day; 497e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 498e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 499e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount case View.FOCUS_UP: { 500e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int col = findClosestColumn(previouslyFocusedRect); 501e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int maxWeeks = (offset + mDaysInMonth) / DAYS_IN_WEEK; 502e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int day = col - offset + (DAYS_IN_WEEK * maxWeeks) + 1; 503e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = day > mDaysInMonth ? day - DAYS_IN_WEEK : day; 504e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount break; 505e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 506e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 507e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount ensureFocusedDay(); 508e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount invalidate(); 509e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 510e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 511e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 512e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 513e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount /** 514e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount * Returns the row (0 indexed) closest to previouslyFocusedRect or center if null. 515e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount */ 516e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private int findClosestRow(@Nullable Rect previouslyFocusedRect) { 517e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (previouslyFocusedRect == null) { 518e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return 3; 519e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } else { 520e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount int centerY = previouslyFocusedRect.centerY(); 521e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 522e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final TextPaint p = mDayPaint; 523e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int headerHeight = mMonthHeight + mDayOfWeekHeight; 524e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int rowHeight = mDayHeight; 525e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 526e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // Text is vertically centered within the row height. 527e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final float halfLineHeight = (p.ascent() + p.descent()) / 2f; 528e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int rowCenter = headerHeight + rowHeight / 2; 529e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 530e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount centerY -= rowCenter - halfLineHeight; 531e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount int row = Math.round(centerY / (float) rowHeight); 532e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int maxDay = findDayOffset() + mDaysInMonth; 533e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int maxRows = (maxDay / DAYS_IN_WEEK) - ((maxDay % DAYS_IN_WEEK == 0) ? 1 : 0); 534e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 535e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount row = MathUtils.constrain(row, 0, maxRows); 536e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return row; 537e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 538e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 539e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 540e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount /** 541e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount * Returns the column (0 indexed) closest to the previouslyFocusedRect or center if null. 542e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount * The 0 index is related to the first day of the week. 543e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount */ 544e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private int findClosestColumn(@Nullable Rect previouslyFocusedRect) { 545e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (previouslyFocusedRect == null) { 546e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return DAYS_IN_WEEK / 2; 547e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } else { 548e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount int centerX = previouslyFocusedRect.centerX() - mPaddingLeft; 549e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int columnFromLeft = 550e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount MathUtils.constrain(centerX / mCellWidth, 0, DAYS_IN_WEEK - 1); 551e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return isLayoutRtl() ? DAYS_IN_WEEK - columnFromLeft - 1: columnFromLeft; 552e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 553e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 554e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 555e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount @Override 556e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount public void getFocusedRect(Rect r) { 557e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (mHighlightedDay > 0) { 558e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount getBoundsForDay(mHighlightedDay, r); 559e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } else { 560e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount super.getFocusedRect(r); 561e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 562e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 563e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 564e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount @Override 565e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount protected void onFocusLost() { 566e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (!mIsTouchHighlighted) { 567e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount // Unhighlight a day. 568e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mPreviouslyHighlightedDay = mHighlightedDay; 569e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = -1; 570e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount invalidate(); 571e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 572e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount super.onFocusLost(); 573e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 574e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 575e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount /** 576e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount * Ensure some day is highlighted. If a day isn't highlighted, it chooses the selected day, 577e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount * if possible, or the first day of the month if not. 578e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount */ 579e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private void ensureFocusedDay() { 580e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (mHighlightedDay != -1) { 581e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return; 582e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 583e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (mPreviouslyHighlightedDay != -1) { 584e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = mPreviouslyHighlightedDay; 585e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return; 586e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 587e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount if (mActivatedDay != -1) { 588e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = mActivatedDay; 589e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return; 590e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 591e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mHighlightedDay = 1; 592e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 593e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 594e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private boolean isFirstDayOfWeek(int day) { 595e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int offset = findDayOffset(); 596e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return (offset + day - 1) % DAYS_IN_WEEK == 0; 597e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 598e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 599e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount private boolean isLastDayOfWeek(int day) { 600e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final int offset = findDayOffset(); 601e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount return (offset + day) % DAYS_IN_WEEK == 0; 602e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } 603e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount 604e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount @Override 605bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onDraw(Canvas canvas) { 6060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int paddingLeft = getPaddingLeft(); 6070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int paddingTop = getPaddingTop(); 6080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette canvas.translate(paddingLeft, paddingTop); 6090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette drawMonth(canvas); 6110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette drawDaysOfWeek(canvas); 612bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio drawDays(canvas); 6130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette canvas.translate(-paddingLeft, -paddingTop); 6150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 6160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private void drawMonth(Canvas canvas) { 6180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final float x = mPaddedWidth / 2f; 6190ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Vertically centered within the month header height. 6210ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent(); 6220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final float y = (mMonthHeight - lineHeight) / 2f; 6230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 624e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette canvas.drawText(mMonthYearLabel, x, y, mMonthPaint); 625e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette } 626e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette 627e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette public String getMonthYearLabel() { 628e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette return mMonthYearLabel; 6290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 6300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private void drawDaysOfWeek(Canvas canvas) { 63260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final TextPaint p = mDayOfWeekPaint; 63360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int headerHeight = mMonthHeight; 63460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int rowHeight = mDayOfWeekHeight; 635f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int colWidth = mCellWidth; 63660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 63760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Text is vertically centered within the day of week height. 63860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final float halfLineHeight = (p.ascent() + p.descent()) / 2f; 63960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int rowCenter = headerHeight + rowHeight / 2; 64060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 64160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette for (int col = 0; col < DAYS_IN_WEEK; col++) { 64260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int colCenter = colWidth * col + colWidth / 2; 643ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette final int colCenterRtl; 644ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette if (isLayoutRtl()) { 645ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette colCenterRtl = mPaddedWidth - colCenter; 646ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } else { 647ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette colCenterRtl = colCenter; 648ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } 649ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette 650d5c85c824bfe343144af68ede9e86f12d5bba1f7Alan Viverette final String label = mDayOfWeekLabels[col]; 651ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p); 6520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 6530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 6540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 6560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Draws the month days. 6570ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 6580ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private void drawDays(Canvas canvas) { 65960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final TextPaint p = mDayPaint; 66060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int headerHeight = mMonthHeight + mDayOfWeekHeight; 66160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int rowHeight = mDayHeight; 662f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int colWidth = mCellWidth; 6630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 66460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Text is vertically centered within the row height. 66560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final float halfLineHeight = (p.ascent() + p.descent()) / 2f; 66660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette int rowCenter = headerHeight + rowHeight / 2; 6670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 66860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) { 66960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int colCenter = colWidth * col + colWidth / 2; 670ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette final int colCenterRtl; 671ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette if (isLayoutRtl()) { 672ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette colCenterRtl = mPaddedWidth - colCenter; 673ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } else { 674ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette colCenterRtl = colCenter; 675ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } 676ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette 6770ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette int stateMask = 0; 6780ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6795c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette final boolean isDayEnabled = isDayEnabled(day); 6805c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (isDayEnabled) { 6810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette stateMask |= StateSet.VIEW_STATE_ENABLED; 6820ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 6830ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6840ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final boolean isDayActivated = mActivatedDay == day; 685e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final boolean isDayHighlighted = mHighlightedDay == day; 6860ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (isDayActivated) { 6870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette stateMask |= StateSet.VIEW_STATE_ACTIVATED; 6880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6890ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Adjust the circle to be centered on the row. 690e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount final Paint paint = isDayHighlighted ? mDayHighlightSelectorPaint : 691e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount mDaySelectorPaint; 692e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, paint); 693e998c3f5f6e7ac21b18cd449088ac2b76b65671fGeorge Mount } else if (isDayHighlighted) { 6940ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette stateMask |= StateSet.VIEW_STATE_PRESSED; 6950ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 6965c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (isDayEnabled) { 6975c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette // Adjust the circle to be centered on the row. 6985c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette canvas.drawCircle(colCenterRtl, rowCenter, 6995c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette mDaySelectorRadius, mDayHighlightPaint); 7005c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette } 7010ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 7020ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 7030ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final boolean isDayToday = mToday == day; 7040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int dayTextColor; 7050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (isDayToday && !isDayActivated) { 7060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette dayTextColor = mDaySelectorPaint.getColor(); 7070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } else { 7080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int[] stateSet = StateSet.get(stateMask); 7090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette dayTextColor = mDayTextColor.getColorForState(stateSet, 0); 7100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 71160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette p.setColor(dayTextColor); 7120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 7131ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haase canvas.drawText(mDayFormatter.format(day), colCenterRtl, rowCenter - halfLineHeight, p); 7140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 71560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette col++; 7160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 71760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (col == DAYS_IN_WEEK) { 71860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette col = 0; 71960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette rowCenter += rowHeight; 7200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 7210ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 722bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 723bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 7245c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette private boolean isDayEnabled(int day) { 7255c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette return day >= mEnabledDayStart && day <= mEnabledDayEnd; 7265c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette } 7275c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette 7285c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette private boolean isValidDayOfMonth(int day) { 7295c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette return day >= 1 && day <= mDaysInMonth; 7305c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette } 7315c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette 732518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette private static boolean isValidDayOfWeek(int day) { 7335878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY; 7345878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette } 7355878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette 7365878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette private static boolean isValidMonth(int month) { 7375878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette return month >= Calendar.JANUARY && month <= Calendar.DECEMBER; 73875b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio } 73975b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio 740bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 7410ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Sets the selected day. 742bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 7430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param dayOfMonth the selected day of the month, or {@code -1} to clear 7440ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * the selection 7450ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 7460ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setSelectedDay(int dayOfMonth) { 7470ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mActivatedDay = dayOfMonth; 7480ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 7490ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Invalidate cached accessibility information. 7500ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTouchHelper.invalidateRoot(); 7510ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 7520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 7530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 7540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 7550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Sets the first day of the week. 7560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * 7570ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param weekStart which day the week should start on, valid values are 7580ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY} 7590ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 7600ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setFirstDayOfWeek(int weekStart) { 7610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (isValidDayOfWeek(weekStart)) { 7620ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mWeekStart = weekStart; 7630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } else { 7640ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mWeekStart = mCalendar.getFirstDayOfWeek(); 7650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 7660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 767e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette updateDayOfWeekLabels(); 768e264f9503b8ffcb2cae84e4e5bb2a03dd0610221Alan Viverette 7690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Invalidate cached accessibility information. 7700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTouchHelper.invalidateRoot(); 7710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 7720ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 7730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 7740ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 7750ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Sets all the parameters for displaying this week. 7760ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * <p> 7770ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Parameters have a default value and will only update if a new value is 7780ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * included, except for focus month, which will always default to no focus 7790ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * month if no value is passed in. The only required parameter is the week 7800ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * start. 7810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * 7820ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param selectedDay the selected day of the month, or -1 for no selection 7830ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param month the month 7840ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param year the year 7850ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param weekStart which day the week should start on, valid values are 7860ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY} 7870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param enabledDayStart the first enabled day 7880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param enabledDayEnd the last enabled day 789bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 79075b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart, 79175b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio int enabledDayEnd) { 792c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mActivatedDay = selectedDay; 793bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 7945878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette if (isValidMonth(month)) { 79575b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio mMonth = month; 79675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio } 79775b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio mYear = year; 798bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 799bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mCalendar.set(Calendar.MONTH, mMonth); 800bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mCalendar.set(Calendar.YEAR, mYear); 801bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mCalendar.set(Calendar.DAY_OF_MONTH, 1); 802bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK); 803bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 804518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette if (isValidDayOfWeek(weekStart)) { 80575b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio mWeekStart = weekStart; 806bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else { 807bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mWeekStart = mCalendar.getFirstDayOfWeek(); 808bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 809bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 8100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Figure out what day today is. 8110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final Calendar today = Calendar.getInstance(); 8120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mToday = -1; 8130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDaysInMonth = getDaysInMonth(mMonth, mYear); 8140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette for (int i = 0; i < mDaysInMonth; i++) { 815bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int day = i + 1; 816bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (sameDay(day, today)) { 817bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mToday = day; 818bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 819bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 8200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 8215c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette mEnabledDayStart = MathUtils.constrain(enabledDayStart, 1, mDaysInMonth); 8225c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette mEnabledDayEnd = MathUtils.constrain(enabledDayEnd, mEnabledDayStart, mDaysInMonth); 8235c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette 82408dbbcdc9b4176b4bd91904065c87f0dae29f2d4Alan Viverette updateMonthYearLabel(); 82508dbbcdc9b4176b4bd91904065c87f0dae29f2d4Alan Viverette updateDayOfWeekLabels(); 82608dbbcdc9b4176b4bd91904065c87f0dae29f2d4Alan Viverette 827bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Invalidate cached accessibility information. 828bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mTouchHelper.invalidateRoot(); 82908dbbcdc9b4176b4bd91904065c87f0dae29f2d4Alan Viverette invalidate(); 830bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 831bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 832bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static int getDaysInMonth(int month, int year) { 833bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio switch (month) { 834bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.JANUARY: 835bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.MARCH: 836bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.MAY: 837bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.JULY: 838bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.AUGUST: 839bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.OCTOBER: 840bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.DECEMBER: 841bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return 31; 842bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.APRIL: 843bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.JUNE: 844bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.SEPTEMBER: 845bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.NOVEMBER: 846bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return 30; 847bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.FEBRUARY: 848bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return (year % 4 == 0) ? 29 : 28; 849bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio default: 850bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio throw new IllegalArgumentException("Invalid Month"); 851bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 852bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 853bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 8540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private boolean sameDay(int day, Calendar today) { 8550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH) 8560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette && day == today.get(Calendar.DAY_OF_MONTH); 857bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 858bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 859bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 860bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 861816aa142023c75cf427401c21831193998bdf233Alan Viverette final int preferredHeight = mDesiredDayHeight * MAX_WEEKS_IN_MONTH 862816aa142023c75cf427401c21831193998bdf233Alan Viverette + mDesiredDayOfWeekHeight + mDesiredMonthHeight 863816aa142023c75cf427401c21831193998bdf233Alan Viverette + getPaddingTop() + getPaddingBottom(); 864f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK 8650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette + getPaddingStart() + getPaddingEnd(); 8660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec); 8670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec); 8680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette setMeasuredDimension(resolvedWidth, resolvedHeight); 869bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 870bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 871bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 872ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) { 873ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette super.onRtlPropertiesChanged(layoutDirection); 874ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette 875ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette requestLayout(); 876ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } 877ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette 878ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette @Override 879f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 880f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette if (!changed) { 881f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette return; 882f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette } 883f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 884f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // Let's initialize a completely reasonable number of variables. 885f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int w = right - left; 886f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int h = bottom - top; 887f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddingLeft = getPaddingLeft(); 888f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddingTop = getPaddingTop(); 889f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddingRight = getPaddingRight(); 890f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddingBottom = getPaddingBottom(); 891f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddedRight = w - paddingRight; 892f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddedBottom = h - paddingBottom; 893f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddedWidth = paddedRight - paddingLeft; 894f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddedHeight = paddedBottom - paddingTop; 895f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette if (paddedWidth == mPaddedWidth || paddedHeight == mPaddedHeight) { 896f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette return; 897f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette } 898f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 899f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mPaddedWidth = paddedWidth; 900f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mPaddedHeight = paddedHeight; 901f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 902f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // We may have been laid out smaller than our preferred size. If so, 903f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // scale all dimensions to fit. 904f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int measuredPaddedHeight = getMeasuredHeight() - paddingTop - paddingBottom; 905f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final float scaleH = paddedHeight / (float) measuredPaddedHeight; 906f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int monthHeight = (int) (mDesiredMonthHeight * scaleH); 90760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int cellWidth = mPaddedWidth / DAYS_IN_WEEK; 908f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mMonthHeight = monthHeight; 909f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDayOfWeekHeight = (int) (mDesiredDayOfWeekHeight * scaleH); 910f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDayHeight = (int) (mDesiredDayHeight * scaleH); 911f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mCellWidth = cellWidth; 912f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 913f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // Compute the largest day selector radius that's still within the clip 914f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // bounds and desired selector radius. 915f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int maxSelectorWidth = cellWidth / 2 + Math.min(paddingLeft, paddingRight); 916f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int maxSelectorHeight = mDayHeight / 2 + paddingBottom; 917f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius, 918f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette Math.min(maxSelectorWidth, maxSelectorHeight)); 91960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 920bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Invalidate cached accessibility information. 921bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mTouchHelper.invalidateRoot(); 922bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 923bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 9240ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int findDayOffset() { 9250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int offset = mDayOfWeekStart - mWeekStart; 9260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (mDayOfWeekStart < mWeekStart) { 9270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return offset + DAYS_IN_WEEK; 928bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 9290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return offset; 930bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 931bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 932bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 93378bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette * Calculates the day of the month at the specified touch position. Returns 93478bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette * the day of the month or -1 if the position wasn't in a valid day. 9350ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * 9360ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param x the x position of the touch event 9370ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param y the y position of the touch event 93878bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette * @return the day of the month at (x, y), or -1 if the position wasn't in 93978bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette * a valid day 940bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 94178bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette private int getDayAtLocation(int x, int y) { 94260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int paddedX = x - getPaddingLeft(); 9430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (paddedX < 0 || paddedX >= mPaddedWidth) { 9440ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return -1; 9450ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 946c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 9470ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int headerHeight = mMonthHeight + mDayOfWeekHeight; 94860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int paddedY = y - getPaddingTop(); 9490ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (paddedY < headerHeight || paddedY >= mPaddedHeight) { 9500ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return -1; 9510ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 952c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 953ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette // Adjust for RTL after applying padding. 954ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette final int paddedXRtl; 955ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette if (isLayoutRtl()) { 956ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette paddedXRtl = mPaddedWidth - paddedX; 957ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } else { 958ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette paddedXRtl = paddedX; 959ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } 960ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette 9610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int row = (paddedY - headerHeight) / mDayHeight; 962ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette final int col = (paddedXRtl * DAYS_IN_WEEK) / mPaddedWidth; 9630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int index = col + row * DAYS_IN_WEEK; 9640ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int day = index + 1 - findDayOffset(); 9655c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (!isValidDayOfMonth(day)) { 9660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return -1; 967bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 968bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 9690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return day; 970bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 971bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 972bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 9730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Calculates the bounds of the specified day. 974bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 97578bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette * @param id the day of the month 9760ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param outBounds the rect to populate with bounds 977bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 97878bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette private boolean getBoundsForDay(int id, Rect outBounds) { 9795c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (!isValidDayOfMonth(id)) { 9800ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return false; 981bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 982bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 98360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int index = id - 1 + findDayOffset(); 98460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 985ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette // Compute left edge, taking into account RTL. 9860ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int col = index % DAYS_IN_WEEK; 987f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int colWidth = mCellWidth; 988ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette final int left; 989ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette if (isLayoutRtl()) { 990ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette left = getWidth() - getPaddingRight() - (col + 1) * colWidth; 991ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } else { 992ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette left = getPaddingLeft() + col * colWidth; 993ddf655c49f4173aa55c9ba1a2622cf75cf5bc2f2Alan Viverette } 9940ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 99560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Compute top edge. 99660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int row = index / DAYS_IN_WEEK; 99760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int rowHeight = mDayHeight; 9980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int headerHeight = mMonthHeight + mDayOfWeekHeight; 99960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int top = getPaddingTop() + headerHeight + row * rowHeight; 10000ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 100160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette outBounds.set(left, top, left + colWidth, top + rowHeight); 1002bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 100378bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette return true; 100460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 100560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 100660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette /** 1007bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Called when the user clicks on a day. Handles callbacks to the 1008bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * {@link OnDayClickListener} if one is set. 1009bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 10100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param day the day that was clicked 1011bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 101260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private boolean onDayClicked(int day) { 10135c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (!isValidDayOfMonth(day) || !isDayEnabled(day)) { 101460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return false; 101560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 101660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 1017bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (mOnDayClickListener != null) { 101860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final Calendar date = Calendar.getInstance(); 1019bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio date.set(mYear, mMonth, day); 1020bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mOnDayClickListener.onDayClick(this, date); 1021bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1022bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1023bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // This is a no-op if accessibility is turned off. 1024bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED); 102560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return true; 102660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 102760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 102860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette /** 1029bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Provides a virtual view hierarchy for interfacing with an accessibility 1030bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * service. 1031bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 1032bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private class MonthViewTouchHelper extends ExploreByTouchHelper { 1033bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final String DATE_FORMAT = "dd MMMM yyyy"; 1034bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1035bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private final Rect mTempRect = new Rect(); 1036bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private final Calendar mTempCalendar = Calendar.getInstance(); 1037bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1038bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public MonthViewTouchHelper(View host) { 1039bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio super(host); 1040bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1041bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1042bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 1043bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected int getVirtualViewAt(float x, float y) { 104478bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette final int day = getDayAtLocation((int) (x + 0.5f), (int) (y + 0.5f)); 10455c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (day != -1) { 1046bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return day; 1047bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1048bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return ExploreByTouchHelper.INVALID_ID; 1049bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1050bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1051bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 1052ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette protected void getVisibleVirtualViews(IntArray virtualViewIds) { 10530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette for (int day = 1; day <= mDaysInMonth; day++) { 1054bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio virtualViewIds.add(day); 1055bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1056bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1057bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1058bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 1059bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { 106078bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette event.setContentDescription(getDayDescription(virtualViewId)); 1061bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1062bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1063bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 1064bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) { 106578bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect); 10660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 10670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (!hasBounds) { 10680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // The day is invalid, kill the node. 10690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTempRect.setEmpty(); 10700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette node.setContentDescription(""); 10710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette node.setBoundsInParent(mTempRect); 10720ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette node.setVisibleToUser(false); 10730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return; 10740ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 1075bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 107678bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette node.setText(getDayText(virtualViewId)); 107778bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette node.setContentDescription(getDayDescription(virtualViewId)); 1078bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio node.setBoundsInParent(mTempRect); 10795c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette 10805c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette final boolean isDayEnabled = isDayEnabled(virtualViewId); 10815c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (isDayEnabled) { 10825c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette node.addAction(AccessibilityAction.ACTION_CLICK); 10835c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette } 10845c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette 10855c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette node.setEnabled(isDayEnabled); 1086bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1087c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette if (virtualViewId == mActivatedDay) { 108860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // TODO: This should use activated once that's supported. 108960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette node.setChecked(true); 1090bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1091bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1092bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1093bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1094bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 1095bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected boolean onPerformActionForVirtualView(int virtualViewId, int action, 1096bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio Bundle arguments) { 1097bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio switch (action) { 1098bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case AccessibilityNodeInfo.ACTION_CLICK: 109978bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette return onDayClicked(virtualViewId); 1100bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1101bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1102bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return false; 1103bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1104bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1105bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 110660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * Generates a description for a given virtual view. 1107bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 110878bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette * @param id the day to generate a description for 110960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @return a description of the virtual view 1110bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 111178bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette private CharSequence getDayDescription(int id) { 11125c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (isValidDayOfMonth(id)) { 111360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mTempCalendar.set(mYear, mMonth, id); 111460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis()); 111560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 1116bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 111760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return ""; 111860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 111960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 112060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette /** 112160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * Generates displayed text for a given virtual view. 112260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * 112378bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette * @param id the day to generate text for 112460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @return the visible text of the virtual view 112560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette */ 112678bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette private CharSequence getDayText(int id) { 11275c33949d0272c4dfc313cf8b56d28597e2ec2c62Alan Viverette if (isValidDayOfMonth(id)) { 11281ea4715cecf8d1a17a21f3d21ea4df56519e5ea6Chet Haase return mDayFormatter.format(id); 1129bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1130bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 113160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return null; 1132bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1133bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1134bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1135bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 1136bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Handles callbacks when the user clicks on a time object. 1137bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 1138bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public interface OnDayClickListener { 113978bf1d329a4c0210394f846be1fd1390314aefc0Alan Viverette void onDayClick(SimpleMonthView view, Calendar day); 1140bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 1141bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio} 1142