summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2021-07-10 08:19:25 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-07-10 08:19:25 +0000
commitbf857d93f1a29b70986de1ee69efe0dcc602f4dd (patch)
tree48dae73c45f04f343dc2e3c8a9ebedd8304b272a
parent9b3c5ce1f330b40bf072cf78069082ded3813cb3 (diff)
parent8b0a7714ee758e520ec6452617447ace8d3adee3 (diff)
downloadCalendar-bf857d93f1a29b70986de1ee69efe0dcc602f4dd.tar.gz
Merge changes from topic "EventInfoFragment"
* changes: AOSP/Calendar - EventInfoFragment fully converted with bp file AOSP/Calendar - Initial Conversion of EventInfoFragment AOSP/Calendar - Copy of EventInfoFragment.java
-rw-r--r--Android.bp1
-rw-r--r--src/com/android/calendar/EventInfoFragment.kt787
2 files changed, 788 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
index fd318cc3..ae0a3607 100644
--- a/Android.bp
+++ b/Android.bp
@@ -37,6 +37,7 @@ exclude_srcsd = [
"src/**/calendar/DayView.java",
"src/**/calendar/Event.java",
"src/**/calendar/EventInfoActivity.java",
+ "src/**/calendar/EventInfoFragment.java",
"src/**/calendar/StickyHeaderListView.java",
"src/**/calendar/Utils.java",
]
diff --git a/src/com/android/calendar/EventInfoFragment.kt b/src/com/android/calendar/EventInfoFragment.kt
new file mode 100644
index 00000000..2f007ab5
--- /dev/null
+++ b/src/com/android/calendar/EventInfoFragment.kt
@@ -0,0 +1,787 @@
+/*
+ * 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.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ObjectAnimator
+import android.app.Activity
+import android.app.Dialog
+import android.app.DialogFragment
+import android.app.Service
+import android.content.ContentProviderOperation
+import android.content.ContentUris
+import android.content.ContentValues
+import android.content.Context
+import android.content.res.Resources
+import android.database.Cursor
+import android.net.Uri
+import android.os.Bundle
+import android.provider.CalendarContract.Attendees
+import android.provider.CalendarContract.Calendars
+import android.provider.CalendarContract.Events
+import android.text.Spannable
+import android.text.SpannableStringBuilder
+import android.text.TextUtils
+import android.text.style.ForegroundColorSpan
+import android.util.Log
+import android.util.SparseIntArray
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.View.OnClickListener
+import android.view.ViewGroup
+import android.view.Window
+import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
+import android.widget.AdapterView
+import android.widget.RadioButton
+import android.widget.RadioGroup
+import android.widget.RadioGroup.OnCheckedChangeListener
+import android.widget.ScrollView
+import android.widget.TextView
+import com.android.calendar.CalendarController.EventInfo
+import com.android.calendar.CalendarController.EventType
+import com.android.calendarcommon2.DateException
+import com.android.calendarcommon2.Duration
+import java.util.ArrayList
+
+class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarController.EventHandler,
+ OnClickListener {
+ private var mWindowStyle = DIALOG_WINDOW_STYLE
+ private var mCurrentQuery = 0
+
+ companion object {
+ const val DEBUG = false
+ const val TAG = "EventInfoFragment"
+ internal const val BUNDLE_KEY_EVENT_ID = "key_event_id"
+ internal const val BUNDLE_KEY_START_MILLIS = "key_start_millis"
+ internal const val BUNDLE_KEY_END_MILLIS = "key_end_millis"
+ internal const val BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog"
+ internal const val BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible"
+ internal const val BUNDLE_KEY_WINDOW_STYLE = "key_window_style"
+ internal const val BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color"
+ internal const val BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init"
+ internal const val BUNDLE_KEY_CURRENT_COLOR = "key_current_color"
+ internal const val BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key"
+ internal const val BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init"
+ internal const val BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color"
+ internal const val BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init"
+ internal const val BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response"
+ internal const val BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = "key_user_set_attendee_response"
+ internal const val BUNDLE_KEY_TENTATIVE_USER_RESPONSE = "key_tentative_user_response"
+ internal const val BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events"
+ internal const val BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes"
+ internal const val BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods"
+ private const val PERIOD_SPACE = ". "
+ private const val NO_EVENT_COLOR = ""
+
+ /**
+ * These are the corresponding indices into the array of strings
+ * "R.array.change_response_labels" in the resource file.
+ */
+ const val UPDATE_SINGLE = 0
+ const val UPDATE_ALL = 1
+
+ // Style of view
+ const val FULL_WINDOW_STYLE = 0
+ const val DIALOG_WINDOW_STYLE = 1
+
+ // Query tokens for QueryHandler
+ private const val TOKEN_QUERY_EVENT = 1 shl 0
+ private const val TOKEN_QUERY_CALENDARS = 1 shl 1
+ private const val TOKEN_QUERY_ATTENDEES = 1 shl 2
+ private const val TOKEN_QUERY_DUPLICATE_CALENDARS = 1 shl 3
+ private const val TOKEN_QUERY_REMINDERS = 1 shl 4
+ private const val TOKEN_QUERY_VISIBLE_CALENDARS = 1 shl 5
+ private const val TOKEN_QUERY_COLORS = 1 shl 6
+ private const val TOKEN_QUERY_ALL = (TOKEN_QUERY_DUPLICATE_CALENDARS
+ or TOKEN_QUERY_ATTENDEES or TOKEN_QUERY_CALENDARS or TOKEN_QUERY_EVENT
+ or TOKEN_QUERY_REMINDERS or TOKEN_QUERY_VISIBLE_CALENDARS or TOKEN_QUERY_COLORS)
+ private val EVENT_PROJECTION = arrayOf<String>(
+ Events._ID, // 0 do not remove; used in DeleteEventHelper
+ Events.TITLE, // 1 do not remove; used in DeleteEventHelper
+ Events.RRULE, // 2 do not remove; used in DeleteEventHelper
+ Events.ALL_DAY, // 3 do not remove; used in DeleteEventHelper
+ Events.CALENDAR_ID, // 4 do not remove; used in DeleteEventHelper
+ Events.DTSTART, // 5 do not remove; used in DeleteEventHelper
+ Events._SYNC_ID, // 6 do not remove; used in DeleteEventHelper
+ Events.EVENT_TIMEZONE, // 7 do not remove; used in DeleteEventHelper
+ Events.DESCRIPTION, // 8
+ Events.EVENT_LOCATION, // 9
+ Calendars.CALENDAR_ACCESS_LEVEL, // 10
+ Events.CALENDAR_COLOR, // 11
+ Events.EVENT_COLOR, // 12
+ Events.HAS_ATTENDEE_DATA, // 13
+ Events.ORGANIZER, // 14
+ Events.HAS_ALARM, // 15
+ Calendars.MAX_REMINDERS, // 16
+ Calendars.ALLOWED_REMINDERS, // 17
+ Events.CUSTOM_APP_PACKAGE, // 18
+ Events.CUSTOM_APP_URI, // 19
+ Events.DTEND, // 20
+ Events.DURATION, // 21
+ Events.ORIGINAL_SYNC_ID // 22 do not remove; used in DeleteEventHelper
+ )
+ private const val EVENT_INDEX_ID = 0
+ private const val EVENT_INDEX_TITLE = 1
+ private const val EVENT_INDEX_RRULE = 2
+ private const val EVENT_INDEX_ALL_DAY = 3
+ private const val EVENT_INDEX_CALENDAR_ID = 4
+ private const val EVENT_INDEX_DTSTART = 5
+ private const val EVENT_INDEX_SYNC_ID = 6
+ private const val EVENT_INDEX_EVENT_TIMEZONE = 7
+ private const val EVENT_INDEX_DESCRIPTION = 8
+ private const val EVENT_INDEX_EVENT_LOCATION = 9
+ private const val EVENT_INDEX_ACCESS_LEVEL = 10
+ private const val EVENT_INDEX_CALENDAR_COLOR = 11
+ private const val EVENT_INDEX_EVENT_COLOR = 12
+ private const val EVENT_INDEX_HAS_ATTENDEE_DATA = 13
+ private const val EVENT_INDEX_ORGANIZER = 14
+ private const val EVENT_INDEX_HAS_ALARM = 15
+ private const val EVENT_INDEX_MAX_REMINDERS = 16
+ private const val EVENT_INDEX_ALLOWED_REMINDERS = 17
+ private const val EVENT_INDEX_CUSTOM_APP_PACKAGE = 18
+ private const val EVENT_INDEX_CUSTOM_APP_URI = 19
+ private const val EVENT_INDEX_DTEND = 20
+ private const val EVENT_INDEX_DURATION = 21
+ val CALENDARS_PROJECTION = arrayOf<String>(
+ Calendars._ID, // 0
+ Calendars.CALENDAR_DISPLAY_NAME, // 1
+ Calendars.OWNER_ACCOUNT, // 2
+ Calendars.CAN_ORGANIZER_RESPOND, // 3
+ Calendars.ACCOUNT_NAME, // 4
+ Calendars.ACCOUNT_TYPE // 5
+ )
+ const val CALENDARS_INDEX_DISPLAY_NAME = 1
+ const val CALENDARS_INDEX_OWNER_ACCOUNT = 2
+ const val CALENDARS_INDEX_OWNER_CAN_RESPOND = 3
+ const val CALENDARS_INDEX_ACCOUNT_NAME = 4
+ const val CALENDARS_INDEX_ACCOUNT_TYPE = 5
+ val CALENDARS_WHERE: String = Calendars._ID.toString() + "=?"
+ val CALENDARS_DUPLICATE_NAME_WHERE: String =
+ Calendars.CALENDAR_DISPLAY_NAME.toString() + "=?"
+ val CALENDARS_VISIBLE_WHERE: String = Calendars.VISIBLE.toString() + "=?"
+ const val COLORS_INDEX_COLOR = 1
+ const val COLORS_INDEX_COLOR_KEY = 2
+ private var mScale = 0f // Used for supporting different screen densities
+ private var mCustomAppIconSize = 32
+ private const val FADE_IN_TIME = 300 // in milliseconds
+ private const val LOADING_MSG_DELAY = 600 // in milliseconds
+ private const val LOADING_MSG_MIN_DISPLAY_TIME = 600
+ private var mDialogWidth = 500
+ private var mDialogHeight = 600
+ private var DIALOG_TOP_MARGIN = 8
+ fun getResponseFromButtonId(buttonId: Int): Int {
+ return Attendees.ATTENDEE_STATUS_NONE
+ }
+
+ fun findButtonIdForResponse(response: Int): Int {
+ return -1
+ }
+
+ init {
+ if (!Utils.isJellybeanOrLater()) {
+ EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_PACKAGE] = Events._ID // nonessential value
+ EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_URI] = Events._ID // nonessential value
+ }
+ }
+ }
+
+ private var mView: View? = null
+ private var mUri: Uri? = null
+ var eventId: Long = 0
+ private set
+ private val mEventCursor: Cursor? = null
+ private val mCalendarsCursor: Cursor? = null
+ var startMillis: Long = 0
+ private set
+ var endMillis: Long = 0
+ private set
+ private var mAllDay = false
+ private var mOwnerCanRespond = false
+ private var mSyncAccountName: String? = null
+ private var mCalendarOwnerAccount: String? = null
+ private var mIsBusyFreeCalendar = false
+ private val mOriginalAttendeeResponse = 0
+ private var mAttendeeResponseFromIntent: Int = Attendees.ATTENDEE_STATUS_NONE
+ private val mUserSetResponse: Int = Attendees.ATTENDEE_STATUS_NONE
+ private val mWhichEvents = -1
+
+ // Used as the temporary response until the dialog is confirmed. It is also
+ // able to be used as a state marker for configuration changes.
+ private val mTentativeUserSetResponse: Int = Attendees.ATTENDEE_STATUS_NONE
+ private var mHasAlarm = false
+
+ // Used to prevent saving changes in event if it is being deleted.
+ private val mEventDeletionStarted = false
+ private var mTitle: TextView? = null
+ private var mWhenDateTime: TextView? = null
+ private var mWhere: TextView? = null
+ private var mMenu: Menu? = null
+ private var mHeadlines: View? = null
+ private var mScrollView: ScrollView? = null
+ private var mLoadingMsgView: View? = null
+ private var mErrorMsgView: View? = null
+ private var mAnimateAlpha: ObjectAnimator? = null
+ private var mLoadingMsgStartTime: Long = 0
+ private val mDisplayColorKeyMap: SparseIntArray = SparseIntArray()
+ private val mOriginalColor = -1
+ private val mOriginalColorInitialized = false
+ private val mCalendarColor = -1
+ private val mCalendarColorInitialized = false
+ private val mCurrentColor = -1
+ private val mCurrentColorInitialized = false
+ private val mCurrentColorKey = -1
+ private var mNoCrossFade = false // Used to prevent repeated cross-fade
+ private var mResponseRadioGroup: RadioGroup? = null
+ var mToEmails: ArrayList<String> = ArrayList<String>()
+ var mCcEmails: ArrayList<String> = ArrayList<String>()
+ private val mTZUpdater: Runnable = object : Runnable {
+ @Override
+ override fun run() {
+ updateEvent(mView)
+ }
+ }
+ private val mLoadingMsgAlphaUpdater: Runnable = object : Runnable {
+ @Override
+ override fun run() {
+ // Since this is run after a delay, make sure to only show the message
+ // if the event's data is not shown yet.
+ if (!mAnimateAlpha!!.isRunning() && mScrollView!!.getAlpha() == 0f) {
+ mLoadingMsgStartTime = System.currentTimeMillis()
+ mLoadingMsgView?.setAlpha(1f)
+ }
+ }
+ }
+ private var mIsDialog = false
+ private var mIsPaused = true
+ private val mDismissOnResume = false
+ private var mX = -1
+ private var mY = -1
+ private var mMinTop = 0 // Dialog cannot be above this location
+ private var mIsTabletConfig = false
+ private var mActivity: Activity? = null
+ private var mContext: Context? = null
+ private var mController: CalendarController? = null
+ private fun sendAccessibilityEventIfQueryDone(token: Int) {
+ mCurrentQuery = mCurrentQuery or token
+ if (mCurrentQuery == TOKEN_QUERY_ALL) {
+ sendAccessibilityEvent()
+ }
+ }
+
+ constructor(
+ context: Context,
+ uri: Uri?,
+ startMillis: Long,
+ endMillis: Long,
+ attendeeResponse: Int,
+ isDialog: Boolean,
+ windowStyle: Int
+ ) {
+ val r: Resources = context.getResources()
+ if (mScale == 0f) {
+ mScale = context.getResources().getDisplayMetrics().density
+ if (mScale != 1f) {
+ mCustomAppIconSize *= mScale.toInt()
+ if (isDialog) {
+ DIALOG_TOP_MARGIN *= mScale.toInt()
+ }
+ }
+ }
+ if (isDialog) {
+ setDialogSize(r)
+ }
+ mIsDialog = isDialog
+ setStyle(DialogFragment.STYLE_NO_TITLE, 0)
+ mUri = uri
+ this.startMillis = startMillis
+ this.endMillis = endMillis
+ mAttendeeResponseFromIntent = attendeeResponse
+ mWindowStyle = windowStyle
+ }
+
+ // This is currently required by the fragment manager.
+ constructor() {}
+ constructor(
+ context: Context?,
+ eventId: Long,
+ startMillis: Long,
+ endMillis: Long,
+ attendeeResponse: Int,
+ isDialog: Boolean,
+ windowStyle: Int
+ ) : this(
+ context as Context, ContentUris.withAppendedId(Events.CONTENT_URI, eventId), startMillis,
+ endMillis, attendeeResponse, isDialog, windowStyle
+ ) {
+ this.eventId = eventId
+ }
+
+ @Override
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ if (mIsDialog) {
+ applyDialogParams()
+ }
+ val activity: Activity = getActivity()
+ mContext = activity
+ }
+
+ private fun applyDialogParams() {
+ val dialog: Dialog = getDialog()
+ dialog.setCanceledOnTouchOutside(true)
+ val window: Window? = dialog.getWindow()
+ window?.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ val a: WindowManager.LayoutParams? = window?.getAttributes()
+ a!!.dimAmount = .4f
+ a!!.width = mDialogWidth
+ a!!.height = mDialogHeight
+
+ // On tablets , do smart positioning of dialog
+ // On phones , use the whole screen
+ if (mX != -1 || mY != -1) {
+ a!!.x = mX - mDialogWidth / 2
+ a!!.y = mY - mDialogHeight / 2
+ if (a!!.y < mMinTop) {
+ a!!.y = mMinTop + DIALOG_TOP_MARGIN
+ }
+ a!!.gravity = Gravity.LEFT or Gravity.TOP
+ }
+ window?.setAttributes(a)
+ }
+
+ fun setDialogParams(x: Int, y: Int, minTop: Int) {
+ mX = x
+ mY = y
+ mMinTop = minTop
+ }
+
+ // Implements OnCheckedChangeListener
+ @Override
+ override fun onCheckedChanged(group: RadioGroup?, checkedId: Int) {
+ }
+
+ fun onNothingSelected(parent: AdapterView<*>?) {}
+ @Override
+ override fun onDetach() {
+ super.onDetach()
+ mController?.deregisterEventHandler(R.layout.event_info)
+ }
+
+ @Override
+ override fun onAttach(activity: Activity?) {
+ super.onAttach(activity)
+ mActivity = activity
+ // Ensure that mIsTabletConfig is set before creating the menu.
+ mIsTabletConfig = Utils.getConfigBool(mActivity as Context, R.bool.tablet_config)
+ mController = CalendarController.getInstance(mActivity)
+ mController?.registerEventHandler(R.layout.event_info, this)
+ if (!mIsDialog) {
+ setHasOptionsMenu(true)
+ }
+ }
+
+ @Override
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ mView = if (mWindowStyle == DIALOG_WINDOW_STYLE) {
+ inflater.inflate(R.layout.event_info_dialog, container, false)
+ } else {
+ inflater.inflate(R.layout.event_info, container, false)
+ }
+ mScrollView = mView?.findViewById(R.id.event_info_scroll_view) as ScrollView
+ mLoadingMsgView = mView?.findViewById(R.id.event_info_loading_msg)
+ mErrorMsgView = mView?.findViewById(R.id.event_info_error_msg)
+ mTitle = mView?.findViewById(R.id.title) as TextView
+ mWhenDateTime = mView?.findViewById(R.id.when_datetime) as TextView
+ mWhere = mView?.findViewById(R.id.where) as TextView
+ mHeadlines = mView?.findViewById(R.id.event_info_headline)
+ mResponseRadioGroup = mView?.findViewById(R.id.response_value) as RadioGroup
+ mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0f, 1f)
+ mAnimateAlpha?.setDuration(FADE_IN_TIME.toLong())
+ mAnimateAlpha?.addListener(object : AnimatorListenerAdapter() {
+ var defLayerType = 0
+ @Override
+ override fun onAnimationStart(animation: Animator?) {
+ // Use hardware layer for better performance during animation
+ defLayerType = mScrollView?.getLayerType() as Int
+ mScrollView?.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ // Ensure that the loading message is gone before showing the
+ // event info
+ mLoadingMsgView?.removeCallbacks(mLoadingMsgAlphaUpdater)
+ mLoadingMsgView?.setVisibility(View.GONE)
+ }
+
+ @Override
+ override fun onAnimationCancel(animation: Animator?) {
+ mScrollView?.setLayerType(defLayerType, null)
+ }
+
+ @Override
+ override fun onAnimationEnd(animation: Animator?) {
+ mScrollView?.setLayerType(defLayerType, null)
+ // Do not cross fade after the first time
+ mNoCrossFade = true
+ }
+ })
+ mLoadingMsgView?.setAlpha(0f)
+ mScrollView?.setAlpha(0f)
+ mErrorMsgView?.setVisibility(View.INVISIBLE)
+ mLoadingMsgView?.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY.toLong())
+
+ // Hide Edit/Delete buttons if in full screen mode on a phone
+ if (!mIsDialog && !mIsTabletConfig || mWindowStyle == FULL_WINDOW_STYLE) {
+ mView?.findViewById<View>(R.id.event_info_buttons_container)?.setVisibility(View.GONE)
+ }
+ return mView
+ }
+
+ private fun updateTitle() {
+ val res: Resources = getActivity().getResources()
+ getActivity().setTitle(res.getString(R.string.event_info_title))
+ }
+
+ /**
+ * Initializes the event cursor, which is expected to point to the first
+ * (and only) result from a query.
+ * @return false if the cursor is empty, true otherwise
+ */
+ private fun initEventCursor(): Boolean {
+ if (mEventCursor == null || mEventCursor.getCount() === 0) {
+ return false
+ }
+ mEventCursor.moveToFirst()
+ eventId = mEventCursor.getInt(EVENT_INDEX_ID).toLong()
+ val rRule: String = mEventCursor.getString(EVENT_INDEX_RRULE)
+ // mHasAlarm will be true if it was saved in the event already.
+ mHasAlarm = if (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) === 1) true else false
+ return true
+ }
+
+ @Override
+ override fun onSaveInstanceState(outState: Bundle?) {
+ super.onSaveInstanceState(outState)
+ }
+
+ @Override
+ override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
+ // Show color/edit/delete buttons only in non-dialog configuration
+ if (!mIsDialog && !mIsTabletConfig || mWindowStyle == FULL_WINDOW_STYLE) {
+ inflater.inflate(R.menu.event_info_title_bar, menu)
+ mMenu = menu
+ }
+ }
+
+ @Override
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+
+ // If we're a dialog we don't want to handle menu buttons
+ if (mIsDialog) {
+ return false
+ }
+ // Handles option menu selections:
+ // Home button - close event info activity and start the main calendar
+ // one
+ // Edit button - start the event edit activity and close the info
+ // activity
+ // Delete button - start a delete query that calls a runnable that close
+ // the info activity
+ val itemId: Int = item.getItemId()
+ if (itemId == android.R.id.home) {
+ Utils.returnToCalendarHome(mContext as Context)
+ mActivity?.finish()
+ return true
+ } else if (itemId == R.id.info_action_edit) {
+ mActivity?.finish()
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ @Override
+ override fun onStop() {
+ super.onStop()
+ }
+
+ @Override
+ override fun onDestroy() {
+ if (mEventCursor != null) {
+ mEventCursor.close()
+ }
+ if (mCalendarsCursor != null) {
+ mCalendarsCursor.close()
+ }
+ super.onDestroy()
+ }
+
+ /**
+ * Creates an exception to a recurring event. The only change we're making is to the
+ * "self attendee status" value. The provider will take care of updating the corresponding
+ * Attendees.attendeeStatus entry.
+ *
+ * @param eventId The recurring event.
+ * @param status The new value for selfAttendeeStatus.
+ */
+ private fun createExceptionResponse(eventId: Long, status: Int) {
+ val values = ContentValues()
+ values.put(Events.ORIGINAL_INSTANCE_TIME, startMillis)
+ values.put(Events.SELF_ATTENDEE_STATUS, status)
+ values.put(Events.STATUS, Events.STATUS_CONFIRMED)
+ val ops: ArrayList<ContentProviderOperation> = ArrayList<ContentProviderOperation>()
+ val exceptionUri: Uri = Uri.withAppendedPath(
+ Events.CONTENT_EXCEPTION_URI,
+ eventId.toString()
+ )
+ ops.add(ContentProviderOperation.newInsert(exceptionUri).withValues(values).build())
+ }
+
+ private fun displayEventNotFound() {
+ mErrorMsgView?.setVisibility(View.VISIBLE)
+ mScrollView?.setVisibility(View.GONE)
+ mLoadingMsgView?.setVisibility(View.GONE)
+ }
+
+ private fun updateEvent(view: View?) {
+ if (mEventCursor == null || view == null) {
+ return
+ }
+ val context: Context = view.getContext() ?: return
+ var eventName: String = mEventCursor.getString(EVENT_INDEX_TITLE)
+ if (eventName == null || eventName.length == 0) {
+ eventName = getActivity().getString(R.string.no_title_label)
+ }
+
+ // 3rd parties might not have specified the start/end time when firing the
+ // Events.CONTENT_URI intent. Update these with values read from the db.
+ if (startMillis == 0L && endMillis == 0L) {
+ startMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART)
+ endMillis = mEventCursor.getLong(EVENT_INDEX_DTEND)
+ if (endMillis == 0L) {
+ val duration: String = mEventCursor.getString(EVENT_INDEX_DURATION)
+ if (!TextUtils.isEmpty(duration)) {
+ try {
+ val d = Duration()
+ d.parse(duration)
+ val endMillis: Long = startMillis + d.getMillis()
+ if (endMillis >= startMillis) {
+ this.endMillis = endMillis
+ } else {
+ Log.d(TAG, "Invalid duration string: $duration")
+ }
+ } catch (e: DateException) {
+ Log.d(TAG, "Error parsing duration string $duration", e)
+ }
+ }
+ if (endMillis == 0L) {
+ endMillis = startMillis
+ }
+ }
+ }
+ mAllDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) !== 0
+ val location: String = mEventCursor.getString(EVENT_INDEX_EVENT_LOCATION)
+ val description: String = mEventCursor.getString(EVENT_INDEX_DESCRIPTION)
+ val rRule: String = mEventCursor.getString(EVENT_INDEX_RRULE)
+ val eventTimezone: String = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE)
+ mHeadlines?.setBackgroundColor(mCurrentColor)
+
+ // What
+ if (eventName != null) {
+ setTextCommon(view, R.id.title, eventName)
+ }
+
+ // When
+ // Set the date and repeats (if any)
+ val localTimezone: String? = Utils.getTimeZone(mActivity, mTZUpdater)
+ val resources: Resources = context.getResources()
+ var displayedDatetime: String? = Utils.getDisplayedDatetime(
+ startMillis, endMillis,
+ System.currentTimeMillis(), localTimezone as String, mAllDay, context
+ )
+ var displayedTimezone: String? = null
+ if (!mAllDay) {
+ displayedTimezone = Utils.getDisplayedTimezone(
+ startMillis, localTimezone,
+ eventTimezone
+ )
+ }
+ // Display the datetime. Make the timezone (if any) transparent.
+ if (displayedTimezone == null) {
+ setTextCommon(view, R.id.when_datetime, displayedDatetime as CharSequence)
+ } else {
+ val timezoneIndex: Int = displayedDatetime!!.length
+ displayedDatetime += " $displayedTimezone"
+ val sb = SpannableStringBuilder(displayedDatetime)
+ val transparentColorSpan = ForegroundColorSpan(
+ resources.getColor(R.color.event_info_headline_transparent_color)
+ )
+ sb.setSpan(
+ transparentColorSpan, timezoneIndex, displayedDatetime!!.length,
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE
+ )
+ setTextCommon(view, R.id.when_datetime, sb)
+ }
+ view.findViewById<View>(R.id.when_repeat).setVisibility(View.GONE)
+
+ // Organizer view is setup in the updateCalendar method
+
+ // Where
+ if (location == null || location.trim().length == 0) {
+ setVisibilityCommon(view, R.id.where, View.GONE)
+ } else {
+ val textView: TextView? = mWhere
+ if (textView != null) {
+ textView.setText(location.trim())
+ }
+ }
+
+ // Launch Custom App
+ if (Utils.isJellybeanOrLater()) {
+ updateCustomAppButton()
+ }
+ }
+
+ private fun updateCustomAppButton() {
+ setVisibilityCommon(mView, R.id.launch_custom_app_container, View.GONE)
+ return
+ }
+
+ private fun sendAccessibilityEvent() {
+ val am: AccessibilityManager =
+ getActivity().getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager
+ if (!am.isEnabled()) {
+ return
+ }
+ val event: AccessibilityEvent =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+ event.setClassName(EventInfoFragment::class.java.getName())
+ event.setPackageName(getActivity().getPackageName())
+ var text = event.getText()
+ if (mResponseRadioGroup?.getVisibility() == View.VISIBLE) {
+ val id: Int = mResponseRadioGroup!!.getCheckedRadioButtonId()
+ if (id != View.NO_ID) {
+ text.add((getView()?.findViewById(R.id.response_label) as TextView)?.getText())
+ text.add(
+ (mResponseRadioGroup?.findViewById(id) as RadioButton)
+ .getText().toString() + PERIOD_SPACE
+ )
+ }
+ }
+ am.sendAccessibilityEvent(event)
+ }
+
+ private fun updateCalendar(view: View?) {
+ mCalendarOwnerAccount = ""
+ if (mCalendarsCursor != null && mEventCursor != null) {
+ mCalendarsCursor.moveToFirst()
+ val tempAccount: String = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT)
+ mCalendarOwnerAccount = tempAccount ?: ""
+ mOwnerCanRespond = mCalendarsCursor.getInt(CALENDARS_INDEX_OWNER_CAN_RESPOND) !== 0
+ mSyncAccountName = mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME)
+ setVisibilityCommon(view, R.id.organizer_container, View.GONE)
+ mIsBusyFreeCalendar =
+ mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) === Calendars.CAL_ACCESS_FREEBUSY
+ if (!mIsBusyFreeCalendar) {
+ val b: View? = mView?.findViewById(R.id.edit)
+ b?.setEnabled(true)
+ b?.setOnClickListener(object : OnClickListener {
+ @Override
+ override fun onClick(v: View?) {
+ // For dialogs, just close the fragment
+ // For full screen, close activity on phone, leave it for tablet
+ if (mIsDialog) {
+ this@EventInfoFragment.dismiss()
+ } else if (!mIsTabletConfig) {
+ getActivity().finish()
+ }
+ }
+ })
+ }
+ var button: View
+ if ((!mIsDialog && !mIsTabletConfig ||
+ mWindowStyle == FULL_WINDOW_STYLE) && mMenu != null
+ ) {
+ mActivity?.invalidateOptionsMenu()
+ }
+ } else {
+ setVisibilityCommon(view, R.id.calendar, View.GONE)
+ sendAccessibilityEventIfQueryDone(TOKEN_QUERY_DUPLICATE_CALENDARS)
+ }
+ }
+
+ private fun setTextCommon(view: View, id: Int, text: CharSequence) {
+ val textView: TextView = view.findViewById(id) as TextView ?: return
+ textView.setText(text)
+ }
+
+ private fun setVisibilityCommon(view: View?, id: Int, visibility: Int) {
+ val v: View? = view?.findViewById(id)
+ if (v != null) {
+ v.setVisibility(visibility)
+ }
+ return
+ }
+
+ @Override
+ override fun onPause() {
+ mIsPaused = true
+ super.onPause()
+ }
+
+ @Override
+ override fun onResume() {
+ super.onResume()
+ if (mIsDialog) {
+ setDialogSize(getActivity().getResources())
+ applyDialogParams()
+ }
+ mIsPaused = false
+ if (mTentativeUserSetResponse != Attendees.ATTENDEE_STATUS_NONE) {
+ val buttonId = findButtonIdForResponse(mTentativeUserSetResponse)
+ mResponseRadioGroup?.check(buttonId)
+ }
+ }
+
+ @Override
+ override fun eventsChanged() {
+ }
+
+ @get:Override override val supportedEventTypes: Long
+ get() = EventType.EVENTS_CHANGED
+
+ @Override
+ override fun handleEvent(event: EventInfo?) {
+ reloadEvents()
+ }
+
+ fun reloadEvents() {}
+ @Override
+ override fun onClick(view: View?) {
+ }
+
+ private fun setDialogSize(r: Resources) {
+ mDialogWidth = r.getDimension(R.dimen.event_info_dialog_width).toInt()
+ mDialogHeight = r.getDimension(R.dimen.event_info_dialog_height).toInt()
+ }
+} \ No newline at end of file