CalendarEventModel.java revision c27e56f7150b77da3d8ae22e48a4b3699bd2e19c
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 17package com.android.calendar; 18 19import android.content.Context; 20import android.content.Intent; 21import android.content.SharedPreferences; 22import android.provider.Calendar.Attendees; 23import android.provider.Calendar.Calendars; 24import android.provider.Calendar.Events; 25import android.provider.Calendar.Reminders; 26import android.text.TextUtils; 27import android.util.Log; 28 29import java.io.Serializable; 30import java.util.ArrayList; 31import java.util.Arrays; 32import java.util.Collections; 33import java.util.LinkedHashMap; 34import java.util.TimeZone; 35 36/** 37 * Stores all the information needed to fill out an entry in the events table. 38 * This is a convenient way for storing information needed by the UI to write to 39 * the events table. Only fields that are important to the UI are included. 40 */ 41public class CalendarEventModel implements Serializable { 42 private static final String TAG = "CalendarEventModel"; 43 44 public static class Attendee implements Serializable { 45 @Override 46 public int hashCode() { 47 return (mEmail == null) ? 0 : mEmail.hashCode(); 48 } 49 50 @Override 51 public boolean equals(Object obj) { 52 if (this == obj) { 53 return true; 54 } 55 if (!(obj instanceof Attendee)) { 56 return false; 57 } 58 Attendee other = (Attendee) obj; 59 if (!TextUtils.equals(mEmail, other.mEmail)) { 60 return false; 61 } 62 return true; 63 } 64 65 public String mName; 66 public String mEmail; 67 public int mStatus; 68 69 public Attendee(String name, String email) { 70 mName = name; 71 mEmail = email; 72 mStatus = Attendees.ATTENDEE_STATUS_NONE; 73 } 74 } 75 76 /** 77 * A single reminder entry. 78 * 79 * Instances of the class are immutable. 80 */ 81 public static class ReminderEntry implements Comparable<ReminderEntry>, Serializable { 82 private final int mMinutes; 83 private final int mMethod; 84 85 /** 86 * Returns a new ReminderEntry, with the specified minutes and method. 87 * 88 * @param minutes Number of minutes before the start of the event that the alert will fire. 89 * @param method Type of alert ({@link Reminders#METHOD_ALERT}, etc). 90 */ 91 public static ReminderEntry valueOf(int minutes, int method) { 92 // TODO: cache common instances 93 return new ReminderEntry(minutes, method); 94 } 95 96 /** 97 * Returns a ReminderEntry, with the specified number of minutes and a default alert method. 98 * 99 * @param minutes Number of minutes before the start of the event that the alert will fire. 100 */ 101 public static ReminderEntry valueOf(int minutes) { 102 return valueOf(minutes, Reminders.METHOD_DEFAULT); 103 } 104 105 /** 106 * Constructs a new ReminderEntry. 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 private ReminderEntry(int minutes, int method) { 112 // TODO: error-check args 113 mMinutes = minutes; 114 mMethod = method; 115 } 116 117 @Override 118 public int hashCode() { 119 return mMinutes * 10 + mMethod; 120 } 121 122 @Override 123 public boolean equals(Object obj) { 124 if (this == obj) 125 return true; 126 if (!(obj instanceof ReminderEntry)) { 127 return false; 128 } 129 130 ReminderEntry re = (ReminderEntry) obj; 131 132 if (re.mMinutes != mMinutes) 133 return false; 134 135 // treat ALERT and DEFAULT as equivalent (TODO: is this necessary?) 136 return re.mMethod == mMethod || 137 (re.mMethod == Reminders.METHOD_DEFAULT && mMethod == Reminders.METHOD_ALERT) || 138 (re.mMethod == Reminders.METHOD_ALERT && mMethod == Reminders.METHOD_DEFAULT); 139 } 140 141 @Override 142 public String toString() { 143 return "ReminderEntry min=" + mMinutes + " meth=" + mMethod; 144 } 145 146 /** 147 * Comparison function for a sort ordered descending by minutes. 148 */ 149 public int compareTo(ReminderEntry re) { 150 if (re.mMinutes != mMinutes) { 151 return re.mMinutes - mMinutes; 152 } 153 if (re.mMethod != mMethod) { 154 return re.mMethod - mMethod; 155 } 156 return 0; 157 } 158 159 /** Returns the minutes. */ 160 public int getMinutes() { 161 return mMinutes; 162 } 163 164 /** Returns the alert method. */ 165 public int getMethod() { 166 return mMethod; 167 } 168 } 169 170 // TODO strip out fields that don't ever get used 171 /** 172 * The uri of the event in the db. This should only be null for new events. 173 */ 174 public String mUri = null; 175 public long mId = -1; 176 public long mCalendarId = -1; 177 public String mCalendarDisplayName = ""; // Make sure this is in sync with the mCalendarId 178 public int mCalendarColor = 0; 179 180 public String mSyncId = null; 181 public String mSyncAccount = null; 182 public String mSyncAccountType = null; 183 184 // PROVIDER_NOTES owner account comes from the calendars table 185 public String mOwnerAccount = null; 186 public String mTitle = null; 187 public String mLocation = null; 188 public String mDescription = null; 189 public String mRrule = null; 190 public String mOrganizer = null; 191 public String mOrganizerDisplayName = null; 192 /** 193 * Read-Only - Derived from other fields 194 */ 195 public boolean mIsOrganizer = true; 196 public boolean mIsFirstEventInSeries = true; 197 198 // This should be set the same as mStart when created and is used for making changes to 199 // recurring events. It should not be updated after it is initially set. 200 public long mOriginalStart = -1; 201 public long mStart = -1; 202 203 // This should be set the same as mEnd when created and is used for making changes to 204 // recurring events. It should not be updated after it is initially set. 205 public long mOriginalEnd = -1; 206 public long mEnd = -1; 207 public String mDuration = null; 208 public String mTimezone = null; 209 public String mTimezone2 = null; 210 public boolean mAllDay = false; 211 public boolean mHasAlarm = false; 212 public boolean mTransparency = false; 213 214 // PROVIDER_NOTES How does an event not have attendee data? The owner is added 215 // as an attendee by default. 216 public boolean mHasAttendeeData = true; 217 public int mSelfAttendeeStatus = -1; 218 public int mOwnerAttendeeId = -1; 219 public String mOriginalEvent = null; 220 public Long mOriginalTime = null; 221 public Boolean mOriginalAllDay = null; 222 public boolean mGuestsCanModify = false; 223 public boolean mGuestsCanInviteOthers = false; 224 public boolean mGuestsCanSeeGuests = false; 225 226 public boolean mOrganizerCanRespond = false; 227 public int mCalendarAccessLevel = Calendars.CONTRIBUTOR_ACCESS; 228 229 // The model can't be updated with a calendar cursor until it has been 230 // updated with an event cursor. 231 public boolean mModelUpdatedWithEventCursor; 232 233 public int mVisibility = 0; 234 public ArrayList<ReminderEntry> mReminders; 235 236 // PROVIDER_NOTES Using EditEventHelper the owner should not be included in this 237 // list and will instead be added by saveEvent. Is this what we want? 238 public LinkedHashMap<String, Attendee> mAttendeesList; 239 240 public CalendarEventModel() { 241 mReminders = new ArrayList<ReminderEntry>(); 242 mAttendeesList = new LinkedHashMap<String, Attendee>(); 243 mTimezone = TimeZone.getDefault().getID(); 244 } 245 246 public CalendarEventModel(Context context) { 247 this(); 248 249 mTimezone = Utils.getTimeZone(context, null); 250 SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); 251 252 String defaultReminder = prefs.getString( 253 GeneralPreferences.KEY_DEFAULT_REMINDER, GeneralPreferences.NO_REMINDER_STRING); 254 int defaultReminderMins = Integer.parseInt(defaultReminder); 255 if (defaultReminderMins != GeneralPreferences.NO_REMINDER) { 256 mHasAlarm = true; 257 mReminders.add(ReminderEntry.valueOf(defaultReminderMins)); 258 } 259 } 260 261 public CalendarEventModel(Context context, Intent intent) { 262 this(context); 263 264 String title = intent.getStringExtra(Events.TITLE); 265 if (title != null) { 266 mTitle = title; 267 } 268 269 String location = intent.getStringExtra(Events.EVENT_LOCATION); 270 if (location != null) { 271 mLocation = location; 272 } 273 274 String description = intent.getStringExtra(Events.DESCRIPTION); 275 if (description != null) { 276 mDescription = description; 277 } 278 279 int transparency = intent.getIntExtra(Events.AVAILABILITY, -1); 280 if (transparency != -1) { 281 mTransparency = transparency != 0; 282 } 283 284 int visibility = intent.getIntExtra(Events.ACCESS_LEVEL, -1); 285 if (visibility != -1) { 286 mVisibility = visibility; 287 } 288 289 String rrule = intent.getStringExtra(Events.RRULE); 290 if (!TextUtils.isEmpty(rrule)) { 291 mRrule = rrule; 292 } 293 } 294 295 public boolean isValid() { 296 if (mCalendarId == -1) { 297 return false; 298 } 299 if (TextUtils.isEmpty(mOwnerAccount)) { 300 return false; 301 } 302 return true; 303 } 304 305 private boolean isEmpty() { 306 if (mTitle != null && mTitle.length() > 0) { 307 return false; 308 } 309 310 if (mLocation != null && mLocation.length() > 0) { 311 return false; 312 } 313 314 if (mDescription != null && mDescription.length() > 0) { 315 return false; 316 } 317 318 return true; 319 } 320 321 public void clear() { 322 mUri = null; 323 mId = -1; 324 mCalendarId = -1; 325 326 mSyncId = null; 327 mSyncAccount = null; 328 mSyncAccountType = null; 329 mOwnerAccount = null; 330 331 mTitle = null; 332 mLocation = null; 333 mDescription = null; 334 mRrule = null; 335 mOrganizer = null; 336 mOrganizerDisplayName = null; 337 mIsOrganizer = true; 338 mIsFirstEventInSeries = true; 339 340 mOriginalStart = -1; 341 mStart = -1; 342 mOriginalEnd = -1; 343 mEnd = -1; 344 mDuration = null; 345 mTimezone = null; 346 mTimezone2 = null; 347 mAllDay = false; 348 mHasAlarm = false; 349 350 mHasAttendeeData = true; 351 mSelfAttendeeStatus = -1; 352 mOwnerAttendeeId = -1; 353 mOriginalEvent = null; 354 mOriginalTime = null; 355 mOriginalAllDay = null; 356 357 mGuestsCanModify = false; 358 mGuestsCanInviteOthers = false; 359 mGuestsCanSeeGuests = false; 360 mVisibility = 0; 361 mOrganizerCanRespond = false; 362 mCalendarAccessLevel = Calendars.CONTRIBUTOR_ACCESS; 363 mModelUpdatedWithEventCursor = false; 364 365 mReminders = new ArrayList<ReminderEntry>(); 366 mAttendeesList.clear(); 367 } 368 369 public void addAttendee(Attendee attendee) { 370 mAttendeesList.put(attendee.mEmail, attendee); 371 } 372 373 public void removeAttendee(Attendee attendee) { 374 mAttendeesList.remove(attendee.mEmail); 375 } 376 377 public String getAttendeesString() { 378 StringBuilder b = new StringBuilder(); 379 for (Attendee attendee : mAttendeesList.values()) { 380 String name = attendee.mName; 381 String email = attendee.mEmail; 382 String status = Integer.toString(attendee.mStatus); 383 b.append("name:").append(name); 384 b.append(" email:").append(email); 385 b.append(" status:").append(status); 386 } 387 return b.toString(); 388 } 389 390 @Override 391 public int hashCode() { 392 final int prime = 31; 393 int result = 1; 394 result = prime * result + (mAllDay ? 1231 : 1237); 395 result = prime * result + ((mAttendeesList == null) ? 0 : getAttendeesString().hashCode()); 396 result = prime * result + (int) (mCalendarId ^ (mCalendarId >>> 32)); 397 result = prime * result + ((mDescription == null) ? 0 : mDescription.hashCode()); 398 result = prime * result + ((mDuration == null) ? 0 : mDuration.hashCode()); 399 result = prime * result + (int) (mEnd ^ (mEnd >>> 32)); 400 result = prime * result + (mGuestsCanInviteOthers ? 1231 : 1237); 401 result = prime * result + (mGuestsCanModify ? 1231 : 1237); 402 result = prime * result + (mGuestsCanSeeGuests ? 1231 : 1237); 403 result = prime * result + (mOrganizerCanRespond ? 1231 : 1237); 404 result = prime * result + (mModelUpdatedWithEventCursor ? 1231 : 1237); 405 result = prime * result + mCalendarAccessLevel; 406 result = prime * result + (mHasAlarm ? 1231 : 1237); 407 result = prime * result + (mHasAttendeeData ? 1231 : 1237); 408 result = prime * result + (int) (mId ^ (mId >>> 32)); 409 result = prime * result + (mIsFirstEventInSeries ? 1231 : 1237); 410 result = prime * result + (mIsOrganizer ? 1231 : 1237); 411 result = prime * result + ((mLocation == null) ? 0 : mLocation.hashCode()); 412 result = prime * result + ((mOrganizer == null) ? 0 : mOrganizer.hashCode()); 413 result = prime * result + ((mOriginalAllDay == null) ? 0 : mOriginalAllDay.hashCode()); 414 result = prime * result + (int) (mOriginalEnd ^ (mOriginalEnd >>> 32)); 415 result = prime * result + ((mOriginalEvent == null) ? 0 : mOriginalEvent.hashCode()); 416 result = prime * result + (int) (mOriginalStart ^ (mOriginalStart >>> 32)); 417 result = prime * result + ((mOriginalTime == null) ? 0 : mOriginalTime.hashCode()); 418 result = prime * result + ((mOwnerAccount == null) ? 0 : mOwnerAccount.hashCode()); 419 result = prime * result + ((mReminders == null) ? 0 : mReminders.hashCode()); 420 result = prime * result + ((mRrule == null) ? 0 : mRrule.hashCode()); 421 result = prime * result + mSelfAttendeeStatus; 422 result = prime * result + mOwnerAttendeeId; 423 result = prime * result + (int) (mStart ^ (mStart >>> 32)); 424 result = prime * result + ((mSyncAccount == null) ? 0 : mSyncAccount.hashCode()); 425 result = prime * result + ((mSyncAccountType == null) ? 0 : mSyncAccountType.hashCode()); 426 result = prime * result + ((mSyncId == null) ? 0 : mSyncId.hashCode()); 427 result = prime * result + ((mTimezone == null) ? 0 : mTimezone.hashCode()); 428 result = prime * result + ((mTimezone2 == null) ? 0 : mTimezone2.hashCode()); 429 result = prime * result + ((mTitle == null) ? 0 : mTitle.hashCode()); 430 result = prime * result + (mTransparency ? 1231 : 1237); 431 result = prime * result + ((mUri == null) ? 0 : mUri.hashCode()); 432 result = prime * result + mVisibility; 433 return result; 434 } 435 436 // Autogenerated equals method 437 @Override 438 public boolean equals(Object obj) { 439 if (this == obj) { 440 return true; 441 } 442 if (obj == null) { 443 return false; 444 } 445 if (!(obj instanceof CalendarEventModel)) { 446 return false; 447 } 448 449 CalendarEventModel other = (CalendarEventModel) obj; 450 if (!checkOriginalModelFields(other)) { 451 return false; 452 } 453 454 if (mEnd != other.mEnd) { 455 return false; 456 } 457 if (mIsFirstEventInSeries != other.mIsFirstEventInSeries) { 458 return false; 459 } 460 if (mOriginalEnd != other.mOriginalEnd) { 461 return false; 462 } 463 464 if (mOriginalStart != other.mOriginalStart) { 465 return false; 466 } 467 if (mStart != other.mStart) { 468 return false; 469 } 470 471 if (mOriginalEvent == null) { 472 if (other.mOriginalEvent != null) { 473 return false; 474 } 475 } else if (!mOriginalEvent.equals(other.mOriginalEvent)) { 476 return false; 477 } 478 479 if (mRrule == null) { 480 if (other.mRrule != null) { 481 return false; 482 } 483 } else if (!mRrule.equals(other.mRrule)) { 484 return false; 485 } 486 return true; 487 } 488 489 /** 490 * Whether the event has been modified based on its original model. 491 * 492 * @param originalModel 493 * @return true if the model is unchanged, false otherwise 494 */ 495 public boolean isUnchanged(CalendarEventModel originalModel) { 496 if (this == originalModel) { 497 return true; 498 } 499 if (originalModel == null) { 500 return false; 501 } 502 503 if (!checkOriginalModelFields(originalModel)) { 504 return false; 505 } 506 if (mEnd != mOriginalEnd) { 507 return false; 508 } 509 if (mStart != mOriginalStart) { 510 return false; 511 } 512 513 if (mRrule == null) { 514 if (originalModel.mRrule != null) { 515 if (mOriginalEvent == null || !mOriginalEvent.equals(originalModel.mSyncId)) { 516 return false; 517 } 518 } 519 } else if (!mRrule.equals(originalModel.mRrule)) { 520 return false; 521 } 522 523 return true; 524 } 525 526 /** 527 * Checks against an original model for changes to an event. This covers all 528 * the fields that should remain consistent between an original event model 529 * and the new one if nothing in the event was modified. This is also the 530 * portion that overlaps with equality between two event models. 531 * 532 * @param originalModel 533 * @return true if these fields are unchanged, false otherwise 534 */ 535 protected boolean checkOriginalModelFields(CalendarEventModel originalModel) { 536 if (mAllDay != originalModel.mAllDay) { 537 return false; 538 } 539 if (mAttendeesList == null) { 540 if (originalModel.mAttendeesList != null) { 541 return false; 542 } 543 } else if (!mAttendeesList.equals(originalModel.mAttendeesList)) { 544 return false; 545 } 546 547 if (mCalendarId != originalModel.mCalendarId) { 548 return false; 549 } 550 551 if (mDescription == null) { 552 if (originalModel.mDescription != null) { 553 return false; 554 } 555 } else if (!mDescription.equals(originalModel.mDescription)) { 556 return false; 557 } 558 559 if (mDuration == null) { 560 if (originalModel.mDuration != null) { 561 return false; 562 } 563 } else if (!mDuration.equals(originalModel.mDuration)) { 564 return false; 565 } 566 567 if (mGuestsCanInviteOthers != originalModel.mGuestsCanInviteOthers) { 568 return false; 569 } 570 if (mGuestsCanModify != originalModel.mGuestsCanModify) { 571 return false; 572 } 573 if (mGuestsCanSeeGuests != originalModel.mGuestsCanSeeGuests) { 574 return false; 575 } 576 if (mOrganizerCanRespond != originalModel.mOrganizerCanRespond) { 577 return false; 578 } 579 if (mCalendarAccessLevel != originalModel.mCalendarAccessLevel) { 580 return false; 581 } 582 if (mModelUpdatedWithEventCursor != originalModel.mModelUpdatedWithEventCursor) { 583 return false; 584 } 585 if (mHasAlarm != originalModel.mHasAlarm) { 586 return false; 587 } 588 if (mHasAttendeeData != originalModel.mHasAttendeeData) { 589 return false; 590 } 591 if (mId != originalModel.mId) { 592 return false; 593 } 594 if (mIsOrganizer != originalModel.mIsOrganizer) { 595 return false; 596 } 597 598 if (mLocation == null) { 599 if (originalModel.mLocation != null) { 600 return false; 601 } 602 } else if (!mLocation.equals(originalModel.mLocation)) { 603 return false; 604 } 605 606 if (mOrganizer == null) { 607 if (originalModel.mOrganizer != null) { 608 return false; 609 } 610 } else if (!mOrganizer.equals(originalModel.mOrganizer)) { 611 return false; 612 } 613 614 if (mOriginalAllDay == null) { 615 if (originalModel.mOriginalAllDay != null) { 616 return false; 617 } 618 } else if (!mOriginalAllDay.equals(originalModel.mOriginalAllDay)) { 619 return false; 620 } 621 622 if (mOriginalTime == null) { 623 if (originalModel.mOriginalTime != null) { 624 return false; 625 } 626 } else if (!mOriginalTime.equals(originalModel.mOriginalTime)) { 627 return false; 628 } 629 630 if (mOwnerAccount == null) { 631 if (originalModel.mOwnerAccount != null) { 632 return false; 633 } 634 } else if (!mOwnerAccount.equals(originalModel.mOwnerAccount)) { 635 return false; 636 } 637 638 if (mReminders == null) { 639 if (originalModel.mReminders != null) { 640 return false; 641 } 642 } else if (!mReminders.equals(originalModel.mReminders)) { 643 return false; 644 } 645 646 if (mSelfAttendeeStatus != originalModel.mSelfAttendeeStatus) { 647 return false; 648 } 649 if (mOwnerAttendeeId != originalModel.mOwnerAttendeeId) { 650 return false; 651 } 652 if (mSyncAccount == null) { 653 if (originalModel.mSyncAccount != null) { 654 return false; 655 } 656 } else if (!mSyncAccount.equals(originalModel.mSyncAccount)) { 657 return false; 658 } 659 660 if (mSyncAccountType == null) { 661 if (originalModel.mSyncAccountType != null) { 662 return false; 663 } 664 } else if (!mSyncAccountType.equals(originalModel.mSyncAccountType)) { 665 return false; 666 } 667 668 if (mSyncId == null) { 669 if (originalModel.mSyncId != null) { 670 return false; 671 } 672 } else if (!mSyncId.equals(originalModel.mSyncId)) { 673 return false; 674 } 675 676 if (mTimezone == null) { 677 if (originalModel.mTimezone != null) { 678 return false; 679 } 680 } else if (!mTimezone.equals(originalModel.mTimezone)) { 681 return false; 682 } 683 684 if (mTimezone2 == null) { 685 if (originalModel.mTimezone2 != null) { 686 return false; 687 } 688 } else if (!mTimezone2.equals(originalModel.mTimezone2)) { 689 return false; 690 } 691 692 if (mTitle == null) { 693 if (originalModel.mTitle != null) { 694 return false; 695 } 696 } else if (!mTitle.equals(originalModel.mTitle)) { 697 return false; 698 } 699 700 if (mTransparency != originalModel.mTransparency) { 701 return false; 702 } 703 704 if (mUri == null) { 705 if (originalModel.mUri != null) { 706 return false; 707 } 708 } else if (!mUri.equals(originalModel.mUri)) { 709 return false; 710 } 711 712 if (mVisibility != originalModel.mVisibility) { 713 return false; 714 } 715 return true; 716 } 717 718 /** 719 * Sort and uniquify mReminderMinutes. 720 * 721 * @return true (for convenience of caller) 722 */ 723 public boolean normalizeReminders() { 724 if (mReminders.size() <= 1) { 725 return true; 726 } 727 728 // sort 729 Collections.sort(mReminders); 730 731 // remove duplicates 732 ReminderEntry prev = mReminders.get(mReminders.size()-1); 733 for (int i = mReminders.size()-2; i >= 0; --i) { 734 ReminderEntry cur = mReminders.get(i); 735 if (prev.equals(cur)) { 736 // match, remove later entry 737 mReminders.remove(i+1); 738 } 739 prev = cur; 740 } 741 742 return true; 743 } 744} 745