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