blob: d38a225ae209aa8cee8a0f2860d545992341eb1d [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;
Alan Viverette0ef59ac2015-03-23 13:13:25 -070037import android.widget.DayPickerView.OnDaySelectedListener;
38import android.widget.YearPickerView.OnYearSelectedListener;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070039
40import com.android.internal.R;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070041
42import java.text.SimpleDateFormat;
43import java.util.Calendar;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070044import java.util.Locale;
45
46/**
47 * A delegate for picking up a date (day / month / year).
48 */
Alan Viverette0ef59ac2015-03-23 13:13:25 -070049class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
Alan Viverette0a04bb02014-09-03 20:48:02 -070050 private static final int USE_LOCALE = 0;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070051
52 private static final int UNINITIALIZED = -1;
Alan Viverette0ef59ac2015-03-23 13:13:25 -070053 private static final int VIEW_MONTH_DAY = 0;
54 private static final int VIEW_YEAR = 1;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070055
56 private static final int DEFAULT_START_YEAR = 1900;
57 private static final int DEFAULT_END_YEAR = 2100;
58
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070059 private static final int ANIMATION_DURATION = 300;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070060
Alan Viverette60b674e2015-03-25 13:00:42 -070061 private static final int[] ATTRS_TEXT_COLOR = new int[] {
62 com.android.internal.R.attr.textColor};
63 private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
Alan Viverette0ef59ac2015-03-23 13:13:25 -070064 com.android.internal.R.attr.disabledAlpha};
Alan Viverette7119d0d2014-08-25 17:27:02 -070065
Alan Viverette0ef59ac2015-03-23 13:13:25 -070066 private SimpleDateFormat mYearFormat;
67 private SimpleDateFormat mMonthDayFormat;
Alan Viverette7119d0d2014-08-25 17:27:02 -070068
Alan Viverette0ef59ac2015-03-23 13:13:25 -070069 // Top-level container.
70 private ViewGroup mContainer;
Alan Viverette7119d0d2014-08-25 17:27:02 -070071
Alan Viverette0ef59ac2015-03-23 13:13:25 -070072 // Header views.
73 private TextView mHeaderYear;
74 private TextView mHeaderMonthDay;
75
76 // Picker views.
77 private ViewAnimator mAnimator;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070078 private DayPickerView mDayPickerView;
79 private YearPickerView mYearPickerView;
80
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070081 // Accessibility strings.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070082 private String mSelectDay;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070083 private String mSelectYear;
84
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070085 private DatePicker.OnDateChangedListener mDateChangedListener;
86
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070087 private int mCurrentView = UNINITIALIZED;
88
Alan Viverette452fe342015-03-23 14:26:09 -070089 private final Calendar mCurrentDate;
90 private final Calendar mTempDate;
91 private final Calendar mMinDate;
92 private final Calendar mMaxDate;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070093
Alan Viverette0a04bb02014-09-03 20:48:02 -070094 private int mFirstDayOfWeek = USE_LOCALE;
95
Chet Haase3053b2f2014-08-06 07:51:50 -070096 public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070097 int defStyleAttr, int defStyleRes) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070098 super(delegator, context);
99
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700100 final Locale locale = mCurrentLocale;
Alan Viverette452fe342015-03-23 14:26:09 -0700101 mCurrentDate = Calendar.getInstance(locale);
102 mTempDate = Calendar.getInstance(locale);
103 mMinDate = Calendar.getInstance(locale);
104 mMaxDate = Calendar.getInstance(locale);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700105
You Kim49e6c4a2015-02-26 19:06:16 +0900106 mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
107 mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700108
Alan Viverette60727e02014-07-28 16:56:32 -0700109 final Resources res = mDelegator.getResources();
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700110 final TypedArray a = mContext.obtainStyledAttributes(attrs,
111 R.styleable.DatePicker, defStyleAttr, defStyleRes);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700112 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
113 Context.LAYOUT_INFLATER_SERVICE);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700114 final int layoutResourceId = a.getResourceId(
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700115 R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700116
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700117 // Set up and attach container.
118 mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
Alan Viverette7119d0d2014-08-25 17:27:02 -0700119
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700120 // Set up header views.
121 final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
122 mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year);
123 mHeaderYear.setOnClickListener(mOnHeaderClickListener);
124 mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date);
125 mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
Alan Viverette60727e02014-07-28 16:56:32 -0700126
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700127 // For the sake of backwards compatibility, attempt to extract the text
128 // color from the header month text appearance. If it's set, we'll let
129 // that override the "real" header text color.
130 ColorStateList headerTextColor = null;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700131
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700132 @SuppressWarnings("deprecation")
133 final int monthHeaderTextAppearance = a.getResourceId(
Alan Viverette4e5168f2015-01-08 15:24:45 -0800134 R.styleable.DatePicker_headerMonthTextAppearance, 0);
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700135 if (monthHeaderTextAppearance != 0) {
136 final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
137 ATTRS_TEXT_COLOR, 0, monthHeaderTextAppearance);
138 final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
139 headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
140 textAppearance.recycle();
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700141 }
142
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700143 if (headerTextColor == null) {
144 headerTextColor = a.getColorStateList(R.styleable.DatePicker_headerTextColor);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700145 }
146
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700147 if (headerTextColor != null) {
148 mHeaderYear.setTextColor(headerTextColor);
149 mHeaderMonthDay.setTextColor(headerTextColor);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700150 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700151
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700152 // Set up header background, if available.
153 if (a.hasValueOrEmpty(R.styleable.DatePicker_headerBackground)) {
154 header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
155 }
156
Alan Viverette60b674e2015-03-25 13:00:42 -0700157 a.recycle();
158
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700159 // Set up picker container.
160 mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
161
162 // Set up day picker view.
163 mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker);
Alan Viverettee763c9b2014-11-06 15:22:31 -0800164 mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
Alan Viverette46127402014-11-13 10:50:37 -0800165 mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
166 mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
167 mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
Alan Viverettee763c9b2014-11-06 15:22:31 -0800168 mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
Alan Viverette50eb0252014-10-24 14:34:26 -0700169
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700170 // Set up year picker view.
171 mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
Alan Viverette2a90fa62015-02-17 13:09:04 -0800172 mYearPickerView.setRange(mMinDate, mMaxDate);
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700173 mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
174 mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
Alan Viverette4e5168f2015-01-08 15:24:45 -0800175
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700176 // Set up content descriptions.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700177 mSelectDay = res.getString(R.string.select_day);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700178 mSelectYear = res.getString(R.string.select_year);
179
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700180 // Initialize for current locale. This also initializes the date, so no
181 // need to call onDateChanged.
182 onLocaleChanged(mCurrentLocale);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700183
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700184 setCurrentView(VIEW_MONTH_DAY);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700185 }
186
187 /**
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700188 * The legacy text color might have been poorly defined. Ensures that it
189 * has an appropriate activated state, using the selected state if one
190 * exists or modifying the default text color otherwise.
191 *
192 * @param color a legacy text color, or {@code null}
193 * @return a color state list with an appropriate activated state, or
194 * {@code null} if a valid activated state could not be generated
195 */
196 @Nullable
197 private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
198 if (color == null || color.hasState(R.attr.state_activated)) {
199 return color;
200 }
201
202 final int activatedColor;
203 final int defaultColor;
204 if (color.hasState(R.attr.state_selected)) {
205 activatedColor = color.getColorForState(StateSet.get(
206 StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
207 defaultColor = color.getColorForState(StateSet.get(
208 StateSet.VIEW_STATE_ENABLED), 0);
209 } else {
210 activatedColor = color.getDefaultColor();
211
212 // Generate a non-activated color using the disabled alpha.
213 final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
214 final float disabledAlpha = ta.getFloat(0, 0.30f);
215 defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
216 }
217
218 if (activatedColor == 0 || defaultColor == 0) {
219 // We somehow failed to obtain the colors.
220 return null;
221 }
222
223 final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
224 final int[] colors = new int[] { activatedColor, defaultColor };
225 return new ColorStateList(stateSet, colors);
226 }
227
228 private int multiplyAlphaComponent(int color, float alphaMod) {
229 final int srcRgb = color & 0xFFFFFF;
230 final int srcAlpha = (color >> 24) & 0xFF;
231 final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
232 return srcRgb | (dstAlpha << 24);
233 }
234
235 /**
236 * Listener called when the user selects a day in the day picker view.
237 */
238 private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() {
239 @Override
240 public void onDaySelected(DayPickerView view, Calendar day) {
241 mCurrentDate.setTimeInMillis(day.getTimeInMillis());
242 onDateChanged(true, true);
243 }
244 };
245
246 /**
247 * Listener called when the user selects a year in the year picker view.
248 */
249 private final OnYearSelectedListener mOnYearSelectedListener = new OnYearSelectedListener() {
250 @Override
251 public void onYearChanged(YearPickerView view, int year) {
252 // If the newly selected month / year does not contain the
253 // currently selected day number, change the selected day number
254 // to the last day of the selected month or year.
255 // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
256 // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
257 final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
258 final int month = mCurrentDate.get(Calendar.MONTH);
259 final int daysInMonth = getDaysInMonth(month, year);
260 if (day > daysInMonth) {
261 mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
262 }
263
264 mCurrentDate.set(Calendar.YEAR, year);
265 onDateChanged(true, true);
266
267 // Automatically switch to day picker.
268 setCurrentView(VIEW_MONTH_DAY);
269 }
270 };
271
272 /**
273 * Listener called when the user clicks on a header item.
274 */
275 private final OnClickListener mOnHeaderClickListener = new OnClickListener() {
276 @Override
277 public void onClick(View v) {
278 tryVibrate();
279
280 switch (v.getId()) {
281 case R.id.date_picker_header_year:
282 setCurrentView(VIEW_YEAR);
283 break;
284 case R.id.date_picker_header_date:
285 setCurrentView(VIEW_MONTH_DAY);
286 break;
287 }
288 }
289 };
290
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700291 @Override
292 protected void onLocaleChanged(Locale locale) {
293 final TextView headerYear = mHeaderYear;
294 if (headerYear == null) {
295 // Abort, we haven't initialized yet. This method will get called
296 // again later after everything has been set up.
297 return;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700298 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700299
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700300 // Update the date formatter.
301 final String datePattern = DateFormat.getBestDateTimePattern(locale, "EMMMd");
302 mMonthDayFormat = new SimpleDateFormat(datePattern, locale);
303 mYearFormat = new SimpleDateFormat("y", locale);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700304
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700305 // Update the header text.
306 onCurrentDateChanged(false);
307 }
308
309 private void onCurrentDateChanged(boolean announce) {
310 if (mHeaderYear == null) {
311 // Abort, we haven't initialized yet. This method will get called
312 // again later after everything has been set up.
313 return;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700314 }
315
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700316 final String year = mYearFormat.format(mCurrentDate.getTime());
317 mHeaderYear.setText(year);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700318
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700319 final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime());
320 mHeaderMonthDay.setText(monthDay);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700321
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700322 // TODO: This should use live regions.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700323 if (announce) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700324 final long millis = mCurrentDate.getTimeInMillis();
325 final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
326 final String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700327 mAnimator.announceForAccessibility(fullDateText);
328 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700329 }
330
331 private void setCurrentView(final int viewIndex) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700332 switch (viewIndex) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700333 case VIEW_MONTH_DAY:
334 mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
335
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700336 if (mCurrentView != viewIndex) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700337 mHeaderMonthDay.setActivated(true);
338 mHeaderYear.setActivated(false);
339 mAnimator.setDisplayedChild(VIEW_MONTH_DAY);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700340 mCurrentView = viewIndex;
341 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700342
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700343 mAnimator.announceForAccessibility(mSelectDay);
344 break;
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700345 case VIEW_YEAR:
346 mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
347
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700348 if (mCurrentView != viewIndex) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700349 mHeaderMonthDay.setActivated(false);
350 mHeaderYear.setActivated(true);
351 mAnimator.setDisplayedChild(VIEW_YEAR);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700352 mCurrentView = viewIndex;
353 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700354
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700355 mAnimator.announceForAccessibility(mSelectYear);
356 break;
357 }
358 }
359
360 @Override
361 public void init(int year, int monthOfYear, int dayOfMonth,
362 DatePicker.OnDateChangedListener callBack) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700363 mCurrentDate.set(Calendar.YEAR, year);
364 mCurrentDate.set(Calendar.MONTH, monthOfYear);
365 mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800366
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800367 mDateChangedListener = callBack;
Alan Viverette0cecbc92014-12-12 14:32:43 -0800368
369 onDateChanged(false, false);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700370 }
371
372 @Override
373 public void updateDate(int year, int month, int dayOfMonth) {
374 mCurrentDate.set(Calendar.YEAR, year);
375 mCurrentDate.set(Calendar.MONTH, month);
376 mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800377
Alan Viverette0cecbc92014-12-12 14:32:43 -0800378 onDateChanged(false, true);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800379 }
380
Alan Viverette0cecbc92014-12-12 14:32:43 -0800381 private void onDateChanged(boolean fromUser, boolean callbackToClient) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700382 final int year = mCurrentDate.get(Calendar.YEAR);
383
Alan Viverette0cecbc92014-12-12 14:32:43 -0800384 if (callbackToClient && mDateChangedListener != null) {
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800385 final int monthOfYear = mCurrentDate.get(Calendar.MONTH);
386 final int dayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
387 mDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
Alan Viverette9468c6a2014-08-21 13:56:54 -0700388 }
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800389
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700390 mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
391 mYearPickerView.setYear(year);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800392
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700393 onCurrentDateChanged(fromUser);
Alan Viverettee6ec07f2014-12-10 11:24:27 -0800394
395 if (fromUser) {
396 tryVibrate();
397 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700398 }
399
400 @Override
401 public int getYear() {
402 return mCurrentDate.get(Calendar.YEAR);
403 }
404
405 @Override
406 public int getMonth() {
407 return mCurrentDate.get(Calendar.MONTH);
408 }
409
410 @Override
411 public int getDayOfMonth() {
412 return mCurrentDate.get(Calendar.DAY_OF_MONTH);
413 }
414
415 @Override
416 public void setMinDate(long minDate) {
417 mTempDate.setTimeInMillis(minDate);
418 if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
419 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
420 return;
421 }
422 if (mCurrentDate.before(mTempDate)) {
423 mCurrentDate.setTimeInMillis(minDate);
Alan Viverette0cecbc92014-12-12 14:32:43 -0800424 onDateChanged(false, true);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700425 }
426 mMinDate.setTimeInMillis(minDate);
Alan Viverette46127402014-11-13 10:50:37 -0800427 mDayPickerView.setMinDate(minDate);
Alan Viverette50eb0252014-10-24 14:34:26 -0700428 mYearPickerView.setRange(mMinDate, mMaxDate);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700429 }
430
431 @Override
432 public Calendar getMinDate() {
433 return mMinDate;
434 }
435
436 @Override
437 public void setMaxDate(long maxDate) {
438 mTempDate.setTimeInMillis(maxDate);
439 if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
440 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
441 return;
442 }
443 if (mCurrentDate.after(mTempDate)) {
444 mCurrentDate.setTimeInMillis(maxDate);
Alan Viverette0cecbc92014-12-12 14:32:43 -0800445 onDateChanged(false, true);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700446 }
447 mMaxDate.setTimeInMillis(maxDate);
Alan Viverette46127402014-11-13 10:50:37 -0800448 mDayPickerView.setMaxDate(maxDate);
Alan Viverette50eb0252014-10-24 14:34:26 -0700449 mYearPickerView.setRange(mMinDate, mMaxDate);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700450 }
451
452 @Override
453 public Calendar getMaxDate() {
454 return mMaxDate;
455 }
456
457 @Override
Alan Viverette0a04bb02014-09-03 20:48:02 -0700458 public void setFirstDayOfWeek(int firstDayOfWeek) {
459 mFirstDayOfWeek = firstDayOfWeek;
Alan Viverettee763c9b2014-11-06 15:22:31 -0800460
461 mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
Alan Viverette0a04bb02014-09-03 20:48:02 -0700462 }
463
464 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700465 public int getFirstDayOfWeek() {
Alan Viverette0a04bb02014-09-03 20:48:02 -0700466 if (mFirstDayOfWeek != USE_LOCALE) {
467 return mFirstDayOfWeek;
468 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700469 return mCurrentDate.getFirstDayOfWeek();
470 }
471
472 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700473 public void setEnabled(boolean enabled) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700474 mContainer.setEnabled(false);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700475 }
476
477 @Override
478 public boolean isEnabled() {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700479 return mContainer.isEnabled();
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700480 }
481
482 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700483 public CalendarView getCalendarView() {
484 throw new UnsupportedOperationException(
485 "CalendarView does not exists for the new DatePicker");
486 }
487
488 @Override
489 public void setCalendarViewShown(boolean shown) {
490 // No-op for compatibility with the old DatePicker.
491 }
492
493 @Override
494 public boolean getCalendarViewShown() {
495 return false;
496 }
497
498 @Override
499 public void setSpinnersShown(boolean shown) {
500 // No-op for compatibility with the old DatePicker.
501 }
502
503 @Override
504 public boolean getSpinnersShown() {
505 return false;
506 }
507
508 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700509 public void onConfigurationChanged(Configuration newConfig) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700510 setCurrentLocale(newConfig.locale);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700511 }
512
513 @Override
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700514 public Parcelable onSaveInstanceState(Parcelable superState) {
515 final int year = mCurrentDate.get(Calendar.YEAR);
516 final int month = mCurrentDate.get(Calendar.MONTH);
517 final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
518
519 int listPosition = -1;
520 int listPositionOffset = -1;
521
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700522 if (mCurrentView == VIEW_MONTH_DAY) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700523 listPosition = mDayPickerView.getMostVisiblePosition();
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700524 } else if (mCurrentView == VIEW_YEAR) {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700525 listPosition = mYearPickerView.getFirstVisiblePosition();
526 listPositionOffset = mYearPickerView.getFirstPositionOffset();
527 }
528
529 return new SavedState(superState, year, month, day, mMinDate.getTimeInMillis(),
530 mMaxDate.getTimeInMillis(), mCurrentView, listPosition, listPositionOffset);
531 }
532
533 @Override
534 public void onRestoreInstanceState(Parcelable state) {
Alan Viverette816aa142015-04-10 15:41:10 -0700535 final SavedState ss = (SavedState) state;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700536
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700537 // TODO: Move instance state into DayPickerView, YearPickerView.
Craig Mautnera67d9092014-09-16 15:38:47 -0700538 mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700539 mMinDate.setTimeInMillis(ss.getMinDate());
540 mMaxDate.setTimeInMillis(ss.getMaxDate());
541
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700542 onCurrentDateChanged(false);
Alan Viverette816aa142015-04-10 15:41:10 -0700543
544 final int currentView = ss.getCurrentView();
545 setCurrentView(currentView);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700546
547 final int listPosition = ss.getListPosition();
548 if (listPosition != -1) {
Alan Viverette816aa142015-04-10 15:41:10 -0700549 if (currentView == VIEW_MONTH_DAY) {
Alan Viverette78bf1d32015-04-17 10:39:22 -0700550 mDayPickerView.setPosition(listPosition);
Alan Viverette816aa142015-04-10 15:41:10 -0700551 } else if (currentView == VIEW_YEAR) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700552 final int listPositionOffset = ss.getListPositionOffset();
553 mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700554 }
555 }
556 }
557
558 @Override
559 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
560 onPopulateAccessibilityEvent(event);
561 return true;
562 }
563
564 @Override
565 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
566 event.getText().add(mCurrentDate.getTime().toString());
567 }
568
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -0800569 public CharSequence getAccessibilityClassName() {
570 return DatePicker.class.getName();
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700571 }
572
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700573 public static int getDaysInMonth(int month, int year) {
574 switch (month) {
575 case Calendar.JANUARY:
576 case Calendar.MARCH:
577 case Calendar.MAY:
578 case Calendar.JULY:
579 case Calendar.AUGUST:
580 case Calendar.OCTOBER:
581 case Calendar.DECEMBER:
582 return 31;
583 case Calendar.APRIL:
584 case Calendar.JUNE:
585 case Calendar.SEPTEMBER:
586 case Calendar.NOVEMBER:
587 return 30;
588 case Calendar.FEBRUARY:
589 return (year % 4 == 0) ? 29 : 28;
590 default:
591 throw new IllegalArgumentException("Invalid Month");
592 }
593 }
594
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700595 private void tryVibrate() {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700596 mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
597 }
598
Alan Viverettee763c9b2014-11-06 15:22:31 -0800599 /**
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700600 * Class for managing state storing/restoring.
601 */
602 private static class SavedState extends View.BaseSavedState {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700603 private final int mSelectedYear;
604 private final int mSelectedMonth;
605 private final int mSelectedDay;
606 private final long mMinDate;
607 private final long mMaxDate;
608 private final int mCurrentView;
609 private final int mListPosition;
610 private final int mListPositionOffset;
611
612 /**
613 * Constructor called from {@link DatePicker#onSaveInstanceState()}
614 */
615 private SavedState(Parcelable superState, int year, int month, int day,
616 long minDate, long maxDate, int currentView, int listPosition,
617 int listPositionOffset) {
618 super(superState);
619 mSelectedYear = year;
620 mSelectedMonth = month;
621 mSelectedDay = day;
622 mMinDate = minDate;
623 mMaxDate = maxDate;
624 mCurrentView = currentView;
625 mListPosition = listPosition;
626 mListPositionOffset = listPositionOffset;
627 }
628
629 /**
630 * Constructor called from {@link #CREATOR}
631 */
632 private SavedState(Parcel in) {
633 super(in);
634 mSelectedYear = in.readInt();
635 mSelectedMonth = in.readInt();
636 mSelectedDay = in.readInt();
637 mMinDate = in.readLong();
638 mMaxDate = in.readLong();
639 mCurrentView = in.readInt();
640 mListPosition = in.readInt();
641 mListPositionOffset = in.readInt();
642 }
643
644 @Override
645 public void writeToParcel(Parcel dest, int flags) {
646 super.writeToParcel(dest, flags);
647 dest.writeInt(mSelectedYear);
648 dest.writeInt(mSelectedMonth);
649 dest.writeInt(mSelectedDay);
650 dest.writeLong(mMinDate);
651 dest.writeLong(mMaxDate);
652 dest.writeInt(mCurrentView);
653 dest.writeInt(mListPosition);
654 dest.writeInt(mListPositionOffset);
655 }
656
657 public int getSelectedDay() {
658 return mSelectedDay;
659 }
660
661 public int getSelectedMonth() {
662 return mSelectedMonth;
663 }
664
665 public int getSelectedYear() {
666 return mSelectedYear;
667 }
668
669 public long getMinDate() {
670 return mMinDate;
671 }
672
673 public long getMaxDate() {
674 return mMaxDate;
675 }
676
677 public int getCurrentView() {
678 return mCurrentView;
679 }
680
681 public int getListPosition() {
682 return mListPosition;
683 }
684
685 public int getListPositionOffset() {
686 return mListPositionOffset;
687 }
688
689 @SuppressWarnings("all")
690 // suppress unused and hiding
691 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
692
693 public SavedState createFromParcel(Parcel in) {
694 return new SavedState(in);
695 }
696
697 public SavedState[] newArray(int size) {
698 return new SavedState[size];
699 }
700 };
701 }
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700702}