summaryrefslogtreecommitdiff
path: root/src/com/android/calendar/month/MonthByWeekAdapter.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/calendar/month/MonthByWeekAdapter.kt')
-rw-r--r--src/com/android/calendar/month/MonthByWeekAdapter.kt406
1 files changed, 406 insertions, 0 deletions
diff --git a/src/com/android/calendar/month/MonthByWeekAdapter.kt b/src/com/android/calendar/month/MonthByWeekAdapter.kt
new file mode 100644
index 00000000..c67b3562
--- /dev/null
+++ b/src/com/android/calendar/month/MonthByWeekAdapter.kt
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar.month
+
+import android.content.Context
+import android.content.res.Configuration
+import android.os.Handler
+import android.os.Message
+import android.text.format.Time
+import android.util.Log
+import android.view.GestureDetector
+import android.view.HapticFeedbackConstants
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewConfiguration
+import android.view.ViewGroup
+import android.widget.AbsListView.LayoutParams
+import com.android.calendar.CalendarController
+import com.android.calendar.CalendarController.EventType
+import com.android.calendar.CalendarController.ViewType
+import com.android.calendar.Event
+import com.android.calendar.R
+import com.android.calendar.Utils
+import java.util.ArrayList
+import java.util.HashMap
+
+class MonthByWeekAdapter(context: Context?, params: HashMap<String?, Int?>) :
+ SimpleWeeksAdapter(context as Context, params) {
+ protected var mController: CalendarController? = null
+ protected var mHomeTimeZone: String? = null
+ protected var mTempTime: Time? = null
+ protected var mToday: Time? = null
+ protected var mFirstJulianDay = 0
+ protected var mQueryDays = 0
+ protected var mIsMiniMonth = true
+ protected var mOrientation: Int = Configuration.ORIENTATION_LANDSCAPE
+ private val mShowAgendaWithMonth: Boolean
+ protected var mEventDayList: ArrayList<ArrayList<Event>> = ArrayList<ArrayList<Event>>()
+ protected var mEvents: ArrayList<Event>? = null
+ private var mAnimateToday = false
+ private var mAnimateTime: Long = 0
+ private val mEventDialogHandler: Handler? = null
+ var mClickedView: MonthWeekEventsView? = null
+ var mSingleTapUpView: MonthWeekEventsView? = null
+ var mLongClickedView: MonthWeekEventsView? = null
+ var mClickedXLocation = 0f // Used to find which day was clicked
+ var mClickTime: Long = 0 // Used to calculate minimum click animation time
+
+ fun animateToday() {
+ mAnimateToday = true
+ mAnimateTime = System.currentTimeMillis()
+ }
+
+ @Override
+ protected override fun init() {
+ super.init()
+ mGestureDetector = GestureDetector(mContext, CalendarGestureListener())
+ mController = CalendarController.getInstance(mContext)
+ mHomeTimeZone = Utils.getTimeZone(mContext, null)
+ mSelectedDay?.switchTimezone(mHomeTimeZone)
+ mToday = Time(mHomeTimeZone)
+ mToday?.setToNow()
+ mTempTime = Time(mHomeTimeZone)
+ }
+
+ private fun updateTimeZones() {
+ mSelectedDay!!.timezone = mHomeTimeZone
+ mSelectedDay?.normalize(true)
+ mToday!!.timezone = mHomeTimeZone
+ mToday?.setToNow()
+ mTempTime?.switchTimezone(mHomeTimeZone)
+ }
+
+ @Override
+ override fun setSelectedDay(selectedTime: Time?) {
+ mSelectedDay?.set(selectedTime)
+ val millis: Long = mSelectedDay!!.normalize(true)
+ mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(
+ Time.getJulianDay(millis, mSelectedDay!!.gmtoff), mFirstDayOfWeek
+ )
+ notifyDataSetChanged()
+ }
+
+ fun setEvents(firstJulianDay: Int, numDays: Int, events: ArrayList<Event>?) {
+ if (mIsMiniMonth) {
+ if (Log.isLoggable(TAG, Log.ERROR)) {
+ Log.e(
+ TAG, "Attempted to set events for mini view. Events only supported in full" +
+ " view."
+ )
+ }
+ return
+ }
+ mEvents = events
+ mFirstJulianDay = firstJulianDay
+ mQueryDays = numDays
+ // Create a new list, this is necessary since the weeks are referencing
+ // pieces of the old list
+ val eventDayList: ArrayList<ArrayList<Event>> = ArrayList<ArrayList<Event>>()
+ for (i in 0 until numDays) {
+ eventDayList.add(ArrayList<Event>())
+ }
+ if (events == null || events.size == 0) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "No events. Returning early--go schedule something fun.")
+ }
+ mEventDayList = eventDayList
+ refresh()
+ return
+ }
+
+ // Compute the new set of days with events
+ for (event in events) {
+ var startDay: Int = event.startDay - mFirstJulianDay
+ var endDay: Int = event.endDay - mFirstJulianDay + 1
+ if (startDay < numDays || endDay >= 0) {
+ if (startDay < 0) {
+ startDay = 0
+ }
+ if (startDay > numDays) {
+ continue
+ }
+ if (endDay < 0) {
+ continue
+ }
+ if (endDay > numDays) {
+ endDay = numDays
+ }
+ for (j in startDay until endDay) {
+ eventDayList.get(j).add(event)
+ }
+ }
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Processed " + events.size.toString() + " events.")
+ }
+ mEventDayList = eventDayList
+ refresh()
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ if (mIsMiniMonth) {
+ return super.getView(position, convertView, parent)
+ }
+ var v: MonthWeekEventsView
+ val params = LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT
+ )
+ var drawingParams: HashMap<String?, Int?>? = null
+ var isAnimatingToday = false
+ if (convertView != null) {
+ v = convertView as MonthWeekEventsView
+ // Checking updateToday uses the current params instead of the new
+ // params, so this is assuming the view is relatively stable
+ if (mAnimateToday && v.updateToday(mSelectedDay!!.timezone)) {
+ val currentTime: Long = System.currentTimeMillis()
+ // If it's been too long since we tried to start the animation
+ // don't show it. This can happen if the user stops a scroll
+ // before reaching today.
+ if (currentTime - mAnimateTime > ANIMATE_TODAY_TIMEOUT) {
+ mAnimateToday = false
+ mAnimateTime = 0
+ } else {
+ isAnimatingToday = true
+ // There is a bug that causes invalidates to not work some
+ // of the time unless we recreate the view.
+ v = MonthWeekEventsView(mContext)
+ }
+ } else {
+ drawingParams = v.getTag() as HashMap<String?, Int?>
+ }
+ } else {
+ v = MonthWeekEventsView(mContext)
+ }
+ if (drawingParams == null) {
+ drawingParams = HashMap<String?, Int?>()
+ }
+ drawingParams.clear()
+ v.setLayoutParams(params)
+ v.setClickable(true)
+ v.setOnTouchListener(this)
+ var selectedDay = -1
+ if (mSelectedWeek === position) {
+ selectedDay = mSelectedDay!!.weekDay
+ }
+ drawingParams.put(
+ SimpleWeekView.VIEW_PARAMS_HEIGHT,
+ (parent.getHeight() + parent.getTop()) / mNumWeeks
+ )
+ drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay)
+ drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, if (mShowWeekNumber) 1 else 0)
+ drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek)
+ drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek)
+ drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position)
+ drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth)
+ drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ORIENTATION, mOrientation)
+ if (isAnimatingToday) {
+ drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ANIMATE_TODAY, 1)
+ mAnimateToday = false
+ }
+ v.setWeekParams(drawingParams, mSelectedDay!!.timezone)
+ return v
+ }
+
+ @Override
+ internal override fun refresh() {
+ mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext)
+ mShowWeekNumber = Utils.getShowWeekNumber(mContext)
+ mHomeTimeZone = Utils.getTimeZone(mContext, null)
+ mOrientation = mContext.getResources().getConfiguration().orientation
+ updateTimeZones()
+ notifyDataSetChanged()
+ }
+
+ @Override
+ protected override fun onDayTapped(day: Time) {
+ setDayParameters(day)
+ if (mShowAgendaWithMonth || mIsMiniMonth) {
+ // If agenda view is visible with month view , refresh the views
+ // with the selected day's info
+ mController?.sendEvent(
+ mContext as Object?, EventType.GO_TO, day, day, -1,
+ ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null
+ )
+ } else {
+ // Else , switch to the detailed view
+ mController?.sendEvent(
+ mContext as Object?, EventType.GO_TO, day, day, -1,
+ ViewType.DETAIL, CalendarController.EXTRA_GOTO_DATE
+ or CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null
+ )
+ }
+ }
+
+ private fun setDayParameters(day: Time) {
+ day.timezone = mHomeTimeZone
+ val currTime = Time(mHomeTimeZone)
+ currTime.set(mController!!.time as Long)
+ day.hour = currTime.hour
+ day.minute = currTime.minute
+ day.allDay = false
+ day.normalize(true)
+ }
+
+ @Override
+ override fun onTouch(v: View, event: MotionEvent): Boolean {
+ if (v !is MonthWeekEventsView) {
+ return super.onTouch(v, event)
+ }
+ val action: Int = event!!.getAction()
+
+ // Event was tapped - switch to the detailed view making sure the click animation
+ // is done first.
+ if (mGestureDetector!!.onTouchEvent(event)) {
+ mSingleTapUpView = v as MonthWeekEventsView?
+ val delay: Long = System.currentTimeMillis() - mClickTime
+ // Make sure the animation is visible for at least mOnTapDelay - mOnDownDelay ms
+ mListView?.postDelayed(
+ mDoSingleTapUp,
+ if (delay > mTotalClickDelay) 0 else mTotalClickDelay - delay
+ )
+ return true
+ } else {
+ // Animate a click - on down: show the selected day in the "clicked" color.
+ // On Up/scroll/move/cancel: hide the "clicked" color.
+ when (action) {
+ MotionEvent.ACTION_DOWN -> {
+ mClickedView = v as MonthWeekEventsView
+ mClickedXLocation = event.getX()
+ mClickTime = System.currentTimeMillis()
+ mListView?.postDelayed(mDoClick, mOnDownDelay.toLong())
+ }
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_CANCEL ->
+ clearClickedView(
+ v as MonthWeekEventsView?
+ )
+ MotionEvent.ACTION_MOVE -> // No need to cancel on vertical movement,
+ // ACTION_SCROLL will do that.
+ if (Math.abs(event.getX() - mClickedXLocation) > mMovedPixelToCancel) {
+ clearClickedView(v as MonthWeekEventsView?)
+ }
+ else -> {
+ }
+ }
+ }
+ // Do not tell the frameworks we consumed the touch action so that fling actions can be
+ // processed by the fragment.
+ return false
+ }
+
+ /**
+ * This is here so we can identify events and process them
+ */
+ protected inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() {
+ @Override
+ override fun onSingleTapUp(e: MotionEvent): Boolean {
+ return true
+ }
+
+ @Override
+ override fun onLongPress(e: MotionEvent) {
+ if (mLongClickedView != null) {
+ val day: Time? = mLongClickedView?.getDayFromLocation(mClickedXLocation)
+ if (day != null) {
+ mLongClickedView?.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
+ val message = Message()
+ message.obj = day
+ }
+ mLongClickedView?.clearClickedDay()
+ mLongClickedView = null
+ }
+ }
+ }
+
+ // Clear the visual cues of the click animation and related running code.
+ private fun clearClickedView(v: MonthWeekEventsView?) {
+ mListView?.removeCallbacks(mDoClick)
+ synchronized(v as Any) { v?.clearClickedDay() }
+ mClickedView = null
+ }
+
+ // Perform the tap animation in a runnable to allow a delay before showing the tap color.
+ // This is done to prevent a click animation when a fling is done.
+ private val mDoClick: Runnable = object : Runnable {
+ @Override
+ override fun run() {
+ if (mClickedView != null) {
+ synchronized(mClickedView as MonthWeekEventsView) {
+ mClickedView?.setClickedDay(mClickedXLocation) }
+ mLongClickedView = mClickedView
+ mClickedView = null
+ // This is a workaround , sometimes the top item on the listview doesn't refresh on
+ // invalidate, so this forces a re-draw.
+ mListView?.invalidate()
+ }
+ }
+ }
+
+ // Performs the single tap operation: go to the tapped day.
+ // This is done in a runnable to allow the click animation to finish before switching views
+ private val mDoSingleTapUp: Runnable = object : Runnable {
+ @Override
+ override fun run() {
+ if (mSingleTapUpView != null) {
+ val day: Time? = mSingleTapUpView?.getDayFromLocation(mClickedXLocation)
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(
+ TAG,
+ "Touched day at Row=" + mSingleTapUpView?.mWeek?.toString() +
+ " day=" + day?.toString()
+ )
+ }
+ if (day != null) {
+ onDayTapped(day)
+ }
+ clearClickedView(mSingleTapUpView)
+ mSingleTapUpView = null
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "MonthByWeekAdapter"
+ const val WEEK_PARAMS_IS_MINI = "mini_month"
+ protected var DEFAULT_QUERY_DAYS = 7 * 8 // 8 weeks
+ private const val ANIMATE_TODAY_TIMEOUT: Long = 1000
+
+ // Used to insure minimal time for seeing the click animation before switching views
+ private const val mOnTapDelay = 100
+
+ // Minimal time for a down touch action before stating the click animation, this ensures
+ // that there is no click animation on flings
+ private var mOnDownDelay: Int = 0
+ private var mTotalClickDelay: Int = 0
+
+ // Minimal distance to move the finger in order to cancel the click animation
+ private var mMovedPixelToCancel: Float = 0f
+ }
+
+ init {
+ if (params.containsKey(WEEK_PARAMS_IS_MINI)) {
+ mIsMiniMonth = params.get(WEEK_PARAMS_IS_MINI) != 0
+ }
+ mShowAgendaWithMonth = Utils.getConfigBool(context as Context,
+ R.bool.show_agenda_with_month)
+ val vc: ViewConfiguration = ViewConfiguration.get(context)
+ mOnDownDelay = ViewConfiguration.getTapTimeout()
+ mMovedPixelToCancel = vc.getScaledTouchSlop().toFloat()
+ mTotalClickDelay = mOnDownDelay + mOnTapDelay
+ }
+} \ No newline at end of file