1 /* 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 17 package android.app; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.StyleRes; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.DialogInterface.OnClickListener; 26 import android.os.Bundle; 27 import android.util.TypedValue; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.widget.Button; 31 import android.widget.DatePicker; 32 import android.widget.DatePicker.OnDateChangedListener; 33 import android.widget.DatePicker.ValidationCallback; 34 35 import com.android.internal.R; 36 37 import java.util.Calendar; 38 39 /** 40 * A simple dialog containing an {@link android.widget.DatePicker}. 41 * <p> 42 * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a> 43 * guide. 44 */ 45 public class DatePickerDialog extends AlertDialog implements OnClickListener, 46 OnDateChangedListener { 47 private static final String YEAR = "year"; 48 private static final String MONTH = "month"; 49 private static final String DAY = "day"; 50 51 @UnsupportedAppUsage 52 private final DatePicker mDatePicker; 53 54 private OnDateSetListener mDateSetListener; 55 56 /** 57 * Creates a new date picker dialog for the current date using the parent 58 * context's default date picker dialog theme. 59 * 60 * @param context the parent context 61 */ DatePickerDialog(@onNull Context context)62 public DatePickerDialog(@NonNull Context context) { 63 this(context, 0, null, Calendar.getInstance(), -1, -1, -1); 64 } 65 66 /** 67 * Creates a new date picker dialog for the current date. 68 * 69 * @param context the parent context 70 * @param themeResId the resource ID of the theme against which to inflate 71 * this dialog, or {@code 0} to use the parent 72 * {@code context}'s default alert dialog theme 73 */ DatePickerDialog(@onNull Context context, @StyleRes int themeResId)74 public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId) { 75 this(context, themeResId, null, Calendar.getInstance(), -1, -1, -1); 76 } 77 78 /** 79 * Creates a new date picker dialog for the specified date using the parent 80 * context's default date picker dialog theme. 81 * 82 * @param context the parent context 83 * @param listener the listener to call when the user sets the date 84 * @param year the initially selected year 85 * @param month the initially selected month (0-11 for compatibility with 86 * {@link Calendar#MONTH}) 87 * @param dayOfMonth the initially selected day of month (1-31, depending 88 * on month) 89 */ DatePickerDialog(@onNull Context context, @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth)90 public DatePickerDialog(@NonNull Context context, @Nullable OnDateSetListener listener, 91 int year, int month, int dayOfMonth) { 92 this(context, 0, listener, null, year, month, dayOfMonth); 93 } 94 95 /** 96 * Creates a new date picker dialog for the specified date. 97 * 98 * @param context the parent context 99 * @param themeResId the resource ID of the theme against which to inflate 100 * this dialog, or {@code 0} to use the parent 101 * {@code context}'s default alert dialog theme 102 * @param listener the listener to call when the user sets the date 103 * @param year the initially selected year 104 * @param monthOfYear the initially selected month of the year (0-11 for 105 * compatibility with {@link Calendar#MONTH}) 106 * @param dayOfMonth the initially selected day of month (1-31, depending 107 * on month) 108 */ DatePickerDialog(@onNull Context context, @StyleRes int themeResId, @Nullable OnDateSetListener listener, int year, int monthOfYear, int dayOfMonth)109 public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId, 110 @Nullable OnDateSetListener listener, int year, int monthOfYear, int dayOfMonth) { 111 this(context, themeResId, listener, null, year, monthOfYear, dayOfMonth); 112 } 113 DatePickerDialog(@onNull Context context, @StyleRes int themeResId, @Nullable OnDateSetListener listener, @Nullable Calendar calendar, int year, int monthOfYear, int dayOfMonth)114 private DatePickerDialog(@NonNull Context context, @StyleRes int themeResId, 115 @Nullable OnDateSetListener listener, @Nullable Calendar calendar, int year, 116 int monthOfYear, int dayOfMonth) { 117 super(context, resolveDialogTheme(context, themeResId)); 118 119 final Context themeContext = getContext(); 120 final LayoutInflater inflater = LayoutInflater.from(themeContext); 121 final View view = inflater.inflate(R.layout.date_picker_dialog, null); 122 setView(view); 123 124 setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this); 125 setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this); 126 setButtonPanelLayoutHint(LAYOUT_HINT_SIDE); 127 128 if (calendar != null) { 129 year = calendar.get(Calendar.YEAR); 130 monthOfYear = calendar.get(Calendar.MONTH); 131 dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); 132 } 133 134 mDatePicker = (DatePicker) view.findViewById(R.id.datePicker); 135 mDatePicker.init(year, monthOfYear, dayOfMonth, this); 136 mDatePicker.setValidationCallback(mValidationCallback); 137 138 mDateSetListener = listener; 139 } 140 resolveDialogTheme(@onNull Context context, @StyleRes int themeResId)141 static @StyleRes int resolveDialogTheme(@NonNull Context context, @StyleRes int themeResId) { 142 if (themeResId == 0) { 143 final TypedValue outValue = new TypedValue(); 144 context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true); 145 return outValue.resourceId; 146 } else { 147 return themeResId; 148 } 149 } 150 151 @Override onDateChanged(@onNull DatePicker view, int year, int month, int dayOfMonth)152 public void onDateChanged(@NonNull DatePicker view, int year, int month, int dayOfMonth) { 153 mDatePicker.init(year, month, dayOfMonth, this); 154 } 155 156 /** 157 * Sets the listener to call when the user sets the date. 158 * 159 * @param listener the listener to call when the user sets the date 160 */ setOnDateSetListener(@ullable OnDateSetListener listener)161 public void setOnDateSetListener(@Nullable OnDateSetListener listener) { 162 mDateSetListener = listener; 163 } 164 165 @Override onClick(@onNull DialogInterface dialog, int which)166 public void onClick(@NonNull DialogInterface dialog, int which) { 167 switch (which) { 168 case BUTTON_POSITIVE: 169 if (mDateSetListener != null) { 170 // Clearing focus forces the dialog to commit any pending 171 // changes, e.g. typed text in a NumberPicker. 172 mDatePicker.clearFocus(); 173 mDateSetListener.onDateSet(mDatePicker, mDatePicker.getYear(), 174 mDatePicker.getMonth(), mDatePicker.getDayOfMonth()); 175 } 176 break; 177 case BUTTON_NEGATIVE: 178 cancel(); 179 break; 180 } 181 } 182 183 /** 184 * Returns the {@link DatePicker} contained in this dialog. 185 * 186 * @return the date picker 187 */ 188 @NonNull getDatePicker()189 public DatePicker getDatePicker() { 190 return mDatePicker; 191 } 192 193 /** 194 * Sets the current date. 195 * 196 * @param year the year 197 * @param month the month (0-11 for compatibility with 198 * {@link Calendar#MONTH}) 199 * @param dayOfMonth the day of month (1-31, depending on month) 200 */ updateDate(int year, int month, int dayOfMonth)201 public void updateDate(int year, int month, int dayOfMonth) { 202 mDatePicker.updateDate(year, month, dayOfMonth); 203 } 204 205 @Override onSaveInstanceState()206 public Bundle onSaveInstanceState() { 207 final Bundle state = super.onSaveInstanceState(); 208 state.putInt(YEAR, mDatePicker.getYear()); 209 state.putInt(MONTH, mDatePicker.getMonth()); 210 state.putInt(DAY, mDatePicker.getDayOfMonth()); 211 return state; 212 } 213 214 @Override onRestoreInstanceState(Bundle savedInstanceState)215 public void onRestoreInstanceState(Bundle savedInstanceState) { 216 super.onRestoreInstanceState(savedInstanceState); 217 final int year = savedInstanceState.getInt(YEAR); 218 final int month = savedInstanceState.getInt(MONTH); 219 final int day = savedInstanceState.getInt(DAY); 220 mDatePicker.init(year, month, day, this); 221 } 222 223 private final ValidationCallback mValidationCallback = new ValidationCallback() { 224 @Override 225 public void onValidationChanged(boolean valid) { 226 final Button positive = getButton(BUTTON_POSITIVE); 227 if (positive != null) { 228 positive.setEnabled(valid); 229 } 230 } 231 }; 232 233 /** 234 * The listener used to indicate the user has finished selecting a date. 235 */ 236 public interface OnDateSetListener { 237 /** 238 * @param view the picker associated with the dialog 239 * @param year the selected year 240 * @param month the selected month (0-11 for compatibility with 241 * {@link Calendar#MONTH}) 242 * @param dayOfMonth the selected day of the month (1-31, depending on 243 * month) 244 */ onDateSet(DatePicker view, int year, int month, int dayOfMonth)245 void onDateSet(DatePicker view, int year, int month, int dayOfMonth); 246 } 247 } 248