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