DayPickerView.java revision 4612740ddc76b3518dc6d189d5f8b5b7f60e9d64
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.Configuration; 22bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.os.Bundle; 23bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.util.Log; 2450eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viveretteimport android.util.MathUtils; 25bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.View; 26bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.ViewConfiguration; 27bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityEvent; 28bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo; 29bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 30bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.text.SimpleDateFormat; 31bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Calendar; 32bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglioimport java.util.Locale; 33bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 34bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio/** 35bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * This displays a list of months in a calendar format with selectable days. 36bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 37e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viveretteclass DayPickerView extends ListView implements AbsListView.OnScrollListener { 38bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final String TAG = "DayPickerView"; 39bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 40bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // How long the GoTo fling animation should last 41bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final int GOTO_SCROLL_DURATION = 250; 42bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 43bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // How long to wait after receiving an onScrollStateChanged notification before acting on it 44bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private static final int SCROLL_CHANGE_DELAY = 40; 45bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 46e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette // so that the top line will be under the separator 47e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette private static final int LIST_TOP_OFFSET = -1; 48bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 49e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette private final SimpleMonthAdapter mAdapter = new SimpleMonthAdapter(getContext()); 50e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette 51e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette private final ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this); 52bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 53e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault()); 54bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 55bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // highlighted time 56bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private Calendar mSelectedDay = Calendar.getInstance(); 57bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private Calendar mTempDay = Calendar.getInstance(); 5850eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette private Calendar mMinDate = Calendar.getInstance(); 5950eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette private Calendar mMaxDate = Calendar.getInstance(); 6050eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette 614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette private Calendar mTempCalendar; 624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 63e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette private OnDaySelectedListener mOnDaySelectedListener; 64bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 65bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // which month should be displayed/highlighted [0-11] 66bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mCurrentMonthDisplayed; 67bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // used for tracking what state listview is in 68bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE; 69bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // used for tracking what state listview is in 70bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE; 71bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 72bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private boolean mPerformingScroll; 73bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 74e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette public DayPickerView(Context context) { 75bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio super(context); 7650eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette 77bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setAdapter(mAdapter); 78bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 79bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setDrawSelectorOnTop(false); 80bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setUpListView(); 81e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette 824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette goTo(mSelectedDay.getTimeInMillis(), false, false, true); 83e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette 84e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener); 85e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette } 86e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette 874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette /** 884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * Sets the currently selected date to the specified timestamp. Jumps 894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * immediately to the new date. To animate to the new date, use 904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * {@link #setDate(long, boolean, boolean)}. 914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * 924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * @param timeInMillis 934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette */ 944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public void setDate(long timeInMillis) { 954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette setDate(timeInMillis, false, true); 964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public void setDate(long timeInMillis, boolean animate, boolean forceScroll) { 994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette goTo(timeInMillis, animate, true, forceScroll); 1004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 1014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 1024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public long getDate() { 1034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette return mSelectedDay.getTimeInMillis(); 104e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette } 105e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette 106e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette public void setFirstDayOfWeek(int firstDayOfWeek) { 107e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette mAdapter.setFirstDayOfWeek(firstDayOfWeek); 108bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 109bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public int getFirstDayOfWeek() { 1114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette return mAdapter.getFirstDayOfWeek(); 1124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 1134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 1144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public void setMinDate(long timeInMillis) { 1154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette mMinDate.setTimeInMillis(timeInMillis); 1164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette onRangeChanged(); 1174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 1184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 1194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public long getMinDate() { 1204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette return mMinDate.getTimeInMillis(); 1214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 1224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 1234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public void setMaxDate(long timeInMillis) { 1244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette mMaxDate.setTimeInMillis(timeInMillis); 1254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette onRangeChanged(); 1264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 12750eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette 1284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public long getMaxDate() { 1294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette return mMaxDate.getTimeInMillis(); 1304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 1314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 1324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette /** 1334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * Handles changes to date range. 1344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette */ 1354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette public void onRangeChanged() { 13650eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette mAdapter.setRange(mMinDate, mMaxDate); 13750eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette 1385ecbfeb38b6bdcfe8f3561f8cdcb4af9ba30c886Alan Viverette // Changing the min/max date changes the selection position since we 1394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette // don't really have stable IDs. Jumps immediately to the new position. 1404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette goTo(mSelectedDay.getTimeInMillis(), false, false, true); 14150eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette } 14250eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette 14350eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette /** 144e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette * Sets the listener to call when the user selects a day. 14550eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette * 146e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette * @param listener The listener to call. 147bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 148e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette public void setOnDaySelectedListener(OnDaySelectedListener listener) { 149e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette mOnDaySelectedListener = listener; 150bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 151bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 152bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /* 153bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Sets all the required fields for the list view. Override this method to 154bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * set a different list view behavior. 155bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 156e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette private void setUpListView() { 157bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Transparent background on scroll 158bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setCacheColorHint(0); 159bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // No dividers 160bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setDivider(null); 161bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Items are clickable 162bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setItemsCanFocus(true); 163bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // The thumb gets in the way, so disable it 164bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setFastScrollEnabled(false); 165bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setVerticalScrollBarEnabled(false); 166bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setOnScrollListener(this); 167bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setFadingEdgeLength(0); 168bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Make the scrolling behavior nicer 169e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette setFriction(ViewConfiguration.getScrollFriction()); 170bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 171bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 17250eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette private int getDiffMonths(Calendar start, Calendar end) { 173bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int diffYears = end.get(Calendar.YEAR) - start.get(Calendar.YEAR); 174bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int diffMonths = end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears; 175bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return diffMonths; 176bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 177bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette private int getPositionFromDay(long timeInMillis) { 17950eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette final int diffMonthMax = getDiffMonths(mMinDate, mMaxDate); 1804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette final int diffMonth = getDiffMonths(mMinDate, getTempCalendarForTime(timeInMillis)); 18150eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette return MathUtils.constrain(diffMonth, 0, diffMonthMax); 182bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 183bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 1844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette private Calendar getTempCalendarForTime(long timeInMillis) { 1854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette if (mTempCalendar == null) { 1864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette mTempCalendar = Calendar.getInstance(); 1874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 1884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette mTempCalendar.setTimeInMillis(timeInMillis); 1894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette return mTempCalendar; 1904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 1914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 192bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 193bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * This moves to the specified time in the view. If the time is not already 194bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * in range it will move the list so that the first of the month containing 195bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * the time is at the top of the view. If the new time is already in view 196bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * the list will not be scrolled unless forceScroll is true. This time may 197bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * optionally be highlighted as selected as well. 198bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 199bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @param day The day to move to 200bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @param animate Whether to scroll to the given time or just redraw at the 201bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * new location 202bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @param setSelected Whether to set the given time as selected 203bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @param forceScroll Whether to recenter even if the time is already 204bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * visible 205bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @return Whether or not the view animated to the new location 206bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 2074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette private boolean goTo(long day, boolean animate, boolean setSelected, boolean forceScroll) { 208bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 209bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Set the selected day 210bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (setSelected) { 2114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette mSelectedDay.setTimeInMillis(day); 212bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 213bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 2144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette mTempDay.setTimeInMillis(day); 215bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int position = getPositionFromDay(day); 216bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 217bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio View child; 218bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int i = 0; 219bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int top = 0; 220bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Find a child that's completely in the view 221bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio do { 222bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio child = getChildAt(i++); 223bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (child == null) { 224bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio break; 225bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 226bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio top = child.getTop(); 227bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } while (top < 0); 228bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 229bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Compute the first and last position visible 230bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int selectedPosition; 231bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (child != null) { 232bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio selectedPosition = getPositionForView(child); 233bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else { 234bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio selectedPosition = 0; 235bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 236bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 237bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (setSelected) { 238bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mAdapter.setSelectedDay(mSelectedDay); 239bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 240bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 241bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Check if the selected day is now outside of our visible range 242bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // and if so scroll to the month that contains it 243bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (position != selectedPosition || forceScroll) { 244bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setMonthDisplayed(mTempDay); 245bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING; 246bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (animate) { 247bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio smoothScrollToPositionFromTop( 248bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION); 249bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return true; 250bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else { 251bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio postSetSelection(position); 252bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 253bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else if (setSelected) { 254bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setMonthDisplayed(mSelectedDay); 255bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 256bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return false; 257bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 258bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 259bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void postSetSelection(final int position) { 260bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio clearFocus(); 261bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio post(new Runnable() { 262bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 263bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 264bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void run() { 265bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio setSelection(position); 266bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 267bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio }); 268bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio onScrollStateChanged(this, OnScrollListener.SCROLL_STATE_IDLE); 269bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 270bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 271bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 272bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Updates the title and selected month if the view has moved to a new 273bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * month. 274bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 275bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 276bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 277bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int totalItemCount) { 278bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio SimpleMonthView child = (SimpleMonthView) view.getChildAt(0); 279bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (child == null) { 280bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return; 281bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 282bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 283bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mPreviousScrollState = mCurrentScrollState; 284bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 285bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 286bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 287bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Sets the month displayed at the top of this view based on time. Override 288bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * to add custom events when the title is changed. 289bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 290bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void setMonthDisplayed(Calendar date) { 291bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (mCurrentMonthDisplayed != date.get(Calendar.MONTH)) { 292bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mCurrentMonthDisplayed = date.get(Calendar.MONTH); 293bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio invalidateViews(); 294bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 295bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 296bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 297bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 298bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void onScrollStateChanged(AbsListView view, int scrollState) { 299bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // use a post to prevent re-entering onScrollStateChanged before it 300bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // exits 301bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); 302bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 303bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 304bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio void setCalendarTextColor(ColorStateList colors) { 305bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mAdapter.setCalendarTextColor(colors); 306bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 307bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 3084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette void setCalendarTextAppearance(int resId) { 3094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette mAdapter.setCalendarTextAppearance(resId); 3104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette } 3114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette 312bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected class ScrollStateRunnable implements Runnable { 313bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private int mNewState; 314bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private View mParent; 315bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 316bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio ScrollStateRunnable(View view) { 317bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mParent = view; 318bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 319bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 320bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 321bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Sets up the runnable with a short delay in case the scroll state 322bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * immediately changes again. 323bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 324bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @param view The list view that changed state 325bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @param scrollState The new state it changed to 326bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 327bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void doScrollStateChange(AbsListView view, int scrollState) { 328bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mParent.removeCallbacks(this); 329bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mNewState = scrollState; 330bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mParent.postDelayed(this, SCROLL_CHANGE_DELAY); 331bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 332bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 333bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 334bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void run() { 335bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mCurrentScrollState = mNewState; 336bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (Log.isLoggable(TAG, Log.DEBUG)) { 337bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio Log.d(TAG, 338bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio "new scroll state: " + mNewState + " old state: " + mPreviousScrollState); 339bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 340bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Fix the position after a scroll or a fling ends 341bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (mNewState == OnScrollListener.SCROLL_STATE_IDLE 342bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE 343bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio && mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { 344bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mPreviousScrollState = mNewState; 345bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int i = 0; 346bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio View child = getChildAt(i); 347bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio while (child != null && child.getBottom() <= 0) { 348bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio child = getChildAt(++i); 349bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 350bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (child == null) { 351bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // The view is no longer visible, just return 352bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return; 353bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 354bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int firstPosition = getFirstVisiblePosition(); 355bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int lastPosition = getLastVisiblePosition(); 356bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1; 357bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int top = child.getTop(); 358bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int bottom = child.getBottom(); 359bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int midpoint = getHeight() / 2; 360bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (scroll && top < LIST_TOP_OFFSET) { 361bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (bottom > midpoint) { 362bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio smoothScrollBy(top, GOTO_SCROLL_DURATION); 363bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else { 364bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio smoothScrollBy(bottom, GOTO_SCROLL_DURATION); 365bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 366bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 367bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else { 368bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mPreviousScrollState = mNewState; 369bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 370bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 371bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 372bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 373bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 374bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Gets the position of the view that is most prominently displayed within the list view. 375bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 376bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public int getMostVisiblePosition() { 377bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int firstPosition = getFirstVisiblePosition(); 378bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int height = getHeight(); 379bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 380bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int maxDisplayedHeight = 0; 381bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int mostVisibleIndex = 0; 382bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int i=0; 383bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int bottom = 0; 384bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio while (bottom < height) { 385bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio View child = getChildAt(i); 386bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (child == null) { 387bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio break; 388bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 389bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio bottom = child.getBottom(); 390bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio int displayedHeight = Math.min(bottom, height) - Math.max(0, child.getTop()); 391bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (displayedHeight > maxDisplayedHeight) { 392bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mostVisibleIndex = i; 393bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio maxDisplayedHeight = displayedHeight; 394bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 395bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio i++; 396bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 397bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return firstPosition + mostVisibleIndex; 398bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 399bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 400bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 401bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Attempts to return the date that has accessibility focus. 402bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 403bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @return The date that has accessibility focus, or {@code null} if no date 404bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * has focus. 405bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 406bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private Calendar findAccessibilityFocus() { 407bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int childCount = getChildCount(); 408bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio for (int i = 0; i < childCount; i++) { 409bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final View child = getChildAt(i); 410bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (child instanceof SimpleMonthView) { 411bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final Calendar focus = ((SimpleMonthView) child).getAccessibilityFocus(); 412bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (focus != null) { 413bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return focus; 414bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 415bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 416bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 417bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 418bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return null; 419bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 420bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 421bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 422bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Attempts to restore accessibility focus to a given date. No-op if 423bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * {@code day} is {@code null}. 424bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * 425bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @param day The date that should receive accessibility focus 426bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * @return {@code true} if focus was restored 427bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 428bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private boolean restoreAccessibilityFocus(Calendar day) { 429bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (day == null) { 430bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return false; 431bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 432bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 433bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final int childCount = getChildCount(); 434bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio for (int i = 0; i < childCount; i++) { 435bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final View child = getChildAt(i); 436bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (child instanceof SimpleMonthView) { 437bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (((SimpleMonthView) child).restoreAccessibilityFocus(day)) { 438bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return true; 439bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 440bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 441bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 442bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 443bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return false; 444bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 445bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 446bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 447bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void layoutChildren() { 448bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio final Calendar focusedDay = findAccessibilityFocus(); 449bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio super.layoutChildren(); 450bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (mPerformingScroll) { 451bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mPerformingScroll = false; 452bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else { 453bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio restoreAccessibilityFocus(focusedDay); 454bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 455bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 456bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 457bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 458bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio protected void onConfigurationChanged(Configuration newConfig) { 459bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault()); 460bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 461bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 462bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 463bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 464bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio super.onInitializeAccessibilityEvent(event); 465bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio event.setItemCount(-1); 466bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 467bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 468bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio private String getMonthAndYearString(Calendar day) { 4694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette final StringBuilder sbuf = new StringBuilder(); 470bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault())); 471bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio sbuf.append(" "); 472bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio sbuf.append(mYearFormat.format(day.getTime())); 473bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return sbuf.toString(); 474bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 475bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 476bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 477bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * Necessary for accessibility, to ensure we support "scrolling" forward and backward 478bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * in the month list. 479bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 480bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 481bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 482bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio super.onInitializeAccessibilityNodeInfo(info); 4834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); 4844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD); 485bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 486bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 487bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio /** 488bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio * When scroll forward/backward events are received, announce the newly scrolled-to month. 489bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio */ 490bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio @Override 491bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio public boolean performAccessibilityAction(int action, Bundle arguments) { 492bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (action != AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && 493bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio action != AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) { 494bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return super.performAccessibilityAction(action, arguments); 495bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 496bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 497bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Figure out what month is showing. 49850eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette final int firstVisiblePosition = getFirstVisiblePosition(); 49950eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette final int month = firstVisiblePosition % 12; 50050eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette final int year = firstVisiblePosition / 12 + mMinDate.get(Calendar.YEAR); 50150eb025c2fea7e364e0be951ce8ba6ca605f901aAlan Viverette final Calendar day = Calendar.getInstance(); 502bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio day.set(year, month, 1); 503bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 504bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Scroll either forward or backward one month. 505bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) { 506bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio day.add(Calendar.MONTH, 1); 507bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (day.get(Calendar.MONTH) == 12) { 508bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio day.set(Calendar.MONTH, 0); 509bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio day.add(Calendar.YEAR, 1); 510bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 511bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) { 512bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio View firstVisibleView = getChildAt(0); 513bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // If the view is fully visible, jump one month back. Otherwise, we'll just jump 514bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // to the first day of first visible month. 515bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (firstVisibleView != null && firstVisibleView.getTop() >= -1) { 516bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // There's an off-by-one somewhere, so the top of the first visible item will 517bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // actually be -1 when it's at the exact top. 518bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio day.add(Calendar.MONTH, -1); 519bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio if (day.get(Calendar.MONTH) == -1) { 520bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio day.set(Calendar.MONTH, 11); 521bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio day.add(Calendar.YEAR, -1); 522bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 523bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 524bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 525bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio 526bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio // Go to that month. 527bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio announceForAccessibility(getMonthAndYearString(day)); 5284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette goTo(day.getTimeInMillis(), true, false, true); 529bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio mPerformingScroll = true; 530bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio return true; 531bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio } 532e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette 533e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette public interface OnDaySelectedListener { 534e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette public void onDaySelected(DayPickerView view, Calendar day); 535e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette } 536e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette 537e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette private final SimpleMonthAdapter.OnDaySelectedListener 538e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette mProxyOnDaySelectedListener = new SimpleMonthAdapter.OnDaySelectedListener() { 539e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette @Override 540e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette public void onDaySelected(SimpleMonthAdapter adapter, Calendar day) { 541e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette if (mOnDaySelectedListener != null) { 542e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette mOnDaySelectedListener.onDaySelected(DayPickerView.this, day); 543e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette } 544e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette } 545e763c9bd6ed0ca46daafc21fc4313ebcad4bcafaAlan Viverette }; 546bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio} 547