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