gt:         if (description != null && description.length() != 0) {
context: /*
   * 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.Service;
  import android.content.ActivityNotFoundException;
  import android.content.ContentProviderOperation;
  import android.content.ContentProviderResult;
  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.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.SpannableString;
  import android.text.SpannableStringBuilder;
  import android.text.Spanned;
  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.style.StrikethroughSpan;
  import android.text.style.URLSpan;
  import android.text.util.Linkify;
  import android.text.util.Rfc822Token;
  import android.util.Log;
  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;
  ///M: for SNS plugin @{
  import android.widget.ImageView;
  ///@}
  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.CalendarEventModel.Attendee;
  import com.android.calendar.CalendarEventModel.ReminderEntry;
  import com.android.calendar.alerts.QuickResponseActivity;
  import com.android.calendar.event.AttendeesView;
  import com.android.calendar.event.EditEventActivity;
  import com.android.calendar.event.EditEventHelper;
  import com.android.calendar.event.EventViewUtils;
  import com.android.calendarcommon2.EventRecurrence;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.regex.Pattern;
  
  import com.mediatek.calendar.extension.ExtensionFactory;
  import com.mediatek.calendar.extension.IOptionsMenuExt;
  import com.mediatek.calendar.MTKUtils;
  ///M: for SNS plugin @{
  import com.mediatek.calendar.SNSCalendarDataHelper;
  ///@}
  
  public class EventInfoFragment extends DialogFragment implements OnCheckedChangeListener,
          CalendarController.EventHandler, OnClickListener, DeleteEventHelper.DeleteNotifyListener {
  
      public static final boolean DEBUG = false;
  
      public static final String TAG = "EventInfoFragment";
  
      public static final String FRAGMENT_TAG = "fragment_tag";
  
      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_ATTENDEE_RESPONSE = "key_attendee_response";
      /** M: For save state when rotate phone @{ */
      protected static final String BUNDLE_KEY_ORIGINAL_REMINDERS = "key_original_reminders";
      protected static final String BUNDLE_KEY_REMINDERS = "key_reminders";
      protected static final String BUNDLE_KEY_WHICH_EVENTS = "key_which_events";
      /** @} */
  
      private static final String PERIOD_SPACE = ". ";
  
      /**
       * 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_ALL = TOKEN_QUERY_DUPLICATE_CALENDARS
              | TOKEN_QUERY_ATTENDEES | TOKEN_QUERY_CALENDARS | TOKEN_QUERY_EVENT
              | TOKEN_QUERY_REMINDERS | TOKEN_QUERY_VISIBLE_CALENDARS;
      /// M: Insert token for create exception event @{
      private static final int TOKEN_INSERT_EXCEPTION_EVENT = 1 << 6;
      /// @}
  
      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.DISPLAY_COLOR,        // 11 If SDK < 16, set to Calendars.CALENDAR_COLOR.
          Events.HAS_ATTENDEE_DATA,    // 12
          Events.ORGANIZER,            // 13
          Events.HAS_ALARM,            // 14
          Calendars.MAX_REMINDERS,     //15
          Calendars.ALLOWED_REMINDERS, // 16
          Events.CUSTOM_APP_PACKAGE,   // 17
          Events.CUSTOM_APP_URI,       // 18
          Events.ORIGINAL_SYNC_ID,     // 19 do not remove; used in DeleteEventHelper
          Calendars.ACCOUNT_TYPE,      // M: 20 
      };
      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_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_COLOR = 11;
      private static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 12;
      private static final int EVENT_INDEX_ORGANIZER = 13;
      private static final int EVENT_INDEX_HAS_ALARM = 14;
      private static final int EVENT_INDEX_MAX_REMINDERS = 15;
      private static final int EVENT_INDEX_ALLOWED_REMINDERS = 16;
      private static final int EVENT_INDEX_CUSTOM_APP_PACKAGE = 17;
      private static final int EVENT_INDEX_CUSTOM_APP_URI = 18;
  
      private static final String[] ATTENDEES_PROJECTION = new String[] {
          Attendees._ID,                      // 0
          Attendees.ATTENDEE_NAME,            // 1
          Attendees.ATTENDEE_EMAIL,           // 2
          Attendees.ATTENDEE_RELATIONSHIP,    // 3
          Attendees.ATTENDEE_STATUS,          // 4
          Attendees.ATTENDEE_IDENTITY,        // 5
          Attendees.ATTENDEE_ID_NAMESPACE     // 6
      };
      private static final int ATTENDEES_INDEX_ID = 0;
      private static final int ATTENDEES_INDEX_NAME = 1;
      private static final int ATTENDEES_INDEX_EMAIL = 2;
      private static final int ATTENDEES_INDEX_RELATIONSHIP = 3;
      private static final int ATTENDEES_INDEX_STATUS = 4;
      private static final int ATTENDEES_INDEX_IDENTITY = 5;
      private static final int ATTENDEES_INDEX_ID_NAMESPACE = 6;
  
      static {
          if (!Utils.isJellybeanOrLater()) {
              EVENT_PROJECTION[EVENT_INDEX_COLOR] = Calendars.CALENDAR_COLOR;
              EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_PACKAGE] = Events._ID; // dummy value
              EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_URI] = Events._ID; // dummy value
  
              ATTENDEES_PROJECTION[ATTENDEES_INDEX_IDENTITY] = Attendees._ID; // dummy value
              ATTENDEES_PROJECTION[ATTENDEES_INDEX_ID_NAMESPACE] = Attendees._ID; // dummy value
          }
      }
  
      private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
  
      private static final String ATTENDEES_SORT_ORDER = Attendees.ATTENDEE_NAME + " ASC, "
              + Attendees.ATTENDEE_EMAIL + " ASC";
  
      private static final String[] REMINDERS_PROJECTION = new String[] {
          Reminders._ID,                      // 0
          Reminders.MINUTES,            // 1
          Reminders.METHOD           // 2
      };
      private static final int REMINDERS_INDEX_ID = 0;
      private static final int REMINDERS_MINUTES_ID = 1;
      private static final int REMINDERS_METHOD_ID = 2;
  
      private static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=?";
  
      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
      };
      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 String CALENDARS_WHERE = Calendars._ID + "=?";
      static final String CALENDARS_DUPLICATE_NAME_WHERE = Calendars.CALENDAR_DISPLAY_NAME + "=?";
      static final String CALENDARS_VISIBLE_WHERE = Calendars.VISIBLE + "=?";
  
      private static final String NANP_ALLOWED_SYMBOLS = "()+-*#.";
      private static final int NANP_MIN_DIGITS = 7;
      private static final int NANP_MAX_DIGITS = 11;
  
  
      private View mView;
  
      private Uri mUri;
      private long mEventId;
      private Cursor mEventCursor;
      private Cursor mAttendeesCursor;
      private Cursor mCalendarsCursor;
      private Cursor mRemindersCursor;
  
      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 mHasAttendeeData;
      private String mEventOrganizerEmail;
      private String mEventOrganizerDisplayName = "";
      private boolean mIsOrganizer;
      private long mCalendarOwnerAttendeeId = EditEventHelper.ATTENDEE_ID_NONE;
      private boolean mOwnerCanRespond;
      private String mSyncAccountName;
      private String mCalendarOwnerAccount;
      private boolean mCanModifyCalendar;
      private boolean mCanModifyEvent;
      private boolean mIsBusyFreeCalendar;
      private int mNumOfAttendees;
      private EditResponseHelper mEditResponseHelper;
      private boolean mDeleteDialogVisible = false;
      private DeleteEventHelper mDeleteHelper;
  
      private int mOriginalAttendeeResponse;
      private int mAttendeeResponseFromIntent = Attendees.ATTENDEE_STATUS_NONE;
      private int mUserSetResponse = Attendees.ATTENDEE_STATUS_NONE;
      private boolean mIsRepeating;
      private boolean mHasAlarm;
      private int mMaxReminders;
      private String mCalendarAllowedReminders;
      // 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 ExpandableTextView mDesc;
      private AttendeesView mLongAttendees;
      private Button emailAttendeesButton;
      private Menu mMenu = null;
      private View mHeadlines;
      private ScrollView mScrollView;
      private View mLoadingMsgView;
      private ObjectAnimator mAnimateAlpha;
      private long mLoadingMsgStartTime;
      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
      /** M: Use for save event in DB while user exit this activity, while don't save when
       * rotate phone, however, we save some states like "reminders" "response" to bundle @{
       */
      private boolean mNeedSaveEvent = false;
      private ArrayList<ReminderEntry> mSavedReminders;
      /** @} */
  
      /// M: checkstyle @{
      private static final Pattern WILDCARD_PATTERN = Pattern.compile("^.*$");
      /// @}
  
      ArrayList<Attendee> mAcceptedAttendees = new ArrayList<Attendee>();
      ArrayList<Attendee> mDeclinedAttendees = new ArrayList<Attendee>();
      ArrayList<Attendee> mTentativeAttendees = new ArrayList<Attendee>();
      ArrayList<Attendee> mNoResponseAttendees = new ArrayList<Attendee>();
      ArrayList<String> mToEmails = new ArrayList<String>();
      ArrayList<String> mCcEmails = new ArrayList<String>();
      private int mColor;
  
  
      private int mDefaultReminderMinutes;
      private final ArrayList<LinearLayout> mReminderViews = new ArrayList<LinearLayout>(0);
      public ArrayList<ReminderEntry> mReminders;
      public ArrayList<ReminderEntry> mOriginalReminders = new ArrayList<ReminderEntry>();
      public ArrayList<ReminderEntry> mUnsupportedReminders = new ArrayList<ReminderEntry>();
      private boolean mUserModifiedReminders = false;
  
      /**
       * Contents of the "minutes" spinner.  This has default values from the XML file, augmented
       * with any additional values that were already associated with the event.
       */
      private ArrayList<Integer> mReminderMinuteValues;
      private ArrayList<String> mReminderMinuteLabels;
  
      /**
       * Contents of the "methods" spinner.  The "values" list specifies the method constant
       * (e.g. {@link Reminders#METHOD_ALERT}) associated with the labels.  Any methods that
       * aren't allowed by the Calendar will be removed.
       */
      private ArrayList<Integer> mReminderMethodValues;
      private ArrayList<String> mReminderMethodLabels;
  
      private QueryHandler mHandler;
  
  
      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 OnItemSelectedListener mReminderChangeListener;
  
      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 class QueryHandler extends AsyncQueryService {
          public QueryHandler(Context context) {
              super(context);
          }
  
          @Override
          protected void onBatchComplete(int token, Object cookie, ContentProviderResult[] results) {
              super.onBatchComplete(token, cookie, results);
              /// M: we save reminders for single update repeating event here, cause we can only
              // get right event id here
              if (token == TOKEN_INSERT_EXCEPTION_EVENT) {
                  if (results == null || results.length == 0) {
                      Log.e(TAG, "Create exception event fail: no results returned");
                      return;
                  }
                  Uri uri = results[0].uri;
                  if (uri == null) {
                      Log.e(TAG, "Create exception event fail: uri = null");
                      return;
                  }
                  Log.d(TAG, "Create exception event success: " + uri);
                  try {
                      long eventId = Long.parseLong(uri.getLastPathSegment());
                      saveRemindersForEvent(eventId);
                  } catch (NumberFormatException nfe) {
                      Log.e(TAG, nfe.toString());
                  }
              }
          }
  
          @Override
          protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
              // if the activity is finishing, then close the cursor and return
              final Activity activity = getActivity();
              if (activity == null || activity.isFinishing()) {
                  if (cursor != null) {
                      cursor.close();
                  }
                  return;
              }
  
              switch (token) {
              case TOKEN_QUERY_EVENT:
                  mEventCursor = Utils.matrixCursorFromCursor(cursor);
                  if (initEventCursor()) {
                      // The cursor is empty. This can happen if the event was
                      // deleted.
                      // FRAG_TODO we should no longer rely on Activity.finish()
                      activity.finish();
                      return;
                  }
                  updateEvent(mView);
                  prepareReminders();
  
                  // start calendar query
                  Uri uri = Calendars.CONTENT_URI;
                  String[] args = new String[] {
                          Long.toString(mEventCursor.getLong(EVENT_INDEX_CALENDAR_ID))};
                  startQuery(TOKEN_QUERY_CALENDARS, null, uri, CALENDARS_PROJECTION,
                          CALENDARS_WHERE, args, null);
                  break;
              case TOKEN_QUERY_CALENDARS:
                  mCalendarsCursor = Utils.matrixCursorFromCursor(cursor);
                  updateCalendar(mView);
                  // FRAG_TODO fragments shouldn't set the title anymore
                  updateTitle();
  
                  if (!mIsBusyFreeCalendar) {
                      args = new String[] { Long.toString(mEventId) };
  
                      // start attendees query
                      uri = Attendees.CONTENT_URI;
                      startQuery(TOKEN_QUERY_ATTENDEES, null, uri, ATTENDEES_PROJECTION,
                              ATTENDEES_WHERE, args, ATTENDEES_SORT_ORDER);
                  } else {
                      sendAccessibilityEventIfQueryDone(TOKEN_QUERY_ATTENDEES);
                  }
                  /** M: We restore reminders from saved reminders preferentially @{ */
                  if (mSavedReminders != null) {
                      restoreReminders(mSavedReminders);
                      mSavedReminders = null;
                      sendAccessibilityEventIfQueryDone(TOKEN_QUERY_REMINDERS);
                  } else {
                      if (mHasAlarm) {
                          // start reminders query
                          args = new String[] { Long.toString(mEventId) };
                          uri = Reminders.CONTENT_URI;
                          startQuery(TOKEN_QUERY_REMINDERS, null, uri,
                                  REMINDERS_PROJECTION, REMINDERS_WHERE, args, null);
                      } else {
                          sendAccessibilityEventIfQueryDone(TOKEN_QUERY_REMINDERS);
                      }
                  }
                  /** @} */
                  break;
              case TOKEN_QUERY_ATTENDEES:
                  mAttendeesCursor = Utils.matrixCursorFromCursor(cursor);
                  initAttendeesCursor(mView);
                  updateResponse(mView);
                  break;
              case TOKEN_QUERY_REMINDERS:
                  mRemindersCursor = Utils.matrixCursorFromCursor(cursor);
                  initReminders(mView, mRemindersCursor);
                  break;
              case TOKEN_QUERY_VISIBLE_CALENDARS:
                  if (cursor.getCount() > 1) {
                      // Start duplicate calendars query to detect whether to add the calendar
                      // email to the calendar owner display.
                      String displayName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
                      mHandler.startQuery(TOKEN_QUERY_DUPLICATE_CALENDARS, null,
                              Calendars.CONTENT_URI, CALENDARS_PROJECTION,
                              CALENDARS_DUPLICATE_NAME_WHERE, new String[] {displayName}, null);
                  } else {
                      // Don't need to display the calendar owner when there is only a single
                      // calendar.  Skip the duplicate calendars query.
                      setVisibilityCommon(mView, R.id.calendar_container, View.GONE);
                      mCurrentQuery |= TOKEN_QUERY_DUPLICATE_CALENDARS;
                  }
                  break;
              case TOKEN_QUERY_DUPLICATE_CALENDARS:
                  Resources res = activity.getResources();
                  SpannableStringBuilder sb = new SpannableStringBuilder();
  
                  // Calendar display name
                  String calendarName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
                  sb.append(calendarName);
  
                  // Show email account if display name is not unique and
                  // display name != email
                  String email = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
                  if (cursor.getCount() > 1 && !calendarName.equalsIgnoreCase(email) &&
                          Utils.isValidEmail(email)) {
                      sb.append(" (").append(email).append(")");
                  }
  
                  setVisibilityCommon(mView, R.id.calendar_container, View.VISIBLE);
                  setTextCommon(mView, R.id.calendar_name, sb);
                  break;
              }
              cursor.close();
              sendAccessibilityEventIfQueryDone(token);
  
              // All queries are done, show the view.
              if (mCurrentQuery == TOKEN_QUERY_ALL) {
                  if (mLoadingMsgView.getAlpha() == 1) {
                      // Loading message is showing, let it stay a bit more (to prevent
                      // flashing) by adding a start delay to the event animation
                      long timeDiff = LOADING_MSG_MIN_DISPLAY_TIME - (System.currentTimeMillis() -
                              mLoadingMsgStartTime);
                      if (timeDiff > 0) {
                          mAnimateAlpha.setStartDelay(timeDiff);
                      }
                  }
                  if (!mAnimateAlpha.isRunning() &&!mAnimateAlpha.isStarted() && !mNoCrossFade) {
                      mAnimateAlpha.start();
                  } else {
                      mScrollView.setAlpha(1);
                      mLoadingMsgView.setVisibility(View.GONE);
                  }
              }
          }
      }
  
      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);
  
          mReminderChangeListener = new OnItemSelectedListener() {
              @Override
              public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                  Integer prevValue = (Integer) parent.getTag();
                  if (prevValue == null || prevValue != position) {
                      parent.setTag(position);
                      mUserModifiedReminders = true;
                  }
              }
  
              @Override
              public void onNothingSelected(AdapterView<?> parent) {
                  // do nothing
              }
  
          };
  
          if (savedInstanceState != null) {
              mIsDialog = savedInstanceState.getBoolean(BUNDLE_KEY_IS_DIALOG, false);
              mWindowStyle = savedInstanceState.getInt(BUNDLE_KEY_WINDOW_STYLE,
                      DIALOG_WINDOW_STYLE);
          }
  
          if (mIsDialog) {
              applyDialogParams();
          }
          mContext = getActivity();
      }
  
      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) {
          // If this is not a repeating event, then don't display the dialog
          // asking which events to change.
          mUserSetResponse = getResponseFromButtonId(checkedId);
          if (!mIsRepeating) {
              return;
          }
  
          // If the selection is the same as the original, then don't display the
          // dialog asking which events to change.
          if (checkedId == findButtonIdForResponse(mOriginalAttendeeResponse)) {
              return;
          }
  
          // This is a repeating event. We need to ask the user if they mean to
          // change just this one instance or all instances.
          mEditResponseHelper.showDialog(mEditResponseHelper.getWhichEvents());
      }
  
      public void onNothingSelected(AdapterView<?> parent) {
      }
  
      @Override
      public void onAttach(Activity activity) {
          super.onAttach(activity);
          mActivity = activity;
          mEditResponseHelper = new EditResponseHelper(activity);
  
          if (mAttendeeResponseFromIntent != Attendees.ATTENDEE_STATUS_NONE) {
              mEditResponseHelper.setWhichEvents(UPDATE_ALL);
          }
          mHandler = new QueryHandler(activity);
          if (!mIsDialog) {
              setHasOptionsMenu(true);
          }
      }
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
              Bundle savedInstanceState) {
  
          if (savedInstanceState != null) {
              mIsDialog = savedInstanceState.getBoolean(BUNDLE_KEY_IS_DIALOG, false);
              mWindowStyle = savedInstanceState.getInt(BUNDLE_KEY_WINDOW_STYLE,
                      DIALOG_WINDOW_STYLE);
              mDeleteDialogVisible =
                  savedInstanceState.getBoolean(BUNDLE_KEY_DELETE_DIALOG_VISIBLE,false);
              /** M: read the saved state @{ */
              mUserSetResponse = savedInstanceState.getInt(BUNDLE_KEY_ATTENDEE_RESPONSE,
                      Attendees.ATTENDEE_STATUS_NONE);
              mEditResponseHelper.setWhichEvents(savedInstanceState.getInt(BUNDLE_KEY_WHICH_EVENTS, UPDATE_ALL));
              mOriginalReminders = (ArrayList<ReminderEntry>) savedInstanceState.getSerializable(
                      BUNDLE_KEY_ORIGINAL_REMINDERS);
              mSavedReminders = (ArrayList<ReminderEntry>) savedInstanceState.getSerializable(BUNDLE_KEY_REMINDERS);
              /** @} */
              /**M:**/
              mWhichDelete = savedInstanceState.getInt(BUNDLE_KEY_WHICH_DELETE, -1);
          }
  
          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);
          mTitle = (TextView) mView.findViewById(R.id.title);
          mWhenDateTime = (TextView) mView.findViewById(R.id.when_datetime);
          mWhere = (TextView) mView.findViewById(R.id.where);
          mDesc = (ExpandableTextView) mView.findViewById(R.id.description);
          mHeadlines = mView.findViewById(R.id.event_info_headline);
          mLongAttendees = (AttendeesView)mView.findViewById(R.id.long_attendee_list);
          mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config);
  
          if (mUri == null) {
              // restore event ID from bundle
              mEventId = savedInstanceState.getLong(BUNDLE_KEY_EVENT_ID);
              mUri = ContentUris.withAppendedId(Events.CONTENT_URI, mEventId);
              mStartMillis = savedInstanceState.getLong(BUNDLE_KEY_START_MILLIS);
              mEndMillis = savedInstanceState.getLong(BUNDLE_KEY_END_MILLIS);
          }
  
          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);
          mLoadingMsgView.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY);
  
          // start loading the data
  
          mHandler.startQuery(TOKEN_QUERY_EVENT, null, mUri, EVENT_PROJECTION,
                  null, null, null);
  
          View b = mView.findViewById(R.id.delete);
          b.setOnClickListener(new OnClickListener() {
              @Override
              public void onClick(View v) {
                  if (!mCanModifyCalendar) {
                      return;
                  }
                  mDeleteHelper =
                          new DeleteEventHelper(mContext, mActivity, !mIsDialog && !mIsTabletConfig /* exitWhenDone */);
                  mDeleteHelper.setDeleteNotificationListener(EventInfoFragment.this);
                  mDeleteHelper.setOnDismissListener(createDeleteOnDismissListener());
                  mDeleteDialogVisible = true;
                  mDeleteHelper.delete(mStartMillis, mEndMillis, mEventId, -1, onDeleteRunnable);
              }
          });
          
          ///M: Add a "share icon". For Tablet only. @{
          if (mIsTabletConfig) {
              View c = mView.findViewById(R.id.share);
              c.setOnClickListener(new OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      MTKUtils.sendShareEvent(mContext, mEventId);
                  }
              });
          }
          ///@} 
  
          // 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);
          }
  
          // Create a listener for the email guests button
          emailAttendeesButton = (Button) mView.findViewById(R.id.email_attendees_button);
          if (emailAttendeesButton != null) {
              emailAttendeesButton.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      emailAttendees();
                  }
              });
          }
  
          // Create a listener for the add reminder button
          mAddReminderBtn = (Button)mView.findViewById(R.id.reminder_add);
          View.OnClickListener addReminderOnClickListener = new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  addReminder();
                  mUserModifiedReminders = true;
              }
          };
          mAddReminderBtn.setOnClickListener(addReminderOnClickListener);
  
          // Set reminders variables
  
          SharedPreferences prefs = GeneralPreferences.getSharedPreferences(mActivity);
          String defaultReminderString = prefs.getString(
                  GeneralPreferences.KEY_DEFAULT_REMINDER, GeneralPreferences.NO_REMINDER_STRING);
          mDefaultReminderMinutes = Integer.parseInt(defaultReminderString);
          prepareReminders();
  
          return mView;
      }
  
      private final Runnable onDeleteRunnable = new Runnable() {
          @Override
          public void run() {
              if (EventInfoFragment.this.mIsPaused) {
                  mDismissOnResume = true;
                  return;
              }
              if (EventInfoFragment.this.isVisible()) {
                  EventInfoFragment.this.dismiss();
              }
          }
      };
  
      private void updateTitle() {
          Resources res = getActivity().getResources();
          if (mCanModifyCalendar && !mIsOrganizer) {
              getActivity().setTitle(res.getString(R.string.event_info_title_invite));
          } else {
              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 true if the cursor is empty.
       */
      private boolean initEventCursor() {
          if ((mEventCursor == null) || (mEventCursor.getCount() == 0)) {
              return true;
          }
          mEventCursor.moveToFirst();
          mEventId = mEventCursor.getInt(EVENT_INDEX_ID);
          String rRule = mEventCursor.getString(EVENT_INDEX_RRULE);
          mIsRepeating = !TextUtils.isEmpty(rRule);
          mHasAlarm = (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) == 1)?true:false;
          mMaxReminders = mEventCursor.getInt(EVENT_INDEX_MAX_REMINDERS);
          mCalendarAllowedReminders =  mEventCursor.getString(EVENT_INDEX_ALLOWED_REMINDERS);
          return false;
      }
  
      @SuppressWarnings("fallthrough")
      private void initAttendeesCursor(View view) {
          mOriginalAttendeeResponse = Attendees.ATTENDEE_STATUS_NONE;
          mCalendarOwnerAttendeeId = EditEventHelper.ATTENDEE_ID_NONE;
          mNumOfAttendees = 0;
          if (mAttendeesCursor != null) {
              mNumOfAttendees = mAttendeesCursor.getCount();
              if (mAttendeesCursor.moveToFirst()) {
                  mAcceptedAttendees.clear();
                  mDeclinedAttendees.clear();
                  mTentativeAttendees.clear();
                  mNoResponseAttendees.clear();
  
                  do {
                      int status = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
                      String name = mAttendeesCursor.getString(ATTENDEES_INDEX_NAME);
                      String email = mAttendeesCursor.getString(ATTENDEES_INDEX_EMAIL);
  
                      if (mAttendeesCursor.getInt(ATTENDEES_INDEX_RELATIONSHIP) ==
                              Attendees.RELATIONSHIP_ORGANIZER) {
  
                          // Overwrites the one from Event table if available
                          if (!TextUtils.isEmpty(name)) {
                              mEventOrganizerDisplayName = name;
                              if (!mIsOrganizer) {
                                  setVisibilityCommon(view, R.id.organizer_container, View.VISIBLE);
                                  setTextCommon(view, R.id.organizer, mEventOrganizerDisplayName);
                              }
                          }
                      }
  
                      if (mCalendarOwnerAttendeeId == EditEventHelper.ATTENDEE_ID_NONE &&
                              mCalendarOwnerAccount.equalsIgnoreCase(email)) {
                          mCalendarOwnerAttendeeId = mAttendeesCursor.getInt(ATTENDEES_INDEX_ID);
                          mOriginalAttendeeResponse = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
                      } else {
                          String identity = null;
                          String idNamespace = null;
  
                          if (Utils.isJellybeanOrLater()) {
                              identity = mAttendeesCursor.getString(ATTENDEES_INDEX_IDENTITY);
                              idNamespace = mAttendeesCursor.getString(ATTENDEES_INDEX_ID_NAMESPACE);
                          }
  
                          // Don't show your own status in the list because:
                          //  1) it doesn't make sense for event without other guests.
                          //  2) there's a spinner for that for events with guests.
                          switch(status) {
                              case Attendees.ATTENDEE_STATUS_ACCEPTED:
                                  mAcceptedAttendees.add(new Attendee(name, email,
                                          Attendees.ATTENDEE_STATUS_ACCEPTED, identity,
                                          idNamespace));
                                  break;
                              case Attendees.ATTENDEE_STATUS_DECLINED:
                                  mDeclinedAttendees.add(new Attendee(name, email,
                                          Attendees.ATTENDEE_STATUS_DECLINED, identity,
                                          idNamespace));
                                  break;
                              case Attendees.ATTENDEE_STATUS_TENTATIVE:
                                  mTentativeAttendees.add(new Attendee(name, email,
                                          Attendees.ATTENDEE_STATUS_TENTATIVE, identity,
                                          idNamespace));
                                  break;
                              default:
                                  mNoResponseAttendees.add(new Attendee(name, email,
                                          Attendees.ATTENDEE_STATUS_NONE, identity,
                                          idNamespace));
                          }
                      }
                  } while (mAttendeesCursor.moveToNext());
                  mAttendeesCursor.moveToFirst();
  
                  updateAttendees(view);
              }
          }
      }
  
      @Override
      public void onSaveInstanceState(Bundle outState) {
          outState.putLong(BUNDLE_KEY_EVENT_ID, mEventId);
          outState.putLong(BUNDLE_KEY_START_MILLIS, mStartMillis);
          outState.putLong(BUNDLE_KEY_END_MILLIS, mEndMillis);
          outState.putBoolean(BUNDLE_KEY_IS_DIALOG, mIsDialog);
          outState.putInt(BUNDLE_KEY_WINDOW_STYLE, mWindowStyle);
          outState.putBoolean(BUNDLE_KEY_DELETE_DIALOG_VISIBLE, mDeleteDialogVisible);
          /** Read states from UI and save instance @{ */
          int response;
          if (mUserSetResponse != Attendees.ATTENDEE_STATUS_NONE) {
              response = mUserSetResponse;
          } else if (mAttendeeResponseFromIntent != Attendees.ATTENDEE_STATUS_NONE) {
              response = mAttendeeResponseFromIntent;
          } else {
              response = mOriginalAttendeeResponse;
          }
          outState.putInt(BUNDLE_KEY_ATTENDEE_RESPONSE, response);
          outState.putInt(BUNDLE_KEY_WHICH_EVENTS, mEditResponseHelper.getWhichEvents());
          outState.putSerializable(BUNDLE_KEY_ORIGINAL_REMINDERS, mOriginalReminders);
          mReminders = EventViewUtils.reminderItemsToReminders(mReminderViews,
                  mReminderMinuteValues, mReminderMethodValues);
          outState.putSerializable(BUNDLE_KEY_REMINDERS, mReminders);
          /** @} */
          /** M: **/
          outState.putInt(BUNDLE_KEY_WHICH_DELETE, mWhichDelete);
          /**M: call super at last*/
          super.onSaveInstanceState(outState);
      }
  
  
      @Override
      public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
          super.onCreateOptionsMenu(menu, inflater);
          // Show 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;
              updateMenu();
              ///M: #extension# @{
              mOptionsMenuExt = ExtensionFactory.getEventInfoOptionsMenuExt(getActivity(), mEventId);
              mOptionsMenuExt.onCreateOptionsMenu(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) {
              ///M: @{
              if(saveReminders()){
                 Toast.makeText(getActivity(), R.string.saving_event, Toast.LENGTH_SHORT).show();
              }
              ///@}
              Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, mEventId);
              Intent intent = new Intent(Intent.ACTION_EDIT, uri);
              intent.putExtra(EXTRA_EVENT_BEGIN_TIME, mStartMillis);
              intent.putExtra(EXTRA_EVENT_END_TIME, mEndMillis);
              intent.putExtra(EXTRA_EVENT_ALL_DAY, mAllDay);
              intent.setClass(mActivity, EditEventActivity.class);
              intent.putExtra(EVENT_EDIT_ON_LAUNCH, true);
              startActivity(intent);
              mActivity.finish();
          } else if (itemId == R.id.info_action_delete) {
              mDeleteHelper =
                      new DeleteEventHelper(mActivity, mActivity, true /* exitWhenDone */);
              mDeleteHelper.setDeleteNotificationListener(EventInfoFragment.this);
              mDeleteHelper.setOnDismissListener(createDeleteOnDismissListener());
              mDeleteDialogVisible = true;
              mDeleteHelper.delete(mStartMillis, mEndMillis, mEventId, -1, onDeleteRunnable);
          /**
           * M: #extension# Show edit/delete buttons only in non-dialog configuration @{
           */
          } else if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE){
              mOptionsMenuExt.onOptionsItemSelected(item.getItemId());
          /** @} */
          }
          return super.onOptionsItemSelected(item);
      }
  
      @Override
      public void onDestroyView() {
          /** M: only save event when it's really necessary @{ */
          if (!mEventDeletionStarted && mNeedSaveEvent) {
              mNeedSaveEvent = false;
              boolean responseSaved = saveResponse();
              /// M: If save response for repeating events is udpate_single, we have to
              // try to save reminders for new exception event in onInsertComplete, and toast,
              // cause saveReminders() will use mEventId
              if (responseSaved && mIsRepeating
                      && mEditResponseHelper.getWhichEvents() == UPDATE_SINGLE) {
                  // Read reminders from UI
                  mReminders = EventViewUtils.reminderItemsToReminders(mReminderViews,
                          mReminderMinuteValues, mReminderMethodValues);
                  Toast.makeText(getActivity(), R.string.saving_event, Toast.LENGTH_SHORT).show();
              } else {
                  boolean remindersSaved = saveReminders();
                  if (remindersSaved || responseSaved) {
                      Toast.makeText(getActivity(), R.string.saving_event, Toast.LENGTH_SHORT).show();
                  }
              }
          }
          /** @} */
          super.onDestroyView();
      }
  
      @Override
      public void onDestroy() {
          if (mEventCursor != null) {
              mEventCursor.close();
          }
          if (mCalendarsCursor != null) {
              mCalendarsCursor.close();
          }
          if (mAttendeesCursor != null) {
              mAttendeesCursor.close();
          }
          super.onDestroy();
      }
  
      /**
       * Asynchronously saves the response to an invitation if the user changed
       * the response. Returns true if the database will be updated.
       *
       * @return true if the database will be changed
       */
      private boolean saveResponse() {
          if (mAttendeesCursor == null || mEventCursor == null) {
              return false;
          }
  
          RadioGroup radioGroup = (RadioGroup) getView().findViewById(R.id.response_value);
          int status = getResponseFromButtonId(radioGroup.getCheckedRadioButtonId());
          if (status == Attendees.ATTENDEE_STATUS_NONE) {
              return false;
          }
  
          // If the status has not changed, then don't update the database
          if (status == mOriginalAttendeeResponse) {
              return false;
          }
  
          // If we never got an owner attendee id we can't set the status
          if (mCalendarOwnerAttendeeId == EditEventHelper.ATTENDEE_ID_NONE) {
              return false;
          }
  
          if (!mIsRepeating) {
              // This is a non-repeating event
              updateResponse(mEventId, mCalendarOwnerAttendeeId, status);
              return true;
          }
  
          // This is a repeating event
          int whichEvents = mEditResponseHelper.getWhichEvents();
          switch (whichEvents) {
              case -1:
                  return false;
              case UPDATE_SINGLE:
                  createExceptionResponse(mEventId, status);
                  return true;
              case UPDATE_ALL:
                  updateResponse(mEventId, mCalendarOwnerAttendeeId, status);
                  return true;
              default:
                  Log.e(TAG, "Unexpected choice for updating invitation response");
                  break;
          }
          return false;
      }
  
      private void updateResponse(long eventId, long attendeeId, int status) {
          // Update the attendee status in the attendees table.  the provider
          // takes care of updating the self attendance status.
          ContentValues values = new ContentValues();
  
          if (!TextUtils.isEmpty(mCalendarOwnerAccount)) {
              values.put(Attendees.ATTENDEE_EMAIL, mCalendarOwnerAccount);
          }
          values.put(Attendees.ATTENDEE_STATUS, status);
          values.put(Attendees.EVENT_ID, eventId);
  
          Uri uri = ContentUris.withAppendedId(Attendees.CONTENT_URI, attendeeId);
  
          mHandler.startUpdate(mHandler.getNextToken(), null, uri, values,
                  null, null, Utils.UNDO_DELAY);
      }
  
      /**
       * 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());
  
          /// M: use special token @{
          mHandler.startBatch(TOKEN_INSERT_EXCEPTION_EVENT, null, CalendarContract.AUTHORITY, ops,
                  Utils.UNDO_DELAY);
          /// @}
     }
  
      public static int getResponseFromButtonId(int buttonId) {
          int response;
          if (buttonId == R.id.response_yes) {
              response = Attendees.ATTENDEE_STATUS_ACCEPTED;
          } else if (buttonId == R.id.response_maybe) {
              response = Attendees.ATTENDEE_STATUS_TENTATIVE;
          } else if (buttonId == R.id.response_no) {
              response = Attendees.ATTENDEE_STATUS_DECLINED;
          } else {
              response = Attendees.ATTENDEE_STATUS_NONE;
          }
          return response;
      }
  
      public static int findButtonIdForResponse(int response) {
          int buttonId;
          switch (response) {
              case Attendees.ATTENDEE_STATUS_ACCEPTED:
                  buttonId = R.id.response_yes;
                  break;
              case Attendees.ATTENDEE_STATUS_TENTATIVE:
                  buttonId = R.id.response_maybe;
                  break;
              case Attendees.ATTENDEE_STATUS_DECLINED:
                  buttonId = R.id.response_no;
                  break;
                  default:
                      buttonId = -1;
          }
          return buttonId;
      }
  
      private void doEdit() {
          Context c = getActivity();
          // This ensures that we aren't in the process of closing and have been
          // unattached already
          if (c != null) {
              CalendarController.getInstance(c).sendEventRelatedEvent(
                      this, EventType.EDIT_EVENT, mEventId, mStartMillis, mEndMillis, 0
                      , 0, -1);
          }
      }
  
      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);
          }
  
          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);
  
          mColor = Utils.getDisplayColorFromColor(mEventCursor.getInt(EVENT_INDEX_COLOR));
          mHeadlines.setBackgroundColor(mColor);
  
          // 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);
          }
  
          // Display the repeat string (if any)
          String repeatString = null;
          if (!TextUtils.isEmpty(rRule)) {
              EventRecurrence eventRecurrence = new EventRecurrence();
              eventRecurrence.parse(rRule);
              Time date = new Time(localTimezone);
              date.set(mStartMillis);
              if (mAllDay) {
                  date.timezone = Time.TIMEZONE_UTC;
              }
              eventRecurrence.setStartDate(date);
              repeatString = EventRecurrenceFormatter.getRepeatString(resources, eventRecurrence);
          }
          if (repeatString == null) {
              view.findViewById(R.id.when_repeat).setVisibility(View.GONE);
          } else {
              setTextCommon(view, R.id.when_repeat, repeatString);
          }
  
          // 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.setAutoLinkMask(0);
                  textView.setText(location.trim());
                  try {
                      linkifyTextView(textView);
                  } catch (Exception ex) {
                      // unexpected
                      Log.e(TAG, "Linkification failed", ex);
                  }
  
                  textView.setOnTouchListener(new OnTouchListener() {
                      @Override
                      public boolean onTouch(View v, MotionEvent event) {
                          try {
                              return v.onTouchEvent(event);
                          } catch (ActivityNotFoundException e) {
                              // ignore
                              return true;
                          }
                      }
                  });
              }
          }
  
          // Description
  