SimpleMonthView.java revision f63757b7a6d06138860640f8e95326dc1ff47646
1bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio/* 2bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Copyright (C) 2014 The Android Open Source Project 3bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 4bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Licensed under the Apache License, Version 2.0 (the "License"); 5bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * you may not use this file except in compliance with the License. 6bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * You may obtain a copy of the License at 7bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 8bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * http://www.apache.org/licenses/LICENSE-2.0 9bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 10bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Unless required by applicable law or agreed to in writing, software 11bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * distributed under the License is distributed on an "AS IS" BASIS, 12bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * See the License for the specific language governing permissions and 14bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * limitations under the License. 15bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 16bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 17bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Megliopackage android.widget; 18bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 19bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.Context; 20bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.res.ColorStateList; 21bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.content.res.Resources; 220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viveretteimport android.content.res.TypedArray; 23bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Canvas; 24bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint; 25bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint.Align; 26bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Paint.Style; 27bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Rect; 28bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.graphics.Typeface; 2960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viveretteimport android.graphics.drawable.Drawable; 30bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.os.Bundle; 315dc973cb03889c58988703b58aefbd2397fb02c3Alan Viveretteimport android.text.TextPaint; 32bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.text.format.DateFormat; 33bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.util.AttributeSet; 34ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viveretteimport android.util.IntArray; 35c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viveretteimport android.util.StateSet; 36bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.MotionEvent; 37bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.View; 38bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityEvent; 39bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo; 400ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viveretteimport android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 41bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 42bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport com.android.internal.R; 43bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport com.android.internal.widget.ExploreByTouchHelper; 44bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 45fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viveretteimport java.text.SimpleDateFormat; 46bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Calendar; 47bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Locale; 48bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 49bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio/** 50bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * A calendar-like view displaying a specified month and the appropriate selectable day numbers 51bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * within the specified month. 52bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 53bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioclass SimpleMonthView extends View { 540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private static final int DAYS_IN_WEEK = 7; 550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private static final int MAX_WEEKS_IN_MONTH = 6; 56bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 57bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final int DEFAULT_SELECTED_DAY = -1; 58bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final int DEFAULT_WEEK_START = Calendar.SUNDAY; 59bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 600ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private static final String DEFAULT_TITLE_FORMAT = "MMMMy"; 610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private static final String DAY_OF_WEEK_FORMAT = "EEEEE"; 62c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 6360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette /** Virtual view ID for previous button. */ 6460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private static final int ITEM_ID_PREV = 0x101; 6560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 6660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette /** Virtual view ID for next button. */ 6760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private static final int ITEM_ID_NEXT = 0x100; 6860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 695dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette private final TextPaint mMonthPaint = new TextPaint(); 705dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette private final TextPaint mDayOfWeekPaint = new TextPaint(); 715dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette private final TextPaint mDayPaint = new TextPaint(); 720ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final Paint mDaySelectorPaint = new Paint(); 730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final Paint mDayHighlightPaint = new Paint(); 745dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette 750ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final Calendar mCalendar = Calendar.getInstance(); 7660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance(); 77bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 780ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final MonthViewTouchHelper mTouchHelper; 79fd2dd208d9b0957657589b26c14c48b8e2b7f387Alan Viverette 800ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final SimpleDateFormat mTitleFormatter; 810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private final SimpleDateFormat mDayOfWeekFormatter; 82bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 83f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // Desired dimensions. 84f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredMonthHeight; 85f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredDayOfWeekHeight; 86f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredDayHeight; 87f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredCellWidth; 88f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private final int mDesiredDaySelectorRadius; 8960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 9060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Next/previous drawables. 9160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private final Drawable mPrevDrawable; 9260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private final Drawable mNextDrawable; 9360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private final Rect mPrevHitArea; 9460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private final Rect mNextHitArea; 9560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private final CharSequence mPrevContentDesc; 9660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private final CharSequence mNextContentDesc; 9760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private CharSequence mTitle; 99bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 100bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mMonth; 101bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mYear; 102bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 103f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // Dimensions as laid out. 104f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mMonthHeight; 105f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mDayOfWeekHeight; 106f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mDayHeight; 107f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mCellWidth; 108f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette private int mDaySelectorRadius; 109f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 1100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mPaddedWidth; 1110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mPaddedHeight; 112bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The day of month for the selected day, or -1 if no day is selected. */ 114c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette private int mActivatedDay = -1; 115bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 1170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * The day of month for today, or -1 if the today is not in the current 1180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * month. 1190ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 120bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mToday = DEFAULT_SELECTED_DAY; 121bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The first day of the week (ex. Calendar.SUNDAY). */ 123bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mWeekStart = DEFAULT_WEEK_START; 124bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The number of days (ex. 28) in the current month. */ 1260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mDaysInMonth; 127bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 1290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * The day of week (ex. Calendar.SUNDAY) for the first day of the current 1300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * month. 1310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 1320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mDayOfWeekStart; 133bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1340ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The day of month for the first (inclusive) enabled day. */ 135bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mEnabledDayStart = 1; 136bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1370ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The day of month for the last (inclusive) enabled day. */ 138bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mEnabledDayEnd = 31; 139bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1400ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** The number of week rows needed to display the current month. */ 1410ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int mNumWeeks = MAX_WEEKS_IN_MONTH; 142bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** Optional listener for handling day click actions. */ 144bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private OnDayClickListener mOnDayClickListener; 145bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 146c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette private ColorStateList mDayTextColor; 147c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 14860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private int mTouchedItem = -1; 14960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 15060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private boolean mPrevEnabled; 15160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private boolean mNextEnabled; 1520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 153bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public SimpleMonthView(Context context) { 154bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio this(context, null); 155bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 156bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 157bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public SimpleMonthView(Context context, AttributeSet attrs) { 158bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio this(context, attrs, R.attr.datePickerStyle); 159bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 160bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 16150eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette public SimpleMonthView(Context context, AttributeSet attrs, int defStyleAttr) { 16250eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette this(context, attrs, defStyleAttr, 0); 16350eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette } 164bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 16550eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette public SimpleMonthView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 16650eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette super(context, attrs, defStyleAttr, defStyleRes); 167bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 16850eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette final Resources res = context.getResources(); 169f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height); 170f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height); 171f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height); 172f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width); 173f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDesiredDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius); 174bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 17560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left); 17660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right); 17760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mPrevHitArea = mPrevDrawable != null ? new Rect() : null; 17860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mNextHitArea = mNextDrawable != null ? new Rect() : null; 17960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mPrevContentDesc = res.getText(R.string.date_picker_prev_month_button); 18060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mNextContentDesc = res.getText(R.string.date_picker_next_month_button); 18160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 182bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Set up accessibility components. 183bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mTouchHelper = new MonthViewTouchHelper(this); 184bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setAccessibilityDelegate(mTouchHelper); 185bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); 186bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final Locale locale = res.getConfiguration().locale; 1880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT); 1890ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTitleFormatter = new SimpleDateFormat(titleFormat, locale); 1900ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale); 1910ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 1920ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette setClickable(true); 1930ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette initPaints(res); 1940ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 1950ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 19660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette public void setNextEnabled(boolean enabled) { 19760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mNextEnabled = enabled; 19860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mTouchHelper.invalidateRoot(); 19960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette invalidate(); 20060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 20160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 20260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette public void setPrevEnabled(boolean enabled) { 20360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mPrevEnabled = enabled; 20460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mTouchHelper.invalidateRoot(); 20560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette invalidate(); 20660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 20760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 2080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 2090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Applies the specified text appearance resource to a paint, returning the 2100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * text color if one is set in the text appearance. 2110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * 2120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param p the paint to modify 2130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param resId the resource ID of the text appearance 2140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @return the text color, if available 2150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 2160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private ColorStateList applyTextAppearance(Paint p, int resId) { 2170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final TypedArray ta = mContext.obtainStyledAttributes(null, 2180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.styleable.TextAppearance, 0, resId); 2190ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily); 2210ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (fontFamily != null) { 2220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette p.setTypeface(Typeface.create(fontFamily, 0)); 2230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2240ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette p.setTextSize(ta.getDimensionPixelSize( 2260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.styleable.TextAppearance_textSize, (int) p.getTextSize())); 2270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor); 2290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (textColor != null) { 2300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0); 2310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette p.setColor(enabledColor); 2320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2340ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette ta.recycle(); 2350ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2360ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return textColor; 2370ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2380ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2390ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setMonthTextAppearance(int resId) { 24060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final ColorStateList monthColor = applyTextAppearance(mMonthPaint, resId); 24160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (monthColor != null) { 24260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mPrevDrawable != null) { 24360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mPrevDrawable.setTintList(monthColor); 24460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 24560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mNextDrawable != null) { 24660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mNextDrawable.setTintList(monthColor); 24760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 24860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 24960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 2500ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 2510ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setDayOfWeekTextAppearance(int resId) { 2540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette applyTextAppearance(mDayOfWeekPaint, resId); 2550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 2560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2570ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2580ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setDayTextAppearance(int resId) { 2590ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final ColorStateList textColor = applyTextAppearance(mDayPaint, resId); 2600ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (textColor != null) { 2610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayTextColor = textColor; 2620ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2640ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 2650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public CharSequence getTitle() { 2680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (mTitle == null) { 2690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTitle = mTitleFormatter.format(mCalendar.getTime()); 2700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 2710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return mTitle; 272c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette } 273c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 274c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette /** 275c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette * Sets up the text and style properties for painting. 276c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette */ 2770ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private void initPaints(Resources res) { 2780ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String monthTypeface = res.getString(R.string.date_picker_month_typeface); 2790ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String dayOfWeekTypeface = res.getString(R.string.date_picker_day_of_week_typeface); 2800ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final String dayTypeface = res.getString(R.string.date_picker_day_typeface); 2810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 2820ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int monthTextSize = res.getDimensionPixelSize( 2830ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.dimen.date_picker_month_text_size); 2840ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int dayOfWeekTextSize = res.getDimensionPixelSize( 2850ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.dimen.date_picker_day_of_week_text_size); 2860ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int dayTextSize = res.getDimensionPixelSize( 2870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette R.dimen.date_picker_day_text_size); 2880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 289c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mMonthPaint.setAntiAlias(true); 2900ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mMonthPaint.setTextSize(monthTextSize); 2910ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0)); 292c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mMonthPaint.setTextAlign(Align.CENTER); 293c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mMonthPaint.setStyle(Style.FILL); 294c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 295c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayOfWeekPaint.setAntiAlias(true); 2960ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayOfWeekPaint.setTextSize(dayOfWeekTextSize); 2970ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0)); 298c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayOfWeekPaint.setTextAlign(Align.CENTER); 299c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayOfWeekPaint.setStyle(Style.FILL); 300c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 3010ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDaySelectorPaint.setAntiAlias(true); 3020ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDaySelectorPaint.setStyle(Style.FILL); 3030ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 3040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayHighlightPaint.setAntiAlias(true); 3050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayHighlightPaint.setStyle(Style.FILL); 306c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 307c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayPaint.setAntiAlias(true); 3080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayPaint.setTextSize(dayTextSize); 3090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayPaint.setTypeface(Typeface.create(dayTypeface, 0)); 310c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayPaint.setTextAlign(Align.CENTER); 311c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayPaint.setStyle(Style.FILL); 312bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 313bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 314c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette void setMonthTextColor(ColorStateList monthTextColor) { 315c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0); 316c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mMonthPaint.setColor(enabledColor); 317c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette invalidate(); 318c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette } 319bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 320c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette void setDayOfWeekTextColor(ColorStateList dayOfWeekTextColor) { 321c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette final int enabledColor = dayOfWeekTextColor.getColorForState(ENABLED_STATE_SET, 0); 322c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayOfWeekPaint.setColor(enabledColor); 323c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette invalidate(); 324c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette } 325bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 326c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette void setDayTextColor(ColorStateList dayTextColor) { 327c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mDayTextColor = dayTextColor; 328c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette invalidate(); 329c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette } 330bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 3310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette void setDaySelectorColor(ColorStateList dayBackgroundColor) { 3325dc973cb03889c58988703b58aefbd2397fb02c3Alan Viverette final int activatedColor = dayBackgroundColor.getColorForState( 333c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0); 3340ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDaySelectorPaint.setColor(activatedColor); 335c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette invalidate(); 336bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 337bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 3380ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette void setDayHighlightColor(ColorStateList dayHighlightColor) { 3390ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int pressedColor = dayHighlightColor.getColorForState( 3400ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED), 0); 3410ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDayHighlightPaint.setColor(pressedColor); 3420ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 343bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 344bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 345bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void setOnDayClickListener(OnDayClickListener listener) { 346bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mOnDayClickListener = listener; 347bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 348bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 349bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 350bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public boolean dispatchHoverEvent(MotionEvent event) { 351bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // First right-of-refusal goes the touch exploration helper. 3520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event); 353bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 354bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 355bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 356bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public boolean onTouchEvent(MotionEvent event) { 35760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int x = (int) (event.getX() + 0.5f); 35860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int y = (int) (event.getY() + 0.5f); 35960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 360bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio switch (event.getAction()) { 3610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette case MotionEvent.ACTION_DOWN: 3620ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette case MotionEvent.ACTION_MOVE: 36360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int touchedItem = getItemAtLocation(x, y); 36460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mTouchedItem != touchedItem) { 36560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mTouchedItem = touchedItem; 3660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 367bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 368bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio break; 3690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 3700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette case MotionEvent.ACTION_UP: 37160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int clickedItem = getItemAtLocation(x, y); 37260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette onItemClicked(clickedItem, true); 3730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Fall through. 3740ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette case MotionEvent.ACTION_CANCEL: 3750ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Reset touched day on stream end. 37660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mTouchedItem = -1; 3770ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 3780ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette break; 379bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 380bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return true; 381bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 382bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 383bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 384bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onDraw(Canvas canvas) { 3850ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int paddingLeft = getPaddingLeft(); 3860ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int paddingTop = getPaddingTop(); 3870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette canvas.translate(paddingLeft, paddingTop); 3880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 3890ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette drawMonth(canvas); 3900ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette drawDaysOfWeek(canvas); 391bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio drawDays(canvas); 39260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette drawButtons(canvas); 3930ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 3940ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette canvas.translate(-paddingLeft, -paddingTop); 3950ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 3960ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 3970ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private void drawMonth(Canvas canvas) { 3980ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final float x = mPaddedWidth / 2f; 3990ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 4000ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Vertically centered within the month header height. 4010ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent(); 4020ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final float y = (mMonthHeight - lineHeight) / 2f; 4030ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 4040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette canvas.drawText(getTitle().toString(), x, y, mMonthPaint); 4050ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 4060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 4070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private void drawDaysOfWeek(Canvas canvas) { 40860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final TextPaint p = mDayOfWeekPaint; 40960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int headerHeight = mMonthHeight; 41060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int rowHeight = mDayOfWeekHeight; 411f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int colWidth = mCellWidth; 41260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 41360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Text is vertically centered within the day of week height. 41460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final float halfLineHeight = (p.ascent() + p.descent()) / 2f; 41560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int rowCenter = headerHeight + rowHeight / 2; 41660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 41760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette for (int col = 0; col < DAYS_IN_WEEK; col++) { 41860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int colCenter = colWidth * col + colWidth / 2; 41960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK; 42060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final String label = getDayOfWeekLabel(dayOfWeek); 42160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p); 4220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 4230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 4240ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 42560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private String getDayOfWeekLabel(int dayOfWeek) { 42660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); 42760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return mDayOfWeekFormatter.format(mDayOfWeekLabelCalendar.getTime()); 42860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 42960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 4300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 4310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Draws the month days. 4320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 4330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private void drawDays(Canvas canvas) { 43460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final TextPaint p = mDayPaint; 43560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int headerHeight = mMonthHeight + mDayOfWeekHeight; 43660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int rowHeight = mDayHeight; 437f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int colWidth = mCellWidth; 4380ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 43960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Text is vertically centered within the row height. 44060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final float halfLineHeight = (p.ascent() + p.descent()) / 2f; 44160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette int rowCenter = headerHeight + rowHeight / 2; 4420ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 44360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) { 44460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int colCenter = colWidth * col + colWidth / 2; 4450ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette int stateMask = 0; 4460ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 4470ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (day >= mEnabledDayStart && day <= mEnabledDayEnd) { 4480ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette stateMask |= StateSet.VIEW_STATE_ENABLED; 4490ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 4500ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 4510ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final boolean isDayActivated = mActivatedDay == day; 4520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (isDayActivated) { 4530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette stateMask |= StateSet.VIEW_STATE_ACTIVATED; 4540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 4550ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Adjust the circle to be centered on the row. 45660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint); 45760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } else if (mTouchedItem == day) { 4580ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette stateMask |= StateSet.VIEW_STATE_PRESSED; 4590ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 4600ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Adjust the circle to be centered on the row. 46160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint); 4620ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 4630ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 4640ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final boolean isDayToday = mToday == day; 4650ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int dayTextColor; 4660ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (isDayToday && !isDayActivated) { 4670ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette dayTextColor = mDaySelectorPaint.getColor(); 4680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } else { 4690ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int[] stateSet = StateSet.get(stateMask); 4700ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette dayTextColor = mDayTextColor.getColorForState(stateSet, 0); 4710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 47260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette p.setColor(dayTextColor); 4730ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 47460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p); 4750ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 47660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette col++; 4770ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 47860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (col == DAYS_IN_WEEK) { 47960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette col = 0; 48060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette rowCenter += rowHeight; 4810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 4820ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 483bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 484bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 48560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private void drawButtons(Canvas canvas) { 48660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mPrevEnabled && mPrevDrawable != null) { 48760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mPrevDrawable.draw(canvas); 48860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 48960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 49060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mNextEnabled && mNextDrawable != null) { 49160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mNextDrawable.draw(canvas); 49260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 49360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 49460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 495518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette private static boolean isValidDayOfWeek(int day) { 4965878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY; 4975878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette } 4985878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette 4995878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette private static boolean isValidMonth(int month) { 5005878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette return month >= Calendar.JANUARY && month <= Calendar.DECEMBER; 50175b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio } 50275b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio 503bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 5040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Sets the selected day. 505bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 5060ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param dayOfMonth the selected day of the month, or {@code -1} to clear 5070ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * the selection 5080ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 5090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setSelectedDay(int dayOfMonth) { 5100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mActivatedDay = dayOfMonth; 5110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 5120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Invalidate cached accessibility information. 5130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTouchHelper.invalidateRoot(); 5140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 5150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 5160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 5170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 5180ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Sets the first day of the week. 5190ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * 5200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param weekStart which day the week should start on, valid values are 5210ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY} 5220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette */ 5230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette public void setFirstDayOfWeek(int weekStart) { 5240ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (isValidDayOfWeek(weekStart)) { 5250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mWeekStart = weekStart; 5260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } else { 5270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mWeekStart = mCalendar.getFirstDayOfWeek(); 5280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 5290ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 5300ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Invalidate cached accessibility information. 5310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTouchHelper.invalidateRoot(); 5320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette invalidate(); 5330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 5340ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 5350ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette /** 5360ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Sets all the parameters for displaying this week. 5370ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * <p> 5380ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Parameters have a default value and will only update if a new value is 5390ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * included, except for focus month, which will always default to no focus 5400ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * month if no value is passed in. The only required parameter is the week 5410ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * start. 5420ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * 5430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param selectedDay the selected day of the month, or -1 for no selection 5440ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param month the month 5450ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param year the year 5460ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param weekStart which day the week should start on, valid values are 5470ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY} 5480ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param enabledDayStart the first enabled day 5490ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param enabledDayEnd the last enabled day 550bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 55175b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart, 55275b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio int enabledDayEnd) { 553c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette mActivatedDay = selectedDay; 554bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 5555878076456a2eb510c03b6160a0cfb99a54e0cfdAlan Viverette if (isValidMonth(month)) { 55675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio mMonth = month; 55775b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio } 55875b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio mYear = year; 559bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 560bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mCalendar.set(Calendar.MONTH, mMonth); 561bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mCalendar.set(Calendar.YEAR, mYear); 562bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mCalendar.set(Calendar.DAY_OF_MONTH, 1); 563bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK); 564bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 565518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette if (isValidDayOfWeek(weekStart)) { 56675b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio mWeekStart = weekStart; 567bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else { 568bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mWeekStart = mCalendar.getFirstDayOfWeek(); 569bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 570bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 57175b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio if (enabledDayStart > 0 && enabledDayEnd < 32) { 57275b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio mEnabledDayStart = enabledDayStart; 573bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 57475b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio if (enabledDayEnd > 0 && enabledDayEnd < 32 && enabledDayEnd >= enabledDayStart) { 57575b121553f9c7498581a4b10d932f1c499feef25Fabrice Di Meglio mEnabledDayEnd = enabledDayEnd; 576bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 577bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 5780ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Figure out what day today is. 5790ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final Calendar today = Calendar.getInstance(); 5800ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mToday = -1; 5810ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mDaysInMonth = getDaysInMonth(mMonth, mYear); 5820ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette for (int i = 0; i < mDaysInMonth; i++) { 583bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int day = i + 1; 584bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (sameDay(day, today)) { 585bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mToday = day; 586bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 587bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 5880ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mNumWeeks = calculateNumRows(); 5890ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 5900ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // Invalidate the old title. 5910ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTitle = null; 592bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 593bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Invalidate cached accessibility information. 594bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mTouchHelper.invalidateRoot(); 595bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 596bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 597bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static int getDaysInMonth(int month, int year) { 598bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio switch (month) { 599bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.JANUARY: 600bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.MARCH: 601bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.MAY: 602bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.JULY: 603bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.AUGUST: 604bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.OCTOBER: 605bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.DECEMBER: 606bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return 31; 607bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.APRIL: 608bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.JUNE: 609bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.SEPTEMBER: 610bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.NOVEMBER: 611bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return 30; 612bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case Calendar.FEBRUARY: 613bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return (year % 4 == 0) ? 29 : 28; 614bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio default: 615bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio throw new IllegalArgumentException("Invalid Month"); 616bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 617bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 618bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 619bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void reuse() { 6200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mNumWeeks = MAX_WEEKS_IN_MONTH; 621bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio requestLayout(); 622bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 623bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 624bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int calculateNumRows() { 6250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int offset = findDayOffset(); 6260ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int dividend = (offset + mDaysInMonth) / DAYS_IN_WEEK; 6270ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int remainder = (offset + mDaysInMonth) % DAYS_IN_WEEK; 6280ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return dividend + (remainder > 0 ? 1 : 0); 629bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 630bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 6310ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private boolean sameDay(int day, Calendar today) { 6320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH) 6330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette && day == today.get(Calendar.DAY_OF_MONTH); 634bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 635bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 636bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 637bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 638f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int preferredHeight = mDesiredDayHeight * mNumWeeks + mDesiredDayOfWeekHeight 639f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette + mDesiredMonthHeight + getPaddingTop() + getPaddingBottom(); 640f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK 6410ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette + getPaddingStart() + getPaddingEnd(); 6420ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec); 6430ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec); 6440ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette setMeasuredDimension(resolvedWidth, resolvedHeight); 645bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 646bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 647bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 648f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 649f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette if (!changed) { 650f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette return; 651f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette } 652f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 653f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // Let's initialize a completely reasonable number of variables. 654f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int w = right - left; 655f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int h = bottom - top; 656f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddingLeft = getPaddingLeft(); 657f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddingTop = getPaddingTop(); 658f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddingRight = getPaddingRight(); 659f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddingBottom = getPaddingBottom(); 660f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddedRight = w - paddingRight; 661f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddedBottom = h - paddingBottom; 662f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddedWidth = paddedRight - paddingLeft; 663f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int paddedHeight = paddedBottom - paddingTop; 664f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette if (paddedWidth == mPaddedWidth || paddedHeight == mPaddedHeight) { 665f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette return; 666f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette } 667f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 668f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mPaddedWidth = paddedWidth; 669f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mPaddedHeight = paddedHeight; 670f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 671f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // We may have been laid out smaller than our preferred size. If so, 672f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // scale all dimensions to fit. 673f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int measuredPaddedHeight = getMeasuredHeight() - paddingTop - paddingBottom; 674f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final float scaleH = paddedHeight / (float) measuredPaddedHeight; 675f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int monthHeight = (int) (mDesiredMonthHeight * scaleH); 67660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int cellWidth = mPaddedWidth / DAYS_IN_WEEK; 677f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mMonthHeight = monthHeight; 678f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDayOfWeekHeight = (int) (mDesiredDayOfWeekHeight * scaleH); 679f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDayHeight = (int) (mDesiredDayHeight * scaleH); 680f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mCellWidth = cellWidth; 681f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette 682f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // Compute the largest day selector radius that's still within the clip 683f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette // bounds and desired selector radius. 684f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int maxSelectorWidth = cellWidth / 2 + Math.min(paddingLeft, paddingRight); 685f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int maxSelectorHeight = mDayHeight / 2 + paddingBottom; 686f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius, 687f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette Math.min(maxSelectorWidth, maxSelectorHeight)); 68860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 68960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Vertically center the previous/next drawables within the month 69060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // header, horizontally center within the day cell, then expand the 69160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // hit area to ensure it's at least 48x48dp. 69260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final Drawable prevDrawable = mPrevDrawable; 69360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (prevDrawable != null) { 69460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int dW = prevDrawable.getIntrinsicWidth(); 69560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int dH = prevDrawable.getIntrinsicHeight(); 69660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int iconTop = (monthHeight - dH) / 2; 69760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int iconLeft = (cellWidth - dW) / 2; 69860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 69960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Button bounds don't include padding, but hit area does. 70060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH); 701f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mPrevHitArea.set(0, 0, paddingLeft + cellWidth, paddingTop + monthHeight); 70260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 70360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 70460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final Drawable nextDrawable = mNextDrawable; 70560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (nextDrawable != null) { 70660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int dW = nextDrawable.getIntrinsicWidth(); 70760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int dH = nextDrawable.getIntrinsicHeight(); 70860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int iconTop = (monthHeight - dH) / 2; 709f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int iconRight = paddedWidth - (cellWidth - dW) / 2; 71060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 71160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Button bounds don't include padding, but hit area does. 71260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH); 713f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette mNextHitArea.set(paddedRight - cellWidth, 0, w, paddingTop + monthHeight); 71460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 715bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 716bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Invalidate cached accessibility information. 717bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mTouchHelper.invalidateRoot(); 718bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 719bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 7200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette private int findDayOffset() { 7210ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int offset = mDayOfWeekStart - mWeekStart; 7220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (mDayOfWeekStart < mWeekStart) { 7230ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return offset + DAYS_IN_WEEK; 724bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 7250ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return offset; 726bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 727bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 728bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 72960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * Calculates the day of the month or item identifier at the specified 73060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * touch position. Returns the day of the month or -1 if the position 73160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * wasn't in a valid day. 7320ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * 7330ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param x the x position of the touch event 7340ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param y the y position of the touch event 73560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @return the day of the month at (x, y), an item identifier, or -1 if the 73660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * position wasn't in a valid day or item 737bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 73860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private int getItemAtLocation(int x, int y) { 73960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mNextEnabled && mNextDrawable != null && mNextHitArea.contains(x, y)) { 74060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return ITEM_ID_NEXT; 74160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } else if (mPrevEnabled && mPrevDrawable != null && mPrevHitArea.contains(x, y)) { 74260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return ITEM_ID_PREV; 74360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 74460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 74560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int paddedX = x - getPaddingLeft(); 7460ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (paddedX < 0 || paddedX >= mPaddedWidth) { 7470ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return -1; 7480ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 749c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 7500ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int headerHeight = mMonthHeight + mDayOfWeekHeight; 75160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int paddedY = y - getPaddingTop(); 7520ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (paddedY < headerHeight || paddedY >= mPaddedHeight) { 7530ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return -1; 7540ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 755c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette 7560ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int row = (paddedY - headerHeight) / mDayHeight; 7570ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth; 7580ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int index = col + row * DAYS_IN_WEEK; 7590ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int day = index + 1 - findDayOffset(); 7600ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (day < 1 || day > mDaysInMonth) { 7610ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return -1; 762bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 763bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 7640ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return day; 765bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 766bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 767bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 7680ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * Calculates the bounds of the specified day. 769bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 77060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @param id the day of the month, or an item identifier 7710ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param outBounds the rect to populate with bounds 772bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 77360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private boolean getBoundsForItem(int id, Rect outBounds) { 77460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mNextEnabled && id == ITEM_ID_NEXT) { 77560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mNextDrawable != null) { 77660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette outBounds.set(mNextHitArea); 77760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return true; 77860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 77960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } else if (mPrevEnabled && id == ITEM_ID_PREV) { 78060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mPrevDrawable != null) { 78160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette outBounds.set(mPrevHitArea); 78260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return true; 78360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 78460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 78560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 78660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (id < 1 || id > mDaysInMonth) { 7870ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return false; 788bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 789bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 79060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int index = id - 1 + findDayOffset(); 79160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 79260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Compute left edge. 7930ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int col = index % DAYS_IN_WEEK; 794f63757b7a6d06138860640f8e95326dc1ff47646Alan Viverette final int colWidth = mCellWidth; 79560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int left = getPaddingLeft() + col * colWidth; 7960ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 79760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // Compute top edge. 79860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int row = index / DAYS_IN_WEEK; 79960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int rowHeight = mDayHeight; 8000ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette final int headerHeight = mMonthHeight + mDayOfWeekHeight; 80160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int top = getPaddingTop() + headerHeight + row * rowHeight; 8020ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 80360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette outBounds.set(left, top, left + colWidth, top + rowHeight); 8040ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return true; 805bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 806bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 807bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 80860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * Called when an item is clicked. 80960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * 81060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @param id the day number or item identifier 81160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette */ 81260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private boolean onItemClicked(int id, boolean animate) { 81360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return onNavigationClicked(id, animate) || onDayClicked(id); 81460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 81560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 81660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette /** 817bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Called when the user clicks on a day. Handles callbacks to the 818bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * {@link OnDayClickListener} if one is set. 819bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 8200ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette * @param day the day that was clicked 821bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 82260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private boolean onDayClicked(int day) { 82360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (day < 0 || day > mDaysInMonth) { 82460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return false; 82560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 82660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 827bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (mOnDayClickListener != null) { 82860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final Calendar date = Calendar.getInstance(); 829bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio date.set(mYear, mMonth, day); 830bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mOnDayClickListener.onDayClick(this, date); 831bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 832bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 833bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // This is a no-op if accessibility is turned off. 834bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED); 83560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return true; 83660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 83760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 83860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette /** 83960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * Called when the user clicks on a navigation button. Handles callbacks to 84060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * the {@link OnDayClickListener} if one is set. 84160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * 84260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @param id the item identifier 84360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette */ 84460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private boolean onNavigationClicked(int id, boolean animate) { 84560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int direction; 84660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (id == ITEM_ID_NEXT) { 84760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette direction = 1; 84860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } else if (id == ITEM_ID_PREV) { 84960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette direction = -1; 85060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } else { 85160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return false; 85260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 85360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 85460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mOnDayClickListener != null) { 85560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mOnDayClickListener.onNavigationClick(this, direction, animate); 85660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 85760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 85860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // This is a no-op if accessibility is turned off. 85960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mTouchHelper.sendEventForVirtualView(id, AccessibilityEvent.TYPE_VIEW_CLICKED); 86060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return true; 861bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 862bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 863bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 864bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Provides a virtual view hierarchy for interfacing with an accessibility 865bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * service. 866bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 867bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private class MonthViewTouchHelper extends ExploreByTouchHelper { 868bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final String DATE_FORMAT = "dd MMMM yyyy"; 869bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 870bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private final Rect mTempRect = new Rect(); 871bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private final Calendar mTempCalendar = Calendar.getInstance(); 872bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 873bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public MonthViewTouchHelper(View host) { 874bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio super(host); 875bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 876bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 877bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 878bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected int getVirtualViewAt(float x, float y) { 87960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final int day = getItemAtLocation((int) (x + 0.5f), (int) (y + 0.5f)); 880bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (day >= 0) { 881bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return day; 882bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 883bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return ExploreByTouchHelper.INVALID_ID; 884bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 885bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 886bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 887ffb46bf2956d89e3190007ccf2ef3ce3eed005feAlan Viverette protected void getVisibleVirtualViews(IntArray virtualViewIds) { 88860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mNextEnabled && mNextDrawable != null) { 88960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette virtualViewIds.add(ITEM_ID_PREV); 89060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 89160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 89260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (mPrevEnabled && mPrevDrawable != null) { 89360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette virtualViewIds.add(ITEM_ID_NEXT); 89460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 89560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 8960ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette for (int day = 1; day <= mDaysInMonth; day++) { 897bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio virtualViewIds.add(day); 898bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 899bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 900bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 901bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 902bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { 903bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio event.setContentDescription(getItemDescription(virtualViewId)); 904bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 905bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 906bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 907bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) { 90860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette final boolean hasBounds = getBoundsForItem(virtualViewId, mTempRect); 9090ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette 9100ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette if (!hasBounds) { 9110ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette // The day is invalid, kill the node. 9120ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette mTempRect.setEmpty(); 9130ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette node.setContentDescription(""); 9140ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette node.setBoundsInParent(mTempRect); 9150ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette node.setVisibleToUser(false); 9160ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette return; 9170ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette } 918bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 91960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette node.setText(getItemText(virtualViewId)); 920bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio node.setContentDescription(getItemDescription(virtualViewId)); 921bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio node.setBoundsInParent(mTempRect); 9220ef59ac0e57e9b99d174d4a53f7d9639357743acAlan Viverette node.addAction(AccessibilityAction.ACTION_CLICK); 923bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 924c5b95c20b6fd3f4e63147efb22dd19c657b17001Alan Viverette if (virtualViewId == mActivatedDay) { 92560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette // TODO: This should use activated once that's supported. 92660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette node.setChecked(true); 927bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 928bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 929bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 930bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 931bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 932bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected boolean onPerformActionForVirtualView(int virtualViewId, int action, 933bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio Bundle arguments) { 934bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio switch (action) { 935bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio case AccessibilityNodeInfo.ACTION_CLICK: 93660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return onItemClicked(virtualViewId, false); 937bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 938bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 939bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return false; 940bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 941bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 942bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 94360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * Generates a description for a given virtual view. 944bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 94560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @param id the day or item identifier to generate a description for 94660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @return a description of the virtual view 947bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 94860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private CharSequence getItemDescription(int id) { 94960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (id == ITEM_ID_NEXT) { 95060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return mNextContentDesc; 95160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } else if (id == ITEM_ID_PREV) { 95260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return mPrevContentDesc; 95360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } else if (id >= 1 && id <= mDaysInMonth) { 95460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette mTempCalendar.set(mYear, mMonth, id); 95560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis()); 95660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 957bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 95860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return ""; 95960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } 96060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette 96160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette /** 96260b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * Generates displayed text for a given virtual view. 96360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * 96460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @param id the day or item identifier to generate text for 96560b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette * @return the visible text of the virtual view 96660b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette */ 96760b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette private CharSequence getItemText(int id) { 96860b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette if (id == ITEM_ID_NEXT || id == ITEM_ID_PREV) { 96960b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return null; 97060b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette } else if (id >= 1 && id <= mDaysInMonth) { 97160b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return Integer.toString(id); 972bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 973bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 97460b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette return null; 975bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 976bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 977bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 978bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 979bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Handles callbacks when the user clicks on a time object. 980bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 981bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public interface OnDayClickListener { 982bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void onDayClick(SimpleMonthView view, Calendar day); 98360b674e07bf7346a673abd4a5f40bddeca16e7ffAlan Viverette public void onNavigationClick(SimpleMonthView view, int direction, boolean animate); 984bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 985bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio} 986