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}