1 /*
2  * Copyright (C) 2013 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.datetimepicker.date;
18 
19 import android.annotation.SuppressLint;
20 import android.content.Context;
21 import android.text.format.Time;
22 import android.view.View;
23 import android.view.ViewGroup;
24 import android.widget.AbsListView.LayoutParams;
25 import android.widget.BaseAdapter;
26 
27 import com.android.datetimepicker.date.MonthView.OnDayClickListener;
28 
29 import java.util.Calendar;
30 import java.util.HashMap;
31 
32 /**
33  * An adapter for a list of {@link MonthView} items.
34  */
35 public abstract class MonthAdapter extends BaseAdapter implements OnDayClickListener {
36 
37     private static final String TAG = "SimpleMonthAdapter";
38 
39     private final Context mContext;
40     protected final DatePickerController mController;
41 
42     private CalendarDay mSelectedDay;
43 
44     protected static int WEEK_7_OVERHANG_HEIGHT = 7;
45     protected static final int MONTHS_IN_YEAR = 12;
46 
47     /**
48      * A convenience class to represent a specific date.
49      */
50     public static class CalendarDay {
51         private Calendar calendar;
52         private Time time;
53         int year;
54         int month;
55         int day;
56 
CalendarDay()57         public CalendarDay() {
58             setTime(System.currentTimeMillis());
59         }
60 
CalendarDay(long timeInMillis)61         public CalendarDay(long timeInMillis) {
62             setTime(timeInMillis);
63         }
64 
CalendarDay(Calendar calendar)65         public CalendarDay(Calendar calendar) {
66             year = calendar.get(Calendar.YEAR);
67             month = calendar.get(Calendar.MONTH);
68             day = calendar.get(Calendar.DAY_OF_MONTH);
69         }
70 
CalendarDay(int year, int month, int day)71         public CalendarDay(int year, int month, int day) {
72             setDay(year, month, day);
73         }
74 
set(CalendarDay date)75         public void set(CalendarDay date) {
76             year = date.year;
77             month = date.month;
78             day = date.day;
79         }
80 
setDay(int year, int month, int day)81         public void setDay(int year, int month, int day) {
82             this.year = year;
83             this.month = month;
84             this.day = day;
85         }
86 
setJulianDay(int julianDay)87         public synchronized void setJulianDay(int julianDay) {
88             if (time == null) {
89                 time = new Time();
90             }
91             time.setJulianDay(julianDay);
92             setTime(time.toMillis(false));
93         }
94 
setTime(long timeInMillis)95         private void setTime(long timeInMillis) {
96             if (calendar == null) {
97                 calendar = Calendar.getInstance();
98             }
99             calendar.setTimeInMillis(timeInMillis);
100             month = calendar.get(Calendar.MONTH);
101             year = calendar.get(Calendar.YEAR);
102             day = calendar.get(Calendar.DAY_OF_MONTH);
103         }
104 
getYear()105         public int getYear() {
106             return year;
107         }
108 
getMonth()109         public int getMonth() {
110             return month;
111         }
112 
getDay()113         public int getDay() {
114             return day;
115         }
116     }
117 
MonthAdapter(Context context, DatePickerController controller)118     public MonthAdapter(Context context,
119             DatePickerController controller) {
120         mContext = context;
121         mController = controller;
122         init();
123         setSelectedDay(mController.getSelectedDay());
124     }
125 
126     /**
127      * Updates the selected day and related parameters.
128      *
129      * @param day The day to highlight
130      */
setSelectedDay(CalendarDay day)131     public void setSelectedDay(CalendarDay day) {
132         mSelectedDay = day;
133         notifyDataSetChanged();
134     }
135 
getSelectedDay()136     public CalendarDay getSelectedDay() {
137         return mSelectedDay;
138     }
139 
140     /**
141      * Set up the gesture detector and selected time
142      */
init()143     protected void init() {
144         mSelectedDay = new CalendarDay(System.currentTimeMillis());
145     }
146 
147     @Override
getCount()148     public int getCount() {
149         return ((mController.getMaxYear() - mController.getMinYear()) + 1) * MONTHS_IN_YEAR;
150     }
151 
152     @Override
getItem(int position)153     public Object getItem(int position) {
154         return null;
155     }
156 
157     @Override
getItemId(int position)158     public long getItemId(int position) {
159         return position;
160     }
161 
162     @Override
hasStableIds()163     public boolean hasStableIds() {
164         return true;
165     }
166 
167     @SuppressLint("NewApi")
168     @SuppressWarnings("unchecked")
169     @Override
getView(int position, View convertView, ViewGroup parent)170     public View getView(int position, View convertView, ViewGroup parent) {
171         MonthView v;
172         HashMap<String, Integer> drawingParams = null;
173         if (convertView != null) {
174             v = (MonthView) convertView;
175             // We store the drawing parameters in the view so it can be recycled
176             drawingParams = (HashMap<String, Integer>) v.getTag();
177         } else {
178             v = createMonthView(mContext);
179             // Set up the new view
180             LayoutParams params = new LayoutParams(
181                     LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
182             v.setLayoutParams(params);
183             v.setClickable(true);
184             v.setOnDayClickListener(this);
185         }
186         if (drawingParams == null) {
187             drawingParams = new HashMap<String, Integer>();
188         }
189         drawingParams.clear();
190 
191         final int month = position % MONTHS_IN_YEAR;
192         final int year = position / MONTHS_IN_YEAR + mController.getMinYear();
193 
194         int selectedDay = -1;
195         if (isSelectedDayInMonth(year, month)) {
196             selectedDay = mSelectedDay.day;
197         }
198 
199         // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
200         // height/number of weeks before being displayed.
201         v.reuse();
202 
203         drawingParams.put(MonthView.VIEW_PARAMS_SELECTED_DAY, selectedDay);
204         drawingParams.put(MonthView.VIEW_PARAMS_YEAR, year);
205         drawingParams.put(MonthView.VIEW_PARAMS_MONTH, month);
206         drawingParams.put(MonthView.VIEW_PARAMS_WEEK_START, mController.getFirstDayOfWeek());
207         v.setMonthParams(drawingParams);
208         v.invalidate();
209         return v;
210     }
211 
createMonthView(Context context)212     public abstract MonthView createMonthView(Context context);
213 
isSelectedDayInMonth(int year, int month)214     private boolean isSelectedDayInMonth(int year, int month) {
215         return mSelectedDay.year == year && mSelectedDay.month == month;
216     }
217 
218 
219     @Override
onDayClick(MonthView view, CalendarDay day)220     public void onDayClick(MonthView view, CalendarDay day) {
221         if (day != null) {
222             onDayTapped(day);
223         }
224     }
225 
226     /**
227      * Maintains the same hour/min/sec but moves the day to the tapped day.
228      *
229      * @param day The day that was tapped
230      */
onDayTapped(CalendarDay day)231     protected void onDayTapped(CalendarDay day) {
232         mController.tryVibrate();
233         mController.onDayOfMonthSelected(day.year, day.month, day.day);
234         setSelectedDay(day);
235     }
236 }
237