SimpleWeeksAdapter.java revision c485b45ef36fe103428b50cef399ba199c57b385
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
17package com.android.calendar.month;
18
19// TODO Remove calendar imports when the required methods have been
20// refactored into the public api
21import com.android.calendar.CalendarController;
22import com.android.calendar.Utils;
23
24import android.content.Context;
25import android.text.format.Time;
26import android.util.Log;
27import android.view.GestureDetector;
28import android.view.MotionEvent;
29import android.view.View;
30import android.view.View.OnTouchListener;
31import android.view.ViewGroup;
32import android.widget.AbsListView.LayoutParams;
33import android.widget.BaseAdapter;
34
35import java.util.HashMap;
36import java.util.Locale;
37
38import libcore.icu.LocaleData;
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 */
48public 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    protected int mFirstDayOfWeek = LocaleData.get(Locale.getDefault()).firstDayOfWeek - 1;
93    protected boolean mShowWeekNumber = false;
94    protected GestureDetector mGestureDetector;
95    protected int mNumWeeks = DEFAULT_NUM_WEEKS;
96    protected int mDaysPerWeek = DEFAULT_DAYS_PER_WEEK;
97    protected int mFocusMonth = DEFAULT_MONTH_FOCUS;
98
99    public SimpleWeeksAdapter(Context context, HashMap<String, Integer> params) {
100        mContext = context;
101
102
103        if (mScale == 0) {
104            mScale = context.getResources().getDisplayMetrics().density;
105            if (mScale != 1) {
106                WEEK_7_OVERHANG_HEIGHT *= mScale;
107            }
108        }
109        init();
110        updateParams(params);
111    }
112
113    /**
114     * Set up the gesture detector and selected time
115     */
116    protected void init() {
117        mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
118        mSelectedDay = new Time();
119        mSelectedDay.setToNow();
120    }
121
122    /**
123     * Parse the parameters and set any necessary fields. See
124     * {@link #WEEK_PARAMS_NUM_WEEKS} for parameter details.
125     *
126     * @param params A list of parameters for this adapter
127     */
128    public void updateParams(HashMap<String, Integer> params) {
129        if (params == null) {
130            Log.e(TAG, "WeekParameters are null! Cannot update adapter.");
131            return;
132        }
133        if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) {
134            mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH);
135        }
136        if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) {
137            mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS);
138        }
139        if (params.containsKey(WEEK_PARAMS_SHOW_WEEK)) {
140            mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) != 0;
141        }
142        if (params.containsKey(WEEK_PARAMS_WEEK_START)) {
143            mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START);
144        }
145        if (params.containsKey(WEEK_PARAMS_JULIAN_DAY)) {
146            int julianDay = params.get(WEEK_PARAMS_JULIAN_DAY);
147            mSelectedDay.setJulianDay(julianDay);
148            mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(julianDay, mFirstDayOfWeek);
149        }
150        if (params.containsKey(WEEK_PARAMS_DAYS_PER_WEEK)) {
151            mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK);
152        }
153        refresh();
154    }
155
156    /**
157     * Updates the selected day and related parameters.
158     *
159     * @param selectedTime The time to highlight
160     */
161    public void setSelectedDay(Time selectedTime) {
162        mSelectedDay.set(selectedTime);
163        long millis = mSelectedDay.normalize(true);
164        mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(
165                Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek);
166        notifyDataSetChanged();
167    }
168
169    /**
170     * Returns the currently highlighted day
171     *
172     * @return
173     */
174    public Time getSelectedDay() {
175        return mSelectedDay;
176    }
177
178    /**
179     * updates any config options that may have changed and refreshes the view
180     */
181    protected void refresh() {
182        notifyDataSetChanged();
183    }
184
185    @Override
186    public int getCount() {
187        return WEEK_COUNT;
188    }
189
190    @Override
191    public Object getItem(int position) {
192        return null;
193    }
194
195    @Override
196    public long getItemId(int position) {
197        return position;
198    }
199
200    @SuppressWarnings("unchecked")
201    @Override
202    public View getView(int position, View convertView, ViewGroup parent) {
203        SimpleWeekView v;
204        HashMap<String, Integer> drawingParams = null;
205        if (convertView != null) {
206            v = (SimpleWeekView) convertView;
207            // We store the drawing parameters in the view so it can be recycled
208            drawingParams = (HashMap<String, Integer>) v.getTag();
209        } else {
210            v = new SimpleWeekView(mContext);
211            // Set up the new view
212            LayoutParams params = new LayoutParams(
213                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
214            v.setLayoutParams(params);
215            v.setClickable(true);
216            v.setOnTouchListener(this);
217        }
218        if (drawingParams == null) {
219            drawingParams = new HashMap<String, Integer>();
220        }
221        drawingParams.clear();
222
223        int selectedDay = -1;
224        if (mSelectedWeek == position) {
225            selectedDay = mSelectedDay.weekDay;
226        }
227
228        // pass in all the view parameters
229        drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT,
230                (parent.getHeight() - WEEK_7_OVERHANG_HEIGHT) / mNumWeeks);
231        drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay);
232        drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0);
233        drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek);
234        drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek);
235        drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position);
236        drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth);
237        v.setWeekParams(drawingParams, mSelectedDay.timezone);
238        v.invalidate();
239
240        return v;
241    }
242
243    /**
244     * Changes which month is in focus and updates the view.
245     *
246     * @param month The month to show as in focus [0-11]
247     */
248    public void updateFocusMonth(int month) {
249        mFocusMonth = month;
250        notifyDataSetChanged();
251    }
252
253    @Override
254    public boolean onTouch(View v, MotionEvent event) {
255        if (mGestureDetector.onTouchEvent(event)) {
256            SimpleWeekView view = (SimpleWeekView) v;
257            Time day = ((SimpleWeekView)v).getDayFromLocation(event.getX());
258            if (Log.isLoggable(TAG, Log.DEBUG)) {
259                Log.d(TAG, "Touched day at Row=" + view.mWeek + " day=" + day.toString());
260            }
261            if (day != null) {
262                onDayTapped(day);
263            }
264            return true;
265        }
266        return false;
267    }
268
269    /**
270     * Maintains the same hour/min/sec but moves the day to the tapped day.
271     *
272     * @param day The day that was tapped
273     */
274    protected void onDayTapped(Time day) {
275        day.hour = mSelectedDay.hour;
276        day.minute = mSelectedDay.minute;
277        day.second = mSelectedDay.second;
278        setSelectedDay(day);
279    }
280
281
282    /**
283     * This is here so we can identify single tap events and set the selected
284     * day correctly
285     */
286    protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
287        @Override
288        public boolean onSingleTapUp(MotionEvent e) {
289            return true;
290        }
291    }
292}
293