OSDN Git Service

Remove unnecessary casts on calls to findViewById
[android-x86/frameworks-base.git] / tests / HwAccelerationTest / src / com / android / test / hwui / DatePicker.java
1 /*
2  * Copyright (C) 2012 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 com.android.test.hwui;
18
19 import android.annotation.Widget;
20 import android.content.Context;
21 import android.content.res.TypedArray;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.text.format.DateFormat;
25 import android.util.AttributeSet;
26 import android.util.SparseArray;
27 import android.view.ContextThemeWrapper;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.widget.CheckBox;
31 import android.widget.CompoundButton;
32 import android.widget.FrameLayout;
33 import android.widget.LinearLayout;
34 import android.widget.NumberPicker;
35
36 import java.text.DateFormatSymbols;
37 import java.text.SimpleDateFormat;
38 import java.util.Calendar;
39
40 /**
41  * A view for selecting a month / year / day based on a calendar like layout.
42  *
43  * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
44  * tutorial</a>.</p>
45  *
46  * For a dialog using this view, see {@link android.app.DatePickerDialog}.
47  */
48 @Widget
49 public class DatePicker extends FrameLayout {
50
51     private static final int DEFAULT_START_YEAR = 1900;
52     private static final int DEFAULT_END_YEAR = 2100;
53
54     /* UI Components */
55     private final CheckBox mYearToggle;
56     private final NumberPicker mDayPicker;
57     private final NumberPicker mMonthPicker;
58     private final NumberPicker mYearPicker;
59
60     /**
61      * How we notify users the date has changed.
62      */
63     private OnDateChangedListener mOnDateChangedListener;
64
65     private int mDay;
66     private int mMonth;
67     private int mYear;
68     private boolean mYearOptional = true;
69     private boolean mHasYear;
70
71     /**
72      * The callback used to indicate the user changes the date.
73      */
74     public interface OnDateChangedListener {
75
76         /**
77          * @param view The view associated with this listener.
78          * @param year The year that was set.
79          * @param monthOfYear The month that was set (0-11) for compatibility
80          *  with {@link java.util.Calendar}.
81          * @param dayOfMonth The day of the month that was set.
82          */
83         void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
84     }
85
86     public DatePicker(Context context) {
87         this(context, null);
88     }
89
90     public DatePicker(Context context, AttributeSet attrs) {
91         this(context, attrs, 0);
92     }
93
94     @SuppressWarnings("deprecation")
95     public DatePicker(Context context, AttributeSet attrs, int defStyle) {
96         super(context, attrs, defStyle);
97
98         ContextThemeWrapper themed = new ContextThemeWrapper(context,
99                 com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert);
100         LayoutInflater inflater = (LayoutInflater) themed.getSystemService(
101                         Context.LAYOUT_INFLATER_SERVICE);
102         inflater.inflate(R.layout.date_picker, this, true);
103
104         mDayPicker = findViewById(R.id.day);
105         mDayPicker.setFormatter(NumberPicker.getTwoDigitFormatter());
106         mDayPicker.setOnLongPressUpdateInterval(100);
107         mDayPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
108             public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
109                 mDay = newVal;
110                 notifyDateChanged();
111             }
112         });
113         mMonthPicker = findViewById(R.id.month);
114         mMonthPicker.setFormatter(NumberPicker.getTwoDigitFormatter());
115         DateFormatSymbols dfs = new DateFormatSymbols();
116         String[] months = dfs.getShortMonths();
117
118         /*
119          * If the user is in a locale where the month names are numeric,
120          * use just the number instead of the "month" character for
121          * consistency with the other fields.
122          */
123         if (months[0].startsWith("1")) {
124             for (int i = 0; i < months.length; i++) {
125                 months[i] = String.valueOf(i + 1);
126             }
127             mMonthPicker.setMinValue(1);
128             mMonthPicker.setMaxValue(12);
129         } else {
130             mMonthPicker.setMinValue(1);
131             mMonthPicker.setMaxValue(12);
132             mMonthPicker.setDisplayedValues(months);
133         }
134
135         mMonthPicker.setOnLongPressUpdateInterval(200);
136         mMonthPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
137             public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
138
139                 /* We display the month 1-12 but store it 0-11 so always
140                  * subtract by one to ensure our internal state is always 0-11
141                  */
142                 mMonth = newVal - 1;
143                 // Adjust max day of the month
144                 adjustMaxDay();
145                 notifyDateChanged();
146                 updateDaySpinner();
147             }
148         });
149         mYearPicker = findViewById(R.id.year);
150         mYearPicker.setOnLongPressUpdateInterval(100);
151         mYearPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
152             public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
153                 mYear = newVal;
154                 // Adjust max day for leap years if needed
155                 adjustMaxDay();
156                 notifyDateChanged();
157                 updateDaySpinner();
158             }
159         });
160
161         mYearToggle = findViewById(R.id.yearToggle);
162         mYearToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
163             @Override
164             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
165                 mHasYear = isChecked;
166                 adjustMaxDay();
167                 notifyDateChanged();
168                 updateSpinners();
169             }
170         });
171
172         // attributes
173         TypedArray a = context.obtainStyledAttributes(attrs,
174                 com.android.internal.R.styleable.DatePicker);
175
176         int mStartYear =
177                 a.getInt(com.android.internal.R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
178         int mEndYear =
179                 a.getInt(com.android.internal.R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
180         mYearPicker.setMinValue(mStartYear);
181         mYearPicker.setMaxValue(mEndYear);
182
183         a.recycle();
184
185         // initialize to current date
186         Calendar cal = Calendar.getInstance();
187         init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), null);
188
189         // re-order the number pickers to match the current date format
190         reorderPickers(months);
191
192         if (!isEnabled()) {
193             setEnabled(false);
194         }
195     }
196
197     @Override
198     public void setEnabled(boolean enabled) {
199         super.setEnabled(enabled);
200         mDayPicker.setEnabled(enabled);
201         mMonthPicker.setEnabled(enabled);
202         mYearPicker.setEnabled(enabled);
203     }
204
205     private void reorderPickers(String[] months) {
206         java.text.DateFormat format;
207         String order;
208
209         /*
210          * If the user is in a locale where the medium date format is
211          * still numeric (Japanese and Czech, for example), respect
212          * the date format order setting.  Otherwise, use the order
213          * that the locale says is appropriate for a spelled-out date.
214          */
215
216         if (months[0].startsWith("1")) {
217             format = DateFormat.getDateFormat(getContext());
218         } else {
219             format = DateFormat.getMediumDateFormat(getContext());
220         }
221
222         if (format instanceof SimpleDateFormat) {
223             order = ((SimpleDateFormat) format).toPattern();
224         } else {
225             // Shouldn't happen, but just in case.
226             order = new String(DateFormat.getDateFormatOrder(getContext()));
227         }
228
229         /* Remove the 3 pickers from their parent and then add them back in the
230          * required order.
231          */
232         LinearLayout parent = findViewById(R.id.parent);
233         parent.removeAllViews();
234
235         boolean quoted = false;
236         boolean didDay = false, didMonth = false, didYear = false;
237
238         for (int i = 0; i < order.length(); i++) {
239             char c = order.charAt(i);
240
241             if (c == '\'') {
242                 quoted = !quoted;
243             }
244
245             if (!quoted) {
246                 if (c == DateFormat.DATE && !didDay) {
247                     parent.addView(mDayPicker);
248                     didDay = true;
249                 } else if ((c == DateFormat.MONTH || c == 'L') && !didMonth) {
250                     parent.addView(mMonthPicker);
251                     didMonth = true;
252                 } else if (c == DateFormat.YEAR && !didYear) {
253                     parent.addView (mYearPicker);
254                     didYear = true;
255                 }
256             }
257         }
258
259         // Shouldn't happen, but just in case.
260         if (!didMonth) {
261             parent.addView(mMonthPicker);
262         }
263         if (!didDay) {
264             parent.addView(mDayPicker);
265         }
266         if (!didYear) {
267             parent.addView(mYearPicker);
268         }
269     }
270
271     public void updateDate(int year, int monthOfYear, int dayOfMonth) {
272         if (mYear != year || mMonth != monthOfYear || mDay != dayOfMonth) {
273             mYear = (mYearOptional && year == 0) ? getCurrentYear() : year;
274             mMonth = monthOfYear;
275             mDay = dayOfMonth;
276             updateSpinners();
277             reorderPickers(new DateFormatSymbols().getShortMonths());
278             notifyDateChanged();
279         }
280     }
281
282     private static int getCurrentYear() {
283         return Calendar.getInstance().get(Calendar.YEAR);
284     }
285
286     private static class SavedState extends BaseSavedState {
287
288         private final int mYear;
289         private final int mMonth;
290         private final int mDay;
291         private final boolean mHasYear;
292         private final boolean mYearOptional;
293
294         /**
295          * Constructor called from {@link DatePicker#onSaveInstanceState()}
296          */
297         private SavedState(Parcelable superState, int year, int month, int day, boolean hasYear,
298                 boolean yearOptional) {
299             super(superState);
300             mYear = year;
301             mMonth = month;
302             mDay = day;
303             mHasYear = hasYear;
304             mYearOptional = yearOptional;
305         }
306
307         /**
308          * Constructor called from {@link #CREATOR}
309          */
310         private SavedState(Parcel in) {
311             super(in);
312             mYear = in.readInt();
313             mMonth = in.readInt();
314             mDay = in.readInt();
315             mHasYear = in.readInt() != 0;
316             mYearOptional = in.readInt() != 0;
317         }
318
319         public int getYear() {
320             return mYear;
321         }
322
323         public int getMonth() {
324             return mMonth;
325         }
326
327         public int getDay() {
328             return mDay;
329         }
330
331         public boolean hasYear() {
332             return mHasYear;
333         }
334
335         public boolean isYearOptional() {
336             return mYearOptional;
337         }
338
339         @Override
340         public void writeToParcel(Parcel dest, int flags) {
341             super.writeToParcel(dest, flags);
342             dest.writeInt(mYear);
343             dest.writeInt(mMonth);
344             dest.writeInt(mDay);
345             dest.writeInt(mHasYear ? 1 : 0);
346             dest.writeInt(mYearOptional ? 1 : 0);
347         }
348
349         @SuppressWarnings("unused")
350         public static final Parcelable.Creator<SavedState> CREATOR =
351                 new Creator<SavedState>() {
352
353                     public SavedState createFromParcel(Parcel in) {
354                         return new SavedState(in);
355                     }
356
357                     public SavedState[] newArray(int size) {
358                         return new SavedState[size];
359                     }
360                 };
361     }
362
363
364     /**
365      * Override so we are in complete control of save / restore for this widget.
366      */
367     @Override
368     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
369         dispatchThawSelfOnly(container);
370     }
371
372     @Override
373     protected Parcelable onSaveInstanceState() {
374         Parcelable superState = super.onSaveInstanceState();
375
376         return new SavedState(superState, mYear, mMonth, mDay, mHasYear, mYearOptional);
377     }
378
379     @Override
380     protected void onRestoreInstanceState(Parcelable state) {
381         SavedState ss = (SavedState) state;
382         super.onRestoreInstanceState(ss.getSuperState());
383         mYear = ss.getYear();
384         mMonth = ss.getMonth();
385         mDay = ss.getDay();
386         mHasYear = ss.hasYear();
387         mYearOptional = ss.isYearOptional();
388         updateSpinners();
389     }
390
391     /**
392      * Initialize the state.
393      * @param year The initial year.
394      * @param monthOfYear The initial month.
395      * @param dayOfMonth The initial day of the month.
396      * @param onDateChangedListener How user is notified date is changed by user, can be null.
397      */
398     public void init(int year, int monthOfYear, int dayOfMonth,
399             OnDateChangedListener onDateChangedListener) {
400         init(year, monthOfYear, dayOfMonth, false, onDateChangedListener);
401     }
402
403     /**
404      * Initialize the state.
405      * @param year The initial year or 0 if no year has been specified
406      * @param monthOfYear The initial month.
407      * @param dayOfMonth The initial day of the month.
408      * @param yearOptional True if the user can toggle the year
409      * @param onDateChangedListener How user is notified date is changed by user, can be null.
410      */
411     public void init(int year, int monthOfYear, int dayOfMonth, boolean yearOptional,
412             OnDateChangedListener onDateChangedListener) {
413         mYear = (yearOptional && year == 0) ? getCurrentYear() : year;
414         mMonth = monthOfYear;
415         mDay = dayOfMonth;
416         mYearOptional = yearOptional;
417         mHasYear = !yearOptional || (year != 0);
418         mOnDateChangedListener = onDateChangedListener;
419         updateSpinners();
420     }
421
422     private void updateSpinners() {
423         updateDaySpinner();
424         mYearToggle.setChecked(mHasYear);
425         mYearToggle.setVisibility(mYearOptional ? View.VISIBLE : View.GONE);
426         mYearPicker.setValue(mYear);
427         mYearPicker.setVisibility(mHasYear ? View.VISIBLE : View.GONE);
428
429         /* The month display uses 1-12 but our internal state stores it
430          * 0-11 so add one when setting the display.
431          */
432         mMonthPicker.setValue(mMonth + 1);
433     }
434
435     private void updateDaySpinner() {
436         Calendar cal = Calendar.getInstance();
437         // if year was not set, use 2000 as it was a leap year
438         cal.set(mHasYear ? mYear : 2000, mMonth, 1);
439         int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
440         mDayPicker.setMinValue(1);
441         mDayPicker.setMaxValue(max);
442         mDayPicker.setValue(mDay);
443     }
444
445     public int getYear() {
446         return (mYearOptional && !mHasYear) ? 0 : mYear;
447     }
448
449     public int getMonth() {
450         return mMonth;
451     }
452
453     public int getDayOfMonth() {
454         return mDay;
455     }
456
457     private void adjustMaxDay(){
458         Calendar cal = Calendar.getInstance();
459         // if year was not set, use 2000 as it was a leap year
460         cal.set(Calendar.YEAR, mHasYear ? mYear : 2000);
461         cal.set(Calendar.MONTH, mMonth);
462         int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
463         if (mDay > max) {
464             mDay = max;
465         }
466     }
467
468     private void notifyDateChanged() {
469         if (mOnDateChangedListener != null) {
470             int year = (mYearOptional && !mHasYear) ? 0 : mYear;
471             mOnDateChangedListener.onDateChanged(DatePicker.this, year, mMonth, mDay);
472         }
473     }
474 }