package com.fourmob.datetimepicker.date; import android.animation.ObjectAnimator; import android.app.Activity; import android.app.DialogFragment; import android.content.res.Resources; import android.graphics.Color; import android.os.Bundle; import android.os.SystemClock; import android.os.Vibrator; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import com.fourmob.datetimepicker.R; import com.fourmob.datetimepicker.Utils; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; public class DatePickerDialog extends DialogFragment implements View.OnClickListener, DatePickerController { private static final String KEY_SELECTED_YEAR = "year"; private static final String KEY_SELECTED_MONTH = "month"; private static final String KEY_SELECTED_DAY = "day"; private static final String KEY_VIBRATE = "vibrate"; // https://code.google.com/p/android/issues/detail?id=13050 private static final int MAX_YEAR = 2037; private static final int MIN_YEAR = 1902; private static final int UNINITIALIZED = -1; private static final int MONTH_AND_DAY_VIEW = 0; private static final int YEAR_VIEW = 1; public static final int ANIMATION_DELAY = 500; public static final String KEY_WEEK_START = "week_start"; public static final String KEY_YEAR_START = "year_start"; public static final String KEY_YEAR_END = "year_end"; public static final String KEY_CURRENT_VIEW = "current_view"; public static final String KEY_LIST_POSITION = "list_position"; public static final String KEY_LIST_POSITION_OFFSET = "list_position_offset"; private static SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("dd", Locale.getDefault()); private static SimpleDateFormat YEAR_FORMAT = new SimpleDateFormat("yyyy", Locale.getDefault()); private DateFormatSymbols mDateFormatSymbols = new DateFormatSymbols(); private final Calendar mCalendar = Calendar.getInstance(); private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>(); private OnDateSetListener mCallBack; private AccessibleDateAnimator mAnimator; private boolean mDelayAnimation = true; private long mLastVibrate; private int mCurrentView = UNINITIALIZED; private int mWeekStart = mCalendar.getFirstDayOfWeek(); private int mMaxYear = MAX_YEAR; private int mMinYear = MIN_YEAR; private String mDayPickerDescription; private String mYearPickerDescription; private String mSelectDay; private String mSelectYear; private TextView mDayOfWeekView; private DayPickerView mDayPickerView; private Button mDoneButton; private LinearLayout mMonthAndDayView; private TextView mSelectedDayTextView; private TextView mSelectedMonthTextView; private Vibrator mVibrator; private YearPickerView mYearPickerView; private TextView mYearView; private boolean mVibrate = true; private boolean mCloseOnSingleTapDay; private void adjustDayInMonthIfNeeded(int month, int year) { int day = mCalendar.get(Calendar.DAY_OF_MONTH); int daysInMonth = Utils.getDaysInMonth(month, year); if (day > daysInMonth) { mCalendar.set(Calendar.DAY_OF_MONTH, daysInMonth); } } public DatePickerDialog() { // Empty constructor required for dialog fragment. DO NOT REMOVE } public static DatePickerDialog newInstance(OnDateSetListener onDateSetListener, int year, int month, int day) { return newInstance(onDateSetListener, year, month, day, true); } public static DatePickerDialog newInstance(OnDateSetListener onDateSetListener, int year, int month, int day, boolean vibrate) { DatePickerDialog datePickerDialog = new DatePickerDialog(); datePickerDialog.initialize(onDateSetListener, year, month, day, vibrate); return datePickerDialog; } public void setVibrate(boolean vibrate) { mVibrate = vibrate; } private void setCurrentView(int currentView) { setCurrentView(currentView, false); } private void setCurrentView(int currentView, boolean forceRefresh) { long timeInMillis = mCalendar.getTimeInMillis(); switch (currentView) { case MONTH_AND_DAY_VIEW: ObjectAnimator monthDayAnim = Utils.getPulseAnimator(mMonthAndDayView, 0.9F, 1.05F); if (mDelayAnimation) { monthDayAnim.setStartDelay(ANIMATION_DELAY); mDelayAnimation = false; } mDayPickerView.onDateChanged(); if (mCurrentView != currentView || forceRefresh) { // mMonthAndDayView.setSelected(true); TextView date_picker_month = (TextView) mMonthAndDayView.findViewById(R.id.date_picker_month); TextView date_picker_day = (TextView) mMonthAndDayView.findViewById(R.id.date_picker_day); date_picker_month.setTextColor(Utils.resolveColor(getActivity(), R.attr.themeColor, Color.BLUE)); date_picker_day.setTextColor(Utils.resolveColor(getActivity(), R.attr.themeColor, Color.BLUE)); // mYearView.setSelected(false); mYearView.setTextColor(getResources().getColor(R.color.date_picker_text_normal)); mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW); mCurrentView = currentView; } monthDayAnim.start(); String monthDayDesc = DateUtils.formatDateTime(getActivity(), timeInMillis, DateUtils.FORMAT_SHOW_DATE); mAnimator.setContentDescription(mDayPickerDescription + ": " + monthDayDesc); Utils.tryAccessibilityAnnounce(mAnimator, mSelectDay); break; case YEAR_VIEW: ObjectAnimator yearAnim = Utils.getPulseAnimator(mYearView, 0.85F, 1.1F); if (mDelayAnimation) { yearAnim.setStartDelay(ANIMATION_DELAY); mDelayAnimation = false; } mYearPickerView.onDateChanged(); if (mCurrentView != currentView || forceRefresh) { // mMonthAndDayView.setSelected(false); // mYearView.setSelected(true); TextView date_picker_month = (TextView) mMonthAndDayView.findViewById(R.id.date_picker_month); TextView date_picker_day = (TextView) mMonthAndDayView.findViewById(R.id.date_picker_day); date_picker_month.setTextColor(getResources().getColor(R.color.date_picker_text_normal)); date_picker_day.setTextColor(getResources().getColor(R.color.date_picker_text_normal)); mYearView.setTextColor(Utils.resolveColor(getActivity(), R.attr.themeColor, Color.BLUE)); mAnimator.setDisplayedChild(YEAR_VIEW); mCurrentView = currentView; } yearAnim.start(); String dayDesc = YEAR_FORMAT.format(timeInMillis); mAnimator.setContentDescription(mYearPickerDescription + ": " + dayDesc); Utils.tryAccessibilityAnnounce(mAnimator, mSelectYear); break; } } private void updateDisplay(boolean announce) { /*if (mDayOfWeekView != null) { mDayOfWeekView.setText(mCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault()).toUpperCase(Locale.getDefault())); } mSelectedMonthTextView.setText(mCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()).toUpperCase(Locale.getDefault()));*/ if (this.mDayOfWeekView != null){ this.mCalendar.setFirstDayOfWeek(mWeekStart); this.mDayOfWeekView.setText(mDateFormatSymbols.getWeekdays()[this.mCalendar.get(Calendar.DAY_OF_WEEK)].toUpperCase(Locale.getDefault())); } this.mSelectedMonthTextView.setText(mDateFormatSymbols.getMonths()[this.mCalendar.get(Calendar.MONTH)].toUpperCase(Locale.getDefault())); mSelectedDayTextView.setText(DAY_FORMAT.format(mCalendar.getTime())); mYearView.setText(YEAR_FORMAT.format(mCalendar.getTime())); // Accessibility. long millis = mCalendar.getTimeInMillis(); mAnimator.setDateMillis(millis); int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR; String monthAndDayText = DateUtils.formatDateTime(getActivity(), millis, flags); mMonthAndDayView.setContentDescription(monthAndDayText); if (announce) { flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; String fullDateText = DateUtils.formatDateTime(getActivity(), millis, flags); Utils.tryAccessibilityAnnounce(mAnimator, fullDateText); } } private void updatePickers() { Iterator<OnDateChangedListener> iterator = mListeners.iterator(); while (iterator.hasNext()) { iterator.next().onDateChanged(); } } public int getFirstDayOfWeek() { return mWeekStart; } public int getMaxYear() { return mMaxYear; } public int getMinYear() { return mMinYear; } public SimpleMonthAdapter.CalendarDay getSelectedDay() { return new SimpleMonthAdapter.CalendarDay(mCalendar); } public void initialize(OnDateSetListener onDateSetListener, int year, int month, int day, boolean vibrate) { if (year > MAX_YEAR) throw new IllegalArgumentException("year end must < " + MAX_YEAR); if (year < MIN_YEAR) throw new IllegalArgumentException("year end must > " + MIN_YEAR); mCallBack = onDateSetListener; mCalendar.set(Calendar.YEAR, year); mCalendar.set(Calendar.MONTH, month); mCalendar.set(Calendar.DAY_OF_MONTH, day); mVibrate = vibrate; } public void onClick(View view) { tryVibrate(); if (view.getId() == R.id.date_picker_year) setCurrentView(YEAR_VIEW); else if (view.getId() == R.id.date_picker_month_and_day) setCurrentView(MONTH_AND_DAY_VIEW); } public void onCreate(Bundle bundle) { super.onCreate(bundle); Activity activity = getActivity(); activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); mVibrator = ((Vibrator) activity.getSystemService("vibrator")); if (bundle != null) { mCalendar.set(Calendar.YEAR, bundle.getInt(KEY_SELECTED_YEAR)); mCalendar.set(Calendar.MONTH, bundle.getInt(KEY_SELECTED_MONTH)); mCalendar.set(Calendar.DAY_OF_MONTH, bundle.getInt(KEY_SELECTED_DAY)); mVibrate = bundle.getBoolean(KEY_VIBRATE); } } public View onCreateView(LayoutInflater layoutInflater, ViewGroup parent, Bundle bundle) { getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); View view = layoutInflater.inflate(R.layout.date_picker_dialog, null); mDayOfWeekView = ((TextView) view.findViewById(R.id.date_picker_header)); mMonthAndDayView = ((LinearLayout) view.findViewById(R.id.date_picker_month_and_day)); mMonthAndDayView.setOnClickListener(this); mSelectedMonthTextView = ((TextView) view.findViewById(R.id.date_picker_month)); mSelectedDayTextView = ((TextView) view.findViewById(R.id.date_picker_day)); mYearView = ((TextView) view.findViewById(R.id.date_picker_year)); mYearView.setOnClickListener(this); int listPosition = -1; int currentView = MONTH_AND_DAY_VIEW; int listPositionOffset = 0; if (bundle != null) { mWeekStart = bundle.getInt(KEY_WEEK_START); mMinYear = bundle.getInt(KEY_YEAR_START); mMaxYear = bundle.getInt(KEY_YEAR_END); currentView = bundle.getInt(KEY_CURRENT_VIEW); listPosition = bundle.getInt(KEY_LIST_POSITION); listPositionOffset = bundle.getInt(KEY_LIST_POSITION_OFFSET); } Activity activity = getActivity(); mDayPickerView = new DayPickerView(activity, this); mYearPickerView = new YearPickerView(activity, this); Resources resources = getResources(); mDayPickerDescription = resources.getString(R.string.day_picker_description); mSelectDay = resources.getString(R.string.select_day); mYearPickerDescription = resources.getString(R.string.year_picker_description); mSelectYear = resources.getString(R.string.select_year); mAnimator = ((AccessibleDateAnimator) view.findViewById(R.id.animator)); mAnimator.addView(mDayPickerView); mAnimator.addView(mYearPickerView); mAnimator.setDateMillis(mCalendar.getTimeInMillis()); AlphaAnimation inAlphaAnimation = new AlphaAnimation(0.0F, 1.0F); inAlphaAnimation.setDuration(300L); mAnimator.setInAnimation(inAlphaAnimation); AlphaAnimation outAlphaAnimation = new AlphaAnimation(1.0F, 0.0F); outAlphaAnimation.setDuration(300L); mAnimator.setOutAnimation(outAlphaAnimation); mDoneButton = ((Button) view.findViewById(R.id.done)); mDoneButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { onDoneButtonClick(); } }); updateDisplay(false); setCurrentView(currentView, true); if (listPosition != -1) { if (currentView == MONTH_AND_DAY_VIEW) { mDayPickerView.postSetSelection(listPosition); } if (currentView == YEAR_VIEW) { mYearPickerView.postSetSelectionFromTop(listPosition, listPositionOffset); } } return view; } private void onDoneButtonClick() { tryVibrate(); if (mCallBack != null) { mCallBack.onDateSet(this, mCalendar.get(Calendar.YEAR), mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DAY_OF_MONTH)); } dismiss(); } public void onDayOfMonthSelected(int year, int month, int day) { mCalendar.set(Calendar.YEAR, year); mCalendar.set(Calendar.MONTH, month); mCalendar.set(Calendar.DAY_OF_MONTH, day); updatePickers(); updateDisplay(true); if(mCloseOnSingleTapDay) { onDoneButtonClick(); } } public void onSaveInstanceState(Bundle bundle) { super.onSaveInstanceState(bundle); bundle.putInt(KEY_SELECTED_YEAR, mCalendar.get(Calendar.YEAR)); bundle.putInt(KEY_SELECTED_MONTH, mCalendar.get(Calendar.MONTH)); bundle.putInt(KEY_SELECTED_DAY, mCalendar.get(Calendar.DAY_OF_MONTH)); bundle.putInt(KEY_WEEK_START, mWeekStart); bundle.putInt(KEY_YEAR_START, mMinYear); bundle.putInt(KEY_YEAR_END, mMaxYear); bundle.putInt(KEY_CURRENT_VIEW, mCurrentView); int listPosition = -1; if (mCurrentView == 0) { listPosition = mDayPickerView.getMostVisiblePosition(); } if (mCurrentView == 1) { listPosition = mYearPickerView.getFirstVisiblePosition(); bundle.putInt(KEY_LIST_POSITION_OFFSET, mYearPickerView.getFirstPositionOffset()); } bundle.putInt(KEY_LIST_POSITION, listPosition); bundle.putBoolean(KEY_VIBRATE, mVibrate); } public void onYearSelected(int year) { adjustDayInMonthIfNeeded(mCalendar.get(Calendar.MONTH), year); mCalendar.set(Calendar.YEAR, year); updatePickers(); setCurrentView(MONTH_AND_DAY_VIEW); updateDisplay(true); } public void registerOnDateChangedListener(OnDateChangedListener onDateChangedListener) { mListeners.add(onDateChangedListener); } public void setFirstDayOfWeek(int startOfWeek) { if (startOfWeek < Calendar.SUNDAY || startOfWeek > Calendar.SATURDAY) { throw new IllegalArgumentException("Value must be between Calendar.SUNDAY and " + "Calendar.SATURDAY"); } mWeekStart = startOfWeek; if (mDayPickerView != null) { mDayPickerView.onChange(); } } public void setOnDateSetListener(OnDateSetListener onDateSetListener) { mCallBack = onDateSetListener; } public void setYearRange(int minYear, int maxYear) { if (maxYear <= minYear) ; // throw new IllegalArgumentException("Year end must be larger than year start"); if (maxYear > MAX_YEAR) throw new IllegalArgumentException("max year end must < " + MAX_YEAR); if (minYear < MIN_YEAR) throw new IllegalArgumentException("min year end must > " + MIN_YEAR); mMinYear = minYear; mMaxYear = maxYear; if (mDayPickerView != null) mDayPickerView.onChange(); } public void tryVibrate() { if (mVibrator != null && mVibrate) { long timeInMillis = SystemClock.uptimeMillis(); if (timeInMillis - mLastVibrate >= 125L) { mVibrator.vibrate(5L); mLastVibrate = timeInMillis; } } } public void setCloseOnSingleTapDay(boolean closeOnSingleTapDay) { mCloseOnSingleTapDay = closeOnSingleTapDay; } static abstract interface OnDateChangedListener { public abstract void onDateChanged(); } public static abstract interface OnDateSetListener { public abstract void onDateSet(DatePickerDialog datePickerDialog, int year, int month, int day); } }