diff options
author | Damian Patel <damianpatel@google.com> | 2021-07-08 15:18:49 +0000 |
---|---|---|
committer | Damian Patel <damianpatel@google.com> | 2021-07-08 15:18:49 +0000 |
commit | 2cba2a7819d117d7adabf9b77cf1c4d12b920bed (patch) | |
tree | 4d947fd510d949992f03d52860545fcfe1336d1f | |
parent | 9b3c5ce1f330b40bf072cf78069082ded3813cb3 (diff) | |
download | Calendar-2cba2a7819d117d7adabf9b77cf1c4d12b920bed.tar.gz |
AOSP/Calendar - Copy of EventInfoFragment.java
The Java code in Event EventInfoFragment.java has
been copied into a corresponding .kt file.
Test: manual - opening both files shows that
they are identical.
Change-Id: I49c7240ba4ea471d7cc0fae53f402dfaf29b73c8
-rw-r--r-- | src/com/android/calendar/EventInfoFragment.kt | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/src/com/android/calendar/EventInfoFragment.kt b/src/com/android/calendar/EventInfoFragment.kt new file mode 100644 index 00000000..0aa83d02 --- /dev/null +++ b/src/com/android/calendar/EventInfoFragment.kt @@ -0,0 +1,877 @@ +/* + * Copyright (C) 2010 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 static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; +import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; +import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; +import static com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH; + +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.FragmentManager; +import android.app.Service; +import android.content.ActivityNotFoundException; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.Color; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.provider.CalendarContract; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.provider.CalendarContract.Reminders; +import android.provider.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds; +import android.provider.ContactsContract.Intents; +import android.provider.ContactsContract.QuickContact; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.format.Time; +import android.text.method.LinkMovementMethod; +import android.text.method.MovementMethod; +import android.text.style.ForegroundColorSpan; +import android.text.util.Rfc822Token; +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.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnTouchListener; +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.AdapterView.OnItemSelectedListener; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.RadioGroup.OnCheckedChangeListener; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import com.android.calendar.CalendarController.EventInfo; +import com.android.calendar.CalendarController.EventType; +import com.android.calendar.alerts.QuickResponseActivity; +import com.android.calendarcommon2.DateException; +import com.android.calendarcommon2.Duration; +import com.android.calendarcommon2.EventRecurrence; +import com.android.colorpicker.HsvColorComparator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class EventInfoFragment extends DialogFragment implements OnCheckedChangeListener, + CalendarController.EventHandler, OnClickListener { + + public static final boolean DEBUG = false; + + public static final String TAG = "EventInfoFragment"; + + protected static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; + protected static final String BUNDLE_KEY_START_MILLIS = "key_start_millis"; + protected static final String BUNDLE_KEY_END_MILLIS = "key_end_millis"; + protected static final String BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog"; + protected static final String BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible"; + protected static final String BUNDLE_KEY_WINDOW_STYLE = "key_window_style"; + protected static final String BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color"; + protected static final String BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init"; + protected static final String BUNDLE_KEY_CURRENT_COLOR = "key_current_color"; + protected static final String BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key"; + protected static final String BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init"; + protected static final String BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color"; + protected static final String BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init"; + protected static final String BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response"; + protected static final String BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = + "key_user_set_attendee_response"; + protected static final String BUNDLE_KEY_TENTATIVE_USER_RESPONSE = + "key_tentative_user_response"; + protected static final String BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events"; + protected static final String BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes"; + protected static final String BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods"; + + + private static final String PERIOD_SPACE = ". "; + + private static final String NO_EVENT_COLOR = ""; + + /** + * These are the corresponding indices into the array of strings + * "R.array.change_response_labels" in the resource file. + */ + static final int UPDATE_SINGLE = 0; + static final int UPDATE_ALL = 1; + + // Style of view + public static final int FULL_WINDOW_STYLE = 0; + public static final int DIALOG_WINDOW_STYLE = 1; + + private int mWindowStyle = DIALOG_WINDOW_STYLE; + + // Query tokens for QueryHandler + private static final int TOKEN_QUERY_EVENT = 1 << 0; + private static final int TOKEN_QUERY_CALENDARS = 1 << 1; + private static final int TOKEN_QUERY_ATTENDEES = 1 << 2; + private static final int TOKEN_QUERY_DUPLICATE_CALENDARS = 1 << 3; + private static final int TOKEN_QUERY_REMINDERS = 1 << 4; + private static final int TOKEN_QUERY_VISIBLE_CALENDARS = 1 << 5; + private static final int TOKEN_QUERY_COLORS = 1 << 6; + + private static final int TOKEN_QUERY_ALL = TOKEN_QUERY_DUPLICATE_CALENDARS + | TOKEN_QUERY_ATTENDEES | TOKEN_QUERY_CALENDARS | TOKEN_QUERY_EVENT + | TOKEN_QUERY_REMINDERS | TOKEN_QUERY_VISIBLE_CALENDARS | TOKEN_QUERY_COLORS; + + private int mCurrentQuery = 0; + + private static final String[] EVENT_PROJECTION = new 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 static final int EVENT_INDEX_ID = 0; + private static final int EVENT_INDEX_TITLE = 1; + private static final int EVENT_INDEX_RRULE = 2; + private static final int EVENT_INDEX_ALL_DAY = 3; + private static final int EVENT_INDEX_CALENDAR_ID = 4; + private static final int EVENT_INDEX_DTSTART = 5; + private static final int EVENT_INDEX_SYNC_ID = 6; + private static final int EVENT_INDEX_EVENT_TIMEZONE = 7; + private static final int EVENT_INDEX_DESCRIPTION = 8; + private static final int EVENT_INDEX_EVENT_LOCATION = 9; + private static final int EVENT_INDEX_ACCESS_LEVEL = 10; + private static final int EVENT_INDEX_CALENDAR_COLOR = 11; + private static final int EVENT_INDEX_EVENT_COLOR = 12; + private static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 13; + private static final int EVENT_INDEX_ORGANIZER = 14; + private static final int EVENT_INDEX_HAS_ALARM = 15; + private static final int EVENT_INDEX_MAX_REMINDERS = 16; + private static final int EVENT_INDEX_ALLOWED_REMINDERS = 17; + private static final int EVENT_INDEX_CUSTOM_APP_PACKAGE = 18; + private static final int EVENT_INDEX_CUSTOM_APP_URI = 19; + private static final int EVENT_INDEX_DTEND = 20; + private static final int EVENT_INDEX_DURATION = 21; + + static { + 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 + } + } + + static final String[] CALENDARS_PROJECTION = new 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 + }; + static final int CALENDARS_INDEX_DISPLAY_NAME = 1; + static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2; + static final int CALENDARS_INDEX_OWNER_CAN_RESPOND = 3; + static final int CALENDARS_INDEX_ACCOUNT_NAME = 4; + static final int CALENDARS_INDEX_ACCOUNT_TYPE = 5; + + static final String CALENDARS_WHERE = Calendars._ID + "=?"; + static final String CALENDARS_DUPLICATE_NAME_WHERE = Calendars.CALENDAR_DISPLAY_NAME + "=?"; + static final String CALENDARS_VISIBLE_WHERE = Calendars.VISIBLE + "=?"; + + public static final int COLORS_INDEX_COLOR = 1; + public static final int COLORS_INDEX_COLOR_KEY = 2; + + private View mView; + + private Uri mUri; + private long mEventId; + private Cursor mEventCursor; + private Cursor mCalendarsCursor; + + private static float mScale = 0; // Used for supporting different screen densities + + private static int mCustomAppIconSize = 32; + + private long mStartMillis; + private long mEndMillis; + private boolean mAllDay; + + private boolean mOwnerCanRespond; + private String mSyncAccountName; + private String mCalendarOwnerAccount; + private boolean mIsBusyFreeCalendar; + + private int mOriginalAttendeeResponse; + private int mAttendeeResponseFromIntent = Attendees.ATTENDEE_STATUS_NONE; + private int mUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; + private int 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 int mTentativeUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; + private boolean mHasAlarm; + // Used to prevent saving changes in event if it is being deleted. + private boolean mEventDeletionStarted = false; + + private TextView mTitle; + private TextView mWhenDateTime; + private TextView mWhere; + private Menu mMenu = null; + private View mHeadlines; + private ScrollView mScrollView; + private View mLoadingMsgView; + private View mErrorMsgView; + private ObjectAnimator mAnimateAlpha; + private long mLoadingMsgStartTime; + + private SparseIntArray mDisplayColorKeyMap = new SparseIntArray(); + private int mOriginalColor = -1; + private boolean mOriginalColorInitialized = false; + private int mCalendarColor = -1; + private boolean mCalendarColorInitialized = false; + private int mCurrentColor = -1; + private boolean mCurrentColorInitialized = false; + private int mCurrentColorKey = -1; + + private static final int FADE_IN_TIME = 300; // in milliseconds + private static final int LOADING_MSG_DELAY = 600; // in milliseconds + private static final int LOADING_MSG_MIN_DISPLAY_TIME = 600; + private boolean mNoCrossFade = false; // Used to prevent repeated cross-fade + private RadioGroup mResponseRadioGroup; + + ArrayList<String> mToEmails = new ArrayList<String>(); + ArrayList<String> mCcEmails = new ArrayList<String>(); + + + private final Runnable mTZUpdater = new Runnable() { + @Override + public void run() { + updateEvent(mView); + } + }; + + private final Runnable mLoadingMsgAlphaUpdater = new Runnable() { + @Override + public void 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() == 0) { + mLoadingMsgStartTime = System.currentTimeMillis(); + mLoadingMsgView.setAlpha(1); + } + } + }; + + private static int mDialogWidth = 500; + private static int mDialogHeight = 600; + private static int DIALOG_TOP_MARGIN = 8; + private boolean mIsDialog = false; + private boolean mIsPaused = true; + private boolean mDismissOnResume = false; + private int mX = -1; + private int mY = -1; + private int mMinTop; // Dialog cannot be above this location + private boolean mIsTabletConfig; + private Activity mActivity; + private Context mContext; + + private CalendarController mController; + + private void sendAccessibilityEventIfQueryDone(int token) { + mCurrentQuery |= token; + if (mCurrentQuery == TOKEN_QUERY_ALL) { + sendAccessibilityEvent(); + } + } + + public EventInfoFragment(Context context, Uri uri, long startMillis, long endMillis, + int attendeeResponse, boolean isDialog, int windowStyle) { + + Resources r = context.getResources(); + if (mScale == 0) { + mScale = context.getResources().getDisplayMetrics().density; + if (mScale != 1) { + mCustomAppIconSize *= mScale; + if (isDialog) { + DIALOG_TOP_MARGIN *= mScale; + } + } + } + if (isDialog) { + setDialogSize(r); + } + mIsDialog = isDialog; + + setStyle(DialogFragment.STYLE_NO_TITLE, 0); + mUri = uri; + mStartMillis = startMillis; + mEndMillis = endMillis; + mAttendeeResponseFromIntent = attendeeResponse; + mWindowStyle = windowStyle; + } + + // This is currently required by the fragment manager. + public EventInfoFragment() { + } + + public EventInfoFragment(Context context, long eventId, long startMillis, long endMillis, + int attendeeResponse, boolean isDialog, int windowStyle) { + this(context, ContentUris.withAppendedId(Events.CONTENT_URI, eventId), startMillis, + endMillis, attendeeResponse, isDialog, windowStyle); + mEventId = eventId; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + if (mIsDialog) { + applyDialogParams(); + } + + final Activity activity = getActivity(); + mContext = activity; + } + + private void applyDialogParams() { + Dialog dialog = getDialog(); + dialog.setCanceledOnTouchOutside(true); + + Window window = dialog.getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + + WindowManager.LayoutParams a = 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 | Gravity.TOP; + } + window.setAttributes(a); + } + + public void setDialogParams(int x, int y, int minTop) { + mX = x; + mY = y; + mMinTop = minTop; + } + + // Implements OnCheckedChangeListener + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + } + + public void onNothingSelected(AdapterView<?> parent) { + } + + @Override + public void onDetach() { + super.onDetach(); + mController.deregisterEventHandler(R.layout.event_info); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mActivity = activity; + // Ensure that mIsTabletConfig is set before creating the menu. + mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config); + mController = CalendarController.getInstance(mActivity); + mController.registerEventHandler(R.layout.event_info, this); + + if (!mIsDialog) { + setHasOptionsMenu(true); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + if (mWindowStyle == DIALOG_WINDOW_STYLE) { + mView = inflater.inflate(R.layout.event_info_dialog, container, false); + } else { + mView = inflater.inflate(R.layout.event_info, container, false); + } + mScrollView = (ScrollView) mView.findViewById(R.id.event_info_scroll_view); + mLoadingMsgView = mView.findViewById(R.id.event_info_loading_msg); + mErrorMsgView = mView.findViewById(R.id.event_info_error_msg); + mTitle = (TextView) mView.findViewById(R.id.title); + mWhenDateTime = (TextView) mView.findViewById(R.id.when_datetime); + mWhere = (TextView) mView.findViewById(R.id.where); + mHeadlines = mView.findViewById(R.id.event_info_headline); + + mResponseRadioGroup = (RadioGroup) mView.findViewById(R.id.response_value); + + mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0, 1); + mAnimateAlpha.setDuration(FADE_IN_TIME); + mAnimateAlpha.addListener(new AnimatorListenerAdapter() { + int defLayerType; + + @Override + public void onAnimationStart(Animator animation) { + // Use hardware layer for better performance during animation + defLayerType = mScrollView.getLayerType(); + 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 + public void onAnimationCancel(Animator animation) { + mScrollView.setLayerType(defLayerType, null); + } + + @Override + public void onAnimationEnd(Animator animation) { + mScrollView.setLayerType(defLayerType, null); + // Do not cross fade after the first time + mNoCrossFade = true; + } + }); + + mLoadingMsgView.setAlpha(0); + mScrollView.setAlpha(0); + mErrorMsgView.setVisibility(View.INVISIBLE); + mLoadingMsgView.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY); + + // Hide Edit/Delete buttons if in full screen mode on a phone + if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) { + mView.findViewById(R.id.event_info_buttons_container).setVisibility(View.GONE); + } + + return mView; + } + + private void updateTitle() { + Resources res = 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 boolean initEventCursor() { + if ((mEventCursor == null) || (mEventCursor.getCount() == 0)) { + return false; + } + mEventCursor.moveToFirst(); + mEventId = mEventCursor.getInt(EVENT_INDEX_ID); + String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); + // mHasAlarm will be true if it was saved in the event already. + mHasAlarm = (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) == 1)? true : false; + return true; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + // Show color/edit/delete buttons only in non-dialog configuration + if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) { + inflater.inflate(R.menu.event_info_title_bar, menu); + mMenu = menu; + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + // 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 + + final int itemId = item.getItemId(); + if (itemId == android.R.id.home) { + Utils.returnToCalendarHome(mContext); + mActivity.finish(); + return true; + } else if (itemId == R.id.info_action_edit) { + mActivity.finish(); + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onStop() { + super.onStop(); + } + + @Override + public void 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 void createExceptionResponse(long eventId, int status) { + ContentValues values = new ContentValues(); + values.put(Events.ORIGINAL_INSTANCE_TIME, mStartMillis); + values.put(Events.SELF_ATTENDEE_STATUS, status); + values.put(Events.STATUS, Events.STATUS_CONFIRMED); + + ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); + Uri exceptionUri = Uri.withAppendedPath(Events.CONTENT_EXCEPTION_URI, + String.valueOf(eventId)); + ops.add(ContentProviderOperation.newInsert(exceptionUri).withValues(values).build()); + } + + public static int getResponseFromButtonId(int buttonId) { + return Attendees.ATTENDEE_STATUS_NONE; + } + + public static int findButtonIdForResponse(int response) { + return -1; + } + + private void displayEventNotFound() { + mErrorMsgView.setVisibility(View.VISIBLE); + mScrollView.setVisibility(View.GONE); + mLoadingMsgView.setVisibility(View.GONE); + } + + private void updateEvent(View view) { + if (mEventCursor == null || view == null) { + return; + } + + Context context = view.getContext(); + if (context == null) { + return; + } + + String eventName = 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 (mStartMillis == 0 && mEndMillis == 0) { + mStartMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART); + mEndMillis = mEventCursor.getLong(EVENT_INDEX_DTEND); + if (mEndMillis == 0) { + String duration = mEventCursor.getString(EVENT_INDEX_DURATION); + if (!TextUtils.isEmpty(duration)) { + try { + Duration d = new Duration(); + d.parse(duration); + long endMillis = mStartMillis + d.getMillis(); + if (endMillis >= mStartMillis) { + mEndMillis = endMillis; + } else { + Log.d(TAG, "Invalid duration string: " + duration); + } + } catch (DateException e) { + Log.d(TAG, "Error parsing duration string " + duration, e); + } + } + if (mEndMillis == 0) { + mEndMillis = mStartMillis; + } + } + } + + mAllDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0; + String location = mEventCursor.getString(EVENT_INDEX_EVENT_LOCATION); + String description = mEventCursor.getString(EVENT_INDEX_DESCRIPTION); + String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); + String eventTimezone = 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) + String localTimezone = Utils.getTimeZone(mActivity, mTZUpdater); + + Resources resources = context.getResources(); + String displayedDatetime = Utils.getDisplayedDatetime(mStartMillis, mEndMillis, + System.currentTimeMillis(), localTimezone, mAllDay, context); + + String displayedTimezone = null; + if (!mAllDay) { + displayedTimezone = Utils.getDisplayedTimezone(mStartMillis, localTimezone, + eventTimezone); + } + // Display the datetime. Make the timezone (if any) transparent. + if (displayedTimezone == null) { + setTextCommon(view, R.id.when_datetime, displayedDatetime); + } else { + int timezoneIndex = displayedDatetime.length(); + displayedDatetime += " " + displayedTimezone; + SpannableStringBuilder sb = new SpannableStringBuilder(displayedDatetime); + ForegroundColorSpan transparentColorSpan = new 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(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 { + final TextView textView = mWhere; + if (textView != null) { + textView.setText(location.trim()); + } + } + + // Launch Custom App + if (Utils.isJellybeanOrLater()) { + updateCustomAppButton(); + } + } + + private void updateCustomAppButton() { + setVisibilityCommon(mView, R.id.launch_custom_app_container, View.GONE); + return; + } + + private void sendAccessibilityEvent() { + AccessibilityManager am = + (AccessibilityManager) getActivity().getSystemService(Service.ACCESSIBILITY_SERVICE); + if (!am.isEnabled()) { + return; + } + + AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); + event.setClassName(EventInfoFragment.class.getName()); + event.setPackageName(getActivity().getPackageName()); + List<CharSequence> text = event.getText(); + + if (mResponseRadioGroup.getVisibility() == View.VISIBLE) { + int id = mResponseRadioGroup.getCheckedRadioButtonId(); + if (id != View.NO_ID) { + text.add(((TextView) getView().findViewById(R.id.response_label)).getText()); + text.add((((RadioButton) (mResponseRadioGroup.findViewById(id))) + .getText() + PERIOD_SPACE)); + } + } + + am.sendAccessibilityEvent(event); + } + + private void updateCalendar(View view) { + + mCalendarOwnerAccount = ""; + if (mCalendarsCursor != null && mEventCursor != null) { + mCalendarsCursor.moveToFirst(); + String tempAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT); + mCalendarOwnerAccount = (tempAccount == null) ? "" : 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) { + + View b = mView.findViewById(R.id.edit); + b.setEnabled(true); + b.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // For dialogs, just close the fragment + // For full screen, close activity on phone, leave it for tablet + if (mIsDialog) { + EventInfoFragment.this.dismiss(); + } + else if (!mIsTabletConfig){ + getActivity().finish(); + } + } + }); + } + View button; + if ((!mIsDialog && !mIsTabletConfig || + mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) && mMenu != null) { + mActivity.invalidateOptionsMenu(); + } + } else { + setVisibilityCommon(view, R.id.calendar, View.GONE); + sendAccessibilityEventIfQueryDone(TOKEN_QUERY_DUPLICATE_CALENDARS); + } + } + + private void setTextCommon(View view, int id, CharSequence text) { + TextView textView = (TextView) view.findViewById(id); + if (textView == null) + return; + textView.setText(text); + } + + private void setVisibilityCommon(View view, int id, int visibility) { + View v = view.findViewById(id); + if (v != null) { + v.setVisibility(visibility); + } + return; + } + + @Override + public void onPause() { + mIsPaused = true; + super.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + if (mIsDialog) { + setDialogSize(getActivity().getResources()); + applyDialogParams(); + } + mIsPaused = false; + if (mTentativeUserSetResponse != Attendees.ATTENDEE_STATUS_NONE) { + int buttonId = findButtonIdForResponse(mTentativeUserSetResponse); + mResponseRadioGroup.check(buttonId); + } + } + + @Override + public void eventsChanged() { + } + + @Override + public long getSupportedEventTypes() { + return EventType.EVENTS_CHANGED; + } + + @Override + public void handleEvent(EventInfo event) { + reloadEvents(); + } + + public void reloadEvents() { + } + + @Override + public void onClick(View view) { + } + + public long getEventId() { + return mEventId; + } + + public long getStartMillis() { + return mStartMillis; + } + public long getEndMillis() { + return mEndMillis; + } + private void setDialogSize(Resources r) { + mDialogWidth = (int)r.getDimension(R.dimen.event_info_dialog_width); + mDialogHeight = (int)r.getDimension(R.dimen.event_info_dialog_height); + } +} |