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