CalendarContract.java revision 083cd2dfeb4b35b0b888921b02aa28464fa53165
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.provider;
18
19
20import android.app.AlarmManager;
21import android.app.PendingIntent;
22import android.content.ContentProviderClient;
23import android.content.ContentResolver;
24import android.content.ContentUris;
25import android.content.ContentValues;
26import android.content.Context;
27import android.content.CursorEntityIterator;
28import android.content.Entity;
29import android.content.EntityIterator;
30import android.content.Intent;
31import android.database.Cursor;
32import android.database.DatabaseUtils;
33import android.net.Uri;
34import android.os.RemoteException;
35import android.text.format.DateUtils;
36import android.text.format.Time;
37import android.util.Log;
38
39/**
40 * <p>
41 * The contract between the calendar provider and applications. Contains
42 * definitions for the supported URIs and data columns.
43 * </p>
44 * <h3>Overview</h3>
45 * <p>
46 * CalendarContract defines the data model of calendar and event related
47 * information. This data is stored in a number of tables:
48 * </p>
49 * <ul>
50 * <li>The {@link Calendars} table holds the calendar specific information. Each
51 * row in this table contains the details for a single calendar, such as the
52 * name, color, sync info, etc.</li>
53 * <li>The {@link Events} table holds the event specific information. Each row
54 * in this table has the info for a single event. It contains information such
55 * as event title, location, start time, end time, etc. The event can occur
56 * one-time or can recur multiple times. Attendees, reminders, and extended
57 * properties are stored on separate tables and reference the {@link Events#_ID}
58 * to link them with the event.</li>
59 * <li>The {@link Instances} table holds the start and end time for occurrences
60 * of an event. Each row in this table represents a single occurrence. For
61 * one-time events there will be a 1:1 mapping of instances to events. For
62 * recurring events, multiple rows will automatically be generated which
63 * correspond to multiple occurrences of that event.</li>
64 * <li>The {@link Attendees} table holds the event attendee or guest
65 * information. Each row represents a single guest of an event. It specifies the
66 * type of guest they are and their attendance response for the event.</li>
67 * <li>The {@link Reminders} table holds the alert/notification data. Each row
68 * represents a single alert for an event. An event can have multiple reminders.
69 * The number of reminders per event is specified in
70 * {@link Calendars#MAX_REMINDERS} which is set by the Sync Adapter that owns
71 * the given calendar. Reminders are specified in minutes before the event and
72 * have a type.</li>
73 * <li>The {@link ExtendedProperties} table hold opaque data fields used by the
74 * sync adapter. The provider takes no action with items in this table except to
75 * delete them when their related events are deleted.</li>
76 * </ul>
77 * <p>
78 * Other tables include:
79 * </p>
80 * <ul>
81 * <li>
82 * {@link SyncState}, which contains free-form data maintained by the sync
83 * adapters</li>
84 * </ul>
85 *
86 * @hide
87 */
88public final class CalendarContract {
89    private static final String TAG = "Calendar";
90
91    /**
92     * Broadcast Action: This is the intent that gets fired when an alarm
93     * notification needs to be posted for a reminder.
94     *
95     * @SdkConstant
96     */
97    public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER";
98
99    /**
100     * Intent Extras key: The start time of an event or an instance of a
101     * recurring event. (milliseconds since epoch)
102     */
103    public static final String EXTRA_EVENT_BEGIN_TIME = "beginTime";
104
105    /**
106     * Intent Extras key: The end time of an event or an instance of a recurring
107     * event. (milliseconds since epoch)
108     */
109    public static final String EXTRA_EVENT_END_TIME = "endTime";
110
111    /**
112     * This authority is used for writing to or querying from the calendar
113     * provider. Note: This is set at first run and cannot be changed without
114     * breaking apps that access the provider.
115     */
116    public static final String AUTHORITY = "com.android.calendar";
117
118    /**
119     * The content:// style URL for the top-level calendar authority
120     */
121    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
122
123    /**
124     * An optional insert, update or delete URI parameter that allows the caller
125     * to specify that it is a sync adapter. The default value is false. If set
126     * to true, the modified row is not marked as "dirty" (needs to be synced)
127     * and when the provider calls
128     * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
129     * , the third parameter "syncToNetwork" is set to false. Furthermore, if
130     * set to true, the caller must also include
131     * {@link Calendars#ACCOUNT_NAME} and {@link Calendars#ACCOUNT_TYPE} as
132     * query parameters.
133     *
134     * @see Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)
135     */
136    public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
137
138    /**
139     * A special account type for calendars not associated with any account.
140     * Normally calendars that do not match an account on the device will be
141     * removed. Setting the account_type on a calendar to this will prevent it
142     * from being wiped if it does not match an existing account.
143     *
144     * @see SyncColumns#ACCOUNT_TYPE
145     */
146    public static final String ACCOUNT_TYPE_LOCAL = "LOCAL";
147
148    /**
149     * Generic columns for use by sync adapters. The specific functions of these
150     * columns are private to the sync adapter. Other clients of the API should
151     * not attempt to either read or write this column. These columns are
152     * editable as part of the Calendars Uri, but can only be read if accessed
153     * through any other Uri.
154     */
155    protected interface CalendarSyncColumns {
156
157
158        /**
159         * Generic column for use by sync adapters. Column name.
160         * <P>Type: TEXT</P>
161         */
162        public static final String CAL_SYNC1 = "cal_sync1";
163
164        /**
165         * Generic column for use by sync adapters. Column name.
166         * <P>Type: TEXT</P>
167         */
168        public static final String CAL_SYNC2 = "cal_sync2";
169
170        /**
171         * Generic column for use by sync adapters. Column name.
172         * <P>Type: TEXT</P>
173         */
174        public static final String CAL_SYNC3 = "cal_sync3";
175
176        /**
177         * Generic column for use by sync adapters. Column name.
178         * <P>Type: TEXT</P>
179         */
180        public static final String CAL_SYNC4 = "cal_sync4";
181
182        /**
183         * Generic column for use by sync adapters. Column name.
184         * <P>Type: TEXT</P>
185         */
186        public static final String CAL_SYNC5 = "cal_sync5";
187
188        /**
189         * Generic column for use by sync adapters. Column name.
190         * <P>Type: TEXT</P>
191         */
192        public static final String CAL_SYNC6 = "cal_sync6";
193
194        /**
195         * Generic column for use by sync adapters. Column name.
196         * <P>Type: TEXT</P>
197         */
198        public static final String CAL_SYNC7 = "cal_sync7";
199
200        /**
201         * Generic column for use by sync adapters. Column name.
202         * <P>Type: TEXT</P>
203         */
204        public static final String CAL_SYNC8 = "cal_sync8";
205
206        /**
207         * Generic column for use by sync adapters. Column name.
208         * <P>Type: TEXT</P>
209         */
210        public static final String CAL_SYNC9 = "cal_sync9";
211
212        /**
213         * Generic column for use by sync adapters. Column name.
214         * <P>Type: TEXT</P>
215         */
216        public static final String CAL_SYNC10 = "cal_sync10";
217    }
218
219    /**
220     * Columns for Sync information used by Calendars and Events tables. These
221     * have specific uses which are expected to be consistent by the app and
222     * sync adapter.
223     *
224     */
225    protected interface SyncColumns extends CalendarSyncColumns {
226        /**
227         * The account that was used to sync the entry to the device. If the
228         * account_type is not {@link #ACCOUNT_TYPE_LOCAL} then the name and
229         * type must match an account on the device or the calendar will be
230         * deleted.
231         * <P>Type: TEXT</P>
232         */
233        public static final String ACCOUNT_NAME = "account_name";
234
235        /**
236         * The type of the account that was used to sync the entry to the
237         * device. A type of {@link #ACCOUNT_TYPE_LOCAL} will keep this event
238         * form being deleted if there are no matching accounts on the device.
239         * <P>Type: TEXT</P>
240         */
241        public static final String ACCOUNT_TYPE = "account_type";
242
243        /**
244         * The unique ID for a row assigned by the sync source. NULL if the row
245         * has never been synced. This is used as a reference id for exceptions
246         * along with {@link BaseColumns#_ID}.
247         * <P>Type: TEXT</P>
248         */
249        public static final String _SYNC_ID = "_sync_id";
250
251        /**
252         * Used to indicate that local, unsynced, changes are present.
253         * <P>Type: INTEGER (long)</P>
254         */
255        public static final String DIRTY = "dirty";
256
257        /**
258         * Whether the row has been deleted but not synced to the server. A
259         * deleted row should be ignored.
260         * <P>
261         * Type: INTEGER (boolean)
262         * </P>
263         */
264        public static final String DELETED = "deleted";
265
266        /**
267         * If set to 1 this causes events on this calendar to be duplicated with
268         * {@link Events#LAST_SYNCED} set to 1 whenever the event
269         * transitions from non-dirty to dirty. The duplicated event will not be
270         * expanded in the instances table and will only show up in sync adapter
271         * queries of the events table. It will also be deleted when the
272         * originating event has its dirty flag cleared by the sync adapter.
273         * <P>Type: INTEGER (boolean)</P>
274         */
275        public static final String CAN_PARTIALLY_UPDATE = "canPartiallyUpdate";
276    }
277
278    /**
279     * Columns specific to the Calendars Uri that other Uris can query.
280     */
281    protected interface CalendarColumns {
282        /**
283         * The color of the calendar
284         * <P>Type: INTEGER (color value)</P>
285         */
286        public static final String CALENDAR_COLOR = "calendar_color";
287
288        /**
289         * The display name of the calendar. Column name.
290         * <P>Type: TEXT</P>
291         */
292        public static final String CALENDAR_DISPLAY_NAME = "calendar_displayName";
293
294        /**
295         * The level of access that the user has for the calendar
296         * <P>Type: INTEGER (one of the values below)</P>
297         */
298        public static final String CALENDAR_ACCESS_LEVEL = "calendar_access_level";
299
300        /** Cannot access the calendar */
301        public static final int CAL_ACCESS_NONE = 0;
302        /** Can only see free/busy information about the calendar */
303        public static final int CAL_ACCESS_FREEBUSY = 100;
304        /** Can read all event details */
305        public static final int CAL_ACCESS_READ = 200;
306        /** Can reply yes/no/maybe to an event */
307        public static final int CAL_ACCESS_RESPOND = 300;
308        /** not used */
309        public static final int CAL_ACCESS_OVERRIDE = 400;
310        /** Full access to modify the calendar, but not the access control
311         * settings
312         */
313        public static final int CAL_ACCESS_CONTRIBUTOR = 500;
314        /** Full access to modify the calendar, but not the access control
315         * settings
316         */
317        public static final int CAL_ACCESS_EDITOR = 600;
318        /** Full access to the calendar */
319        public static final int CAL_ACCESS_OWNER = 700;
320        /** Domain admin */
321        public static final int CAL_ACCESS_ROOT = 800;
322
323        /**
324         * Is the calendar selected to be displayed?
325         * 0 - do not show events associated with this calendar.
326         * 1 - show events associated with this calendar
327         * <P>Type: INTEGER (boolean)</P>
328         */
329        public static final String VISIBLE = "visible";
330
331        /**
332         * The time zone the calendar is associated with.
333         * <P>Type: TEXT</P>
334         */
335        public static final String CALENDAR_TIME_ZONE = "calendar_timezone";
336
337        /**
338         * Is this calendar synced and are its events stored on the device?
339         * 0 - Do not sync this calendar or store events for this calendar.
340         * 1 - Sync down events for this calendar.
341         * <p>Type: INTEGER (boolean)</p>
342         */
343        public static final String SYNC_EVENTS = "sync_events";
344
345        /**
346         * The owner account for this calendar, based on the calendar feed.
347         * This will be different from the _SYNC_ACCOUNT for delegated calendars.
348         * Column name.
349         * <P>Type: String</P>
350         */
351        public static final String OWNER_ACCOUNT = "ownerAccount";
352
353        /**
354         * Can the organizer respond to the event?  If no, the status of the
355         * organizer should not be shown by the UI.  Defaults to 1. Column name.
356         * <P>Type: INTEGER (boolean)</P>
357         */
358        public static final String CAN_ORGANIZER_RESPOND = "canOrganizerRespond";
359
360        /**
361         * Can the organizer modify the time zone of the event? Column name.
362         * <P>Type: INTEGER (boolean)</P>
363        */
364        public static final String CAN_MODIFY_TIME_ZONE = "canModifyTimeZone";
365
366        /**
367         * The maximum number of reminders allowed for an event. Column name.
368         * <P>Type: INTEGER</P>
369         */
370        public static final String MAX_REMINDERS = "maxReminders";
371
372        /**
373         * A comma separated list of reminder methods supported for this
374         * calendar in the format "#,#,#". Valid types are
375         * {@link Reminders#METHOD_DEFAULT}, {@link Reminders#METHOD_ALERT},
376         * {@link Reminders#METHOD_EMAIL}, {@link Reminders#METHOD_SMS}. Column
377         * name.
378         * <P>Type: TEXT</P>
379         */
380        public static final String ALLOWED_REMINDERS = "allowedReminders";
381    }
382
383    /**
384     * Class that represents a Calendar Entity. There is one entry per calendar.
385     * This is a helper class to make batch operations easier.
386     */
387    public static class CalendarEntity implements BaseColumns, SyncColumns, CalendarColumns {
388
389        /**
390         * The default Uri used when creating a new calendar EntityIterator.
391         */
392        @SuppressWarnings("hiding")
393        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
394                "/calendar_entities");
395
396        /**
397         * Creates an entity iterator for the given cursor. It assumes the
398         * cursor contains a calendars query.
399         *
400         * @param cursor query on {@link #CONTENT_URI}
401         * @return an EntityIterator of calendars
402         */
403        public static EntityIterator newEntityIterator(Cursor cursor) {
404            return new EntityIteratorImpl(cursor);
405        }
406
407        private static class EntityIteratorImpl extends CursorEntityIterator {
408
409            public EntityIteratorImpl(Cursor cursor) {
410                super(cursor);
411            }
412
413            @Override
414            public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException {
415                // we expect the cursor is already at the row we need to read from
416                final long calendarId = cursor.getLong(cursor.getColumnIndexOrThrow(_ID));
417
418                // Create the content value
419                ContentValues cv = new ContentValues();
420                cv.put(_ID, calendarId);
421
422                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_NAME);
423                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_TYPE);
424
425                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
426                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
427
428                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1);
429                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2);
430                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC3);
431                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC4);
432                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC5);
433                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC6);
434                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC7);
435                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC8);
436                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC9);
437                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC10);
438
439                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
440                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
441                        Calendars.CALENDAR_DISPLAY_NAME);
442                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
443                        Calendars.CALENDAR_COLOR);
444                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, CALENDAR_ACCESS_LEVEL);
445                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE);
446                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
447                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
448                        Calendars.CALENDAR_LOCATION);
449                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CALENDAR_TIME_ZONE);
450                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
451                        Calendars.OWNER_ACCOUNT);
452                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
453                        Calendars.CAN_ORGANIZER_RESPOND);
454                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
455                        Calendars.CAN_MODIFY_TIME_ZONE);
456                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
457                        Calendars.MAX_REMINDERS);
458                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
459                        Calendars.CAN_PARTIALLY_UPDATE);
460                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
461                        Calendars.ALLOWED_REMINDERS);
462
463                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
464
465                // Create the Entity from the ContentValue
466                Entity entity = new Entity(cv);
467
468                // Set cursor to next row
469                cursor.moveToNext();
470
471                // Return the created Entity
472                return entity;
473            }
474        }
475     }
476
477    /**
478     * Constants and helpers for the Calendars table, which contains details for
479     * individual calendars. <h3>Operations</h3> All operations can be done
480     * either as an app or as a sync adapter. To perform an operation as a sync
481     * adapter {@link #CALLER_IS_SYNCADAPTER} should be set to true and
482     * {@link #ACCOUNT_NAME} and {@link #ACCOUNT_TYPE} must be set in the Uri
483     * parameters. See
484     * {@link Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)}
485     * for details on adding parameters. Sync adapters have write access to more
486     * columns but are restricted to a single account at a time. Calendars are
487     * designed to be primarily managed by a sync adapter and inserting new
488     * calendars should be done as a sync adapter. For the most part, apps
489     * should only update calendars (such as changing the color or display
490     * name). If a local calendar is required an app can do so by inserting as a
491     * sync adapter and using an {@link #ACCOUNT_TYPE} of
492     * {@link #ACCOUNT_TYPE_LOCAL} .
493     * <dl>
494     * <dt><b>Insert</b></dt>
495     * <dd>When inserting a new calendar the following fields must be included:
496     * <ul>
497     * <li>{@link #ACCOUNT_NAME}</li>
498     * <li>{@link #ACCOUNT_TYPE}</li>
499     * <li>{@link #NAME}</li>
500     * <li>{@link #CALENDAR_DISPLAY_NAME}</li>
501     * <li>{@link #CALENDAR_COLOR}</li>
502     * <li>{@link #CALENDAR_ACCESS_LEVEL}</li>
503     * <li>{@link #OWNER_ACCOUNT}</li>
504     * </ul>
505     * The following fields are not required when inserting a Calendar but are
506     * generally a good idea to include:
507     * <ul>
508     * <li>{@link #SYNC_EVENTS} set to 1</li>
509     * <li>{@link #CALENDAR_TIME_ZONE}</li>
510     * <li>{@link #ALLOWED_REMINDERS}</li>
511     * </ul>
512     * <dt><b>Update</b></dt>
513     * <dd>To perform an update on a calendar the {@link #_ID} of the calendar
514     * should be provided either as an appended id to the Uri (
515     * {@link ContentUris#withAppendedId}) or as the first selection item--the
516     * selection should start with "_id=?" and the first selectionArg should be
517     * the _id of the calendar. Calendars may also be updated using a selection
518     * without the id. In general, the {@link #ACCOUNT_NAME} and
519     * {@link #ACCOUNT_TYPE} should not be changed after a calendar is created
520     * as this can cause issues for sync adapters.
521     * <dt><b>Delete</b></dt>
522     * <dd>Calendars can be deleted either by the {@link #_ID} as an appended id
523     * on the Uri or using any standard selection. Deleting a calendar should
524     * generally be handled by a sync adapter as it will remove the calendar
525     * from the database and all associated data (aka events).</dd>
526     * <dt><b>Query</b></dt>
527     * <dd>Querying the Calendars table will get you all information about a set
528     * of calendars. There will be one row returned for each calendar that
529     * matches the query selection, or at most a single row if the {@link #_ID}
530     * is appended to the Uri.</dd>
531     * </dl>
532     * <h3>Calendar Columns</h3> The following Calendar columns are writable by
533     * both an app and a sync adapter.
534     * <ul>
535     * <li>{@link #NAME}</li>
536     * <li>{@link #CALENDAR_DISPLAY_NAME}</li>
537     * <li>{@link #CALENDAR_COLOR}</li>
538     * <li>{@link #VISIBLE}</li>
539     * <li>{@link #SYNC_EVENTS}</li>
540     * </ul>
541     * The following Calendars columns are writable only by a sync adapter
542     * <ul>
543     * <li>{@link #ACCOUNT_NAME}</li>
544     * <li>{@link #ACCOUNT_TYPE}</li>
545     * <li>{@link #_SYNC_ID}</li>
546     * <li>{@link #DIRTY}</li>
547     * <li>{@link #OWNER_ACCOUNT}</li>
548     * <li>{@link #MAX_REMINDERS}</li>
549     * <li>{@link #ALLOWED_REMINDERS}</li>
550     * <li>{@link #CAN_MODIFY_TIME_ZONE}</li>
551     * <li>{@link #CAN_ORGANIZER_RESPOND}</li>
552     * <li>{@link #CAN_PARTIALLY_UPDATE}</li>
553     * <li>{@link #CALENDAR_LOCATION}</li>
554     * <li>{@link #CALENDAR_TIME_ZONE}</li>
555     * <li>{@link #CALENDAR_ACCESS_LEVEL}</li>
556     * <li>{@link #DELETED}</li>
557     * <li>{@link #CAL_SYNC1}</li>
558     * <li>{@link #CAL_SYNC2}</li>
559     * <li>{@link #CAL_SYNC3}</li>
560     * <li>{@link #CAL_SYNC4}</li>
561     * <li>{@link #CAL_SYNC5}</li>
562     * <li>{@link #CAL_SYNC6}</li>
563     * <li>{@link #CAL_SYNC7}</li>
564     * <li>{@link #CAL_SYNC8}</li>
565     * <li>{@link #CAL_SYNC9}</li>
566     * <li>{@link #CAL_SYNC10}</li>
567     * </ul>
568     */
569    public static class Calendars implements BaseColumns, SyncColumns, CalendarColumns {
570        /**
571         * The content:// style URL for accessing Calendars
572         */
573        @SuppressWarnings("hiding")
574        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars");
575
576        /**
577         * The default sort order for this table
578         */
579        public static final String DEFAULT_SORT_ORDER = "displayName";
580
581        /**
582         * The name of the calendar. Column name.
583         * <P>Type: TEXT</P>
584         */
585        public static final String NAME = "name";
586
587        /**
588         * The default location for the calendar. Column name.
589         * <P>Type: TEXT</P>
590         */
591        public static final String CALENDAR_LOCATION = "calendar_location";
592
593        /**
594         * These fields are only writable by a sync adapter. To modify them the
595         * caller must include {@link #CALLER_IS_SYNCADAPTER},
596         * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the Uri's query
597         * parameters. TODO move to provider
598         *
599         * @hide
600         */
601        public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
602            ACCOUNT_NAME,
603            ACCOUNT_TYPE,
604            _SYNC_ID,
605            DIRTY,
606            OWNER_ACCOUNT,
607            MAX_REMINDERS,
608            ALLOWED_REMINDERS,
609            CAN_MODIFY_TIME_ZONE,
610            CAN_ORGANIZER_RESPOND,
611            CAN_PARTIALLY_UPDATE,
612            CALENDAR_LOCATION,
613            CALENDAR_TIME_ZONE,
614            CALENDAR_ACCESS_LEVEL,
615            DELETED,
616            CAL_SYNC1,
617            CAL_SYNC2,
618            CAL_SYNC3,
619            CAL_SYNC4,
620            CAL_SYNC5,
621            CAL_SYNC6,
622            CAL_SYNC7,
623            CAL_SYNC8,
624            CAL_SYNC9,
625            CAL_SYNC10,
626        };
627    }
628
629    /**
630     * Columns from the Attendees table that other tables join into themselves.
631     */
632    protected interface AttendeesColumns {
633
634        /**
635         * The id of the event. Column name.
636         * <P>Type: INTEGER</P>
637         */
638        public static final String EVENT_ID = "event_id";
639
640        /**
641         * The name of the attendee. Column name.
642         * <P>Type: STRING</P>
643         */
644        public static final String ATTENDEE_NAME = "attendeeName";
645
646        /**
647         * The email address of the attendee. Column name.
648         * <P>Type: STRING</P>
649         */
650        public static final String ATTENDEE_EMAIL = "attendeeEmail";
651
652        /**
653         * The relationship of the attendee to the user. Column name.
654         * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.</P>
655         */
656        public static final String ATTENDEE_RELATIONSHIP = "attendeeRelationship";
657
658        public static final int RELATIONSHIP_NONE = 0;
659        public static final int RELATIONSHIP_ATTENDEE = 1;
660        public static final int RELATIONSHIP_ORGANIZER = 2;
661        public static final int RELATIONSHIP_PERFORMER = 3;
662        public static final int RELATIONSHIP_SPEAKER = 4;
663
664        /**
665         * The type of attendee. Column name.
666         * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL})</P>
667         */
668        public static final String ATTENDEE_TYPE = "attendeeType";
669
670        public static final int TYPE_NONE = 0;
671        public static final int TYPE_REQUIRED = 1;
672        public static final int TYPE_OPTIONAL = 2;
673
674        /**
675         * The attendance status of the attendee. Column name.
676         * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...).</P>
677         */
678        public static final String ATTENDEE_STATUS = "attendeeStatus";
679
680        public static final int ATTENDEE_STATUS_NONE = 0;
681        public static final int ATTENDEE_STATUS_ACCEPTED = 1;
682        public static final int ATTENDEE_STATUS_DECLINED = 2;
683        public static final int ATTENDEE_STATUS_INVITED = 3;
684        public static final int ATTENDEE_STATUS_TENTATIVE = 4;
685    }
686
687    /**
688     * Fields and helpers for interacting with Attendees. Each row of this table
689     * represents a single attendee or guest of an event. Calling
690     * {@link #query(ContentResolver, long)} will return a list of attendees for
691     * the event with the given eventId. Both apps and sync adapters may write
692     * to this table. There are six writable fields and all of them except
693     * {@link #ATTENDEE_NAME} must be included when inserting a new attendee.
694     * They are:
695     * <ul>
696     * <li>{@link #EVENT_ID}</li>
697     * <li>{@link #ATTENDEE_NAME}</li>
698     * <li>{@link #ATTENDEE_EMAIL}</li>
699     * <li>{@link #ATTENDEE_RELATIONSHIP}</li>
700     * <li>{@link #ATTENDEE_TYPE}</li>
701     * <li>{@link #ATTENDEE_STATUS}</li>
702     * </ul>
703     */
704    public static final class Attendees implements BaseColumns, AttendeesColumns, EventsColumns {
705
706        /**
707         * The content:// style URL for accessing Attendees data
708         */
709        @SuppressWarnings("hiding")
710        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
711        /**
712         * the projection used by the attendees query
713         */
714        public static final String[] PROJECTION = new String[] {
715                _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,};
716        private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
717
718        /**
719         * Queries all attendees associated with the given event. This is a
720         * blocking call and should not be done on the UI thread.
721         *
722         * @param cr The content resolver to use for the query
723         * @param eventId The id of the event to retrieve attendees for
724         * @return A Cursor containing all attendees for the event
725         */
726        public static final Cursor query(ContentResolver cr, long eventId) {
727            String[] attArgs = {Long.toString(eventId)};
728            return cr.query(CONTENT_URI, PROJECTION, ATTENDEES_WHERE, attArgs /* selection args */,
729                    null /* sort order */);
730        }
731    }
732
733    /**
734     * Columns from the Events table that other tables join into themselves.
735     */
736    protected interface EventsColumns {
737
738        /**
739         * The {@link Calendars#_ID} of the calendar the event belongs to.
740         * Column name.
741         * <P>Type: INTEGER</P>
742         */
743        public static final String CALENDAR_ID = "calendar_id";
744
745        /**
746         * The title of the event. Column name.
747         * <P>Type: TEXT</P>
748         */
749        public static final String TITLE = "title";
750
751        /**
752         * The description of the event. Column name.
753         * <P>Type: TEXT</P>
754         */
755        public static final String DESCRIPTION = "description";
756
757        /**
758         * Where the event takes place. Column name.
759         * <P>Type: TEXT</P>
760         */
761        public static final String EVENT_LOCATION = "eventLocation";
762
763        /**
764         * A secondary color for the individual event. Column name.
765         * <P>Type: INTEGER</P>
766         */
767        public static final String EVENT_COLOR = "eventColor";
768
769        /**
770         * The event status. Column name.
771         * <P>Type: INTEGER (one of {@link #STATUS_TENTATIVE}...)</P>
772         */
773        public static final String STATUS = "eventStatus";
774
775        public static final int STATUS_TENTATIVE = 0;
776        public static final int STATUS_CONFIRMED = 1;
777        public static final int STATUS_CANCELED = 2;
778
779        /**
780         * This is a copy of the attendee status for the owner of this event.
781         * This field is copied here so that we can efficiently filter out
782         * events that are declined without having to look in the Attendees
783         * table. Column name.
784         *
785         * <P>Type: INTEGER (int)</P>
786         */
787        public static final String SELF_ATTENDEE_STATUS = "selfAttendeeStatus";
788
789        /**
790         * This column is available for use by sync adapters. Column name.
791         * <P>Type: TEXT</P>
792         */
793        public static final String SYNC_DATA1 = "sync_data1";
794
795        /**
796         * This column is available for use by sync adapters. Column name.
797         * <P>Type: TEXT</P>
798         */
799        public static final String SYNC_DATA2 = "sync_data2";
800
801        /**
802         * This column is available for use by sync adapters. Column name.
803         * <P>Type: TEXT</P>
804         */
805        public static final String SYNC_DATA3 = "sync_data3";
806
807        /**
808         * This column is available for use by sync adapters. Column name.
809         * <P>Type: TEXT</P>
810         */
811        public static final String SYNC_DATA4 = "sync_data4";
812
813        /**
814         * This column is available for use by sync adapters. Column name.
815         * <P>Type: TEXT</P>
816         */
817        public static final String SYNC_DATA5 = "sync_data5";
818
819        /**
820         * This column is available for use by sync adapters. Column name.
821         * <P>Type: TEXT</P>
822         */
823        public static final String SYNC_DATA6 = "sync_data6";
824
825        /**
826         * This column is available for use by sync adapters. Column name.
827         * <P>Type: TEXT</P>
828         */
829        public static final String SYNC_DATA7 = "sync_data7";
830
831        /**
832         * This column is available for use by sync adapters. Column name.
833         * <P>Type: TEXT</P>
834         */
835        public static final String SYNC_DATA8 = "sync_data8";
836
837        /**
838         * This column is available for use by sync adapters. Column name.
839         * <P>Type: TEXT</P>
840         */
841        public static final String SYNC_DATA9 = "sync_data9";
842
843        /**
844         * This column is available for use by sync adapters. Column name.
845         * <P>Type: TEXT</P>
846         */
847        public static final String SYNC_DATA10 = "sync_data10";
848
849        /**
850         * Used to indicate that a row is not a real event but an original copy of a locally
851         * modified event. A copy is made when an event changes from non-dirty to dirty and the
852         * event is on a calendar with {@link Calendars#CAN_PARTIALLY_UPDATE} set to 1. This copy
853         * does not get expanded in the instances table and is only visible in queries made by a
854         * sync adapter. The copy gets removed when the event is changed back to non-dirty by a
855         * sync adapter.
856         * <P>Type: INTEGER (boolean)</P>
857         */
858        public static final String LAST_SYNCED = "lastSynced";
859
860        /**
861         * The time the event starts in UTC millis since epoch. Column name.
862         * <P>Type: INTEGER (long; millis since epoch)</P>
863         */
864        public static final String DTSTART = "dtstart";
865
866        /**
867         * The time the event ends in UTC millis since epoch. Column name.
868         * <P>Type: INTEGER (long; millis since epoch)</P>
869         */
870        public static final String DTEND = "dtend";
871
872        /**
873         * The duration of the event in RFC2445 format. Column name.
874         * <P>Type: TEXT (duration in RFC2445 format)</P>
875         */
876        public static final String DURATION = "duration";
877
878        /**
879         * The timezone for the event. Column name.
880         * <P>Type: TEXT</P>
881         */
882        public static final String EVENT_TIMEZONE = "eventTimezone";
883
884        /**
885         * The timezone for the end time of the event. Column name.
886         * <P>Type: TEXT</P>
887         */
888        public static final String EVENT_END_TIMEZONE = "eventEndTimezone";
889
890        /**
891         * Is the event all day (time zone independent). Column name.
892         * <P>Type: INTEGER (boolean)</P>
893         */
894        public static final String ALL_DAY = "allDay";
895
896        /**
897         * Defines how the event shows up for others when the calendar is
898         * shared. Column name.
899         * <P>Type: INTEGER (One of {@link #ACCESS_DEFAULT}, ...)</P>
900         */
901        public static final String ACCESS_LEVEL = "accessLevel";
902
903        /**
904         * Default access is controlled by the server and will be treated as
905         * public on the device.
906         */
907        public static final int ACCESS_DEFAULT = 0;
908        /**
909         * Confidential is not used by the app.
910         */
911        public static final int ACCESS_CONFIDENTIAL = 1;
912        /**
913         * Private shares the event as a free/busy slot with no details.
914         */
915        public static final int ACCESS_PRIVATE = 2;
916        /**
917         * Public makes the contents visible to anyone with access to the
918         * calendar.
919         */
920        public static final int ACCESS_PUBLIC = 3;
921
922        /**
923         * If this event counts as busy time or is still free time that can be
924         * scheduled over. Column name.
925         * <P>Type: INTEGER (One of {@link #AVAILABILITY_BUSY},
926         * {@link #AVAILABILITY_FREE})</P>
927         */
928        public static final String AVAILABILITY = "availability";
929
930        /**
931         * Indicates that this event takes up time and will conflict with other
932         * events.
933         */
934        public static final int AVAILABILITY_BUSY = 0;
935        /**
936         * Indicates that this event is free time and will not conflict with
937         * other events.
938         */
939        public static final int AVAILABILITY_FREE = 1;
940
941        /**
942         * Whether the event has an alarm or not. Column name.
943         * <P>Type: INTEGER (boolean)</P>
944         */
945        public static final String HAS_ALARM = "hasAlarm";
946
947        /**
948         * Whether the event has extended properties or not. Column name.
949         * <P>Type: INTEGER (boolean)</P>
950         */
951        public static final String HAS_EXTENDED_PROPERTIES = "hasExtendedProperties";
952
953        /**
954         * The recurrence rule for the event. Column name.
955         * <P>Type: TEXT</P>
956         */
957        public static final String RRULE = "rrule";
958
959        /**
960         * The recurrence dates for the event. Column name.
961         * <P>Type: TEXT</P>
962         */
963        public static final String RDATE = "rdate";
964
965        /**
966         * The recurrence exception rule for the event. Column name.
967         * <P>Type: TEXT</P>
968         */
969        public static final String EXRULE = "exrule";
970
971        /**
972         * The recurrence exception dates for the event. Column name.
973         * <P>Type: TEXT</P>
974         */
975        public static final String EXDATE = "exdate";
976
977        /**
978         * The {@link Events#_ID} of the original recurring event for which this
979         * event is an exception. Column name.
980         * <P>Type: TEXT</P>
981         */
982        public static final String ORIGINAL_ID = "original_id";
983
984        /**
985         * The _sync_id of the original recurring event for which this event is
986         * an exception. The provider should keep the original_id in sync when
987         * this is updated. Column name.
988         * <P>Type: TEXT</P>
989         */
990        public static final String ORIGINAL_SYNC_ID = "original_sync_id";
991
992        /**
993         * The original instance time of the recurring event for which this
994         * event is an exception. Column name.
995         * <P>Type: INTEGER (long; millis since epoch)</P>
996         */
997        public static final String ORIGINAL_INSTANCE_TIME = "originalInstanceTime";
998
999        /**
1000         * The allDay status (true or false) of the original recurring event
1001         * for which this event is an exception. Column name.
1002         * <P>Type: INTEGER (boolean)</P>
1003         */
1004        public static final String ORIGINAL_ALL_DAY = "originalAllDay";
1005
1006        /**
1007         * The last date this event repeats on, or NULL if it never ends. Column
1008         * name.
1009         * <P>Type: INTEGER (long; millis since epoch)</P>
1010         */
1011        public static final String LAST_DATE = "lastDate";
1012
1013        /**
1014         * Whether the event has attendee information.  True if the event
1015         * has full attendee data, false if the event has information about
1016         * self only. Column name.
1017         * <P>Type: INTEGER (boolean)</P>
1018         */
1019        public static final String HAS_ATTENDEE_DATA = "hasAttendeeData";
1020
1021        /**
1022         * Whether guests can modify the event. Column name.
1023         * <P>Type: INTEGER (boolean)</P>
1024         */
1025        public static final String GUESTS_CAN_MODIFY = "guestsCanModify";
1026
1027        /**
1028         * Whether guests can invite other guests. Column name.
1029         * <P>Type: INTEGER (boolean)</P>
1030         */
1031        public static final String GUESTS_CAN_INVITE_OTHERS = "guestsCanInviteOthers";
1032
1033        /**
1034         * Whether guests can see the list of attendees. Column name.
1035         * <P>Type: INTEGER (boolean)</P>
1036         */
1037        public static final String GUESTS_CAN_SEE_GUESTS = "guestsCanSeeGuests";
1038
1039        /**
1040         * Email of the organizer (owner) of the event. Column name.
1041         * <P>Type: STRING</P>
1042         */
1043        public static final String ORGANIZER = "organizer";
1044
1045        /**
1046         * Whether the user can invite others to the event. The
1047         * GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary
1048         * guest, while CAN_INVITE_OTHERS indicates if the user can invite
1049         * others (either through GUESTS_CAN_INVITE_OTHERS or because the user
1050         * has modify access to the event). Column name.
1051         * <P>Type: INTEGER (boolean, readonly)</P>
1052         */
1053        public static final String CAN_INVITE_OTHERS = "canInviteOthers";
1054    }
1055
1056    /**
1057     * Class that represents an Event Entity. There is one entry per event.
1058     * Recurring events show up as a single entry. This is a helper class to
1059     * make batch operations easier. A {@link ContentResolver} or
1060     * {@link ContentProviderClient} is required as the helper does additional
1061     * queries to add reminders and attendees to each entry.
1062     */
1063    public static final class EventsEntity implements BaseColumns, SyncColumns, EventsColumns {
1064        /**
1065         * The content:// style URL for this table
1066         */
1067        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
1068                "/event_entities");
1069
1070        /**
1071         * Creates a new iterator for events
1072         *
1073         * @param cursor An event query
1074         * @param resolver For performing additional queries
1075         * @return an EntityIterator containing one entity per event in the
1076         *         cursor
1077         */
1078        public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
1079            return new EntityIteratorImpl(cursor, resolver);
1080        }
1081
1082        /**
1083         * Creates a new iterator for events
1084         *
1085         * @param cursor An event query
1086         * @param provider For performing additional queries
1087         * @return an EntityIterator containing one entity per event in the
1088         *         cursor
1089         */
1090        public static EntityIterator newEntityIterator(Cursor cursor,
1091                ContentProviderClient provider) {
1092            return new EntityIteratorImpl(cursor, provider);
1093        }
1094
1095        private static class EntityIteratorImpl extends CursorEntityIterator {
1096            private final ContentResolver mResolver;
1097            private final ContentProviderClient mProvider;
1098
1099            private static final String[] REMINDERS_PROJECTION = new String[] {
1100                    Reminders.MINUTES,
1101                    Reminders.METHOD,
1102            };
1103            private static final int COLUMN_MINUTES = 0;
1104            private static final int COLUMN_METHOD = 1;
1105
1106            private static final String[] ATTENDEES_PROJECTION = new String[] {
1107                    Attendees.ATTENDEE_NAME,
1108                    Attendees.ATTENDEE_EMAIL,
1109                    Attendees.ATTENDEE_RELATIONSHIP,
1110                    Attendees.ATTENDEE_TYPE,
1111                    Attendees.ATTENDEE_STATUS,
1112            };
1113            private static final int COLUMN_ATTENDEE_NAME = 0;
1114            private static final int COLUMN_ATTENDEE_EMAIL = 1;
1115            private static final int COLUMN_ATTENDEE_RELATIONSHIP = 2;
1116            private static final int COLUMN_ATTENDEE_TYPE = 3;
1117            private static final int COLUMN_ATTENDEE_STATUS = 4;
1118            private static final String[] EXTENDED_PROJECTION = new String[] {
1119                    ExtendedProperties._ID,
1120                    ExtendedProperties.NAME,
1121                    ExtendedProperties.VALUE
1122            };
1123            private static final int COLUMN_ID = 0;
1124            private static final int COLUMN_NAME = 1;
1125            private static final int COLUMN_VALUE = 2;
1126
1127            private static final String WHERE_EVENT_ID = "event_id=?";
1128
1129            public EntityIteratorImpl(Cursor cursor, ContentResolver resolver) {
1130                super(cursor);
1131                mResolver = resolver;
1132                mProvider = null;
1133            }
1134
1135            public EntityIteratorImpl(Cursor cursor, ContentProviderClient provider) {
1136                super(cursor);
1137                mResolver = null;
1138                mProvider = provider;
1139            }
1140
1141            @Override
1142            public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException {
1143                // we expect the cursor is already at the row we need to read from
1144                final long eventId = cursor.getLong(cursor.getColumnIndexOrThrow(Events._ID));
1145                ContentValues cv = new ContentValues();
1146                cv.put(Events._ID, eventId);
1147                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, CALENDAR_ID);
1148                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, TITLE);
1149                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DESCRIPTION);
1150                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_LOCATION);
1151                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, STATUS);
1152                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SELF_ATTENDEE_STATUS);
1153                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTSTART);
1154                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTEND);
1155                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION);
1156                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE);
1157                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_END_TIMEZONE);
1158                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY);
1159                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
1160                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY);
1161                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, HAS_ALARM);
1162                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
1163                        HAS_EXTENDED_PROPERTIES);
1164                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, RRULE);
1165                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, RDATE);
1166                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE);
1167                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE);
1168                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_SYNC_ID);
1169                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_ID);
1170                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv,
1171                        ORIGINAL_INSTANCE_TIME);
1172                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY);
1173                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_DATE);
1174                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, HAS_ATTENDEE_DATA);
1175                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
1176                        GUESTS_CAN_INVITE_OTHERS);
1177                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, GUESTS_CAN_MODIFY);
1178                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, GUESTS_CAN_SEE_GUESTS);
1179                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER);
1180                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
1181                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
1182                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_SYNCED);
1183                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
1184                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA1);
1185                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA2);
1186                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA3);
1187                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA4);
1188                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA5);
1189                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA6);
1190                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA7);
1191                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA8);
1192                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA9);
1193                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA10);
1194                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1);
1195                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2);
1196                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC3);
1197                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC4);
1198                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC5);
1199                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC6);
1200                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC7);
1201                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC8);
1202                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC9);
1203                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC10);
1204
1205                Entity entity = new Entity(cv);
1206                Cursor subCursor;
1207                if (mResolver != null) {
1208                    subCursor = mResolver.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION,
1209                            WHERE_EVENT_ID,
1210                            new String[] { Long.toString(eventId) }  /* selectionArgs */,
1211                            null /* sortOrder */);
1212                } else {
1213                    subCursor = mProvider.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION,
1214                            WHERE_EVENT_ID,
1215                            new String[] { Long.toString(eventId) }  /* selectionArgs */,
1216                            null /* sortOrder */);
1217                }
1218                try {
1219                    while (subCursor.moveToNext()) {
1220                        ContentValues reminderValues = new ContentValues();
1221                        reminderValues.put(Reminders.MINUTES, subCursor.getInt(COLUMN_MINUTES));
1222                        reminderValues.put(Reminders.METHOD, subCursor.getInt(COLUMN_METHOD));
1223                        entity.addSubValue(Reminders.CONTENT_URI, reminderValues);
1224                    }
1225                } finally {
1226                    subCursor.close();
1227                }
1228
1229                if (mResolver != null) {
1230                    subCursor = mResolver.query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION,
1231                            WHERE_EVENT_ID,
1232                            new String[] { Long.toString(eventId) } /* selectionArgs */,
1233                            null /* sortOrder */);
1234                } else {
1235                    subCursor = mProvider.query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION,
1236                            WHERE_EVENT_ID,
1237                            new String[] { Long.toString(eventId) } /* selectionArgs */,
1238                            null /* sortOrder */);
1239                }
1240                try {
1241                    while (subCursor.moveToNext()) {
1242                        ContentValues attendeeValues = new ContentValues();
1243                        attendeeValues.put(Attendees.ATTENDEE_NAME,
1244                                subCursor.getString(COLUMN_ATTENDEE_NAME));
1245                        attendeeValues.put(Attendees.ATTENDEE_EMAIL,
1246                                subCursor.getString(COLUMN_ATTENDEE_EMAIL));
1247                        attendeeValues.put(Attendees.ATTENDEE_RELATIONSHIP,
1248                                subCursor.getInt(COLUMN_ATTENDEE_RELATIONSHIP));
1249                        attendeeValues.put(Attendees.ATTENDEE_TYPE,
1250                                subCursor.getInt(COLUMN_ATTENDEE_TYPE));
1251                        attendeeValues.put(Attendees.ATTENDEE_STATUS,
1252                                subCursor.getInt(COLUMN_ATTENDEE_STATUS));
1253                        entity.addSubValue(Attendees.CONTENT_URI, attendeeValues);
1254                    }
1255                } finally {
1256                    subCursor.close();
1257                }
1258
1259                if (mResolver != null) {
1260                    subCursor = mResolver.query(ExtendedProperties.CONTENT_URI, EXTENDED_PROJECTION,
1261                            WHERE_EVENT_ID,
1262                            new String[] { Long.toString(eventId) } /* selectionArgs */,
1263                            null /* sortOrder */);
1264                } else {
1265                    subCursor = mProvider.query(ExtendedProperties.CONTENT_URI, EXTENDED_PROJECTION,
1266                            WHERE_EVENT_ID,
1267                            new String[] { Long.toString(eventId) } /* selectionArgs */,
1268                            null /* sortOrder */);
1269                }
1270                try {
1271                    while (subCursor.moveToNext()) {
1272                        ContentValues extendedValues = new ContentValues();
1273                        extendedValues.put(ExtendedProperties._ID,
1274                                subCursor.getString(COLUMN_ID));
1275                        extendedValues.put(ExtendedProperties.NAME,
1276                                subCursor.getString(COLUMN_NAME));
1277                        extendedValues.put(ExtendedProperties.VALUE,
1278                                subCursor.getString(COLUMN_VALUE));
1279                        entity.addSubValue(ExtendedProperties.CONTENT_URI, extendedValues);
1280                    }
1281                } finally {
1282                    subCursor.close();
1283                }
1284
1285                cursor.moveToNext();
1286                return entity;
1287            }
1288        }
1289    }
1290
1291    /**
1292     * Constants and helpers for the Events table, which contains details for
1293     * individual events. <h3>Operations</h3> All operations can be done either
1294     * as an app or as a sync adapter. To perform an operation as a sync adapter
1295     * {@link #CALLER_IS_SYNCADAPTER} should be set to true and
1296     * {@link #ACCOUNT_NAME} and {@link #ACCOUNT_TYPE} must be set in the Uri
1297     * parameters. See
1298     * {@link Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)}
1299     * for details on adding parameters. Sync adapters have write access to more
1300     * columns but are restricted to a single account at a time.
1301     * <dl>
1302     * <dt><b>Insert</b></dt>
1303     * <dd>When inserting a new event the following fields must be included:
1304     * <ul>
1305     * <li>dtstart</li>
1306     * <li>dtend -or- a (rrule or rdate) and a duration</li>
1307     * <li>a calendar_id</li>
1308     * </ul>
1309     * There are also further requirements when inserting or updating an event.
1310     * See the section on Writing to Events.</dd>
1311     * <dt><b>Update</b></dt>
1312     * <dd>To perform an update of an Event the {@link Events#_ID} of the event
1313     * should be provided either as an appended id to the Uri (
1314     * {@link ContentUris#withAppendedId}) or as the first selection item--the
1315     * selection should start with "_id=?" and the first selectionArg should be
1316     * the _id of the event. Updates may also be done using a selection and no
1317     * id. Updating an event must respect the same rules as inserting and is
1318     * further restricted in the fields that can be written. See the section on
1319     * Writing to Events.</dd>
1320     * <dt><b>Delete</b></dt>
1321     * <dd>Events can be deleted either by the {@link Events#_ID} as an appended
1322     * id on the Uri or using any standard selection. If an appended id is used
1323     * a selection is not allowed. There are two versions of delete: as an app
1324     * and as a sync adapter. An app delete will set the deleted column on an
1325     * event and remove all instances of that event. A sync adapter delete will
1326     * remove the event from the database and all associated data.</dd>
1327     * <dt><b>Query</b></dt>
1328     * <dd>Querying the Events table will get you all information about a set of
1329     * events except their reminders, attendees, and extended properties. There
1330     * will be one row returned for each event that matches the query selection,
1331     * or at most a single row if the {@link Events#_ID} is appended to the Uri.
1332     * Recurring events will only return a single row regardless of the number
1333     * of times that event repeats.</dd>
1334     * </dl>
1335     * <h3>Writing to Events</h3> There are further restrictions on all Updates
1336     * and Inserts in the Events table:
1337     * <ul>
1338     * <li>If allDay is set to 1 eventTimezone must be {@link Time#TIMEZONE_UTC}
1339     * and the time must correspond to a midnight boundary.</li>
1340     * <li>Exceptions are not allowed to recur. If rrule or rdate is not empty,
1341     * original_id and original_sync_id must be empty.</li>
1342     * <li>In general a calendar_id should not be modified after insertion. This
1343     * is not explicitly forbidden but many sync adapters will not behave in an
1344     * expected way if the calendar_id is modified.</li>
1345     * </ul>
1346     * The following Events columns are writable by both an app and a sync
1347     * adapter.
1348     * <ul>
1349     * <li>{@link #CALENDAR_ID}</li>
1350     * <li>{@link #ORGANIZER}</li>
1351     * <li>{@link #TITLE}</li>
1352     * <li>{@link #EVENT_LOCATION}</li>
1353     * <li>{@link #DESCRIPTION}</li>
1354     * <li>{@link #EVENT_COLOR}</li>
1355     * <li>{@link #DTSTART}</li>
1356     * <li>{@link #DTEND}</li>
1357     * <li>{@link #EVENT_TIMEZONE}</li>
1358     * <li>{@link #EVENT_END_TIMEZONE}</li>
1359     * <li>{@link #DURATION}</li>
1360     * <li>{@link #ALL_DAY}</li>
1361     * <li>{@link #RRULE}</li>
1362     * <li>{@link #RDATE}</li>
1363     * <li>{@link #EXRULE}</li>
1364     * <li>{@link #EXDATE}</li>
1365     * <li>{@link #ORIGINAL_ID}</li>
1366     * <li>{@link #ORIGINAL_SYNC_ID}</li>
1367     * <li>{@link #ORIGINAL_INSTANCE_TIME}</li>
1368     * <li>{@link #ORIGINAL_ALL_DAY}</li>
1369     * <li>{@link #ACCESS_LEVEL}</li>
1370     * <li>{@link #AVAILABILITY}</li>
1371     * <li>{@link #GUESTS_CAN_MODIFY}</li>
1372     * <li>{@link #GUESTS_CAN_INVITE_OTHERS}</li>
1373     * <li>{@link #GUESTS_CAN_SEE_GUESTS}</li>
1374     * </ul>
1375     * The following Events columns are writable only by a sync adapter
1376     * <ul>
1377     * <li>{@link #DIRTY}</li>
1378     * <li>{@link #_SYNC_ID}</li>
1379     * <li>{@link #SYNC_DATA1}</li>
1380     * <li>{@link #SYNC_DATA2}</li>
1381     * <li>{@link #SYNC_DATA3}</li>
1382     * <li>{@link #SYNC_DATA4}</li>
1383     * <li>{@link #SYNC_DATA5}</li>
1384     * <li>{@link #SYNC_DATA6}</li>
1385     * <li>{@link #SYNC_DATA7}</li>
1386     * <li>{@link #SYNC_DATA8}</li>
1387     * <li>{@link #SYNC_DATA9}</li>
1388     * <li>{@link #SYNC_DATA10}</li>
1389     * </ul>
1390     * The remaining columns are either updated by the provider only or are
1391     * views into other tables and cannot be changed through the Events table.
1392     */
1393    public static final class Events implements BaseColumns, SyncColumns, EventsColumns,
1394            CalendarColumns {
1395
1396        /**
1397         * The content:// style URL for interacting with events. Appending an
1398         * event id using {@link ContentUris#withAppendedId(Uri, long)} will
1399         * specify a single event.
1400         */
1401        @SuppressWarnings("hiding")
1402        public static final Uri CONTENT_URI =
1403                Uri.parse("content://" + AUTHORITY + "/events");
1404
1405        /**
1406         * The content:// style URI for recurring event exceptions.  Insertions require an
1407         * appended event ID.  Deletion of exceptions requires both the original event ID and
1408         * the exception event ID (see {@link Uri.Builder#appendPath}).
1409         */
1410        public static final Uri CONTENT_EXCEPTION_URI =
1411                Uri.parse("content://" + AUTHORITY + "/exception");
1412
1413        /**
1414         * The default sort order for this table
1415         */
1416        private static final String DEFAULT_SORT_ORDER = "";
1417
1418        /**
1419         * These are columns that should only ever be updated by the provider,
1420         * either because they are views mapped to another table or because they
1421         * are used for provider only functionality. TODO move to provider
1422         *
1423         * @hide
1424         */
1425        public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
1426                ACCOUNT_NAME,
1427                ACCOUNT_TYPE,
1428                CAL_SYNC1,
1429                CAL_SYNC2,
1430                CAL_SYNC3,
1431                CAL_SYNC4,
1432                CAL_SYNC5,
1433                CAL_SYNC6,
1434                CAL_SYNC7,
1435                CAL_SYNC8,
1436                CAL_SYNC9,
1437                CAL_SYNC10,
1438                ALLOWED_REMINDERS,
1439                CALENDAR_ACCESS_LEVEL,
1440                CALENDAR_COLOR,
1441                CALENDAR_TIME_ZONE,
1442                CAN_MODIFY_TIME_ZONE,
1443                CAN_ORGANIZER_RESPOND,
1444                CALENDAR_DISPLAY_NAME,
1445                CAN_PARTIALLY_UPDATE,
1446                SYNC_EVENTS,
1447                VISIBLE,
1448        };
1449
1450        /**
1451         * These fields are only writable by a sync adapter. To modify them the
1452         * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
1453         * _SYNC_ACCOUNT_TYPE in the query parameters. TODO move to provider.
1454         *
1455         * @hide
1456         */
1457        public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
1458            _SYNC_ID,
1459            DIRTY,
1460            SYNC_DATA1,
1461            SYNC_DATA2,
1462            SYNC_DATA3,
1463            SYNC_DATA4,
1464            SYNC_DATA5,
1465            SYNC_DATA6,
1466            SYNC_DATA7,
1467            SYNC_DATA8,
1468            SYNC_DATA9,
1469            SYNC_DATA10,
1470        };
1471    }
1472
1473    /**
1474     * Fields and helpers for interacting with Instances. An instance is a
1475     * single occurrence of an event including time zone specific start and end
1476     * days and minutes. The instances table is not writable and only provides a
1477     * way to query event occurrences.
1478     */
1479    public static final class Instances implements BaseColumns, EventsColumns, CalendarColumns {
1480
1481        private static final String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=?";
1482        private static final String[] WHERE_CALENDARS_ARGS = {
1483            "1"
1484        };
1485
1486        /**
1487         * Performs a query to return all visible instances in the given range.
1488         * This is a blocking function and should not be done on the UI thread.
1489         * This will cause an expansion of recurring events to fill this time
1490         * range if they are not already expanded and will slow down for larger
1491         * time ranges with many recurring events.
1492         *
1493         * @param cr The ContentResolver to use for the query
1494         * @param projection The columns to return
1495         * @param begin The start of the time range to query in UTC millis since
1496         *            epoch
1497         * @param end The end of the time range to query in UTC millis since
1498         *            epoch
1499         * @return A Cursor containing all instances in the given range
1500         */
1501        public static final Cursor query(ContentResolver cr, String[] projection,
1502                                         long begin, long end) {
1503            Uri.Builder builder = CONTENT_URI.buildUpon();
1504            ContentUris.appendId(builder, begin);
1505            ContentUris.appendId(builder, end);
1506            return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
1507                    WHERE_CALENDARS_ARGS, DEFAULT_SORT_ORDER);
1508        }
1509
1510        /**
1511         * Performs a query to return all visible instances in the given range
1512         * that match the given query. This is a blocking function and should
1513         * not be done on the UI thread. This will cause an expansion of
1514         * recurring events to fill this time range if they are not already
1515         * expanded and will slow down for larger time ranges with many
1516         * recurring events.
1517         *
1518         * @param cr The ContentResolver to use for the query
1519         * @param projection The columns to return
1520         * @param begin The start of the time range to query in UTC millis since
1521         *            epoch
1522         * @param end The end of the time range to query in UTC millis since
1523         *            epoch
1524         * @param searchQuery A string of space separated search terms. Segments
1525         *            enclosed by double quotes will be treated as a single
1526         *            term.
1527         * @return A Cursor of instances matching the search terms in the given
1528         *         time range
1529         */
1530        public static final Cursor query(ContentResolver cr, String[] projection,
1531                                         long begin, long end, String searchQuery) {
1532            Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
1533            ContentUris.appendId(builder, begin);
1534            ContentUris.appendId(builder, end);
1535            builder = builder.appendPath(searchQuery);
1536            return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
1537                    WHERE_CALENDARS_ARGS, DEFAULT_SORT_ORDER);
1538        }
1539
1540        /**
1541         * The content:// style URL for querying an instance range. The begin
1542         * and end of the range to query should be added as path segments if
1543         * this is used directly.
1544         */
1545        @SuppressWarnings("hiding")
1546        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
1547                "/instances/when");
1548        /**
1549         * The content:// style URL for querying an instance range by Julian
1550         * Day. The start and end day should be added as path segments if this
1551         * is used directly.
1552         */
1553        public static final Uri CONTENT_BY_DAY_URI =
1554            Uri.parse("content://" + AUTHORITY + "/instances/whenbyday");
1555        /**
1556         * The content:// style URL for querying an instance range with a search
1557         * term. The begin, end, and search string should be appended as path
1558         * segments if this is used directly.
1559         */
1560        public static final Uri CONTENT_SEARCH_URI = Uri.parse("content://" + AUTHORITY +
1561                "/instances/search");
1562        /**
1563         * The content:// style URL for querying an instance range with a search
1564         * term. The start day, end day, and search string should be appended as
1565         * path segments if this is used directly.
1566         */
1567        public static final Uri CONTENT_SEARCH_BY_DAY_URI =
1568            Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
1569
1570        /**
1571         * The default sort order for this table.
1572         */
1573        private static final String DEFAULT_SORT_ORDER = "begin ASC";
1574
1575        /**
1576         * The beginning time of the instance, in UTC milliseconds. Column name.
1577         * <P>Type: INTEGER (long; millis since epoch)</P>
1578         */
1579        public static final String BEGIN = "begin";
1580
1581        /**
1582         * The ending time of the instance, in UTC milliseconds. Column name.
1583         * <P>Type: INTEGER (long; millis since epoch)</P>
1584         */
1585        public static final String END = "end";
1586
1587        /**
1588         * The _id of the event for this instance. Column name.
1589         * <P>Type: INTEGER (long, foreign key to the Events table)</P>
1590         */
1591        public static final String EVENT_ID = "event_id";
1592
1593        /**
1594         * The Julian start day of the instance, relative to the local time
1595         * zone. Column name.
1596         * <P>Type: INTEGER (int)</P>
1597         */
1598        public static final String START_DAY = "startDay";
1599
1600        /**
1601         * The Julian end day of the instance, relative to the local time
1602         * zone. Column name.
1603         * <P>Type: INTEGER (int)</P>
1604         */
1605        public static final String END_DAY = "endDay";
1606
1607        /**
1608         * The start minute of the instance measured from midnight in the
1609         * local time zone. Column name.
1610         * <P>Type: INTEGER (int)</P>
1611         */
1612        public static final String START_MINUTE = "startMinute";
1613
1614        /**
1615         * The end minute of the instance measured from midnight in the
1616         * local time zone. Column name.
1617         * <P>Type: INTEGER (int)</P>
1618         */
1619        public static final String END_MINUTE = "endMinute";
1620    }
1621
1622    protected interface CalendarCacheColumns {
1623        /**
1624         * The key for the setting. Keys are defined in {@link CalendarCache}.
1625         */
1626        public static final String KEY = "key";
1627
1628        /**
1629         * The value of the given setting.
1630         */
1631        public static final String VALUE = "value";
1632    }
1633
1634    /**
1635     * CalendarCache stores some settings for calendar including the current
1636     * time zone for the instances. These settings are stored using a key/value
1637     * scheme. A {@link #KEY} must be specified when updating these values.
1638     */
1639    public static class CalendarCache implements CalendarCacheColumns {
1640        /**
1641         * The URI to use for retrieving the properties from the Calendar db.
1642         */
1643        public static final Uri URI =
1644                Uri.parse("content://" + AUTHORITY + "/properties");
1645
1646        /**
1647         * They key for updating the use of auto/home time zones in Calendar.
1648         * Valid values are {@link #TIMEZONE_TYPE_AUTO} or
1649         * {@link #TIMEZONE_TYPE_HOME}.
1650         */
1651        public static final String KEY_TIMEZONE_TYPE = "timezoneType";
1652
1653        /**
1654         * The key for updating the time zone used by the provider when it
1655         * generates the instances table. This should only be written if the
1656         * type is set to {@link #TIMEZONE_TYPE_HOME}. A valid time zone id
1657         * should be written to this field.
1658         */
1659        public static final String KEY_TIMEZONE_INSTANCES = "timezoneInstances";
1660
1661        /**
1662         * The key for reading the last time zone set by the user. This should
1663         * only be read by apps and it will be automatically updated whenever
1664         * {@link #KEY_TIMEZONE_INSTANCES} is updated with
1665         * {@link #TIMEZONE_TYPE_HOME} set.
1666         */
1667        public static final String KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious";
1668
1669        /**
1670         * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
1671         * should stay in sync with the device's time zone.
1672         */
1673        public static final String TIMEZONE_TYPE_AUTO = "auto";
1674
1675        /**
1676         * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
1677         * should use a fixed time zone set by the user.
1678         */
1679        public static final String TIMEZONE_TYPE_HOME = "home";
1680    }
1681
1682    /**
1683     * A few Calendar globals are needed in the CalendarProvider for expanding
1684     * the Instances table and these are all stored in the first (and only) row
1685     * of the CalendarMetaData table.
1686     *
1687     * @hide
1688     */
1689    protected interface CalendarMetaDataColumns {
1690        /**
1691         * The local timezone that was used for precomputing the fields
1692         * in the Instances table.
1693         */
1694        public static final String LOCAL_TIMEZONE = "localTimezone";
1695
1696        /**
1697         * The minimum time used in expanding the Instances table,
1698         * in UTC milliseconds.
1699         * <P>Type: INTEGER</P>
1700         */
1701        public static final String MIN_INSTANCE = "minInstance";
1702
1703        /**
1704         * The maximum time used in expanding the Instances table,
1705         * in UTC milliseconds.
1706         * <P>Type: INTEGER</P>
1707         */
1708        public static final String MAX_INSTANCE = "maxInstance";
1709
1710        /**
1711         * The minimum Julian day in the EventDays table.
1712         * <P>Type: INTEGER</P>
1713         */
1714        public static final String MIN_EVENTDAYS = "minEventDays";
1715
1716        /**
1717         * The maximum Julian day in the EventDays table.
1718         * <P>Type: INTEGER</P>
1719         */
1720        public static final String MAX_EVENTDAYS = "maxEventDays";
1721    }
1722
1723    /**
1724     * @hide
1725     */
1726    public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
1727    }
1728
1729    protected interface EventDaysColumns {
1730        /**
1731         * The Julian starting day number. Column name.
1732         * <P>Type: INTEGER (int)</P>
1733         */
1734        public static final String STARTDAY = "startDay";
1735        /**
1736         * The Julian ending day number. Column name.
1737         * <P>Type: INTEGER (int)</P>
1738         */
1739        public static final String ENDDAY = "endDay";
1740
1741    }
1742
1743    /**
1744     * Fields and helpers for querying for a list of days that contain events.
1745     */
1746    public static final class EventDays implements EventDaysColumns {
1747        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
1748                + "/instances/groupbyday");
1749
1750        /**
1751         * The projection used by the EventDays query.
1752         */
1753        public static final String[] PROJECTION = {
1754                STARTDAY, ENDDAY
1755        };
1756        private static final String SELECTION = "selected=1";
1757
1758        /**
1759         * Retrieves the days with events for the Julian days starting at
1760         * "startDay" for "numDays". It returns a cursor containing startday and
1761         * endday representing the max range of days for all events beginning on
1762         * each startday.This is a blocking function and should not be done on
1763         * the UI thread.
1764         *
1765         * @param cr the ContentResolver
1766         * @param startDay the first Julian day in the range
1767         * @param numDays the number of days to load (must be at least 1)
1768         * @return a database cursor containing a list of start and end days for
1769         *         events
1770         */
1771        public static final Cursor query(ContentResolver cr, int startDay, int numDays) {
1772            if (numDays < 1) {
1773                return null;
1774            }
1775            int endDay = startDay + numDays - 1;
1776            Uri.Builder builder = CONTENT_URI.buildUpon();
1777            ContentUris.appendId(builder, startDay);
1778            ContentUris.appendId(builder, endDay);
1779            return cr.query(builder.build(), PROJECTION, SELECTION,
1780                    null /* selection args */, STARTDAY);
1781        }
1782    }
1783
1784    protected interface RemindersColumns {
1785        /**
1786         * The event the reminder belongs to. Column name.
1787         * <P>Type: INTEGER (foreign key to the Events table)</P>
1788         */
1789        public static final String EVENT_ID = "event_id";
1790
1791        /**
1792         * The minutes prior to the event that the alarm should ring.  -1
1793         * specifies that we should use the default value for the system.
1794         * Column name.
1795         * <P>Type: INTEGER</P>
1796         */
1797        public static final String MINUTES = "minutes";
1798
1799        /**
1800         * Passing this as a minutes value will use the default reminder
1801         * minutes.
1802         */
1803        public static final int MINUTES_DEFAULT = -1;
1804
1805        /**
1806         * The alarm method, as set on the server. {@link #METHOD_DEFAULT},
1807         * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, and {@link #METHOD_SMS}
1808         * are possible values; the device will only process
1809         * {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders (the
1810         * other types are simply stored so we can send the same reminder info
1811         * back to the server when we make changes).
1812         */
1813        public static final String METHOD = "method";
1814
1815        public static final int METHOD_DEFAULT = 0;
1816        public static final int METHOD_ALERT = 1;
1817        public static final int METHOD_EMAIL = 2;
1818        public static final int METHOD_SMS = 3;
1819    }
1820
1821    /**
1822     * Fields and helpers for accessing reminders for an event. Each row of this
1823     * table represents a single reminder for an event. Calling
1824     * {@link #query(ContentResolver, long)} will return a list of reminders for
1825     * the event with the given eventId. Both apps and sync adapters may write
1826     * to this table. There are three writable fields and all of them must be
1827     * included when inserting a new reminder. They are:
1828     * <ul>
1829     * <li>{@link #EVENT_ID}</li>
1830     * <li>{@link #MINUTES}</li>
1831     * <li>{@link #METHOD}</li>
1832     * </ul>
1833     */
1834    public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns {
1835        private static final String REMINDERS_WHERE = CalendarContract.Reminders.EVENT_ID + "=?";
1836        /**
1837         * The projection used by the reminders query.
1838         */
1839        public static final String[] PROJECTION = new String[] {
1840                _ID, MINUTES, METHOD,};
1841        @SuppressWarnings("hiding")
1842        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
1843
1844        /**
1845         * Queries all reminders associated with the given event. This is a
1846         * blocking call and should not be done on the UI thread.
1847         *
1848         * @param cr The content resolver to use for the query
1849         * @param eventId The id of the event to retrieve reminders for
1850         * @return A Cursor containing all reminders for the event
1851         */
1852        public static final Cursor query(ContentResolver cr, long eventId) {
1853            String[] remArgs = {Long.toString(eventId)};
1854            return cr.query(CONTENT_URI, PROJECTION, REMINDERS_WHERE, remArgs /* selection args */,
1855                    null /* sort order */);
1856        }
1857    }
1858
1859    protected interface CalendarAlertsColumns {
1860        /**
1861         * The event that the alert belongs to. Column name.
1862         * <P>Type: INTEGER (foreign key to the Events table)</P>
1863         */
1864        public static final String EVENT_ID = "event_id";
1865
1866        /**
1867         * The start time of the event, in UTC. Column name.
1868         * <P>Type: INTEGER (long; millis since epoch)</P>
1869         */
1870        public static final String BEGIN = "begin";
1871
1872        /**
1873         * The end time of the event, in UTC. Column name.
1874         * <P>Type: INTEGER (long; millis since epoch)</P>
1875         */
1876        public static final String END = "end";
1877
1878        /**
1879         * The alarm time of the event, in UTC. Column name.
1880         * <P>Type: INTEGER (long; millis since epoch)</P>
1881         */
1882        public static final String ALARM_TIME = "alarmTime";
1883
1884        /**
1885         * The creation time of this database entry, in UTC.
1886         * Useful for debugging missed reminders. Column name.
1887         * <P>Type: INTEGER (long; millis since epoch)</P>
1888         */
1889        public static final String CREATION_TIME = "creationTime";
1890
1891        /**
1892         * The time that the alarm broadcast was received by the Calendar app,
1893         * in UTC. Useful for debugging missed reminders. Column name.
1894         * <P>Type: INTEGER (long; millis since epoch)</P>
1895         */
1896        public static final String RECEIVED_TIME = "receivedTime";
1897
1898        /**
1899         * The time that the notification was created by the Calendar app,
1900         * in UTC. Useful for debugging missed reminders. Column name.
1901         * <P>Type: INTEGER (long; millis since epoch)</P>
1902         */
1903        public static final String NOTIFY_TIME = "notifyTime";
1904
1905        /**
1906         * The state of this alert. It starts out as {@link #STATE_SCHEDULED}, then
1907         * when the alarm goes off, it changes to {@link #STATE_FIRED}, and then when
1908         * the user dismisses the alarm it changes to {@link #STATE_DISMISSED}. Column
1909         * name.
1910         * <P>Type: INTEGER</P>
1911         */
1912        public static final String STATE = "state";
1913
1914        /**
1915         * An alert begins in this state when it is first created.
1916         */
1917        public static final int STATE_SCHEDULED = 0;
1918        /**
1919         * After a notification for an alert has been created it should be
1920         * updated to fired.
1921         */
1922        public static final int STATE_FIRED = 1;
1923        /**
1924         * Once the user has dismissed the notification the alert's state should
1925         * be set to dismissed so it is not fired again.
1926         */
1927        public static final int STATE_DISMISSED = 2;
1928
1929        /**
1930         * The number of minutes that this alarm precedes the start time. Column
1931         * name.
1932         * <P>Type: INTEGER</P>
1933         */
1934        public static final String MINUTES = "minutes";
1935
1936        /**
1937         * The default sort order for this alerts queries
1938         */
1939        public static final String DEFAULT_SORT_ORDER = "begin ASC,title ASC";
1940    }
1941
1942    /**
1943     * Fields and helpers for accessing calendar alerts information. These
1944     * fields are for tracking which alerts have been fired. Scheduled alarms
1945     * will generate an intent using {@link #ACTION_EVENT_REMINDER}. Apps that
1946     * receive this action may update the {@link #STATE} for the reminder when
1947     * they have finished handling it. Apps that have their notifications
1948     * disabled should not modify the table to ensure that they do not conflict
1949     * with another app that is generating a notification. In general, apps
1950     * should not need to write to this table directly except to update the
1951     * state of a reminder.
1952     */
1953    public static final class CalendarAlerts implements BaseColumns,
1954            CalendarAlertsColumns, EventsColumns, CalendarColumns {
1955
1956        /**
1957         * @hide
1958         */
1959        public static final String TABLE_NAME = "CalendarAlerts";
1960        /**
1961         * The Uri for querying calendar alert information
1962         */
1963        @SuppressWarnings("hiding")
1964        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
1965                "/calendar_alerts");
1966
1967        private static final String WHERE_ALARM_EXISTS = EVENT_ID + "=?"
1968                + " AND " + BEGIN + "=?"
1969                + " AND " + ALARM_TIME + "=?";
1970
1971        private static final String WHERE_FINDNEXTALARMTIME = ALARM_TIME + ">=?";
1972        private static final String SORT_ORDER_ALARMTIME_ASC = ALARM_TIME + " ASC";
1973
1974        private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + STATE_SCHEDULED
1975                + " AND " + ALARM_TIME + "<?"
1976                + " AND " + ALARM_TIME + ">?"
1977                + " AND " + END + ">=?";
1978
1979        /**
1980         * This URI is for grouping the query results by event_id and begin
1981         * time.  This will return one result per instance of an event.  So
1982         * events with multiple alarms will appear just once, but multiple
1983         * instances of a repeating event will show up multiple times.
1984         */
1985        public static final Uri CONTENT_URI_BY_INSTANCE =
1986            Uri.parse("content://" + AUTHORITY + "/calendar_alerts/by_instance");
1987
1988        private static final boolean DEBUG = false;
1989
1990        /**
1991         * Helper for inserting an alarm time associated with an event TODO move
1992         * to Provider
1993         *
1994         * @hide
1995         */
1996        public static final Uri insert(ContentResolver cr, long eventId,
1997                long begin, long end, long alarmTime, int minutes) {
1998            ContentValues values = new ContentValues();
1999            values.put(CalendarAlerts.EVENT_ID, eventId);
2000            values.put(CalendarAlerts.BEGIN, begin);
2001            values.put(CalendarAlerts.END, end);
2002            values.put(CalendarAlerts.ALARM_TIME, alarmTime);
2003            long currentTime = System.currentTimeMillis();
2004            values.put(CalendarAlerts.CREATION_TIME, currentTime);
2005            values.put(CalendarAlerts.RECEIVED_TIME, 0);
2006            values.put(CalendarAlerts.NOTIFY_TIME, 0);
2007            values.put(CalendarAlerts.STATE, STATE_SCHEDULED);
2008            values.put(CalendarAlerts.MINUTES, minutes);
2009            return cr.insert(CONTENT_URI, values);
2010        }
2011
2012        /**
2013         * Finds the next alarm after (or equal to) the given time and returns
2014         * the time of that alarm or -1 if no such alarm exists. This is a
2015         * blocking call and should not be done on the UI thread. TODO move to
2016         * provider
2017         *
2018         * @param cr the ContentResolver
2019         * @param millis the time in UTC milliseconds
2020         * @return the next alarm time greater than or equal to "millis", or -1
2021         *         if no such alarm exists.
2022         * @hide
2023         */
2024        public static final long findNextAlarmTime(ContentResolver cr, long millis) {
2025            String selection = ALARM_TIME + ">=" + millis;
2026            // TODO: construct an explicit SQL query so that we can add
2027            // "LIMIT 1" to the end and get just one result.
2028            String[] projection = new String[] { ALARM_TIME };
2029            Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_FINDNEXTALARMTIME,
2030                    (new String[] {
2031                        Long.toString(millis)
2032                    }), SORT_ORDER_ALARMTIME_ASC);
2033            long alarmTime = -1;
2034            try {
2035                if (cursor != null && cursor.moveToFirst()) {
2036                    alarmTime = cursor.getLong(0);
2037                }
2038            } finally {
2039                if (cursor != null) {
2040                    cursor.close();
2041                }
2042            }
2043            return alarmTime;
2044        }
2045
2046        /**
2047         * Searches the CalendarAlerts table for alarms that should have fired
2048         * but have not and then reschedules them. This method can be called at
2049         * boot time to restore alarms that may have been lost due to a phone
2050         * reboot. TODO move to provider
2051         *
2052         * @param cr the ContentResolver
2053         * @param context the Context
2054         * @param manager the AlarmManager
2055         * @hide
2056         */
2057        public static final void rescheduleMissedAlarms(ContentResolver cr,
2058                Context context, AlarmManager manager) {
2059            // Get all the alerts that have been scheduled but have not fired
2060            // and should have fired by now and are not too old.
2061            long now = System.currentTimeMillis();
2062            long ancient = now - DateUtils.DAY_IN_MILLIS;
2063            String[] projection = new String[] {
2064                    ALARM_TIME,
2065            };
2066
2067            // TODO: construct an explicit SQL query so that we can add
2068            // "GROUPBY" instead of doing a sort and de-dup
2069            Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection,
2070                    WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] {
2071                            Long.toString(now), Long.toString(ancient), Long.toString(now)
2072                    }), SORT_ORDER_ALARMTIME_ASC);
2073            if (cursor == null) {
2074                return;
2075            }
2076
2077            if (DEBUG) {
2078                Log.d(TAG, "missed alarms found: " + cursor.getCount());
2079            }
2080
2081            try {
2082                long alarmTime = -1;
2083
2084                while (cursor.moveToNext()) {
2085                    long newAlarmTime = cursor.getLong(0);
2086                    if (alarmTime != newAlarmTime) {
2087                        if (DEBUG) {
2088                            Log.w(TAG, "rescheduling missed alarm. alarmTime: " + newAlarmTime);
2089                        }
2090                        scheduleAlarm(context, manager, newAlarmTime);
2091                        alarmTime = newAlarmTime;
2092                    }
2093                }
2094            } finally {
2095                cursor.close();
2096            }
2097        }
2098
2099        /**
2100         * Schedules an alarm intent with the system AlarmManager that will
2101         * notify listeners when a reminder should be fired. The provider will
2102         * keep scheduled reminders up to date but apps may use this to
2103         * implement snooze functionality without modifying the reminders table.
2104         * Scheduled alarms will generate an intent using
2105         * {@link #ACTION_EVENT_REMINDER}. TODO Move to provider
2106         *
2107         * @param context A context for referencing system resources
2108         * @param manager The AlarmManager to use or null
2109         * @param alarmTime The time to fire the intent in UTC millis since
2110         *            epoch
2111         * @hide
2112         */
2113        public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) {
2114            if (DEBUG) {
2115                Time time = new Time();
2116                time.set(alarmTime);
2117                String schedTime = time.format(" %a, %b %d, %Y %I:%M%P");
2118                Log.d(TAG, "Schedule alarm at " + alarmTime + " " + schedTime);
2119            }
2120
2121            if (manager == null) {
2122                manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
2123            }
2124
2125            Intent intent = new Intent(ACTION_EVENT_REMINDER);
2126            intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
2127            intent.putExtra(ALARM_TIME, alarmTime);
2128            PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
2129            manager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
2130        }
2131
2132        /**
2133         * Searches for an entry in the CalendarAlerts table that matches the
2134         * given event id, begin time and alarm time. If one is found then this
2135         * alarm already exists and this method returns true. TODO Move to
2136         * provider
2137         *
2138         * @param cr the ContentResolver
2139         * @param eventId the event id to match
2140         * @param begin the start time of the event in UTC millis
2141         * @param alarmTime the alarm time of the event in UTC millis
2142         * @return true if there is already an alarm for the given event with
2143         *         the same start time and alarm time.
2144         * @hide
2145         */
2146        public static final boolean alarmExists(ContentResolver cr, long eventId,
2147                long begin, long alarmTime) {
2148            // TODO: construct an explicit SQL query so that we can add
2149            // "LIMIT 1" to the end and get just one result.
2150            String[] projection = new String[] { ALARM_TIME };
2151            Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_ALARM_EXISTS,
2152                    (new String[] {
2153                            Long.toString(eventId), Long.toString(begin), Long.toString(alarmTime)
2154                    }), null);
2155            boolean found = false;
2156            try {
2157                if (cursor != null && cursor.getCount() > 0) {
2158                    found = true;
2159                }
2160            } finally {
2161                if (cursor != null) {
2162                    cursor.close();
2163                }
2164            }
2165            return found;
2166        }
2167    }
2168
2169    protected interface ExtendedPropertiesColumns {
2170        /**
2171         * The event the extended property belongs to. Column name.
2172         * <P>Type: INTEGER (foreign key to the Events table)</P>
2173         */
2174        public static final String EVENT_ID = "event_id";
2175
2176        /**
2177         * The name of the extended property.  This is a uri of the form
2178         * {scheme}#{local-name} convention. Column name.
2179         * <P>Type: TEXT</P>
2180         */
2181        public static final String NAME = "name";
2182
2183        /**
2184         * The value of the extended property. Column name.
2185         * <P>Type: TEXT</P>
2186         */
2187        public static final String VALUE = "value";
2188    }
2189
2190    /**
2191     * Fields for accessing the Extended Properties. This is a generic set of
2192     * name/value pairs for use by sync adapters or apps to add extra
2193     * information to events. There are three writable columns and all three
2194     * must be present when inserting a new value. They are:
2195     * <ul>
2196     * <li>{@link #EVENT_ID}</li>
2197     * <li>{@link #NAME}</li>
2198     * <li>{@link #VALUE}</li>
2199     * </ul>
2200     */
2201   public static final class ExtendedProperties implements BaseColumns,
2202            ExtendedPropertiesColumns, EventsColumns {
2203        public static final Uri CONTENT_URI =
2204                Uri.parse("content://" + AUTHORITY + "/extendedproperties");
2205
2206        // TODO: fill out this class when we actually start utilizing extendedproperties
2207        // in the calendar application.
2208   }
2209
2210    /**
2211     * A table provided for sync adapters to use for storing private sync state data.
2212     *
2213     * @see SyncStateContract
2214     */
2215    public static final class SyncState implements SyncStateContract.Columns {
2216        /**
2217         * This utility class cannot be instantiated
2218         */
2219        private SyncState() {}
2220
2221        private static final String CONTENT_DIRECTORY =
2222                SyncStateContract.Constants.CONTENT_DIRECTORY;
2223
2224        /**
2225         * The content:// style URI for this table
2226         */
2227        public static final Uri CONTENT_URI =
2228                Uri.withAppendedPath(CalendarContract.CONTENT_URI, CONTENT_DIRECTORY);
2229    }
2230
2231    /**
2232     * Columns from the EventsRawTimes table
2233     *
2234     * @hide
2235     */
2236    protected interface EventsRawTimesColumns {
2237        /**
2238         * The corresponding event id. Column name.
2239         * <P>Type: INTEGER (long)</P>
2240         */
2241        public static final String EVENT_ID = "event_id";
2242
2243        /**
2244         * The RFC2445 compliant time the event starts. Column name.
2245         * <P>Type: TEXT</P>
2246         */
2247        public static final String DTSTART_2445 = "dtstart2445";
2248
2249        /**
2250         * The RFC2445 compliant time the event ends. Column name.
2251         * <P>Type: TEXT</P>
2252         */
2253        public static final String DTEND_2445 = "dtend2445";
2254
2255        /**
2256         * The RFC2445 compliant original instance time of the recurring event
2257         * for which this event is an exception. Column name.
2258         * <P>Type: TEXT</P>
2259         */
2260        public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445";
2261
2262        /**
2263         * The RFC2445 compliant last date this event repeats on, or NULL if it
2264         * never ends. Column name.
2265         * <P>Type: TEXT</P>
2266         */
2267        public static final String LAST_DATE_2445 = "lastDate2445";
2268    }
2269
2270    /**
2271     * @hide
2272     */
2273    public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns {
2274    }
2275}
2276