1 /*
2  * Copyright (C) 2010 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.month;
18 
19 // TODO Remove calendar imports when the required methods have been
20 // refactored into the public api
21 import com.android.calendar.CalendarController;
22 import com.android.calendar.Utils;
23 
24 import android.content.Context;
25 import android.text.format.Time;
26 import android.util.Log;
27 import android.view.GestureDetector;
28 import android.view.MotionEvent;
29 import android.view.View;
30 import android.view.View.OnTouchListener;
31 import android.view.ViewGroup;
32 import android.widget.AbsListView.LayoutParams;
33 import android.widget.BaseAdapter;
34 import android.widget.ListView;
35 
36 import java.util.Calendar;
37 import java.util.HashMap;
38 import java.util.Locale;
39 
40 /**
41  * <p>
42  * This is a specialized adapter for creating a list of weeks with selectable
43  * days. It can be configured to display the week number, start the week on a
44  * given day, show a reduced number of days, or display an arbitrary number of
45  * weeks at a time. See {@link SimpleDayPickerFragment} for usage.
46  * </p>
47  */
48 public class SimpleWeeksAdapter extends BaseAdapter implements OnTouchListener {
49 
50     private static final String TAG = "MonthByWeek";
51 
52     /**
53      * The number of weeks to display at a time.
54      */
55     public static final String WEEK_PARAMS_NUM_WEEKS = "num_weeks";
56     /**
57      * Which month should be in focus currently.
58      */
59     public static final String WEEK_PARAMS_FOCUS_MONTH = "focus_month";
60     /**
61      * Whether the week number should be shown. Non-zero to show them.
62      */
63     public static final String WEEK_PARAMS_SHOW_WEEK = "week_numbers";
64     /**
65      * Which day the week should start on. {@link Time#SUNDAY} through
66      * {@link Time#SATURDAY}.
67      */
68     public static final String WEEK_PARAMS_WEEK_START = "week_start";
69     /**
70      * The Julian day to highlight as selected.
71      */
72     public static final String WEEK_PARAMS_JULIAN_DAY = "selected_day";
73     /**
74      * How many days of the week to display [1-7].
75      */
76     public static final String WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week";
77 
78     protected static final int WEEK_COUNT = CalendarController.MAX_CALENDAR_WEEK
79             - CalendarController.MIN_CALENDAR_WEEK;
80     protected static int DEFAULT_NUM_WEEKS = 6;
81     protected static int DEFAULT_MONTH_FOCUS = 0;
82     protected static int DEFAULT_DAYS_PER_WEEK = 7;
83     protected static int DEFAULT_WEEK_HEIGHT = 32;
84     protected static int WEEK_7_OVERHANG_HEIGHT = 7;
85 
86     protected static float mScale = 0;
87     protected Context mContext;
88     // The day to highlight as selected
89     protected Time mSelectedDay;
90     // The week since 1970 that the selected day is in
91     protected int mSelectedWeek;
92     // When the week starts; numbered like Time.<WEEKDAY> (e.g. SUNDAY=0).
93     protected int mFirstDayOfWeek;
94     protected boolean mShowWeekNumber = false;
95     protected GestureDetector mGestureDetector;
96     protected int mNumWeeks = DEFAULT_NUM_WEEKS;
97     protected int mDaysPerWeek = DEFAULT_DAYS_PER_WEEK;
98     protected int mFocusMonth = DEFAULT_MONTH_FOCUS;
99 
SimpleWeeksAdapter(Context context, HashMap<String, Integer> params)100     public SimpleWeeksAdapter(Context context, HashMap<String, Integer> params) {
101         mContext = context;
102 
103         // Get default week start based on locale, subtracting one for use with android Time.
104         Calendar cal = Calendar.getInstance(Locale.getDefault());
105         mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1;
106 
107         if (mScale == 0) {
108             mScale = context.getResources().getDisplayMetrics().density;
109             if (mScale != 1) {
110                 WEEK_7_OVERHANG_HEIGHT *= mScale;
111             }
112         }
113         init();
114         updateParams(params);
115     }
116 
117     /**
118      * Set up the gesture detector and selected time
119      */
init()120     protected void init() {
121         mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
122         mSelectedDay = new Time();
123         mSelectedDay.setToNow();
124     }
125 
126     /**
127      * Parse the parameters and set any necessary fields. See
128      * {@link #WEEK_PARAMS_NUM_WEEKS} for parameter details.
129      *
130      * @param params A list of parameters for this adapter
131      */
updateParams(HashMap<String, Integer> params)132     public void updateParams(HashMap<String, Integer> params) {
133         if (params == null) {
134             Log.e(TAG, "WeekParameters are null! Cannot update adapter.");
135             return;
136         }
137         if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) {
138             mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH);
139         }
140         if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) {
141             mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS);
142         }
143         if (params.containsKey(WEEK_PARAMS_SHOW_WEEK)) {
144             mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) != 0;
145         }
146         if (params.containsKey(WEEK_PARAMS_WEEK_START)) {
147             mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START);
148         }
149         if (params.containsKey(WEEK_PARAMS_JULIAN_DAY)) {
150             int julianDay = params.get(WEEK_PARAMS_JULIAN_DAY);
151             mSelectedDay.setJulianDay(julianDay);
152             mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(julianDay, mFirstDayOfWeek);
153         }
154         if (params.containsKey(WEEK_PARAMS_DAYS_PER_WEEK)) {
155             mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK);
156         }
157         refresh();
158     }
159 
160     /**
161      * Updates the selected day and related parameters.
162      *
163      * @param selectedTime The time to highlight
164      */
setSelectedDay(Time selectedTime)165     public void setSelectedDay(Time selectedTime) {
166         mSelectedDay.set(selectedTime);
167         long millis = mSelectedDay.normalize(true);
168         mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(
169                 Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek);
170         notifyDataSetChanged();
171     }
172 
173     /**
174      * Returns the currently highlighted day
175      *
176      * @return
177      */
getSelectedDay()178     public Time getSelectedDay() {
179         return mSelectedDay;
180     }
181 
182     /**
183      * updates any config options that may have changed and refreshes the view
184      */
refresh()185     protected void refresh() {
186         notifyDataSetChanged();
187     }
188 
189     @Override
getCount()190     public int getCount() {
191         return WEEK_COUNT;
192     }
193 
194     @Override
getItem(int position)195     public Object getItem(int position) {
196         return null;
197     }
198 
199     @Override
getItemId(int position)200     public long getItemId(int position) {
201         return position;
202     }
203 
204     @SuppressWarnings("unchecked")
205     @Override
getView(int position, View convertView, ViewGroup parent)206     public View getView(int position, View convertView, ViewGroup parent) {
207         SimpleWeekView v;
208         HashMap<String, Integer> drawingParams = null;
209         if (convertView != null) {
210             v = (SimpleWeekView) convertView;
211             // We store the drawing parameters in the view so it can be recycled
212             drawingParams = (HashMap<String, Integer>) v.getTag();
213         } else {
214             v = new SimpleWeekView(mContext);
215             // Set up the new view
216             LayoutParams params = new LayoutParams(
217                     LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
218             v.setLayoutParams(params);
219             v.setClickable(true);
220             v.setOnTouchListener(this);
221         }
222         if (drawingParams == null) {
223             drawingParams = new HashMap<String, Integer>();
224         }
225         drawingParams.clear();
226 
227         int selectedDay = -1;
228         if (mSelectedWeek == position) {
229             selectedDay = mSelectedDay.weekDay;
230         }
231 
232         // pass in all the view parameters
233         drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT,
234                 (parent.getHeight() - WEEK_7_OVERHANG_HEIGHT) / mNumWeeks);
235         drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay);
236         drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0);
237         drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek);
238         drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek);
239         drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position);
240         drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth);
241         v.setWeekParams(drawingParams, mSelectedDay.timezone);
242         v.invalidate();
243 
244         return v;
245     }
246 
247     /**
248      * Changes which month is in focus and updates the view.
249      *
250      * @param month The month to show as in focus [0-11]
251      */
updateFocusMonth(int month)252     public void updateFocusMonth(int month) {
253         mFocusMonth = month;
254         notifyDataSetChanged();
255     }
256 
257     @Override
onTouch(View v, MotionEvent event)258     public boolean onTouch(View v, MotionEvent event) {
259         if (mGestureDetector.onTouchEvent(event)) {
260             SimpleWeekView view = (SimpleWeekView) v;
261             Time day = ((SimpleWeekView)v).getDayFromLocation(event.getX());
262             if (Log.isLoggable(TAG, Log.DEBUG)) {
263                 Log.d(TAG, "Touched day at Row=" + view.mWeek + " day=" + day.toString());
264             }
265             if (day != null) {
266                 onDayTapped(day);
267             }
268             return true;
269         }
270         return false;
271     }
272 
273     /**
274      * Maintains the same hour/min/sec but moves the day to the tapped day.
275      *
276      * @param day The day that was tapped
277      */
onDayTapped(Time day)278     protected void onDayTapped(Time day) {
279         day.hour = mSelectedDay.hour;
280         day.minute = mSelectedDay.minute;
281         day.second = mSelectedDay.second;
282         setSelectedDay(day);
283     }
284 
285 
286     /**
287      * This is here so we can identify single tap events and set the selected
288      * day correctly
289      */
290     protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
291         @Override
onSingleTapUp(MotionEvent e)292         public boolean onSingleTapUp(MotionEvent e) {
293             return true;
294         }
295     }
296 
297     ListView mListView;
298 
setListView(ListView lv)299     public void setListView(ListView lv) {
300         mListView = lv;
301     }
302 }
303