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