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