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