blob: 06a5bd28c92e3b1a8e26c02237b6c96d66a38a63 [file] [log] [blame]
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
Alan Viverette0ef59ac2015-03-23 13:13:25 -070019import android.annotation.Nullable;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070020import android.content.Context;
21import android.content.res.ColorStateList;
22import android.content.res.Configuration;
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.text.format.DateFormat;
28import android.text.format.DateUtils;
29import android.util.AttributeSet;
Alan Viverette0ef59ac2015-03-23 13:13:25 -070030import android.util.StateSet;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070031import android.view.HapticFeedbackConstants;
32import android.view.LayoutInflater;
33import android.view.View;
Alan Viverette0ef59ac2015-03-23 13:13:25 -070034import android.view.View.OnClickListener;
35import android.view.ViewGroup;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070036import android.view.accessibility.AccessibilityEvent;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070037import android.view.animation.AlphaAnimation;
38import android.view.animation.Animation;
Alan Viverette0ef59ac2015-03-23 13:13:25 -070039import android.widget.DayPickerView.OnDaySelectedListener;
40import android.widget.YearPickerView.OnYearSelectedListener;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070041
42import com.android.internal.R;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070043
44import java.text.SimpleDateFormat;
45import java.util.Calendar;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070046import java.util.Locale;
47
48/**
49 * A delegate for picking up a date (day / month / year).
50 */
Alan Viverette0ef59ac2015-03-23 13:13:25 -070051class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
Alan Viverette0a04bb02014-09-03 20:48:02 -070052 private static final int USE_LOCALE = 0;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070053
54 private static final int UNINITIALIZED = -1;
Alan Viverette0ef59ac2015-03-23 13:13:25 -070055 private static final int VIEW_MONTH_DAY = 0;
56 private static final int VIEW_YEAR = 1;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070057
58 private static final int DEFAULT_START_YEAR = 1900;
59 private static final int DEFAULT_END_YEAR = 2100;
60
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070061 private static final int ANIMATION_DURATION = 300;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070062
Alan Viverette60b674e2015-03-25 13:00:42 -070063 private static final int[] ATTRS_TEXT_COLOR = new int[] {
64 com.android.internal.R.attr.textColor};
65 private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
Alan Viverette0ef59ac2015-03-23 13:13:25 -070066 com.android.internal.R.attr.disabledAlpha};
Alan Viverette7119d0d2014-08-25 17:27:02 -070067
Alan Viverette0ef59ac2015-03-23 13:13:25 -070068 private SimpleDateFormat mYearFormat;
69 private SimpleDateFormat mMonthDayFormat;
Alan Viverette7119d0d2014-08-25 17:27:02 -070070
Alan Viverette0ef59ac2015-03-23 13:13:25 -070071 // Top-level container.
72 private ViewGroup mContainer;
Alan Viverette7119d0d2014-08-25 17:27:02 -070073
Alan Viverette0ef59ac2015-03-23 13:13:25 -070074 // Header views.
75 private TextView mHeaderYear;
76 private TextView mHeaderMonthDay;
77
78 // Picker views.
79 private ViewAnimator mAnimator;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070080 private DayPickerView mDayPickerView;
81 private YearPickerView mYearPickerView;
82
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070083 // Accessibility strings.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070084 private String mSelectDay;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070085 private String mSelectYear;
86
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070087 private DatePicker.OnDateChangedListener mDateChangedListener;
88
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070089 private int mCurrentView = UNINITIALIZED;
90
Alan Viverette452fe342015-03-23 14:26:09 -070091 private final Calendar mCurrentDate;
92 private final Calendar mTempDate;
93 private final Calendar mMinDate;
94 private final Calendar mMaxDate;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070095
Alan Viverette0a04bb02014-09-03 20:48:02 -070096 private int mFirstDayOfWeek = USE_LOCALE;
97
Chet Haase3053b2f2014-08-06 07:51:50 -070098 public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070099 int defStyleAttr, int defStyleRes) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700100 super(delegator, context);
101
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700102 final Locale locale = mCurrentLocale;
Alan Viverette452fe342015-03-23 14:26:09 -0700103 mCurrentDate = Calendar.getInstance(locale);
104 mTempDate = Calendar.getInstance(locale);
105 mMinDate = Calendar.getInstance(locale);
106 mMaxDate = Calendar.getInstance(locale);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700107
You Kim49e6c4a2015-02-26 19:06:16 +0900108 mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
109 mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700110
Alan Viverette60727e02014-07-28 16:56:32 -0700111 final Resources res = mDelegator.getResources();
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700112 final TypedArray a = mContext.obtainStyledAttributes(attrs,
113 R.styleable.DatePicker, defStyleAttr, defStyleRes);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700114 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
115 Context.LAYOUT_INFLATER_SERVICE);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700116 final int layoutResourceId = a.getResourceId(
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700117 R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700118
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700119 // Set up and attach container.
120 mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
Alan Viverette7119d0d2014-08-25 17:27:02 -0700121
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700122 // Set up header views.
123 final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
124 mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year);
125 mHeaderYear.setOnClickListener(mOnHeaderClickListener);
126 mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date);
127 mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
Alan Viverette60727e02014-07-28 16:56:32 -0700128
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700129 // For the sake of backwards compatibility, attempt to extract the text
130 // color from the header month text appearance. If it's set, we'll let
131 // that override the "real" header text color.
132 ColorStateList headerTextColor = null;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700133
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700134 @SuppressWarnings("deprecation")
135 final int monthHeaderTextAppearance = a.getResourceId(
Alan Viverette4e5168f2015-01-08 15:24:45 -0800136 R.styleable.DatePicker_headerMonthTextAppearance, 0);
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700137 if (monthHeaderTextAppearance != 0) {
138 final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
139 ATTRS_TEXT_COLOR, 0, monthHeaderTextAppearance);
140 final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
141 headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
142 textAppearance.recycle();
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700143 }
144
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700145 if (headerTextColor == null) {
146 headerTextColor = a.getColorStateList(R.styleable.DatePicker_headerTextColor);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700147 }
148
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700149 if (headerTextColor != null) {
150 mHeaderYear.setTextColor(headerTextColor);
151 mHeaderMonthDay.setTextColor(headerTextColor);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700152 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700153
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700154 // Set up header background, if available.
155 if (a.hasValueOrEmpty(R.styleable.DatePicker_headerBackground)) {
156 header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
157 }
158
Alan Viverette60b674e2015-03-25 13:00:42 -0700159 a.recycle();
160
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700161 // Set up picker container.
162 mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
163
164 // Set up day picker view.
165 mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker);
Alan Viverettee763c9b2014-11-06 15:22:31 -0800166 mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
Alan Viverette46127402014-11-13 10:50:37 -0800167 mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
168 mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
169 mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
Alan Viverettee763c9b2014-11-06 15:22:31 -0800170 mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
Alan Viverette50eb0252014-10-24 14:34:26 -0700171
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700172 // Set up year picker view.
173 mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
Alan Viverette2a90fa62015-02-17 13:09:04 -0800174 mYearPickerView.setRange(mMinDate, mMaxDate);
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700175 mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
176 mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
Alan Viverette4e5168f2015-01-08 15:24:45 -0800177
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700178 // Set up content descriptions.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700179 mSelectDay = res.getString(R.string.select_day);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700180 mSelectYear = res.getString(R.string.select_year);
181
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700182 // Initialize for current locale. This also initializes the date, so no
183 // need to call onDateChanged.
184 onLocaleChanged(mCurrentLocale);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700185
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700186 setCurrentView(VIEW_MONTH_DAY);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700187 }
188
189 /**
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700190 * The legacy text color might have been poorly defined. Ensures that it
191 * has an appropriate activated state, using the selected state if one
192 * exists or modifying the default text color otherwise.
193 *
194 * @param color a legacy text color, or {@code null}
195 * @return a color state list with an appropriate activated state, or
196 * {@code null} if a valid activated state could not be generated
197 */
198 @Nullable
199 private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
200 if (color == null || color.hasState(R.attr.state_activated)) {
201 return color;
202 }
203
204 final int activatedColor;
205 final int defaultColor;
206 if (color.hasState(R.attr.state_selected)) {
207 activatedColor = color.getColorForState(StateSet.get(
208 StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
209 defaultColor = color.getColorForState(StateSet.get(
210 StateSet.VIEW_STATE_ENABLED), 0);
211 } else {
212 activatedColor = color.getDefaultColor();
213
214 // Generate a non-activated color using the disabled alpha.
215 final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
216 final float disabledAlpha = ta.getFloat(0, 0.30f);
217 defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
218 }
219
220 if (activatedColor == 0 || defaultColor == 0) {
221 // We somehow failed to obtain the colors.
222 return null;
223 }
224
225 final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
226 final int[] colors = new int[] { activatedColor, defaultColor };
227 return new ColorStateList(stateSet, colors);
228 }
229
230 private int multiplyAlphaComponent(int color, float alphaMod) {
231 final int srcRgb = color & 0xFFFFFF;
232 final int srcAlpha = (color >> 24) & 0xFF;
233 final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
234 return srcRgb | (dstAlpha << 24);
235 }
236
237 /**
238 * Listener called when the user selects a day in the day picker view.
239 */
240 private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() {
241 @Override
242 public void onDaySelected(DayPickerView view, Calendar day) {
243 mCurrentDate.setTimeInMillis(day.getTimeInMillis());
244 onDateChanged(true, true);
245 }
246 };
247
248 /**
249 * Listener called when the user selects a year in the year picker view.
250 */
251 private final OnYearSelectedListener mOnYearSelectedListener = new OnYearSelectedListener() {
252 @Override
253 public void onYearChanged(YearPickerView view, int year) {
254 // If the newly selected month / year does not contain the
255 // currently selected day number, change the selected day number
256 // to the last day of the selected month or year.
257 // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
258 // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
259 final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
260 final int month = mCurrentDate.get(Calendar.MONTH);
261 final int daysInMonth = getDaysInMonth(month, year);
262 if (day > daysInMonth) {
263 mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
264 }
265
266 mCurrentDate.set(Calendar.YEAR, year);
267 onDateChanged(true, true);
268
269 // Automatically switch to day picker.
270 setCurrentView(VIEW_MONTH_DAY);
271 }
272 };
273
274 /**
275 * Listener called when the user clicks on a header item.
276 */
277 private final OnClickListener mOnHeaderClickListener = new OnClickListener() {
278 @Override
279 public void onClick(View v) {
280 tryVibrate();
281
282 switch (v.getId()) {
283 case R.id.date_picker_header_year:
284 setCurrentView(VIEW_YEAR);
285 break;
286 case R.id.date_picker_header_date:
287 setCurrentView(VIEW_MONTH_DAY);
288 break;
289 }
290 }
291 };
292
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700293 @Override
294 protected void onLocaleChanged(Locale locale) {
295 final TextView headerYear = mHeaderYear;
296 if (headerYear == null) {
297 // Abort, we haven't initialized yet. This method will get called
298 // again later after everything has been set up.
299 return;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700300 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700301
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700302 // Update the date formatter.
303 final String datePattern = DateFormat.getBestDateTimePattern(locale, "EMMMd");
304 mMonthDayFormat = new SimpleDateFormat(datePattern, locale);
305 mYearFormat = new SimpleDateFormat("y", locale);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700306
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700307 // Update the header text.
308 onCurrentDateChanged(false);
309 }
310
311 private void onCurrentDateChanged(boolean announce) {
312 if (mHeaderYear == null) {
313 // Abort, we haven't initialized yet. This method will get called
314 // again later after everything has been set up.
315 return;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700316 }
317
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700318 final String year = mYearFormat.format(mCurrentDate.getTime());
319 mHeaderYear.setText(year);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700320
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700321 final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime());
322 mHeaderMonthDay.setText(monthDay);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700323
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700324 // TODO: This should use live regions.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700325 if (announce) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700326 final long millis = mCurrentDate.getTimeInMillis();
327 final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
328 final String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700329 mAnimator.announceForAccessibility(fullDateText);
330 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700331 }
332
333 private void setCurrentView(final int viewIndex) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700334 switch (viewIndex) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700335 case VIEW_MONTH_DAY:
336 mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
337
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700338 if (mCurrentView != viewIndex) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700339 mHeaderMonthDay.setActivated(true);
340 mHeaderYear.setActivated(false);
341 mAnimator.setDisplayedChild(VIEW_MONTH_DAY);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700342 mCurrentView = viewIndex;
343 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700344
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700345 mAnimator.announceForAccessibility(mSelectDay);
346 break;
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700347 case VIEW_YEAR:
348 mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
349
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700350 if (mCurrentView != viewIndex) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700351 mHeaderMonthDay.setActivated(false);
352 mHeaderYear.setActivated(true);
353 mAnimator.setDisplayedChild(VIEW_YEAR);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700354 mCurrentView = viewIndex;
355 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700356
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700357 mAnimator.announceForAccessibility(mSelectYear);
358 break;
359 }
360 }
361
362 @Override
363 public void init(int year, int monthOfYear, int dayOfMonth,
364 DatePicker.OnDateChangedListener callBack) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700365 mCurrentDate.set(Calendar.YEAR, year);
366 mCurrentDate.set(Calendar.MONTH, monthOfYear);
367 mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800368
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800369 mDateChangedListener = callBack;
Alan Viverette0cecbc92014-12-12 14:32:43 -0800370
371 onDateChanged(false, false);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700372 }
373
374 @Override
375 public void updateDate(int year, int month, int dayOfMonth) {
376 mCurrentDate.set(Calendar.YEAR, year);
377 mCurrentDate.set(Calendar.MONTH, month);
378 mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800379
Alan Viverette0cecbc92014-12-12 14:32:43 -0800380 onDateChanged(false, true);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800381 }
382
Alan Viverette0cecbc92014-12-12 14:32:43 -0800383 private void onDateChanged(boolean fromUser, boolean callbackToClient) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700384 final int year = mCurrentDate.get(Calendar.YEAR);
385
Alan Viverette0cecbc92014-12-12 14:32:43 -0800386 if (callbackToClient && mDateChangedListener != null) {
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800387 final int monthOfYear = mCurrentDate.get(Calendar.MONTH);
388 final int dayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
389 mDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
Alan Viverette9468c6a2014-08-21 13:56:54 -0700390 }
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800391
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700392 mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
393 mYearPickerView.setYear(year);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800394
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700395 onCurrentDateChanged(fromUser);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800396
397 if (fromUser) {
398 tryVibrate();
399 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700400 }
401
402 @Override
403 public int getYear() {
404 return mCurrentDate.get(Calendar.YEAR);
405 }
406
407 @Override
408 public int getMonth() {
409 return mCurrentDate.get(Calendar.MONTH);
410 }
411
412 @Override
413 public int getDayOfMonth() {
414 return mCurrentDate.get(Calendar.DAY_OF_MONTH);
415 }
416
417 @Override
418 public void setMinDate(long minDate) {
419 mTempDate.setTimeInMillis(minDate);
420 if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
421 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
422 return;
423 }
424 if (mCurrentDate.before(mTempDate)) {
425 mCurrentDate.setTimeInMillis(minDate);
Alan Viverette0cecbc92014-12-12 14:32:43 -0800426 onDateChanged(false, true);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700427 }
428 mMinDate.setTimeInMillis(minDate);
Alan Viverette46127402014-11-13 10:50:37 -0800429 mDayPickerView.setMinDate(minDate);
Alan Viverette50eb0252014-10-24 14:34:26 -0700430 mYearPickerView.setRange(mMinDate, mMaxDate);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700431 }
432
433 @Override
434 public Calendar getMinDate() {
435 return mMinDate;
436 }
437
438 @Override
439 public void setMaxDate(long maxDate) {
440 mTempDate.setTimeInMillis(maxDate);
441 if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
442 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
443 return;
444 }
445 if (mCurrentDate.after(mTempDate)) {
446 mCurrentDate.setTimeInMillis(maxDate);
Alan Viverette0cecbc92014-12-12 14:32:43 -0800447 onDateChanged(false, true);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700448 }
449 mMaxDate.setTimeInMillis(maxDate);
Alan Viverette46127402014-11-13 10:50:37 -0800450 mDayPickerView.setMaxDate(maxDate);
Alan Viverette50eb0252014-10-24 14:34:26 -0700451 mYearPickerView.setRange(mMinDate, mMaxDate);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700452 }
453
454 @Override
455 public Calendar getMaxDate() {
456 return mMaxDate;
457 }
458
459 @Override
Alan Viverette0a04bb02014-09-03 20:48:02 -0700460 public void setFirstDayOfWeek(int firstDayOfWeek) {
461 mFirstDayOfWeek = firstDayOfWeek;
Alan Viverettee763c9b2014-11-06 15:22:31 -0800462
463 mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
Alan Viverette0a04bb02014-09-03 20:48:02 -0700464 }
465
466 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700467 public int getFirstDayOfWeek() {
Alan Viverette0a04bb02014-09-03 20:48:02 -0700468 if (mFirstDayOfWeek != USE_LOCALE) {
469 return mFirstDayOfWeek;
470 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700471 return mCurrentDate.getFirstDayOfWeek();
472 }
473
474 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700475 public void setEnabled(boolean enabled) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700476 mContainer.setEnabled(false);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700477 }
478
479 @Override
480 public boolean isEnabled() {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700481 return mContainer.isEnabled();
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700482 }
483
484 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700485 public CalendarView getCalendarView() {
486 throw new UnsupportedOperationException(
487 "CalendarView does not exists for the new DatePicker");
488 }
489
490 @Override
491 public void setCalendarViewShown(boolean shown) {
492 // No-op for compatibility with the old DatePicker.
493 }
494
495 @Override
496 public boolean getCalendarViewShown() {
497 return false;
498 }
499
500 @Override
501 public void setSpinnersShown(boolean shown) {
502 // No-op for compatibility with the old DatePicker.
503 }
504
505 @Override
506 public boolean getSpinnersShown() {
507 return false;
508 }
509
510 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700511 public void onConfigurationChanged(Configuration newConfig) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700512 setCurrentLocale(newConfig.locale);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700513 }
514
515 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700516 public Parcelable onSaveInstanceState(Parcelable superState) {
517 final int year = mCurrentDate.get(Calendar.YEAR);
518 final int month = mCurrentDate.get(Calendar.MONTH);
519 final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
520
521 int listPosition = -1;
522 int listPositionOffset = -1;
523
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700524 if (mCurrentView == VIEW_MONTH_DAY) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700525 listPosition = mDayPickerView.getMostVisiblePosition();
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700526 } else if (mCurrentView == VIEW_YEAR) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700527 listPosition = mYearPickerView.getFirstVisiblePosition();
528 listPositionOffset = mYearPickerView.getFirstPositionOffset();
529 }
530
531 return new SavedState(superState, year, month, day, mMinDate.getTimeInMillis(),
532 mMaxDate.getTimeInMillis(), mCurrentView, listPosition, listPositionOffset);
533 }
534
535 @Override
536 public void onRestoreInstanceState(Parcelable state) {
Alan Viverette816aa142015-04-10 15:41:10 -0700537 final SavedState ss = (SavedState) state;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700538
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700539 // TODO: Move instance state into DayPickerView, YearPickerView.
Craig Mautnera67d9092014-09-16 15:38:47 -0700540 mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700541 mMinDate.setTimeInMillis(ss.getMinDate());
542 mMaxDate.setTimeInMillis(ss.getMaxDate());
543
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700544 onCurrentDateChanged(false);
Alan Viverette816aa142015-04-10 15:41:10 -0700545
546 final int currentView = ss.getCurrentView();
547 setCurrentView(currentView);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700548
549 final int listPosition = ss.getListPosition();
550 if (listPosition != -1) {
Alan Viverette816aa142015-04-10 15:41:10 -0700551 if (currentView == VIEW_MONTH_DAY) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700552 mDayPickerView.setCurrentItem(listPosition);
Alan Viverette816aa142015-04-10 15:41:10 -0700553 } else if (currentView == VIEW_YEAR) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700554 final int listPositionOffset = ss.getListPositionOffset();
555 mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700556 }
557 }
558 }
559
560 @Override
561 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
562 onPopulateAccessibilityEvent(event);
563 return true;
564 }
565
566 @Override
567 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
568 event.getText().add(mCurrentDate.getTime().toString());
569 }
570
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -0800571 public CharSequence getAccessibilityClassName() {
572 return DatePicker.class.getName();
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700573 }
574
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700575 public static int getDaysInMonth(int month, int year) {
576 switch (month) {
577 case Calendar.JANUARY:
578 case Calendar.MARCH:
579 case Calendar.MAY:
580 case Calendar.JULY:
581 case Calendar.AUGUST:
582 case Calendar.OCTOBER:
583 case Calendar.DECEMBER:
584 return 31;
585 case Calendar.APRIL:
586 case Calendar.JUNE:
587 case Calendar.SEPTEMBER:
588 case Calendar.NOVEMBER:
589 return 30;
590 case Calendar.FEBRUARY:
591 return (year % 4 == 0) ? 29 : 28;
592 default:
593 throw new IllegalArgumentException("Invalid Month");
594 }
595 }
596
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700597 private void tryVibrate() {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700598 mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
599 }
600
Alan Viverettee763c9b2014-11-06 15:22:31 -0800601 /**
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700602 * Class for managing state storing/restoring.
603 */
604 private static class SavedState extends View.BaseSavedState {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700605 private final int mSelectedYear;
606 private final int mSelectedMonth;
607 private final int mSelectedDay;
608 private final long mMinDate;
609 private final long mMaxDate;
610 private final int mCurrentView;
611 private final int mListPosition;
612 private final int mListPositionOffset;
613
614 /**
615 * Constructor called from {@link DatePicker#onSaveInstanceState()}
616 */
617 private SavedState(Parcelable superState, int year, int month, int day,
618 long minDate, long maxDate, int currentView, int listPosition,
619 int listPositionOffset) {
620 super(superState);
621 mSelectedYear = year;
622 mSelectedMonth = month;
623 mSelectedDay = day;
624 mMinDate = minDate;
625 mMaxDate = maxDate;
626 mCurrentView = currentView;
627 mListPosition = listPosition;
628 mListPositionOffset = listPositionOffset;
629 }
630
631 /**
632 * Constructor called from {@link #CREATOR}
633 */
634 private SavedState(Parcel in) {
635 super(in);
636 mSelectedYear = in.readInt();
637 mSelectedMonth = in.readInt();
638 mSelectedDay = in.readInt();
639 mMinDate = in.readLong();
640 mMaxDate = in.readLong();
641 mCurrentView = in.readInt();
642 mListPosition = in.readInt();
643 mListPositionOffset = in.readInt();
644 }
645
646 @Override
647 public void writeToParcel(Parcel dest, int flags) {
648 super.writeToParcel(dest, flags);
649 dest.writeInt(mSelectedYear);
650 dest.writeInt(mSelectedMonth);
651 dest.writeInt(mSelectedDay);
652 dest.writeLong(mMinDate);
653 dest.writeLong(mMaxDate);
654 dest.writeInt(mCurrentView);
655 dest.writeInt(mListPosition);
656 dest.writeInt(mListPositionOffset);
657 }
658
659 public int getSelectedDay() {
660 return mSelectedDay;
661 }
662
663 public int getSelectedMonth() {
664 return mSelectedMonth;
665 }
666
667 public int getSelectedYear() {
668 return mSelectedYear;
669 }
670
671 public long getMinDate() {
672 return mMinDate;
673 }
674
675 public long getMaxDate() {
676 return mMaxDate;
677 }
678
679 public int getCurrentView() {
680 return mCurrentView;
681 }
682
683 public int getListPosition() {
684 return mListPosition;
685 }
686
687 public int getListPositionOffset() {
688 return mListPositionOffset;
689 }
690
691 @SuppressWarnings("all")
692 // suppress unused and hiding
693 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
694
695 public SavedState createFromParcel(Parcel in) {
696 return new SavedState(in);
697 }
698
699 public SavedState[] newArray(int size) {
700 return new SavedState[size];
701 }
702 };
703 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700704}