Home | History | Annotate | Download | only in calendar
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.calendar;
     18 
     19 import com.android.calendar.event.EditEventHelper;
     20 import com.android.common.Rfc822Validator;
     21 
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.SharedPreferences;
     25 import android.provider.CalendarContract.Attendees;
     26 import android.provider.CalendarContract.Calendars;
     27 import android.provider.CalendarContract.Events;
     28 import android.provider.CalendarContract.Reminders;
     29 import android.text.TextUtils;
     30 import android.text.util.Rfc822Token;
     31 
     32 import java.io.Serializable;
     33 import java.util.ArrayList;
     34 import java.util.Collections;
     35 import java.util.LinkedHashMap;
     36 import java.util.LinkedHashSet;
     37 import java.util.TimeZone;
     38 
     39 /**
     40  * Stores all the information needed to fill out an entry in the events table.
     41  * This is a convenient way for storing information needed by the UI to write to
     42  * the events table. Only fields that are important to the UI are included.
     43  */
     44 public class CalendarEventModel implements Serializable {
     45     private static final String TAG = "CalendarEventModel";
     46 
     47     public static class Attendee implements Serializable {
     48         @Override
     49         public int hashCode() {
     50             return (mEmail == null) ? 0 : mEmail.hashCode();
     51         }
     52 
     53         @Override
     54         public boolean equals(Object obj) {
     55             if (this == obj) {
     56                 return true;
     57             }
     58             if (!(obj instanceof Attendee)) {
     59                 return false;
     60             }
     61             Attendee other = (Attendee) obj;
     62             if (!TextUtils.equals(mEmail, other.mEmail)) {
     63                 return false;
     64             }
     65             return true;
     66         }
     67 
     68         String getDisplayName() {
     69             if (TextUtils.isEmpty(mName)) {
     70                 return mEmail;
     71             } else {
     72                 return mName;
     73             }
     74         }
     75 
     76         public String mName;
     77         public String mEmail;
     78         public int mStatus;
     79         public String mIdentity;
     80         public String mIdNamespace;
     81 
     82         public Attendee(String name, String email) {
     83             this(name, email, Attendees.ATTENDEE_STATUS_NONE, null, null);
     84         }
     85         public Attendee(String name, String email, int status, String identity,
     86                 String idNamespace) {
     87             mName = name;
     88             mEmail = email;
     89             mStatus = status;
     90             mIdentity = identity;
     91             mIdNamespace = idNamespace;
     92         }
     93     }
     94 
     95     /**
     96      * A single reminder entry.
     97      *
     98      * Instances of the class are immutable.
     99      */
    100     public static class ReminderEntry implements Comparable<ReminderEntry>, Serializable {
    101         private final int mMinutes;
    102         private final int mMethod;
    103 
    104         /**
    105          * Returns a new ReminderEntry, with the specified minutes and method.
    106          *
    107          * @param minutes Number of minutes before the start of the event that the alert will fire.
    108          * @param method Type of alert ({@link Reminders#METHOD_ALERT}, etc).
    109          */
    110         public static ReminderEntry valueOf(int minutes, int method) {
    111             // TODO: cache common instances
    112             return new ReminderEntry(minutes, method);
    113         }
    114 
    115         /**
    116          * Returns a ReminderEntry, with the specified number of minutes and a default alert method.
    117          *
    118          * @param minutes Number of minutes before the start of the event that the alert will fire.
    119          */
    120         public static ReminderEntry valueOf(int minutes) {
    121             return valueOf(minutes, Reminders.METHOD_DEFAULT);
    122         }
    123 
    124         /**
    125          * Constructs a new ReminderEntry.
    126          *
    127          * @param minutes Number of minutes before the start of the event that the alert will fire.
    128          * @param method Type of alert ({@link Reminders#METHOD_ALERT}, etc).
    129          */
    130         private ReminderEntry(int minutes, int method) {
    131             // TODO: error-check args
    132             mMinutes = minutes;
    133             mMethod = method;
    134         }
    135 
    136         @Override
    137         public int hashCode() {
    138             return mMinutes * 10 + mMethod;
    139         }
    140 
    141         @Override
    142         public boolean equals(Object obj) {
    143             if (this == obj) {
    144                 return true;
    145             }
    146             if (!(obj instanceof ReminderEntry)) {
    147                 return false;
    148             }
    149 
    150             ReminderEntry re = (ReminderEntry) obj;
    151 
    152             if (re.mMinutes != mMinutes) {
    153                 return false;
    154             }
    155 
    156             // Treat ALERT and DEFAULT as equivalent.  This is useful during the "has anything
    157             // "changed" test, so that if DEFAULT is present, but we don't change anything,
    158             // the internal conversion of DEFAULT to ALERT doesn't force a database update.
    159             return re.mMethod == mMethod ||
    160                 (re.mMethod == Reminders.METHOD_DEFAULT && mMethod == Reminders.METHOD_ALERT) ||
    161                 (re.mMethod == Reminders.METHOD_ALERT && mMethod == Reminders.METHOD_DEFAULT);
    162         }
    163 
    164         @Override
    165         public String toString() {
    166             return "ReminderEntry min=" + mMinutes + " meth=" + mMethod;
    167         }
    168 
    169         /**
    170          * Comparison function for a sort ordered primarily descending by minutes,
    171          * secondarily ascending by method type.
    172          */
    173         public int compareTo(ReminderEntry re) {
    174             if (re.mMinutes != mMinutes) {
    175                 return re.mMinutes - mMinutes;
    176             }
    177             if (re.mMethod != mMethod) {
    178                 return mMethod - re.mMethod;
    179             }
    180             return 0;
    181         }
    182 
    183         /** Returns the minutes. */
    184         public int getMinutes() {
    185             return mMinutes;
    186         }
    187 
    188         /** Returns the alert method. */
    189         public int getMethod() {
    190             return mMethod;
    191         }
    192     }
    193 
    194     // TODO strip out fields that don't ever get used
    195     /**
    196      * The uri of the event in the db. This should only be null for new events.
    197      */
    198     public String mUri = null;
    199     public long mId = -1;
    200     public long mCalendarId = -1;
    201     public String mCalendarDisplayName = ""; // Make sure this is in sync with the mCalendarId
    202     public int mCalendarColor = 0;
    203     public int mCalendarMaxReminders;
    204     public String mCalendarAllowedReminders;
    205     public String mCalendarAllowedAttendeeTypes;
    206     public String mCalendarAllowedAvailability;
    207 
    208     public String mSyncId = null;
    209     public String mSyncAccount = null;
    210     public String mSyncAccountType = null;
    211 
    212     // PROVIDER_NOTES owner account comes from the calendars table
    213     public String mOwnerAccount = null;
    214     public String mTitle = null;
    215     public String mLocation = null;
    216     public String mDescription = null;
    217     public String mRrule = null;
    218     public String mOrganizer = null;
    219     public String mOrganizerDisplayName = null;
    220     /**
    221      * Read-Only - Derived from other fields
    222      */
    223     public boolean mIsOrganizer = true;
    224     public boolean mIsFirstEventInSeries = true;
    225 
    226     // This should be set the same as mStart when created and is used for making changes to
    227     // recurring events. It should not be updated after it is initially set.
    228     public long mOriginalStart = -1;
    229     public long mStart = -1;
    230 
    231     // This should be set the same as mEnd when created and is used for making changes to
    232     // recurring events. It should not be updated after it is initially set.
    233     public long mOriginalEnd = -1;
    234     public long mEnd = -1;
    235     public String mDuration = null;
    236     public String mTimezone = null;
    237     public String mTimezone2 = null;
    238     public boolean mAllDay = false;
    239     public boolean mHasAlarm = false;
    240     public int mAvailability = Events.AVAILABILITY_BUSY;
    241 
    242     // PROVIDER_NOTES How does an event not have attendee data? The owner is added
    243     // as an attendee by default.
    244     public boolean mHasAttendeeData = true;
    245     public int mSelfAttendeeStatus = -1;
    246     public int mOwnerAttendeeId = -1;
    247     public String mOriginalSyncId = null;
    248     public long mOriginalId = -1;
    249     public Long mOriginalTime = null;
    250     public Boolean mOriginalAllDay = null;
    251     public boolean mGuestsCanModify = false;
    252     public boolean mGuestsCanInviteOthers = false;
    253     public boolean mGuestsCanSeeGuests = false;
    254 
    255     public boolean mOrganizerCanRespond = false;
    256     public int mCalendarAccessLevel = Calendars.CAL_ACCESS_CONTRIBUTOR;
    257 
    258     public int mEventStatus = Events.STATUS_CONFIRMED;
    259 
    260     // The model can't be updated with a calendar cursor until it has been
    261     // updated with an event cursor.
    262     public boolean mModelUpdatedWithEventCursor;
    263 
    264     public int mAccessLevel = 0;
    265     public ArrayList<ReminderEntry> mReminders;
    266     public ArrayList<ReminderEntry> mDefaultReminders;
    267 
    268     // PROVIDER_NOTES Using EditEventHelper the owner should not be included in this
    269     // list and will instead be added by saveEvent. Is this what we want?
    270     public LinkedHashMap<String, Attendee> mAttendeesList;
    271 
    272     public CalendarEventModel() {
    273         mReminders = new ArrayList<ReminderEntry>();
    274         mDefaultReminders = new ArrayList<ReminderEntry>();
    275         mAttendeesList = new LinkedHashMap<String, Attendee>();
    276         mTimezone = TimeZone.getDefault().getID();
    277     }
    278 
    279     public CalendarEventModel(Context context) {
    280         this();
    281 
    282         mTimezone = Utils.getTimeZone(context, null);
    283         SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context);
    284 
    285         String defaultReminder = prefs.getString(
    286                 GeneralPreferences.KEY_DEFAULT_REMINDER, GeneralPreferences.NO_REMINDER_STRING);
    287         int defaultReminderMins = Integer.parseInt(defaultReminder);
    288         if (defaultReminderMins != GeneralPreferences.NO_REMINDER) {
    289             // Assume all calendars allow at least one reminder.
    290             mHasAlarm = true;
    291             mReminders.add(ReminderEntry.valueOf(defaultReminderMins));
    292             mDefaultReminders.add(ReminderEntry.valueOf(defaultReminderMins));
    293         }
    294     }
    295 
    296     public CalendarEventModel(Context context, Intent intent) {
    297         this(context);
    298 
    299         if (intent == null) {
    300             return;
    301         }
    302 
    303         String title = intent.getStringExtra(Events.TITLE);
    304         if (title != null) {
    305             mTitle = title;
    306         }
    307 
    308         String location = intent.getStringExtra(Events.EVENT_LOCATION);
    309         if (location != null) {
    310             mLocation = location;
    311         }
    312 
    313         String description = intent.getStringExtra(Events.DESCRIPTION);
    314         if (description != null) {
    315             mDescription = description;
    316         }
    317 
    318         int availability = intent.getIntExtra(Events.AVAILABILITY, -1);
    319         if (availability != -1) {
    320             mAvailability = availability;
    321         }
    322 
    323         int accessLevel = intent.getIntExtra(Events.ACCESS_LEVEL, -1);
    324         if (accessLevel != -1) {
    325             if (accessLevel > 0) {
    326                 // TODO remove this if we add support for
    327                 // Events.ACCESS_CONFIDENTIAL
    328                 accessLevel--;
    329             }
    330             mAccessLevel = accessLevel;
    331         }
    332 
    333         String rrule = intent.getStringExtra(Events.RRULE);
    334         if (!TextUtils.isEmpty(rrule)) {
    335             mRrule = rrule;
    336         }
    337 
    338         String emails = intent.getStringExtra(Intent.EXTRA_EMAIL);
    339         if (!TextUtils.isEmpty(emails)) {
    340             String[] emailArray = emails.split("[ ,;]");
    341             for (String email : emailArray) {
    342                 if (!TextUtils.isEmpty(email) && email.contains("@")) {
    343                     email = email.trim();
    344                     if (!mAttendeesList.containsKey(email)) {
    345                         mAttendeesList.put(email, new Attendee("", email));
    346                     }
    347                 }
    348             }
    349         }
    350     }
    351 
    352     public boolean isValid() {
    353         if (mCalendarId == -1) {
    354             return false;
    355         }
    356         if (TextUtils.isEmpty(mOwnerAccount)) {
    357             return false;
    358         }
    359         return true;
    360     }
    361 
    362     private boolean isEmpty() {
    363         if (mTitle != null && mTitle.length() > 0) {
    364             return false;
    365         }
    366 
    367         if (mLocation != null && mLocation.length() > 0) {
    368             return false;
    369         }
    370 
    371         if (mDescription != null && mDescription.length() > 0) {
    372             return false;
    373         }
    374 
    375         return true;
    376     }
    377 
    378     public void clear() {
    379         mUri = null;
    380         mId = -1;
    381         mCalendarId = -1;
    382 
    383         mSyncId = null;
    384         mSyncAccount = null;
    385         mSyncAccountType = null;
    386         mOwnerAccount = null;
    387 
    388         mTitle = null;
    389         mLocation = null;
    390         mDescription = null;
    391         mRrule = null;
    392         mOrganizer = null;
    393         mOrganizerDisplayName = null;
    394         mIsOrganizer = true;
    395         mIsFirstEventInSeries = true;
    396 
    397         mOriginalStart = -1;
    398         mStart = -1;
    399         mOriginalEnd = -1;
    400         mEnd = -1;
    401         mDuration = null;
    402         mTimezone = null;
    403         mTimezone2 = null;
    404         mAllDay = false;
    405         mHasAlarm = false;
    406 
    407         mHasAttendeeData = true;
    408         mSelfAttendeeStatus = -1;
    409         mOwnerAttendeeId = -1;
    410         mOriginalId = -1;
    411         mOriginalSyncId = null;
    412         mOriginalTime = null;
    413         mOriginalAllDay = null;
    414 
    415         mGuestsCanModify = false;
    416         mGuestsCanInviteOthers = false;
    417         mGuestsCanSeeGuests = false;
    418         mAccessLevel = 0;
    419         mEventStatus = Events.STATUS_CONFIRMED;
    420         mOrganizerCanRespond = false;
    421         mCalendarAccessLevel = Calendars.CAL_ACCESS_CONTRIBUTOR;
    422         mModelUpdatedWithEventCursor = false;
    423         mCalendarAllowedReminders = null;
    424         mCalendarAllowedAttendeeTypes = null;
    425         mCalendarAllowedAvailability = null;
    426 
    427         mReminders = new ArrayList<ReminderEntry>();
    428         mAttendeesList.clear();
    429     }
    430 
    431     public void addAttendee(Attendee attendee) {
    432         mAttendeesList.put(attendee.mEmail, attendee);
    433     }
    434 
    435     public void addAttendees(String attendees, Rfc822Validator validator) {
    436         final LinkedHashSet<Rfc822Token> addresses = EditEventHelper.getAddressesFromList(
    437                 attendees, validator);
    438         synchronized (this) {
    439             for (final Rfc822Token address : addresses) {
    440                 final Attendee attendee = new Attendee(address.getName(), address.getAddress());
    441                 if (TextUtils.isEmpty(attendee.mName)) {
    442                     attendee.mName = attendee.mEmail;
    443                 }
    444                 addAttendee(attendee);
    445             }
    446         }
    447     }
    448 
    449     public void removeAttendee(Attendee attendee) {
    450         mAttendeesList.remove(attendee.mEmail);
    451     }
    452 
    453     public String getAttendeesString() {
    454         StringBuilder b = new StringBuilder();
    455         for (Attendee attendee : mAttendeesList.values()) {
    456             String name = attendee.mName;
    457             String email = attendee.mEmail;
    458             String status = Integer.toString(attendee.mStatus);
    459             b.append("name:").append(name);
    460             b.append(" email:").append(email);
    461             b.append(" status:").append(status);
    462         }
    463         return b.toString();
    464     }
    465 
    466     @Override
    467     public int hashCode() {
    468         final int prime = 31;
    469         int result = 1;
    470         result = prime * result + (mAllDay ? 1231 : 1237);
    471         result = prime * result + ((mAttendeesList == null) ? 0 : getAttendeesString().hashCode());
    472         result = prime * result + (int) (mCalendarId ^ (mCalendarId >>> 32));
    473         result = prime * result + ((mDescription == null) ? 0 : mDescription.hashCode());
    474         result = prime * result + ((mDuration == null) ? 0 : mDuration.hashCode());
    475         result = prime * result + (int) (mEnd ^ (mEnd >>> 32));
    476         result = prime * result + (mGuestsCanInviteOthers ? 1231 : 1237);
    477         result = prime * result + (mGuestsCanModify ? 1231 : 1237);
    478         result = prime * result + (mGuestsCanSeeGuests ? 1231 : 1237);
    479         result = prime * result + (mOrganizerCanRespond ? 1231 : 1237);
    480         result = prime * result + (mModelUpdatedWithEventCursor ? 1231 : 1237);
    481         result = prime * result + mCalendarAccessLevel;
    482         result = prime * result + (mHasAlarm ? 1231 : 1237);
    483         result = prime * result + (mHasAttendeeData ? 1231 : 1237);
    484         result = prime * result + (int) (mId ^ (mId >>> 32));
    485         result = prime * result + (mIsFirstEventInSeries ? 1231 : 1237);
    486         result = prime * result + (mIsOrganizer ? 1231 : 1237);
    487         result = prime * result + ((mLocation == null) ? 0 : mLocation.hashCode());
    488         result = prime * result + ((mOrganizer == null) ? 0 : mOrganizer.hashCode());
    489         result = prime * result + ((mOriginalAllDay == null) ? 0 : mOriginalAllDay.hashCode());
    490         result = prime * result + (int) (mOriginalEnd ^ (mOriginalEnd >>> 32));
    491         result = prime * result + ((mOriginalSyncId == null) ? 0 : mOriginalSyncId.hashCode());
    492         result = prime * result + (int) (mOriginalId ^ (mOriginalEnd >>> 32));
    493         result = prime * result + (int) (mOriginalStart ^ (mOriginalStart >>> 32));
    494         result = prime * result + ((mOriginalTime == null) ? 0 : mOriginalTime.hashCode());
    495         result = prime * result + ((mOwnerAccount == null) ? 0 : mOwnerAccount.hashCode());
    496         result = prime * result + ((mReminders == null) ? 0 : mReminders.hashCode());
    497         result = prime * result + ((mRrule == null) ? 0 : mRrule.hashCode());
    498         result = prime * result + mSelfAttendeeStatus;
    499         result = prime * result + mOwnerAttendeeId;
    500         result = prime * result + (int) (mStart ^ (mStart >>> 32));
    501         result = prime * result + ((mSyncAccount == null) ? 0 : mSyncAccount.hashCode());
    502         result = prime * result + ((mSyncAccountType == null) ? 0 : mSyncAccountType.hashCode());
    503         result = prime * result + ((mSyncId == null) ? 0 : mSyncId.hashCode());
    504         result = prime * result + ((mTimezone == null) ? 0 : mTimezone.hashCode());
    505         result = prime * result + ((mTimezone2 == null) ? 0 : mTimezone2.hashCode());
    506         result = prime * result + ((mTitle == null) ? 0 : mTitle.hashCode());
    507         result = prime * result + (mAvailability);
    508         result = prime * result + ((mUri == null) ? 0 : mUri.hashCode());
    509         result = prime * result + mAccessLevel;
    510         result = prime * result + mEventStatus;
    511         return result;
    512     }
    513 
    514     // Autogenerated equals method
    515     @Override
    516     public boolean equals(Object obj) {
    517         if (this == obj) {
    518             return true;
    519         }
    520         if (obj == null) {
    521             return false;
    522         }
    523         if (!(obj instanceof CalendarEventModel)) {
    524             return false;
    525         }
    526 
    527         CalendarEventModel other = (CalendarEventModel) obj;
    528         if (!checkOriginalModelFields(other)) {
    529             return false;
    530         }
    531 
    532         if (mLocation == null) {
    533             if (other.mLocation != null) {
    534                 return false;
    535             }
    536         } else if (!mLocation.equals(other.mLocation)) {
    537             return false;
    538         }
    539 
    540         if (mTitle == null) {
    541             if (other.mTitle != null) {
    542                 return false;
    543             }
    544         } else if (!mTitle.equals(other.mTitle)) {
    545             return false;
    546         }
    547 
    548         if (mDescription == null) {
    549             if (other.mDescription != null) {
    550                 return false;
    551             }
    552         } else if (!mDescription.equals(other.mDescription)) {
    553             return false;
    554         }
    555 
    556         if (mDuration == null) {
    557             if (other.mDuration != null) {
    558                 return false;
    559             }
    560         } else if (!mDuration.equals(other.mDuration)) {
    561             return false;
    562         }
    563 
    564         if (mEnd != other.mEnd) {
    565             return false;
    566         }
    567         if (mIsFirstEventInSeries != other.mIsFirstEventInSeries) {
    568             return false;
    569         }
    570         if (mOriginalEnd != other.mOriginalEnd) {
    571             return false;
    572         }
    573 
    574         if (mOriginalStart != other.mOriginalStart) {
    575             return false;
    576         }
    577         if (mStart != other.mStart) {
    578             return false;
    579         }
    580 
    581         if (mOriginalId != other.mOriginalId) {
    582             return false;
    583         }
    584 
    585         if (mOriginalSyncId == null) {
    586             if (other.mOriginalSyncId != null) {
    587                 return false;
    588             }
    589         } else if (!mOriginalSyncId.equals(other.mOriginalSyncId)) {
    590             return false;
    591         }
    592 
    593         if (mRrule == null) {
    594             if (other.mRrule != null) {
    595                 return false;
    596             }
    597         } else if (!mRrule.equals(other.mRrule)) {
    598             return false;
    599         }
    600         return true;
    601     }
    602 
    603     /**
    604      * Whether the event has been modified based on its original model.
    605      *
    606      * @param originalModel
    607      * @return true if the model is unchanged, false otherwise
    608      */
    609     public boolean isUnchanged(CalendarEventModel originalModel) {
    610         if (this == originalModel) {
    611             return true;
    612         }
    613         if (originalModel == null) {
    614             return false;
    615         }
    616 
    617         if (!checkOriginalModelFields(originalModel)) {
    618             return false;
    619         }
    620 
    621         if (TextUtils.isEmpty(mLocation)) {
    622             if (!TextUtils.isEmpty(originalModel.mLocation)) {
    623                 return false;
    624             }
    625         } else if (!mLocation.equals(originalModel.mLocation)) {
    626             return false;
    627         }
    628 
    629         if (TextUtils.isEmpty(mTitle)) {
    630             if (!TextUtils.isEmpty(originalModel.mTitle)) {
    631                 return false;
    632             }
    633         } else if (!mTitle.equals(originalModel.mTitle)) {
    634             return false;
    635         }
    636 
    637         if (TextUtils.isEmpty(mDescription)) {
    638             if (!TextUtils.isEmpty(originalModel.mDescription)) {
    639                 return false;
    640             }
    641         } else if (!mDescription.equals(originalModel.mDescription)) {
    642             return false;
    643         }
    644 
    645         if (TextUtils.isEmpty(mDuration)) {
    646             if (!TextUtils.isEmpty(originalModel.mDuration)) {
    647                 return false;
    648             }
    649         } else if (!mDuration.equals(originalModel.mDuration)) {
    650             return false;
    651         }
    652 
    653         if (mEnd != mOriginalEnd) {
    654             return false;
    655         }
    656         if (mStart != mOriginalStart) {
    657             return false;
    658         }
    659 
    660         // If this changed the original id and it's not just an exception to the
    661         // original event
    662         if (mOriginalId != originalModel.mOriginalId && mOriginalId != originalModel.mId) {
    663             return false;
    664         }
    665 
    666         if (TextUtils.isEmpty(mRrule)) {
    667             // if the rrule is no longer empty check if this is an exception
    668             if (!TextUtils.isEmpty(originalModel.mRrule)) {
    669                 boolean syncIdNotReferenced = mOriginalSyncId == null
    670                         || !mOriginalSyncId.equals(originalModel.mSyncId);
    671                 boolean localIdNotReferenced = mOriginalId == -1
    672                         || !(mOriginalId == originalModel.mId);
    673                 if (syncIdNotReferenced && localIdNotReferenced) {
    674                     return false;
    675                 }
    676             }
    677         } else if (!mRrule.equals(originalModel.mRrule)) {
    678             return false;
    679         }
    680 
    681         return true;
    682     }
    683 
    684     /**
    685      * Checks against an original model for changes to an event. This covers all
    686      * the fields that should remain consistent between an original event model
    687      * and the new one if nothing in the event was modified. This is also the
    688      * portion that overlaps with equality between two event models.
    689      *
    690      * @param originalModel
    691      * @return true if these fields are unchanged, false otherwise
    692      */
    693     protected boolean checkOriginalModelFields(CalendarEventModel originalModel) {
    694         if (mAllDay != originalModel.mAllDay) {
    695             return false;
    696         }
    697         if (mAttendeesList == null) {
    698             if (originalModel.mAttendeesList != null) {
    699                 return false;
    700             }
    701         } else if (!mAttendeesList.equals(originalModel.mAttendeesList)) {
    702             return false;
    703         }
    704 
    705         if (mCalendarId != originalModel.mCalendarId) {
    706             return false;
    707         }
    708 
    709         if (mGuestsCanInviteOthers != originalModel.mGuestsCanInviteOthers) {
    710             return false;
    711         }
    712         if (mGuestsCanModify != originalModel.mGuestsCanModify) {
    713             return false;
    714         }
    715         if (mGuestsCanSeeGuests != originalModel.mGuestsCanSeeGuests) {
    716             return false;
    717         }
    718         if (mOrganizerCanRespond != originalModel.mOrganizerCanRespond) {
    719             return false;
    720         }
    721         if (mCalendarAccessLevel != originalModel.mCalendarAccessLevel) {
    722             return false;
    723         }
    724         if (mModelUpdatedWithEventCursor != originalModel.mModelUpdatedWithEventCursor) {
    725             return false;
    726         }
    727         if (mHasAlarm != originalModel.mHasAlarm) {
    728             return false;
    729         }
    730         if (mHasAttendeeData != originalModel.mHasAttendeeData) {
    731             return false;
    732         }
    733         if (mId != originalModel.mId) {
    734             return false;
    735         }
    736         if (mIsOrganizer != originalModel.mIsOrganizer) {
    737             return false;
    738         }
    739 
    740         if (mOrganizer == null) {
    741             if (originalModel.mOrganizer != null) {
    742                 return false;
    743             }
    744         } else if (!mOrganizer.equals(originalModel.mOrganizer)) {
    745             return false;
    746         }
    747 
    748         if (mOriginalAllDay == null) {
    749             if (originalModel.mOriginalAllDay != null) {
    750                 return false;
    751             }
    752         } else if (!mOriginalAllDay.equals(originalModel.mOriginalAllDay)) {
    753             return false;
    754         }
    755 
    756         if (mOriginalTime == null) {
    757             if (originalModel.mOriginalTime != null) {
    758                 return false;
    759             }
    760         } else if (!mOriginalTime.equals(originalModel.mOriginalTime)) {
    761             return false;
    762         }
    763 
    764         if (mOwnerAccount == null) {
    765             if (originalModel.mOwnerAccount != null) {
    766                 return false;
    767             }
    768         } else if (!mOwnerAccount.equals(originalModel.mOwnerAccount)) {
    769             return false;
    770         }
    771 
    772         if (mReminders == null) {
    773             if (originalModel.mReminders != null) {
    774                 return false;
    775             }
    776         } else if (!mReminders.equals(originalModel.mReminders)) {
    777             return false;
    778         }
    779 
    780         if (mSelfAttendeeStatus != originalModel.mSelfAttendeeStatus) {
    781             return false;
    782         }
    783         if (mOwnerAttendeeId != originalModel.mOwnerAttendeeId) {
    784             return false;
    785         }
    786         if (mSyncAccount == null) {
    787             if (originalModel.mSyncAccount != null) {
    788                 return false;
    789             }
    790         } else if (!mSyncAccount.equals(originalModel.mSyncAccount)) {
    791             return false;
    792         }
    793 
    794         if (mSyncAccountType == null) {
    795             if (originalModel.mSyncAccountType != null) {
    796                 return false;
    797             }
    798         } else if (!mSyncAccountType.equals(originalModel.mSyncAccountType)) {
    799             return false;
    800         }
    801 
    802         if (mSyncId == null) {
    803             if (originalModel.mSyncId != null) {
    804                 return false;
    805             }
    806         } else if (!mSyncId.equals(originalModel.mSyncId)) {
    807             return false;
    808         }
    809 
    810         if (mTimezone == null) {
    811             if (originalModel.mTimezone != null) {
    812                 return false;
    813             }
    814         } else if (!mTimezone.equals(originalModel.mTimezone)) {
    815             return false;
    816         }
    817 
    818         if (mTimezone2 == null) {
    819             if (originalModel.mTimezone2 != null) {
    820                 return false;
    821             }
    822         } else if (!mTimezone2.equals(originalModel.mTimezone2)) {
    823             return false;
    824         }
    825 
    826         if (mAvailability != originalModel.mAvailability) {
    827             return false;
    828         }
    829 
    830         if (mUri == null) {
    831             if (originalModel.mUri != null) {
    832                 return false;
    833             }
    834         } else if (!mUri.equals(originalModel.mUri)) {
    835             return false;
    836         }
    837 
    838         if (mAccessLevel != originalModel.mAccessLevel) {
    839             return false;
    840         }
    841 
    842         if (mEventStatus != originalModel.mEventStatus) {
    843             return false;
    844         }
    845         return true;
    846     }
    847 
    848     /**
    849      * Sort and uniquify mReminderMinutes.
    850      *
    851      * @return true (for convenience of caller)
    852      */
    853     public boolean normalizeReminders() {
    854         if (mReminders.size() <= 1) {
    855             return true;
    856         }
    857 
    858         // sort
    859         Collections.sort(mReminders);
    860 
    861         // remove duplicates
    862         ReminderEntry prev = mReminders.get(mReminders.size()-1);
    863         for (int i = mReminders.size()-2; i >= 0; --i) {
    864             ReminderEntry cur = mReminders.get(i);
    865             if (prev.equals(cur)) {
    866                 // match, remove later entry
    867                 mReminders.remove(i+1);
    868             }
    869             prev = cur;
    870         }
    871 
    872         return true;
    873     }
    874 }
    875