1/* 2 * Copyright (C) 2013 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 com.android.datetimepicker.date; 18 19import android.animation.ObjectAnimator; 20import android.app.Activity; 21import android.app.DialogFragment; 22import android.content.res.Resources; 23import android.os.Bundle; 24import android.text.format.DateUtils; 25import android.util.Log; 26import android.view.LayoutInflater; 27import android.view.View; 28import android.view.View.OnClickListener; 29import android.view.ViewGroup; 30import android.view.Window; 31import android.view.WindowManager; 32import android.view.animation.AlphaAnimation; 33import android.view.animation.Animation; 34import android.widget.Button; 35import android.widget.LinearLayout; 36import android.widget.TextView; 37 38import com.android.datetimepicker.HapticFeedbackController; 39import com.android.datetimepicker.R; 40import com.android.datetimepicker.Utils; 41import com.android.datetimepicker.date.MonthAdapter.CalendarDay; 42 43import java.text.SimpleDateFormat; 44import java.util.Calendar; 45import java.util.HashSet; 46import java.util.Iterator; 47import java.util.Locale; 48 49/** 50 * Dialog allowing users to select a date. 51 * 52 * @deprecated Use {@link android.app.DatePickerDialog}. 53 */ 54@Deprecated 55public class DatePickerDialog extends DialogFragment implements 56 OnClickListener, DatePickerController { 57 58 private static final String TAG = "DatePickerDialog"; 59 60 private static final int UNINITIALIZED = -1; 61 private static final int MONTH_AND_DAY_VIEW = 0; 62 private static final int YEAR_VIEW = 1; 63 64 private static final String KEY_SELECTED_YEAR = "year"; 65 private static final String KEY_SELECTED_MONTH = "month"; 66 private static final String KEY_SELECTED_DAY = "day"; 67 private static final String KEY_LIST_POSITION = "list_position"; 68 private static final String KEY_WEEK_START = "week_start"; 69 private static final String KEY_YEAR_START = "year_start"; 70 private static final String KEY_YEAR_END = "year_end"; 71 private static final String KEY_CURRENT_VIEW = "current_view"; 72 private static final String KEY_LIST_POSITION_OFFSET = "list_position_offset"; 73 74 private static final int DEFAULT_START_YEAR = 1900; 75 private static final int DEFAULT_END_YEAR = 2100; 76 77 private static final int ANIMATION_DURATION = 300; 78 private static final int ANIMATION_DELAY = 500; 79 80 private static SimpleDateFormat YEAR_FORMAT = new SimpleDateFormat("yyyy", Locale.getDefault()); 81 private static SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("dd", Locale.getDefault()); 82 83 private final Calendar mCalendar = Calendar.getInstance(); 84 private OnDateSetListener mCallBack; 85 private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>(); 86 87 private AccessibleDateAnimator mAnimator; 88 89 private TextView mDayOfWeekView; 90 private LinearLayout mMonthAndDayView; 91 private TextView mSelectedMonthTextView; 92 private TextView mSelectedDayTextView; 93 private TextView mYearView; 94 private DayPickerView mDayPickerView; 95 private YearPickerView mYearPickerView; 96 private Button mDoneButton; 97 98 private int mCurrentView = UNINITIALIZED; 99 100 private int mWeekStart = mCalendar.getFirstDayOfWeek(); 101 private int mMinYear = DEFAULT_START_YEAR; 102 private int mMaxYear = DEFAULT_END_YEAR; 103 private Calendar mMinDate; 104 private Calendar mMaxDate; 105 106 private HapticFeedbackController mHapticFeedbackController; 107 108 private boolean mDelayAnimation = true; 109 110 // Accessibility strings. 111 private String mDayPickerDescription; 112 private String mSelectDay; 113 private String mYearPickerDescription; 114 private String mSelectYear; 115 116 /** 117 * The callback used to indicate the user is done filling in the date. 118 */ 119 public interface OnDateSetListener { 120 121 /** 122 * @param view The view associated with this listener. 123 * @param year The year that was set. 124 * @param monthOfYear The month that was set (0-11) for compatibility 125 * with {@link java.util.Calendar}. 126 * @param dayOfMonth The day of the month that was set. 127 */ 128 void onDateSet(DatePickerDialog dialog, int year, int monthOfYear, int dayOfMonth); 129 } 130 131 /** 132 * The callback used to notify other date picker components of a change in selected date. 133 */ 134 public interface OnDateChangedListener { 135 136 public void onDateChanged(); 137 } 138 139 140 public DatePickerDialog() { 141 // Empty constructor required for dialog fragment. 142 } 143 144 /** 145 * @param callBack How the parent is notified that the date is set. 146 * @param year The initial year of the dialog. 147 * @param monthOfYear The initial month of the dialog. 148 * @param dayOfMonth The initial day of the dialog. 149 */ 150 public static DatePickerDialog newInstance(OnDateSetListener callBack, int year, 151 int monthOfYear, 152 int dayOfMonth) { 153 DatePickerDialog ret = new DatePickerDialog(); 154 ret.initialize(callBack, year, monthOfYear, dayOfMonth); 155 return ret; 156 } 157 158 public void initialize(OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) { 159 mCallBack = callBack; 160 mCalendar.set(Calendar.YEAR, year); 161 mCalendar.set(Calendar.MONTH, monthOfYear); 162 mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); 163 } 164 165 @Override 166 public void onCreate(Bundle savedInstanceState) { 167 super.onCreate(savedInstanceState); 168 final Activity activity = getActivity(); 169 activity.getWindow().setSoftInputMode( 170 WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 171 if (savedInstanceState != null) { 172 mCalendar.set(Calendar.YEAR, savedInstanceState.getInt(KEY_SELECTED_YEAR)); 173 mCalendar.set(Calendar.MONTH, savedInstanceState.getInt(KEY_SELECTED_MONTH)); 174 mCalendar.set(Calendar.DAY_OF_MONTH, savedInstanceState.getInt(KEY_SELECTED_DAY)); 175 } 176 } 177 178 @Override 179 public void onSaveInstanceState(Bundle outState) { 180 super.onSaveInstanceState(outState); 181 outState.putInt(KEY_SELECTED_YEAR, mCalendar.get(Calendar.YEAR)); 182 outState.putInt(KEY_SELECTED_MONTH, mCalendar.get(Calendar.MONTH)); 183 outState.putInt(KEY_SELECTED_DAY, mCalendar.get(Calendar.DAY_OF_MONTH)); 184 outState.putInt(KEY_WEEK_START, mWeekStart); 185 outState.putInt(KEY_YEAR_START, mMinYear); 186 outState.putInt(KEY_YEAR_END, mMaxYear); 187 outState.putInt(KEY_CURRENT_VIEW, mCurrentView); 188 int listPosition = -1; 189 if (mCurrentView == MONTH_AND_DAY_VIEW) { 190 listPosition = mDayPickerView.getMostVisiblePosition(); 191 } else if (mCurrentView == YEAR_VIEW) { 192 listPosition = mYearPickerView.getFirstVisiblePosition(); 193 outState.putInt(KEY_LIST_POSITION_OFFSET, mYearPickerView.getFirstPositionOffset()); 194 } 195 outState.putInt(KEY_LIST_POSITION, listPosition); 196 } 197 198 @Override 199 public View onCreateView(LayoutInflater inflater, ViewGroup container, 200 Bundle savedInstanceState) { 201 Log.d(TAG, "onCreateView: "); 202 getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); 203 204 View view = inflater.inflate(R.layout.date_picker_dialog, null); 205 206 mDayOfWeekView = (TextView) view.findViewById(R.id.date_picker_header); 207 mMonthAndDayView = (LinearLayout) view.findViewById(R.id.date_picker_month_and_day); 208 mMonthAndDayView.setOnClickListener(this); 209 mSelectedMonthTextView = (TextView) view.findViewById(R.id.date_picker_month); 210 mSelectedDayTextView = (TextView) view.findViewById(R.id.date_picker_day); 211 mYearView = (TextView) view.findViewById(R.id.date_picker_year); 212 mYearView.setOnClickListener(this); 213 214 int listPosition = -1; 215 int listPositionOffset = 0; 216 int currentView = MONTH_AND_DAY_VIEW; 217 if (savedInstanceState != null) { 218 mWeekStart = savedInstanceState.getInt(KEY_WEEK_START); 219 mMinYear = savedInstanceState.getInt(KEY_YEAR_START); 220 mMaxYear = savedInstanceState.getInt(KEY_YEAR_END); 221 currentView = savedInstanceState.getInt(KEY_CURRENT_VIEW); 222 listPosition = savedInstanceState.getInt(KEY_LIST_POSITION); 223 listPositionOffset = savedInstanceState.getInt(KEY_LIST_POSITION_OFFSET); 224 } 225 226 final Activity activity = getActivity(); 227 mDayPickerView = new SimpleDayPickerView(activity, this); 228 mYearPickerView = new YearPickerView(activity, this); 229 230 Resources res = getResources(); 231 mDayPickerDescription = res.getString(R.string.day_picker_description); 232 mSelectDay = res.getString(R.string.select_day); 233 mYearPickerDescription = res.getString(R.string.year_picker_description); 234 mSelectYear = res.getString(R.string.select_year); 235 236 mAnimator = (AccessibleDateAnimator) view.findViewById(R.id.animator); 237 mAnimator.addView(mDayPickerView); 238 mAnimator.addView(mYearPickerView); 239 mAnimator.setDateMillis(mCalendar.getTimeInMillis()); 240 // TODO: Replace with animation decided upon by the design team. 241 Animation animation = new AlphaAnimation(0.0f, 1.0f); 242 animation.setDuration(ANIMATION_DURATION); 243 mAnimator.setInAnimation(animation); 244 // TODO: Replace with animation decided upon by the design team. 245 Animation animation2 = new AlphaAnimation(1.0f, 0.0f); 246 animation2.setDuration(ANIMATION_DURATION); 247 mAnimator.setOutAnimation(animation2); 248 249 mDoneButton = (Button) view.findViewById(R.id.done); 250 mDoneButton.setOnClickListener(new OnClickListener() { 251 252 @Override 253 public void onClick(View v) { 254 tryVibrate(); 255 if (mCallBack != null) { 256 mCallBack.onDateSet(DatePickerDialog.this, mCalendar.get(Calendar.YEAR), 257 mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DAY_OF_MONTH)); 258 } 259 dismiss(); 260 } 261 }); 262 263 updateDisplay(false); 264 setCurrentView(currentView); 265 266 if (listPosition != -1) { 267 if (currentView == MONTH_AND_DAY_VIEW) { 268 mDayPickerView.postSetSelection(listPosition); 269 } else if (currentView == YEAR_VIEW) { 270 mYearPickerView.postSetSelectionFromTop(listPosition, listPositionOffset); 271 } 272 } 273 274 mHapticFeedbackController = new HapticFeedbackController(activity); 275 return view; 276 } 277 278 @Override 279 public void onResume() { 280 super.onResume(); 281 mHapticFeedbackController.start(); 282 } 283 284 @Override 285 public void onPause() { 286 super.onPause(); 287 mHapticFeedbackController.stop(); 288 } 289 290 private void setCurrentView(final int viewIndex) { 291 long millis = mCalendar.getTimeInMillis(); 292 293 switch (viewIndex) { 294 case MONTH_AND_DAY_VIEW: 295 ObjectAnimator pulseAnimator = Utils.getPulseAnimator(mMonthAndDayView, 0.9f, 296 1.05f); 297 if (mDelayAnimation) { 298 pulseAnimator.setStartDelay(ANIMATION_DELAY); 299 mDelayAnimation = false; 300 } 301 mDayPickerView.onDateChanged(); 302 if (mCurrentView != viewIndex) { 303 mMonthAndDayView.setSelected(true); 304 mYearView.setSelected(false); 305 mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW); 306 mCurrentView = viewIndex; 307 } 308 pulseAnimator.start(); 309 310 int flags = DateUtils.FORMAT_SHOW_DATE; 311 String dayString = DateUtils.formatDateTime(getActivity(), millis, flags); 312 mAnimator.setContentDescription(mDayPickerDescription+": "+dayString); 313 Utils.tryAccessibilityAnnounce(mAnimator, mSelectDay); 314 break; 315 case YEAR_VIEW: 316 pulseAnimator = Utils.getPulseAnimator(mYearView, 0.85f, 1.1f); 317 if (mDelayAnimation) { 318 pulseAnimator.setStartDelay(ANIMATION_DELAY); 319 mDelayAnimation = false; 320 } 321 mYearPickerView.onDateChanged(); 322 if (mCurrentView != viewIndex) { 323 mMonthAndDayView.setSelected(false); 324 mYearView.setSelected(true); 325 mAnimator.setDisplayedChild(YEAR_VIEW); 326 mCurrentView = viewIndex; 327 } 328 pulseAnimator.start(); 329 330 CharSequence yearString = YEAR_FORMAT.format(millis); 331 mAnimator.setContentDescription(mYearPickerDescription+": "+yearString); 332 Utils.tryAccessibilityAnnounce(mAnimator, mSelectYear); 333 break; 334 } 335 } 336 337 private void updateDisplay(boolean announce) { 338 if (mDayOfWeekView != null) { 339 mDayOfWeekView.setText(mCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, 340 Locale.getDefault()).toUpperCase(Locale.getDefault())); 341 } 342 343 mSelectedMonthTextView.setText(mCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, 344 Locale.getDefault()).toUpperCase(Locale.getDefault())); 345 mSelectedDayTextView.setText(DAY_FORMAT.format(mCalendar.getTime())); 346 mYearView.setText(YEAR_FORMAT.format(mCalendar.getTime())); 347 348 // Accessibility. 349 long millis = mCalendar.getTimeInMillis(); 350 mAnimator.setDateMillis(millis); 351 int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR; 352 String monthAndDayText = DateUtils.formatDateTime(getActivity(), millis, flags); 353 mMonthAndDayView.setContentDescription(monthAndDayText); 354 355 if (announce) { 356 flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; 357 String fullDateText = DateUtils.formatDateTime(getActivity(), millis, flags); 358 Utils.tryAccessibilityAnnounce(mAnimator, fullDateText); 359 } 360 } 361 362 public void setFirstDayOfWeek(int startOfWeek) { 363 if (startOfWeek < Calendar.SUNDAY || startOfWeek > Calendar.SATURDAY) { 364 throw new IllegalArgumentException("Value must be between Calendar.SUNDAY and " + 365 "Calendar.SATURDAY"); 366 } 367 mWeekStart = startOfWeek; 368 if (mDayPickerView != null) { 369 mDayPickerView.onChange(); 370 } 371 } 372 373 public void setYearRange(int startYear, int endYear) { 374 if (endYear <= startYear) { 375 throw new IllegalArgumentException("Year end must be larger than year start"); 376 } 377 mMinYear = startYear; 378 mMaxYear = endYear; 379 if (mDayPickerView != null) { 380 mDayPickerView.onChange(); 381 } 382 } 383 384 /** 385 * Sets the minimal date supported by this DatePicker. Dates before (but not including) the 386 * specified date will be disallowed from being selected. 387 * @param calendar a Calendar object set to the year, month, day desired as the mindate. 388 */ 389 public void setMinDate(Calendar calendar) { 390 mMinDate = calendar; 391 392 if (mDayPickerView != null) { 393 mDayPickerView.onChange(); 394 } 395 } 396 397 /** 398 * @return The minimal date supported by this DatePicker. Null if it has not been set. 399 */ 400 @Override 401 public Calendar getMinDate() { 402 return mMinDate; 403 } 404 405 /** 406 * Sets the minimal date supported by this DatePicker. Dates after (but not including) the 407 * specified date will be disallowed from being selected. 408 * @param calendar a Calendar object set to the year, month, day desired as the maxdate. 409 */ 410 public void setMaxDate(Calendar calendar) { 411 mMaxDate = calendar; 412 413 if (mDayPickerView != null) { 414 mDayPickerView.onChange(); 415 } 416 } 417 418 /** 419 * @return The maximal date supported by this DatePicker. Null if it has not been set. 420 */ 421 @Override 422 public Calendar getMaxDate() { 423 return mMaxDate; 424 } 425 426 public void setOnDateSetListener(OnDateSetListener listener) { 427 mCallBack = listener; 428 } 429 430 // If the newly selected month / year does not contain the currently selected day number, 431 // change the selected day number to the last day of the selected month or year. 432 // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30 433 // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013 434 private void adjustDayInMonthIfNeeded(int month, int year) { 435 int day = mCalendar.get(Calendar.DAY_OF_MONTH); 436 int daysInMonth = Utils.getDaysInMonth(month, year); 437 if (day > daysInMonth) { 438 mCalendar.set(Calendar.DAY_OF_MONTH, daysInMonth); 439 } 440 } 441 442 @Override 443 public void onClick(View v) { 444 tryVibrate(); 445 if (v.getId() == R.id.date_picker_year) { 446 setCurrentView(YEAR_VIEW); 447 } else if (v.getId() == R.id.date_picker_month_and_day) { 448 setCurrentView(MONTH_AND_DAY_VIEW); 449 } 450 } 451 452 @Override 453 public void onYearSelected(int year) { 454 adjustDayInMonthIfNeeded(mCalendar.get(Calendar.MONTH), year); 455 mCalendar.set(Calendar.YEAR, year); 456 updatePickers(); 457 setCurrentView(MONTH_AND_DAY_VIEW); 458 updateDisplay(true); 459 } 460 461 @Override 462 public void onDayOfMonthSelected(int year, int month, int day) { 463 mCalendar.set(Calendar.YEAR, year); 464 mCalendar.set(Calendar.MONTH, month); 465 mCalendar.set(Calendar.DAY_OF_MONTH, day); 466 updatePickers(); 467 updateDisplay(true); 468 } 469 470 private void updatePickers() { 471 Iterator<OnDateChangedListener> iterator = mListeners.iterator(); 472 while (iterator.hasNext()) { 473 iterator.next().onDateChanged(); 474 } 475 } 476 477 478 @Override 479 public CalendarDay getSelectedDay() { 480 return new CalendarDay(mCalendar); 481 } 482 483 @Override 484 public int getMinYear() { 485 return mMinYear; 486 } 487 488 @Override 489 public int getMaxYear() { 490 return mMaxYear; 491 } 492 493 @Override 494 public int getFirstDayOfWeek() { 495 return mWeekStart; 496 } 497 498 @Override 499 public void registerOnDateChangedListener(OnDateChangedListener listener) { 500 mListeners.add(listener); 501 } 502 503 @Override 504 public void unregisterOnDateChangedListener(OnDateChangedListener listener) { 505 mListeners.remove(listener); 506 } 507 508 @Override 509 public void tryVibrate() { 510 mHapticFeedbackController.tryVibrate(); 511 } 512} 513