summaryrefslogtreecommitdiff
path: root/src/com/android/calendar/CalendarController.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/calendar/CalendarController.kt')
-rw-r--r--src/com/android/calendar/CalendarController.kt743
1 files changed, 743 insertions, 0 deletions
diff --git a/src/com/android/calendar/CalendarController.kt b/src/com/android/calendar/CalendarController.kt
new file mode 100644
index 00000000..16ee8fdd
--- /dev/null
+++ b/src/com/android/calendar/CalendarController.kt
@@ -0,0 +1,743 @@
+/*
+ * 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
+
+import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME
+import android.provider.CalendarContract.EXTRA_EVENT_END_TIME
+import android.provider.CalendarContract.Attendees.ATTENDEE_STATUS
+import android.content.ComponentName
+import android.content.ContentUris
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.provider.CalendarContract.Attendees
+import android.provider.CalendarContract.Events
+import android.text.format.Time
+import android.util.Log
+import android.util.Pair
+import java.lang.ref.WeakReference
+import java.util.LinkedHashMap
+import java.util.LinkedList
+import java.util.WeakHashMap
+
+class CalendarController private constructor(context: Context?) {
+ private var mContext: Context? = null
+
+ // This uses a LinkedHashMap so that we can replace fragments based on the
+ // view id they are being expanded into since we can't guarantee a reference
+ // to the handler will be findable
+ private val eventHandlers: LinkedHashMap<Int, EventHandler> =
+ LinkedHashMap<Int, EventHandler>(5)
+ private val mToBeRemovedEventHandlers: LinkedList<Int> = LinkedList<Int>()
+ private val mToBeAddedEventHandlers: LinkedHashMap<Int, EventHandler> =
+ LinkedHashMap<Int, EventHandler>()
+ private var mFirstEventHandler: Pair<Int, EventHandler>? = null
+ private var mToBeAddedFirstEventHandler: Pair<Int, EventHandler>? = null
+
+ @Volatile
+ private var mDispatchInProgressCounter = 0
+ private val filters: WeakHashMap<Object, Long> = WeakHashMap<Object, Long>(1)
+
+ // Forces the viewType. Should only be used for initialization.
+ var viewType = -1
+ private var mDetailViewType = -1
+ var previousViewType = -1
+ private set
+
+ // The last event ID the edit view was launched with
+ var eventId: Long = -1
+ private val mTime: Time? = Time()
+
+ // The last set of date flags sent with
+ var dateFlags: Long = 0
+ private set
+ private val mUpdateTimezone: Runnable = object : Runnable {
+ @Override
+ override fun run() {
+ mTime?.switchTimezone(Utils.getTimeZone(mContext, this))
+ }
+ }
+
+ /**
+ * One of the event types that are sent to or from the controller
+ */
+ interface EventType {
+ companion object {
+ // Simple view of an event
+ const val VIEW_EVENT = 1L shl 1
+
+ // Full detail view in read only mode
+ const val VIEW_EVENT_DETAILS = 1L shl 2
+
+ // full detail view in edit mode
+ const val EDIT_EVENT = 1L shl 3
+ const val GO_TO = 1L shl 5
+ const val EVENTS_CHANGED = 1L shl 7
+ const val USER_HOME = 1L shl 9
+
+ // date range has changed, update the title
+ const val UPDATE_TITLE = 1L shl 10
+ }
+ }
+
+ /**
+ * One of the Agenda/Day/Week/Month view types
+ */
+ interface ViewType {
+ companion object {
+ const val DETAIL = -1
+ const val CURRENT = 0
+ const val AGENDA = 1
+ const val DAY = 2
+ const val WEEK = 3
+ const val MONTH = 4
+ const val EDIT = 5
+ const val MAX_VALUE = 5
+ }
+ }
+
+ class EventInfo {
+ @JvmField var eventType: Long = 0 // one of the EventType
+ @JvmField var viewType = 0 // one of the ViewType
+ @JvmField var id: Long = 0 // event id
+ @JvmField var selectedTime: Time? = null // the selected time in focus
+
+ // Event start and end times. All-day events are represented in:
+ // - local time for GO_TO commands
+ // - UTC time for VIEW_EVENT and other event-related commands
+ @JvmField var startTime: Time? = null
+ @JvmField var endTime: Time? = null
+ @JvmField var x = 0 // x coordinate in the activity space
+ @JvmField var y = 0 // y coordinate in the activity space
+ @JvmField var query: String? = null // query for a user search
+ @JvmField var componentName: ComponentName? = null // used in combination with query
+ @JvmField var eventTitle: String? = null
+ @JvmField var calendarId: Long = 0
+
+ /**
+ * For EventType.VIEW_EVENT:
+ * It is the default attendee response and an all day event indicator.
+ * Set to Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED,
+ * Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE.
+ * To signal the event is an all-day event, "or" ALL_DAY_MASK with the response.
+ * Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay().
+ *
+ *
+ * For EventType.GO_TO:
+ * Set to [.EXTRA_GOTO_TIME] to go to the specified date/time.
+ * Set to [.EXTRA_GOTO_DATE] to consider the date but ignore the time.
+ * Set to [.EXTRA_GOTO_BACK_TO_PREVIOUS] if back should bring back previous view.
+ * Set to [.EXTRA_GOTO_TODAY] if this is a user request to go to the current time.
+ *
+ *
+ * For EventType.UPDATE_TITLE:
+ * Set formatting flags for Utils.formatDateRange
+ */
+ @JvmField var extraLong: Long = 0
+ val isAllDay: Boolean
+ get() {
+ if (eventType != EventType.VIEW_EVENT) {
+ Log.wtf(TAG, "illegal call to isAllDay , wrong event type $eventType")
+ return false
+ }
+ return if (extraLong and ALL_DAY_MASK != 0L) true else false
+ }
+ val response: Int
+ get() {
+ if (eventType != EventType.VIEW_EVENT) {
+ Log.wtf(TAG, "illegal call to getResponse , wrong event type $eventType")
+ return Attendees.ATTENDEE_STATUS_NONE
+ }
+ val response = (extraLong and ATTENTEE_STATUS_MASK).toInt()
+ when (response) {
+ ATTENDEE_STATUS_NONE_MASK -> return Attendees.ATTENDEE_STATUS_NONE
+ ATTENDEE_STATUS_ACCEPTED_MASK -> return Attendees.ATTENDEE_STATUS_ACCEPTED
+ ATTENDEE_STATUS_DECLINED_MASK -> return Attendees.ATTENDEE_STATUS_DECLINED
+ ATTENDEE_STATUS_TENTATIVE_MASK -> return Attendees.ATTENDEE_STATUS_TENTATIVE
+ else -> Log.wtf(TAG, "Unknown attendee response $response")
+ }
+ return ATTENDEE_STATUS_NONE_MASK
+ }
+
+ companion object {
+ private const val ATTENTEE_STATUS_MASK: Long = 0xFF
+ private const val ALL_DAY_MASK: Long = 0x100
+ private const val ATTENDEE_STATUS_NONE_MASK = 0x01
+ private const val ATTENDEE_STATUS_ACCEPTED_MASK = 0x02
+ private const val ATTENDEE_STATUS_DECLINED_MASK = 0x04
+ private const val ATTENDEE_STATUS_TENTATIVE_MASK = 0x08
+
+ // Used to build the extra long for a VIEW event.
+ @JvmStatic fun buildViewExtraLong(response: Int, allDay: Boolean): Long {
+ var extra = if (allDay) ALL_DAY_MASK else 0
+ extra = when (response) {
+ Attendees.ATTENDEE_STATUS_NONE -> extra or
+ ATTENDEE_STATUS_NONE_MASK.toLong()
+ Attendees.ATTENDEE_STATUS_ACCEPTED -> extra or
+ ATTENDEE_STATUS_ACCEPTED_MASK.toLong()
+ Attendees.ATTENDEE_STATUS_DECLINED -> extra or
+ ATTENDEE_STATUS_DECLINED_MASK.toLong()
+ Attendees.ATTENDEE_STATUS_TENTATIVE -> extra or
+ ATTENDEE_STATUS_TENTATIVE_MASK.toLong()
+ else -> {
+ Log.wtf(
+ TAG,
+ "Unknown attendee response $response"
+ )
+ extra or ATTENDEE_STATUS_NONE_MASK.toLong()
+ }
+ }
+ return extra
+ }
+ }
+ }
+
+ interface EventHandler {
+ val supportedEventTypes: Long
+ fun handleEvent(event: EventInfo?)
+
+ /**
+ * This notifies the handler that the database has changed and it should
+ * update its view.
+ */
+ fun eventsChanged()
+ }
+
+ fun sendEventRelatedEvent(
+ sender: Object?,
+ eventType: Long,
+ eventId: Long,
+ startMillis: Long,
+ endMillis: Long,
+ x: Int,
+ y: Int,
+ selectedMillis: Long
+ ) {
+ // TODO: pass the real allDay status or at least a status that says we don't know the
+ // status and have the receiver query the data.
+ // The current use of this method for VIEW_EVENT is by the day view to show an EventInfo
+ // so currently the missing allDay status has no effect.
+ sendEventRelatedEventWithExtra(
+ sender, eventType, eventId, startMillis, endMillis, x, y,
+ EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false),
+ selectedMillis
+ )
+ }
+
+ /**
+ * Helper for sending New/View/Edit/Delete events
+ *
+ * @param sender object of the caller
+ * @param eventType one of [EventType]
+ * @param eventId event id
+ * @param startMillis start time
+ * @param endMillis end time
+ * @param x x coordinate in the activity space
+ * @param y y coordinate in the activity space
+ * @param extraLong default response value for the "simple event view" and all day indication.
+ * Use Attendees.ATTENDEE_STATUS_NONE for no response.
+ * @param selectedMillis The time to specify as selected
+ */
+ fun sendEventRelatedEventWithExtra(
+ sender: Object?,
+ eventType: Long,
+ eventId: Long,
+ startMillis: Long,
+ endMillis: Long,
+ x: Int,
+ y: Int,
+ extraLong: Long,
+ selectedMillis: Long
+ ) {
+ sendEventRelatedEventWithExtraWithTitleWithCalendarId(
+ sender, eventType, eventId,
+ startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1
+ )
+ }
+
+ /**
+ * Helper for sending New/View/Edit/Delete events
+ *
+ * @param sender object of the caller
+ * @param eventType one of [EventType]
+ * @param eventId event id
+ * @param startMillis start time
+ * @param endMillis end time
+ * @param x x coordinate in the activity space
+ * @param y y coordinate in the activity space
+ * @param extraLong default response value for the "simple event view" and all day indication.
+ * Use Attendees.ATTENDEE_STATUS_NONE for no response.
+ * @param selectedMillis The time to specify as selected
+ * @param title The title of the event
+ * @param calendarId The id of the calendar which the event belongs to
+ */
+ fun sendEventRelatedEventWithExtraWithTitleWithCalendarId(
+ sender: Object?,
+ eventType: Long,
+ eventId: Long,
+ startMillis: Long,
+ endMillis: Long,
+ x: Int,
+ y: Int,
+ extraLong: Long,
+ selectedMillis: Long,
+ title: String?,
+ calendarId: Long
+ ) {
+ val info = EventInfo()
+ info.eventType = eventType
+ if (eventType == EventType.VIEW_EVENT_DETAILS) {
+ info.viewType = ViewType.CURRENT
+ }
+ info.id = eventId
+ info.startTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone))
+ (info.startTime as Time).set(startMillis)
+ if (selectedMillis != -1L) {
+ info.selectedTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone))
+ (info.selectedTime as Time).set(selectedMillis)
+ } else {
+ info.selectedTime = info.startTime
+ }
+ info.endTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone))
+ (info.endTime as Time).set(endMillis)
+ info.x = x
+ info.y = y
+ info.extraLong = extraLong
+ info.eventTitle = title
+ info.calendarId = calendarId
+ this.sendEvent(sender, info)
+ }
+
+ /**
+ * Helper for sending non-calendar-event events
+ *
+ * @param sender object of the caller
+ * @param eventType one of [EventType]
+ * @param start start time
+ * @param end end time
+ * @param eventId event id
+ * @param viewType [ViewType]
+ */
+ fun sendEvent(
+ sender: Object?,
+ eventType: Long,
+ start: Time?,
+ end: Time?,
+ eventId: Long,
+ viewType: Int
+ ) {
+ sendEvent(
+ sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null,
+ null
+ )
+ }
+
+ /**
+ * sendEvent() variant with extraLong, search query, and search component name.
+ */
+ fun sendEvent(
+ sender: Object?,
+ eventType: Long,
+ start: Time?,
+ end: Time?,
+ eventId: Long,
+ viewType: Int,
+ extraLong: Long,
+ query: String?,
+ componentName: ComponentName?
+ ) {
+ sendEvent(
+ sender, eventType, start, end, start, eventId, viewType, extraLong, query,
+ componentName
+ )
+ }
+
+ fun sendEvent(
+ sender: Object?,
+ eventType: Long,
+ start: Time?,
+ end: Time?,
+ selected: Time?,
+ eventId: Long,
+ viewType: Int,
+ extraLong: Long,
+ query: String?,
+ componentName: ComponentName?
+ ) {
+ val info = EventInfo()
+ info.eventType = eventType
+ info.startTime = start
+ info.selectedTime = selected
+ info.endTime = end
+ info.id = eventId
+ info.viewType = viewType
+ info.query = query
+ info.componentName = componentName
+ info.extraLong = extraLong
+ this.sendEvent(sender, info)
+ }
+
+ fun sendEvent(sender: Object?, event: EventInfo) {
+ // TODO Throw exception on invalid events
+ if (DEBUG) {
+ Log.d(TAG, eventInfoToString(event))
+ }
+ val filteredTypes: Long? = filters.get(sender)
+ if (filteredTypes != null && filteredTypes.toLong() and event.eventType != 0L) {
+ // Suppress event per filter
+ if (DEBUG) {
+ Log.d(TAG, "Event suppressed")
+ }
+ return
+ }
+ previousViewType = viewType
+
+ // Fix up view if not specified
+ if (event.viewType == ViewType.DETAIL) {
+ event.viewType = mDetailViewType
+ viewType = mDetailViewType
+ } else if (event.viewType == ViewType.CURRENT) {
+ event.viewType = viewType
+ } else if (event.viewType != ViewType.EDIT) {
+ viewType = event.viewType
+ if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY ||
+ Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK) {
+ mDetailViewType = viewType
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "vvvvvvvvvvvvvvv")
+ Log.d(
+ TAG,
+ "Start " + if (event.startTime == null) "null" else event.startTime.toString()
+ )
+ Log.d(TAG, "End " + if (event.endTime == null) "null" else event.endTime.toString())
+ Log.d(
+ TAG,
+ "Select " + if (event.selectedTime == null) "null"
+ else event.selectedTime.toString()
+ )
+ Log.d(TAG, "mTime " + if (mTime == null) "null" else mTime.toString())
+ }
+ var startMillis: Long = 0
+ val temp = event.startTime
+ if (temp != null) {
+ startMillis = (event.startTime as Time).toMillis(false)
+ }
+
+ // Set mTime if selectedTime is set
+ val temp1 = event.selectedTime
+ if (temp1 != null && temp1?.toMillis(false) != 0L) {
+ mTime?.set(event.selectedTime)
+ } else {
+ if (startMillis != 0L) {
+ // selectedTime is not set so set mTime to startTime iff it is not
+ // within start and end times
+ val mtimeMillis: Long = mTime?.toMillis(false) as Long
+ val temp2 = event.endTime
+ if (mtimeMillis < startMillis ||
+ temp2 != null && mtimeMillis > temp2.toMillis(false)) {
+ mTime?.set(event.startTime)
+ }
+ }
+ event.selectedTime = mTime
+ }
+ // Store the formatting flags if this is an update to the title
+ if (event.eventType == EventType.UPDATE_TITLE) {
+ dateFlags = event.extraLong
+ }
+
+ // Fix up start time if not specified
+ if (startMillis == 0L) {
+ event.startTime = mTime
+ }
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Start " + if (event.startTime == null) "null" else
+ event.startTime.toString()
+ )
+ Log.d(TAG, "End " + if (event.endTime == null) "null" else
+ event.endTime.toString())
+ Log.d(
+ TAG,
+ "Select " + if (event.selectedTime == null) "null" else
+ event.selectedTime.toString()
+ )
+ Log.d(TAG, "mTime " + if (mTime == null) "null" else mTime.toString())
+ Log.d(TAG, "^^^^^^^^^^^^^^^")
+ }
+
+ // Store the eventId if we're entering edit event
+ if ((event.eventType and EventType.VIEW_EVENT_DETAILS) != 0L) {
+ if (event.id > 0) {
+ eventId = event.id
+ } else {
+ eventId = -1
+ }
+ }
+ var handled = false
+ synchronized(this) {
+ mDispatchInProgressCounter++
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "sendEvent: Dispatching to " + eventHandlers.size.toString() + " handlers"
+ )
+ }
+ // Dispatch to event handler(s)
+ val temp3 = mFirstEventHandler
+ if (temp3 != null) {
+ // Handle the 'first' one before handling the others
+ val handler: EventHandler? = mFirstEventHandler?.second
+ if (handler != null && handler.supportedEventTypes and event.eventType != 0L &&
+ !mToBeRemovedEventHandlers.contains(mFirstEventHandler?.first)) {
+ handler.handleEvent(event)
+ handled = true
+ }
+ }
+ val handlers: MutableIterator<MutableMap.MutableEntry<Int,
+ CalendarController.EventHandler>> = eventHandlers.entries.iterator()
+ while (handlers.hasNext()) {
+ val entry: MutableMap.MutableEntry<Int,
+ CalendarController.EventHandler> = handlers.next()
+ val key: Int = entry.key.toInt()
+ val temp4 = mFirstEventHandler
+ if (temp4 != null && key.toInt() == temp4.first.toInt()) {
+ // If this was the 'first' handler it was already handled
+ continue
+ }
+ val eventHandler: EventHandler = entry.value
+ if (eventHandler != null &&
+ eventHandler.supportedEventTypes and event.eventType != 0L) {
+ if (mToBeRemovedEventHandlers.contains(key)) {
+ continue
+ }
+ eventHandler.handleEvent(event)
+ handled = true
+ }
+ }
+ mDispatchInProgressCounter--
+ if (mDispatchInProgressCounter == 0) {
+
+ // Deregister removed handlers
+ if (mToBeRemovedEventHandlers.size > 0) {
+ for (zombie in mToBeRemovedEventHandlers) {
+ eventHandlers.remove(zombie)
+ val temp5 = mFirstEventHandler
+ if (temp5 != null && zombie.equals(temp5.first)) {
+ mFirstEventHandler = null
+ }
+ }
+ mToBeRemovedEventHandlers.clear()
+ }
+ // Add new handlers
+ if (mToBeAddedFirstEventHandler != null) {
+ mFirstEventHandler = mToBeAddedFirstEventHandler
+ mToBeAddedFirstEventHandler = null
+ }
+ if (mToBeAddedEventHandlers.size > 0) {
+ for (food in mToBeAddedEventHandlers.entries) {
+ eventHandlers.put(food.key, food.value)
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds or updates an event handler. This uses a LinkedHashMap so that we can
+ * replace fragments based on the view id they are being expanded into.
+ *
+ * @param key The view id or placeholder for this handler
+ * @param eventHandler Typically a fragment or activity in the calendar app
+ */
+ fun registerEventHandler(key: Int, eventHandler: EventHandler?) {
+ synchronized(this) {
+ if (mDispatchInProgressCounter > 0) {
+ mToBeAddedEventHandlers.put(key,
+ eventHandler as CalendarController.EventHandler)
+ } else {
+ eventHandlers.put(key, eventHandler as CalendarController.EventHandler)
+ }
+ }
+ }
+
+ fun registerFirstEventHandler(key: Int, eventHandler: EventHandler?) {
+ synchronized(this) {
+ registerEventHandler(key, eventHandler)
+ if (mDispatchInProgressCounter > 0) {
+ mToBeAddedFirstEventHandler = Pair<Int, EventHandler>(key, eventHandler)
+ } else {
+ mFirstEventHandler = Pair<Int, EventHandler>(key, eventHandler)
+ }
+ }
+ }
+
+ fun deregisterEventHandler(key: Int) {
+ synchronized(this) {
+ if (mDispatchInProgressCounter > 0) {
+ // To avoid ConcurrencyException, stash away the event handler for now.
+ mToBeRemovedEventHandlers.add(key)
+ } else {
+ eventHandlers.remove(key)
+ val temp6 = mFirstEventHandler
+ if (temp6 != null && temp6.first == key) {
+ mFirstEventHandler = null
+ } else {}
+ }
+ }
+ }
+
+ fun deregisterAllEventHandlers() {
+ synchronized(this) {
+ if (mDispatchInProgressCounter > 0) {
+ // To avoid ConcurrencyException, stash away the event handler for now.
+ mToBeRemovedEventHandlers.addAll(eventHandlers.keys)
+ } else {
+ eventHandlers.clear()
+ mFirstEventHandler = null
+ }
+ }
+ }
+
+ // FRAG_TODO doesn't work yet
+ fun filterBroadcasts(sender: Object?, eventTypes: Long) {
+ filters.put(sender, eventTypes)
+ }
+ /**
+ * @return the time that this controller is currently pointed at
+ */
+ /**
+ * Set the time this controller is currently pointed at
+ *
+ * @param millisTime Time since epoch in millis
+ */
+ var time: Long?
+ get() = mTime?.toMillis(false)
+ set(millisTime) {
+ mTime?.set(millisTime as Long)
+ }
+
+ fun launchViewEvent(eventId: Long, startMillis: Long, endMillis: Long, response: Int) {
+ val intent = Intent(Intent.ACTION_VIEW)
+ val eventUri: Uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId)
+ intent.setData(eventUri)
+ intent.setClass(mContext as Context, AllInOneActivity::class.java)
+ intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis)
+ intent.putExtra(EXTRA_EVENT_END_TIME, endMillis)
+ intent.putExtra(ATTENDEE_STATUS, response)
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ mContext?.startActivity(intent)
+ }
+
+ private fun eventInfoToString(eventInfo: EventInfo): String {
+ var tmp = "Unknown"
+ val builder = StringBuilder()
+ if (eventInfo.eventType and EventType.GO_TO != 0L) {
+ tmp = "Go to time/event"
+ } else if (eventInfo.eventType and EventType.VIEW_EVENT != 0L) {
+ tmp = "View event"
+ } else if (eventInfo.eventType and EventType.VIEW_EVENT_DETAILS != 0L) {
+ tmp = "View details"
+ } else if (eventInfo.eventType and EventType.EVENTS_CHANGED != 0L) {
+ tmp = "Refresh events"
+ } else if (eventInfo.eventType and EventType.USER_HOME != 0L) {
+ tmp = "Gone home"
+ } else if (eventInfo.eventType and EventType.UPDATE_TITLE != 0L) {
+ tmp = "Update title"
+ }
+ builder.append(tmp)
+ builder.append(": id=")
+ builder.append(eventInfo.id)
+ builder.append(", selected=")
+ builder.append(eventInfo.selectedTime)
+ builder.append(", start=")
+ builder.append(eventInfo.startTime)
+ builder.append(", end=")
+ builder.append(eventInfo.endTime)
+ builder.append(", viewType=")
+ builder.append(eventInfo.viewType)
+ builder.append(", x=")
+ builder.append(eventInfo.x)
+ builder.append(", y=")
+ builder.append(eventInfo.y)
+ return builder.toString()
+ }
+
+ companion object {
+ private const val DEBUG = false
+ private const val TAG = "CalendarController"
+ const val EVENT_EDIT_ON_LAUNCH = "editMode"
+ const val MIN_CALENDAR_YEAR = 1970
+ const val MAX_CALENDAR_YEAR = 2036
+ const val MIN_CALENDAR_WEEK = 0
+ const val MAX_CALENDAR_WEEK = 3497 // weeks between 1/1/1970 and 1/1/2037
+ private val instances: WeakHashMap<Context, WeakReference<CalendarController>> =
+ WeakHashMap<Context, WeakReference<CalendarController>>()
+
+ /**
+ * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time
+ * can be ignored
+ */
+ const val EXTRA_GOTO_DATE: Long = 1
+ const val EXTRA_GOTO_TIME: Long = 2
+ const val EXTRA_GOTO_BACK_TO_PREVIOUS: Long = 4
+ const val EXTRA_GOTO_TODAY: Long = 8
+
+ /**
+ * Creates and/or returns an instance of CalendarController associated with
+ * the supplied context. It is best to pass in the current Activity.
+ *
+ * @param context The activity if at all possible.
+ */
+ @JvmStatic fun getInstance(context: Context?): CalendarController? {
+ synchronized(instances) {
+ var controller: CalendarController? = null
+ val weakController: WeakReference<CalendarController>? = instances.get(context)
+ if (weakController != null) {
+ controller = weakController.get()
+ }
+ if (controller == null) {
+ controller = CalendarController(context)
+ instances.put(context, WeakReference(controller))
+ }
+ return controller
+ }
+ }
+
+ /**
+ * Removes an instance when it is no longer needed. This should be called in
+ * an activity's onDestroy method.
+ *
+ * @param context The activity used to create the controller
+ */
+ @JvmStatic fun removeInstance(context: Context?) {
+ instances.remove(context)
+ }
+ }
+
+ init {
+ mContext = context
+ mUpdateTimezone.run()
+ mTime?.setToNow()
+ mDetailViewType = Utils.getSharedPreference(
+ mContext,
+ GeneralPreferences.KEY_DETAILED_VIEW,
+ GeneralPreferences.DEFAULT_DETAILED_VIEW
+ )
+ }
+} \ No newline at end of file