diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2021-07-10 08:19:25 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-07-10 08:19:25 +0000 |
commit | bf857d93f1a29b70986de1ee69efe0dcc602f4dd (patch) | |
tree | 48dae73c45f04f343dc2e3c8a9ebedd8304b272a | |
parent | 9b3c5ce1f330b40bf072cf78069082ded3813cb3 (diff) | |
parent | 8b0a7714ee758e520ec6452617447ace8d3adee3 (diff) | |
download | Calendar-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.bp | 1 | ||||
-rw-r--r-- | src/com/android/calendar/EventInfoFragment.kt | 787 |
2 files changed, 788 insertions, 0 deletions
@@ -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 |