Code drop from //branches/cupcake/...@124589
[android/platform/packages/apps/Calendar.git] / src / com / android / calendar / DateSpinner.java
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 com.android.calendar;
18
19 import android.app.DatePickerDialog;
20 import android.app.DatePickerDialog.OnDateSetListener;
21 import android.content.Context;
22 import android.text.format.Time;
23 import android.util.AttributeSet;
24 import android.view.KeyEvent;
25 import android.view.View;
26 import android.widget.AdapterView;
27 import android.widget.ArrayAdapter;
28 import android.widget.DatePicker;
29 import android.widget.Spinner;
30
31 import java.util.ArrayList;
32 import java.util.Calendar;
33
34 /**
35  * The DateSpinner class is a {@link Spinner} widget that pops up a
36  * {@link DatePickerDialog} when clicked (instead of the usual menu of
37  * options for the Spinner).  This class also provides a callback
38  * {@link DateSpinner.OnDateChangedListener} when the date is changed
39  * either through the Spinner or through the DatePickerDialog.
40  */
41 public class DateSpinner extends Spinner {
42
43     /**
44      * The listener interface for providing a callback when the date is
45      * changed by the user.
46      */
47     public interface OnDateChangedListener {
48         /**
49          * This method is called when the user changes the date through
50          * the Spinner or the DatePickerDialog.
51          * 
52          * @param dateSpinner the DateSpinner object that changed
53          * @param millis the date in UTC milliseconds
54          */
55         public void dateChanged(DateSpinner dateSpinner, long millis);
56     }
57
58     private Context mContext;
59     
60     // mTime and mMillis must be kept in sync
61     private Time mTime = new Time();
62     private long mMillis;
63     private int mWeekStartDay = Calendar.SUNDAY;
64     private OnDateChangedListener mOnDateChangedListener;
65     
66     // The default number of spinner choices is 2 weeks worth of days
67     // surrounding the given date.
68     private static final int NUM_SPINNER_CHOICES = 15;
69     
70     // The array of spinner choices, in UTC milliseconds.
71     private long[] mSpinnerMillis;
72     
73     // The minimum millisecond spinner value.  The DateSpinner can automatically
74     // generate an array of spinner choices for the dates.  This variable
75     // prevents the spinner choices from being less than this date (specified
76     // in UTC milliseconds).
77     private long mMinimumMillis;
78     
79     // The number of spinner choices.  This may be set by the user of this
80     // widget.
81     private int mNumSpinnerChoices;
82     
83     public DateSpinner(Context context) {
84         super(context);
85         mContext = context;
86     }
87
88     public DateSpinner(Context context, AttributeSet attrs) {
89         super(context, attrs);
90         mContext = context;
91     }
92
93     public DateSpinner(Context context,
94                   AttributeSet attrs,
95                   int defStyle) {
96         super(context, attrs, defStyle);
97         mContext = context;
98     }
99     
100     private OnDateSetListener mDateSetListener = new OnDateSetListener() {
101         // This is called by the DatePickerDialog when the user sets the date.
102         public void onDateSet(DatePicker view, int year, int month, int day) {
103             mTime.year = year;
104             mTime.month = month;
105             mTime.monthDay = day;
106             mMillis = mTime.normalize(true /* ignore isDst */);
107             createSpinnerElements();
108             if (mOnDateChangedListener != null) {
109                 mOnDateChangedListener.dateChanged(DateSpinner.this, mMillis);
110             }
111         }
112     };
113     
114     private OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() {
115         // This is called when the user changes the selection in the Spinner.
116         public void onItemSelected(AdapterView parent, View v, int position, long id) {
117             long millis = mSpinnerMillis[position];
118             if (millis == mMillis) {
119                 return;
120             }
121             mMillis = millis;
122             mTime.set(millis);
123             if (mOnDateChangedListener != null) {
124                 mOnDateChangedListener.dateChanged(DateSpinner.this, millis);
125             }
126         }
127     
128         public void onNothingSelected(AdapterView parent) {
129         }
130     };
131
132     @Override
133     public boolean onKeyDown(int keyCode, KeyEvent event) {
134         switch (keyCode) {
135         case KeyEvent.KEYCODE_DPAD_CENTER:
136         case KeyEvent.KEYCODE_ENTER:
137             new DatePickerDialog(mContext, mDateSetListener, mTime.year,
138                     mTime.month, mTime.monthDay).show();
139             return true;
140         }
141         return super.onKeyDown(keyCode, event);
142     }
143
144     public void setMillis(long millis) {
145         mTime.set(millis);
146         mMillis = millis;
147         createSpinnerElements();
148     }
149     
150     public long getMillis() {
151         return mMillis;
152     }
153     
154     private void createSpinnerElements() {
155         // Create spinner elements for a week preceding this date plus a
156         // week following this date.
157         Time time = new Time();
158         time.set(mTime);
159         long millis = time.toMillis(false /* use isDst */);
160         long selectedDay = Time.getJulianDay(millis, time.gmtoff);
161         int numSpinnerChoices = NUM_SPINNER_CHOICES;
162         if (mNumSpinnerChoices > 0) {
163             numSpinnerChoices = mNumSpinnerChoices;
164         }
165         time.monthDay -= numSpinnerChoices / 2;
166         millis = time.normalize(true /* ignore isDst */);
167         if (millis < mMinimumMillis) {
168             long days = (mMinimumMillis - millis) / CalendarView.MILLIS_PER_DAY;
169             millis = mMinimumMillis;
170             time.set(millis);
171         }
172
173         int selectedIndex = 0;
174         ArrayList<Long> millisList = new ArrayList<Long>();
175         for (int pos = 0; pos < numSpinnerChoices; ++pos) {
176             millis = time.normalize(true /* ignore isDst */);
177             int julianDay = Time.getJulianDay(millis, time.gmtoff);
178             if (julianDay == selectedDay) {
179                 selectedIndex = pos;
180             }
181             millisList.add(millis);
182             time.monthDay += 1;
183         }
184
185         // Convert the ArrayList to a long[] array.
186         int len = millisList.size();
187         long[] spinnerMillis = new long[len];
188         for (int pos = 0; pos < len; pos++) {
189             spinnerMillis[pos] = millisList.get(pos);
190         }
191         
192         setSpinnerElements(spinnerMillis, selectedIndex);
193     }
194     
195     public void setSpinnerElements(long[] spinnerMillis, int selectedIndex) {
196         if (spinnerMillis == null || spinnerMillis.length == 0) {
197             return;
198         }
199         mSpinnerMillis = spinnerMillis;
200         long millis = spinnerMillis[selectedIndex];
201         mTime.set(millis);
202         mMillis = millis;
203         
204         Time time = new Time();
205         int len = spinnerMillis.length;
206         String[] choices = new String[len];
207         for (int pos = 0; pos < len; pos++) {
208             millis = spinnerMillis[pos];
209             time.set(millis);
210             choices[pos] = Utils.formatDayDate(time, true);
211         }
212         
213         ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext,
214                 android.R.layout.simple_spinner_item, choices);
215         setAdapter(adapter);
216         setSelection(selectedIndex);
217         setOnItemSelectedListener(mItemSelectedListener);
218     }
219
220     public int getYear() {
221         return mTime.year;
222     }
223
224     public int getMonth() {
225         return mTime.month;
226     }
227
228     public int getMonthDay() {
229         return mTime.monthDay;
230     }
231     
232     /**
233      * Fills in the given {@link Time} object with the year, month, and
234      * monthDay from the DateSpinner.
235      * 
236      * @param time the given Time object, allocated by the caller
237      */
238     public void getDate(Time time) {
239         time.year = mTime.year;
240         time.month = mTime.month;
241         time.monthDay = mTime.monthDay;
242     }
243
244     public void setWeekStartDay(int weekStartDay) {
245         mWeekStartDay = weekStartDay;
246     }
247
248     public int getWeekStartDay() {
249         return mWeekStartDay;
250     }
251
252     public void setOnDateChangedListener(OnDateChangedListener onDateChangedListener) {
253         mOnDateChangedListener = onDateChangedListener;
254     }
255
256     public OnDateChangedListener getOnDateChangedListener() {
257         return mOnDateChangedListener;
258     }
259
260     public void setMinimum(long minimum) {
261         mMinimumMillis = minimum;
262     }
263
264     public long getMinimum() {
265         return mMinimumMillis;
266     }
267
268     public void setNumSpinnerChoices(int numSpinnerChoices) {
269         mNumSpinnerChoices = numSpinnerChoices;
270     }
271
272     public int getNumSpinnerChoices() {
273         return mNumSpinnerChoices;
274     }
275 }