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