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
17package android.app;
18
19import android.content.Context;
20import android.content.DialogInterface;
21import android.content.DialogInterface.OnClickListener;
22import android.os.Bundle;
23import android.text.format.DateUtils;
24import android.util.TypedValue;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.widget.Button;
28import android.widget.DatePicker;
29import android.widget.DatePicker.OnDateChangedListener;
30import android.widget.DatePicker.ValidationCallback;
31
32import com.android.internal.R;
33
34import java.util.Calendar;
35
36/**
37 * A simple dialog containing an {@link android.widget.DatePicker}.
38 *
39 * <p>See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
40 * guide.</p>
41 */
42public class DatePickerDialog extends AlertDialog implements OnClickListener,
43        OnDateChangedListener {
44
45    private static final String YEAR = "year";
46    private static final String MONTH = "month";
47    private static final String DAY = "day";
48
49    private final DatePicker mDatePicker;
50    private final OnDateSetListener mDateSetListener;
51    private final Calendar mCalendar;
52
53    private boolean mTitleNeedsUpdate = true;
54
55    /**
56     * The callback used to indicate the user is done filling in the date.
57     */
58    public interface OnDateSetListener {
59
60        /**
61         * @param view The view associated with this listener.
62         * @param year The year that was set.
63         * @param monthOfYear The month that was set (0-11) for compatibility
64         *  with {@link java.util.Calendar}.
65         * @param dayOfMonth The day of the month that was set.
66         */
67        void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth);
68    }
69
70    /**
71     * @param context The context the dialog is to run in.
72     * @param callBack How the parent is notified that the date is set.
73     * @param year The initial year of the dialog.
74     * @param monthOfYear The initial month of the dialog.
75     * @param dayOfMonth The initial day of the dialog.
76     */
77    public DatePickerDialog(Context context,
78            OnDateSetListener callBack,
79            int year,
80            int monthOfYear,
81            int dayOfMonth) {
82        this(context, 0, callBack, year, monthOfYear, dayOfMonth);
83    }
84
85    static int resolveDialogTheme(Context context, int resid) {
86        if (resid == 0) {
87            final TypedValue outValue = new TypedValue();
88            context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true);
89            return outValue.resourceId;
90        } else {
91            return resid;
92        }
93    }
94
95    /**
96     * @param context The context the dialog is to run in.
97     * @param theme the theme to apply to this dialog
98     * @param listener How the parent is notified that the date is set.
99     * @param year The initial year of the dialog.
100     * @param monthOfYear The initial month of the dialog.
101     * @param dayOfMonth The initial day of the dialog.
102     */
103    public DatePickerDialog(Context context, int theme, OnDateSetListener listener, int year,
104            int monthOfYear, int dayOfMonth) {
105        super(context, resolveDialogTheme(context, theme));
106
107        mDateSetListener = listener;
108        mCalendar = Calendar.getInstance();
109
110        final Context themeContext = getContext();
111        final LayoutInflater inflater = LayoutInflater.from(themeContext);
112        final View view = inflater.inflate(R.layout.date_picker_dialog, null);
113        setView(view);
114        setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
115        setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
116        setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
117
118        mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
119        mDatePicker.init(year, monthOfYear, dayOfMonth, this);
120        mDatePicker.setValidationCallback(mValidationCallback);
121    }
122
123    @Override
124    public void onDateChanged(DatePicker view, int year, int month, int day) {
125        mDatePicker.init(year, month, day, this);
126        updateTitle(year, month, day);
127    }
128
129    @Override
130    public void onClick(DialogInterface dialog, int which) {
131        switch (which) {
132            case BUTTON_POSITIVE:
133                if (mDateSetListener != null) {
134                    // Clearing focus forces the dialog to commit any pending
135                    // changes, e.g. typed text in a NumberPicker.
136                    mDatePicker.clearFocus();
137                    mDateSetListener.onDateSet(mDatePicker, mDatePicker.getYear(),
138                            mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
139                }
140                break;
141            case BUTTON_NEGATIVE:
142                cancel();
143                break;
144        }
145    }
146
147    /**
148     * Gets the {@link DatePicker} contained in this dialog.
149     *
150     * @return The calendar view.
151     */
152    public DatePicker getDatePicker() {
153        return mDatePicker;
154    }
155
156    /**
157     * Sets the current date.
158     *
159     * @param year The date year.
160     * @param monthOfYear The date month.
161     * @param dayOfMonth The date day of month.
162     */
163    public void updateDate(int year, int monthOfYear, int dayOfMonth) {
164        mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
165    }
166
167    private void updateTitle(int year, int month, int day) {
168        if (!mDatePicker.getCalendarViewShown()) {
169            mCalendar.set(Calendar.YEAR, year);
170            mCalendar.set(Calendar.MONTH, month);
171            mCalendar.set(Calendar.DAY_OF_MONTH, day);
172            String title = DateUtils.formatDateTime(mContext,
173                    mCalendar.getTimeInMillis(),
174                    DateUtils.FORMAT_SHOW_DATE
175                    | DateUtils.FORMAT_SHOW_WEEKDAY
176                    | DateUtils.FORMAT_SHOW_YEAR
177                    | DateUtils.FORMAT_ABBREV_MONTH
178                    | DateUtils.FORMAT_ABBREV_WEEKDAY);
179            setTitle(title);
180            mTitleNeedsUpdate = true;
181        } else {
182            if (mTitleNeedsUpdate) {
183                mTitleNeedsUpdate = false;
184                setTitle(R.string.date_picker_dialog_title);
185            }
186        }
187    }
188
189    @Override
190    public Bundle onSaveInstanceState() {
191        final Bundle state = super.onSaveInstanceState();
192        state.putInt(YEAR, mDatePicker.getYear());
193        state.putInt(MONTH, mDatePicker.getMonth());
194        state.putInt(DAY, mDatePicker.getDayOfMonth());
195        return state;
196    }
197
198    @Override
199    public void onRestoreInstanceState(Bundle savedInstanceState) {
200        super.onRestoreInstanceState(savedInstanceState);
201        final int year = savedInstanceState.getInt(YEAR);
202        final int month = savedInstanceState.getInt(MONTH);
203        final int day = savedInstanceState.getInt(DAY);
204        mDatePicker.init(year, month, day, this);
205    }
206
207    private final ValidationCallback mValidationCallback = new ValidationCallback() {
208        @Override
209        public void onValidationChanged(boolean valid) {
210            final Button positive = getButton(BUTTON_POSITIVE);
211            if (positive != null) {
212                positive.setEnabled(valid);
213            }
214        }
215    };
216}
217