blob: 30fb927cc3cc83640ea4b36eed16f57fd130bbf7 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080019import com.android.internal.R;
20
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.annotation.Widget;
22import android.content.Context;
23import android.content.res.TypedArray;
24import android.os.Parcel;
25import android.os.Parcelable;
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080026import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.text.format.DateFormat;
Kenny Rootdddda8d2010-11-15 14:38:51 -080028import android.text.format.DateUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.util.AttributeSet;
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080030import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.util.SparseArray;
32import android.view.LayoutInflater;
Svetoslav Ganov8a2a8952011-01-27 17:40:13 -080033import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganovcedc4462011-01-19 19:25:46 -080034import android.widget.NumberPicker.OnValueChangeListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080036import java.text.ParseException;
Eric Fischer03a80172009-07-23 18:32:42 -070037import java.text.SimpleDateFormat;
Svetoslav Ganov156f2092011-01-24 02:13:36 -080038import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import java.util.Calendar;
Kenny Rootdddda8d2010-11-15 14:38:51 -080040import java.util.Locale;
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080041import java.util.TimeZone;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
43/**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080044 * This class is a widget for selecting a date. The date can be selected by a
45 * year, month, and day spinners or a {@link CalendarView}. The set of spinners
46 * and the calendar view are automatically synchronized. The client can
47 * customize whether only the spinners, or only the calendar view, or both to be
48 * displayed. Also the minimal and maximal date from which dates to be selected
49 * can be customized.
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080050 * <p>
Dirk Dougherty9a143e62011-01-17 20:09:46 -080051 * See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date
52 * Picker tutorial</a>.
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080053 * </p>
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080054 * <p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 * For a dialog using this view, see {@link android.app.DatePickerDialog}.
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080056 * </p>
57 *
58 * @attr ref android.R.styleable#DatePicker_startYear
59 * @attr ref android.R.styleable#DatePicker_endYear
60 * @attr ref android.R.styleable#DatePicker_maxDate
61 * @attr ref android.R.styleable#DatePicker_minDate
62 * @attr ref android.R.styleable#DatePicker_spinnersShown
63 * @attr ref android.R.styleable#DatePicker_calendarViewShown
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 */
65@Widget
66public class DatePicker extends FrameLayout {
67
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080068 private static final String LOG_TAG = DatePicker.class.getSimpleName();
69
70 private static final String DATE_FORMAT = "MM/dd/yyyy";
71
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 private static final int DEFAULT_START_YEAR = 1900;
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 private static final int DEFAULT_END_YEAR = 2100;
Kenny Rootdddda8d2010-11-15 14:38:51 -080075
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080076 private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true;
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080077
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080078 private static final boolean DEFAULT_SPINNERS_SHOWN = true;
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080079
Svetoslav Ganov25f84f32010-12-29 22:39:54 -080080 private static final boolean DEFAULT_ENABLED_STATE = true;
81
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080082 private final NumberPicker mDaySpinner;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080084 private final LinearLayout mSpinners;
85
86 private final NumberPicker mMonthSpinner;
87
88 private final NumberPicker mYearSpinner;
89
90 private final CalendarView mCalendarView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080092 private OnDateChangedListener mOnDateChangedListener;
93
94 private Locale mMonthLocale;
95
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080096 private final Calendar mTempDate = Calendar.getInstance();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080097
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080098 private final int mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080099
100 private final String[] mShortMonths = new String[mNumberOfMonths];
Kenny Rootdddda8d2010-11-15 14:38:51 -0800101
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800102 private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
103
104 private final Calendar mMinDate = Calendar.getInstance();
105
106 private final Calendar mMaxDate = Calendar.getInstance();
107
108 private final Calendar mCurrentDate = Calendar.getInstance();
109
Svetoslav Ganov25f84f32010-12-29 22:39:54 -0800110 private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
Svetoslav Ganov51c52ed2010-12-28 13:45:03 -0800111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 /**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800113 * The callback used to indicate the user changes\d the date.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 */
115 public interface OnDateChangedListener {
116
117 /**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800118 * Called upon a date change.
119 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 * @param view The view associated with this listener.
121 * @param year The year that was set.
122 * @param monthOfYear The month that was set (0-11) for compatibility
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800123 * with {@link java.util.Calendar}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 * @param dayOfMonth The day of the month that was set.
125 */
126 void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
127 }
128
129 public DatePicker(Context context) {
130 this(context, null);
131 }
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 public DatePicker(Context context, AttributeSet attrs) {
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800134 this(context, attrs, R.attr.datePickerStyle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 }
136
137 public DatePicker(Context context, AttributeSet attrs, int defStyle) {
138 super(context, attrs, defStyle);
139
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800140 TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
141 defStyle, 0);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800142 boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown,
143 DEFAULT_SPINNERS_SHOWN);
144 boolean calendarViewShown = attributesArray.getBoolean(
145 R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN);
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800146 int startYear = attributesArray.getInt(R.styleable.DatePicker_startYear,
147 DEFAULT_START_YEAR);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800148 int endYear = attributesArray.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
149 String minDate = attributesArray.getString(R.styleable.DatePicker_minDate);
150 String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate);
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800151 int layoutResourceId = attributesArray.getResourceId(R.styleable.DatePicker_layout,
152 R.layout.date_picker);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800153 attributesArray.recycle();
154
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800155 LayoutInflater inflater = (LayoutInflater) context
156 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800157 inflater.inflate(layoutResourceId, this, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158
Svetoslav Ganovcedc4462011-01-19 19:25:46 -0800159 OnValueChangeListener onChangeListener = new OnValueChangeListener() {
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800160 public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800161 mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
162 // take care of wrapping of days and months to update greater fields
163 if (picker == mDaySpinner) {
164 int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
165 if (oldVal == maxDayOfMonth && newVal == 1) {
166 mTempDate.add(Calendar.DAY_OF_MONTH, 1);
167 } else if (oldVal == 1 && newVal == maxDayOfMonth) {
168 mTempDate.add(Calendar.DAY_OF_MONTH, -1);
169 } else {
170 mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
171 }
172 } else if (picker == mMonthSpinner) {
173 if (oldVal == 11 && newVal == 0) {
174 mTempDate.add(Calendar.MONTH, 1);
175 } else if (oldVal == 0 && newVal == 11) {
176 mTempDate.add(Calendar.MONTH, -1);
177 } else {
178 mTempDate.add(Calendar.MONTH, newVal - oldVal);
179 }
180 } else if (picker == mYearSpinner) {
181 mTempDate.set(Calendar.YEAR, newVal);
182 } else {
183 throw new IllegalArgumentException();
184 }
185 // now set the date to the adjusted one
186 setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
187 mTempDate.get(Calendar.DAY_OF_MONTH));
Svetoslav Ganov58f51252011-01-26 22:50:51 -0800188 updateSpinners();
189 updateCalendarView();
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800190 notifyDateChanged();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800191 }
192 };
193
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800194 mSpinners = (LinearLayout) findViewById(R.id.pickers);
195
196 // calendar view day-picker
197 mCalendarView = (CalendarView) findViewById(R.id.calendar_view);
198 mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
199 public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800200 setDate(year, month, monthDay);
Svetoslav Ganov58f51252011-01-26 22:50:51 -0800201 updateSpinners();
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800202 notifyDateChanged();
Svetoslav Ganov28104e12010-12-19 16:03:07 -0800203 }
204 });
205
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800206 // day
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800207 mDaySpinner = (NumberPicker) findViewById(R.id.day);
208 mDaySpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
209 mDaySpinner.setOnLongPressUpdateInterval(100);
210 mDaySpinner.setOnValueChangedListener(onChangeListener);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800211
212 // month
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800213 mMonthSpinner = (NumberPicker) findViewById(R.id.month);
214 mMonthSpinner.setMinValue(0);
215 mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
216 mMonthSpinner.setDisplayedValues(getShortMonths());
217 mMonthSpinner.setOnLongPressUpdateInterval(200);
218 mMonthSpinner.setOnValueChangedListener(onChangeListener);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800219
220 // year
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800221 mYearSpinner = (NumberPicker) findViewById(R.id.year);
222 mYearSpinner.setOnLongPressUpdateInterval(100);
223 mYearSpinner.setOnValueChangedListener(onChangeListener);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800224
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800225 // show only what the user required but make sure we
226 // show something and the spinners have higher priority
227 if (!spinnersShown && !calendarViewShown) {
228 setSpinnersShown(true);
229 } else {
230 setSpinnersShown(spinnersShown);
231 setCalendarViewShown(calendarViewShown);
Svetoslav Ganov13427a02011-01-31 16:37:05 -0800232 }
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800233
Svetoslav Ganov13427a02011-01-31 16:37:05 -0800234 // set the min date giving priority of the minDate over startYear
235 mTempDate.clear();
236 if (!TextUtils.isEmpty(minDate)) {
237 if (!parseDate(minDate, mTempDate)) {
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800238 mTempDate.set(startYear, 0, 1);
239 }
Svetoslav Ganov13427a02011-01-31 16:37:05 -0800240 } else {
241 mTempDate.set(startYear, 0, 1);
242 }
243 setMinDate(mTempDate.getTimeInMillis());
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800244
Svetoslav Ganov13427a02011-01-31 16:37:05 -0800245 // set the max date giving priority of the maxDate over endYear
246 mTempDate.clear();
247 if (!TextUtils.isEmpty(maxDate)) {
248 if (!parseDate(maxDate, mTempDate)) {
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800249 mTempDate.set(endYear, 11, 31);
250 }
Svetoslav Ganov13427a02011-01-31 16:37:05 -0800251 } else {
252 mTempDate.set(endYear, 11, 31);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800253 }
Svetoslav Ganov13427a02011-01-31 16:37:05 -0800254 setMaxDate(mTempDate.getTimeInMillis());
255
256 // initialize to current date
257 mCurrentDate.setTimeInMillis(System.currentTimeMillis());
258 init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
259 .get(Calendar.DAY_OF_MONTH), null);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800260
261 // re-order the number spinners to match the current date format
262 reorderSpinners();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 }
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800264
Svetoslav Ganov28104e12010-12-19 16:03:07 -0800265 /**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800266 * Gets the minimal date supported by this {@link DatePicker} in
267 * milliseconds since January 1, 1970 00:00:00 in
268 * {@link TimeZone#getDefault()} time zone.
Svetoslav Ganov28104e12010-12-19 16:03:07 -0800269 * <p>
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800270 * Note: The default minimal date is 01/01/1900.
271 * <p>
Svetoslav Ganov28104e12010-12-19 16:03:07 -0800272 *
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800273 * @return The minimal supported date.
Svetoslav Ganov28104e12010-12-19 16:03:07 -0800274 */
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800275 public long getMinDate() {
276 return mCalendarView.getMinDate();
277 }
Svetoslav Ganov28104e12010-12-19 16:03:07 -0800278
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800279 /**
280 * Sets the minimal date supported by this {@link NumberPicker} in
281 * milliseconds since January 1, 1970 00:00:00 in
282 * {@link TimeZone#getDefault()} time zone.
283 *
284 * @param minDate The minimal supported date.
285 */
286 public void setMinDate(long minDate) {
287 mTempDate.setTimeInMillis(minDate);
288 if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
289 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
290 return;
Svetoslav Ganov28104e12010-12-19 16:03:07 -0800291 }
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800292 mMinDate.setTimeInMillis(minDate);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800293 mCalendarView.setMinDate(minDate);
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800294 if (mCurrentDate.before(mMinDate)) {
295 mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
296 updateCalendarView();
297 }
298 updateSpinners();
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800299 }
300
301 /**
302 * Gets the maximal date supported by this {@link DatePicker} in
303 * milliseconds since January 1, 1970 00:00:00 in
304 * {@link TimeZone#getDefault()} time zone.
305 * <p>
306 * Note: The default maximal date is 12/31/2100.
307 * <p>
308 *
309 * @return The maximal supported date.
310 */
311 public long getMaxDate() {
312 return mCalendarView.getMaxDate();
313 }
314
315 /**
316 * Sets the maximal date supported by this {@link DatePicker} in
317 * milliseconds since January 1, 1970 00:00:00 in
318 * {@link TimeZone#getDefault()} time zone.
319 *
320 * @param maxDate The maximal supported date.
321 */
322 public void setMaxDate(long maxDate) {
323 mTempDate.setTimeInMillis(maxDate);
324 if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
325 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
326 return;
327 }
328 mMaxDate.setTimeInMillis(maxDate);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800329 mCalendarView.setMaxDate(maxDate);
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800330 if (mCurrentDate.after(mMaxDate)) {
331 mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
332 updateCalendarView();
333 }
334 updateSpinners();
Svetoslav Ganov28104e12010-12-19 16:03:07 -0800335 }
336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 @Override
338 public void setEnabled(boolean enabled) {
Svetoslav Ganov51c52ed2010-12-28 13:45:03 -0800339 if (mIsEnabled == enabled) {
340 return;
341 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 super.setEnabled(enabled);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800343 mDaySpinner.setEnabled(enabled);
344 mMonthSpinner.setEnabled(enabled);
345 mYearSpinner.setEnabled(enabled);
346 mCalendarView.setEnabled(enabled);
Svetoslav Ganov51c52ed2010-12-28 13:45:03 -0800347 mIsEnabled = enabled;
348 }
349
350 @Override
351 public boolean isEnabled() {
352 return mIsEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 }
354
Svetoslav Ganov8a2a8952011-01-27 17:40:13 -0800355 @Override
Svetoslav Ganov736c2752011-04-22 18:30:36 -0700356 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
357 super.onPopulateAccessibilityEvent(event);
358
359 final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY
Svetoslav Ganov8a2a8952011-01-27 17:40:13 -0800360 | DateUtils.FORMAT_SHOW_YEAR;
361 String selectedDateUtterance = DateUtils.formatDateTime(mContext,
362 mCurrentDate.getTimeInMillis(), flags);
363 event.getText().add(selectedDateUtterance);
Svetoslav Ganov8a2a8952011-01-27 17:40:13 -0800364 }
365
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800366 /**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800367 * Gets whether the {@link CalendarView} is shown.
368 *
369 * @return True if the calendar view is shown.
Svetoslav Ganov5f3f6ce2011-02-24 15:36:13 -0800370 * @see #getCalendarView()
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800371 */
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800372 public boolean getCalendarViewShown() {
373 return mCalendarView.isShown();
374 }
375
376 /**
Svetoslav Ganov5f3f6ce2011-02-24 15:36:13 -0800377 * Gets the {@link CalendarView}.
378 *
379 * @return The calendar view.
380 * @see #getCalendarViewShown()
381 */
382 public CalendarView getCalendarView () {
383 return mCalendarView;
384 }
385
386 /**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800387 * Sets whether the {@link CalendarView} is shown.
388 *
389 * @param shown True if the calendar view is to be shown.
390 */
391 public void setCalendarViewShown(boolean shown) {
392 mCalendarView.setVisibility(shown ? VISIBLE : GONE);
393 }
394
395 /**
396 * Gets whether the spinners are shown.
397 *
398 * @return True if the spinners are shown.
399 */
400 public boolean getSpinnersShown() {
401 return mSpinners.isShown();
402 }
403
404 /**
405 * Sets whether the spinners are shown.
406 *
407 * @param shown True if the spinners are to be shown.
408 */
409 public void setSpinnersShown(boolean shown) {
410 mSpinners.setVisibility(shown ? VISIBLE : GONE);
411 }
412
413 /**
Svetoslav Ganovf583dd32011-03-18 14:41:50 -0700414 * Reorders the spinners according to the date format that is
415 * explicitly set by the user and if no such is set fall back
416 * to the current locale's default format.
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800417 */
418 private void reorderSpinners() {
Svetoslav Ganovf583dd32011-03-18 14:41:50 -0700419 mSpinners.removeAllViews();
420 char[] order = DateFormat.getDateFormatOrder(getContext());
421 for (int i = 0; i < order.length; i++) {
422 switch (order[i]) {
423 case DateFormat.DATE:
424 mSpinners.addView(mDaySpinner);
425 break;
426 case DateFormat.MONTH:
427 mSpinners.addView(mMonthSpinner);
428 break;
429 case DateFormat.YEAR:
430 mSpinners.addView(mYearSpinner);
431 break;
432 default:
433 throw new IllegalArgumentException();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 }
435 }
436 }
437
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800438 /**
439 * Updates the current date.
440 *
441 * @param year The year.
442 * @param month The month which is <strong>starting from zero</strong>.
443 * @param dayOfMonth The day of the month.
444 */
445 public void updateDate(int year, int month, int dayOfMonth) {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800446 if (!isNewDate(year, month, dayOfMonth)) {
447 return;
Kenny Rootdddda8d2010-11-15 14:38:51 -0800448 }
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800449 setDate(year, month, dayOfMonth);
Svetoslav Ganov58f51252011-01-26 22:50:51 -0800450 updateSpinners();
451 updateCalendarView();
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800452 notifyDateChanged();
Kenny Rootdddda8d2010-11-15 14:38:51 -0800453 }
454
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800455 // Override so we are in complete control of save / restore for this widget.
456 @Override
457 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
458 dispatchThawSelfOnly(container);
459 }
460
461 @Override
462 protected Parcelable onSaveInstanceState() {
463 Parcelable superState = super.onSaveInstanceState();
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800464 return new SavedState(superState, getYear(), getMonth(), getDayOfMonth());
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800465 }
466
467 @Override
468 protected void onRestoreInstanceState(Parcelable state) {
469 SavedState ss = (SavedState) state;
470 super.onRestoreInstanceState(ss.getSuperState());
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800471 setDate(ss.mYear, ss.mMonth, ss.mDay);
Svetoslav Ganov58f51252011-01-26 22:50:51 -0800472 updateSpinners();
473 updateCalendarView();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800474 }
475
476 /**
477 * Initialize the state. If the provided values designate an inconsistent
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800478 * date the values are normalized before updating the spinners.
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800479 *
480 * @param year The initial year.
481 * @param monthOfYear The initial month <strong>starting from zero</strong>.
482 * @param dayOfMonth The initial day of the month.
483 * @param onDateChangedListener How user is notified date is changed by
484 * user, can be null.
485 */
486 public void init(int year, int monthOfYear, int dayOfMonth,
487 OnDateChangedListener onDateChangedListener) {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800488 setDate(year, monthOfYear, dayOfMonth);
Svetoslav Ganov58f51252011-01-26 22:50:51 -0800489 updateSpinners();
490 updateCalendarView();
Svetoslav Ganov2f136a82011-01-13 11:33:05 -0800491 mOnDateChangedListener = onDateChangedListener;
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800492 }
493
494 /**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800495 * Parses the given <code>date</code> and in case of success sets the result
496 * to the <code>outDate</code>.
Svetoslav Ganova911d4a2010-12-08 16:11:30 -0800497 *
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800498 * @return True if the date was parsed.
Svetoslav Ganova911d4a2010-12-08 16:11:30 -0800499 */
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800500 private boolean parseDate(String date, Calendar outDate) {
501 try {
502 outDate.setTime(mDateFormat.parse(date));
503 return true;
504 } catch (ParseException e) {
505 Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
506 return false;
507 }
Svetoslav Ganova911d4a2010-12-08 16:11:30 -0800508 }
509
510 /**
511 * @return The short month abbreviations.
512 */
513 private String[] getShortMonths() {
514 final Locale currentLocale = Locale.getDefault();
515 if (currentLocale.equals(mMonthLocale)) {
516 return mShortMonths;
517 } else {
518 for (int i = 0; i < mNumberOfMonths; i++) {
519 mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i,
520 DateUtils.LENGTH_MEDIUM);
521 }
522 mMonthLocale = currentLocale;
523 return mShortMonths;
524 }
525 }
526
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800527 private boolean isNewDate(int year, int month, int dayOfMonth) {
528 return (mCurrentDate.get(Calendar.YEAR) != year
529 || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
530 || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month);
531 }
Svetoslav Ganova911d4a2010-12-08 16:11:30 -0800532
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800533 private void setDate(int year, int month, int dayOfMonth) {
534 mCurrentDate.set(year, month, dayOfMonth);
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800535 if (mCurrentDate.before(mMinDate)) {
536 mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
537 } else if (mCurrentDate.after(mMaxDate)) {
538 mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
539 }
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800540 }
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800541
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800542 private void updateSpinners() {
543 // set the spinner ranges respecting the min and max dates
544 if (mCurrentDate.equals(mMinDate)) {
545 mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
546 mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
547 mDaySpinner.setWrapSelectorWheel(false);
548 mMonthSpinner.setDisplayedValues(null);
549 mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
550 mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
551 mMonthSpinner.setWrapSelectorWheel(false);
552 } else if (mCurrentDate.equals(mMaxDate)) {
553 mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
554 mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
555 mDaySpinner.setWrapSelectorWheel(false);
556 mMonthSpinner.setDisplayedValues(null);
557 mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
558 mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
559 mMonthSpinner.setWrapSelectorWheel(false);
560 } else {
561 mDaySpinner.setMinValue(1);
562 mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
563 mDaySpinner.setWrapSelectorWheel(true);
564 mMonthSpinner.setDisplayedValues(null);
565 mMonthSpinner.setMinValue(0);
566 mMonthSpinner.setMaxValue(11);
567 mMonthSpinner.setWrapSelectorWheel(true);
568 }
569
570 // make sure the month names are a zero based array
571 // with the months in the month spinner
572 String[] displayedValues = Arrays.copyOfRange(getShortMonths(),
573 mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
574 mMonthSpinner.setDisplayedValues(displayedValues);
575
576 // year spinner range does not change based on the current date
577 mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
578 mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
579 mYearSpinner.setWrapSelectorWheel(false);
580
581 // set the spinner values
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800582 mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
583 mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800584 mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800585 }
586
587 /**
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800588 * Updates the calendar view with the current date.
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800589 */
590 private void updateCalendarView() {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800591 mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800592 }
593
594 /**
595 * @return The selected year.
596 */
597 public int getYear() {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800598 return mCurrentDate.get(Calendar.YEAR);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800599 }
600
601 /**
602 * @return The selected month.
603 */
604 public int getMonth() {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800605 return mCurrentDate.get(Calendar.MONTH);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800606 }
607
608 /**
609 * @return The selected day of month.
610 */
611 public int getDayOfMonth() {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800612 return mCurrentDate.get(Calendar.DAY_OF_MONTH);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800613 }
614
615 /**
616 * Notifies the listener, if such, for a change in the selected date.
617 */
618 private void notifyDateChanged() {
Svetoslav Ganov8a2a8952011-01-27 17:40:13 -0800619 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800620 if (mOnDateChangedListener != null) {
Svetoslav Ganov156f2092011-01-24 02:13:36 -0800621 mOnDateChangedListener.onDateChanged(this, getYear(), getMonth(), getDayOfMonth());
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800622 }
623 }
624
625 /**
626 * Class for managing state storing/restoring.
627 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 private static class SavedState extends BaseSavedState {
629
630 private final int mYear;
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800631
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 private final int mMonth;
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800633
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 private final int mDay;
635
636 /**
637 * Constructor called from {@link DatePicker#onSaveInstanceState()}
638 */
639 private SavedState(Parcelable superState, int year, int month, int day) {
640 super(superState);
641 mYear = year;
642 mMonth = month;
643 mDay = day;
644 }
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800645
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 /**
647 * Constructor called from {@link #CREATOR}
648 */
649 private SavedState(Parcel in) {
650 super(in);
651 mYear = in.readInt();
652 mMonth = in.readInt();
653 mDay = in.readInt();
654 }
655
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 @Override
657 public void writeToParcel(Parcel dest, int flags) {
658 super.writeToParcel(dest, flags);
659 dest.writeInt(mYear);
660 dest.writeInt(mMonth);
661 dest.writeInt(mDay);
662 }
663
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800664 @SuppressWarnings("all")
665 // suppress unused and hiding
666 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800668 public SavedState createFromParcel(Parcel in) {
669 return new SavedState(in);
670 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800672 public SavedState[] newArray(int size) {
673 return new SavedState[size];
674 }
675 };
Kenneth Anderssone3491b62010-03-05 09:16:24 +0100676 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677}
Svetoslav Ganovf1148092011-02-25 12:39:59 -0800678