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