• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.UnsupportedAppUsage;
24 import android.app.Activity;
25 import android.app.AlarmManager;
26 import android.app.PendingIntent;
27 import android.app.admin.DevicePolicyManager;
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.Time;
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          * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
801          */
802         @NonNull
803         public static final Uri ENTERPRISE_CONTENT_URI =
804                 Uri.parse("content://" + AUTHORITY + "/enterprise/calendars");
805 
806         /**
807          * The default sort order for this table
808          */
809         public static final String DEFAULT_SORT_ORDER = CALENDAR_DISPLAY_NAME;
810 
811         /**
812          * The name of the calendar. Column name.
813          * <P>Type: TEXT</P>
814          */
815         public static final String NAME = "name";
816 
817         /**
818          * The default location for the calendar. Column name.
819          * <P>Type: TEXT</P>
820          */
821         public static final String CALENDAR_LOCATION = "calendar_location";
822 
823         /**
824          * These fields are only writable by a sync adapter. To modify them the
825          * caller must include {@link #CALLER_IS_SYNCADAPTER},
826          * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the Uri's query
827          * parameters. TODO move to provider
828          *
829          * @hide
830          */
831         @TestApi
832         public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
833             ACCOUNT_NAME,
834             ACCOUNT_TYPE,
835             _SYNC_ID,
836             DIRTY,
837             MUTATORS,
838             OWNER_ACCOUNT,
839             MAX_REMINDERS,
840             ALLOWED_REMINDERS,
841             CAN_MODIFY_TIME_ZONE,
842             CAN_ORGANIZER_RESPOND,
843             CAN_PARTIALLY_UPDATE,
844             CALENDAR_LOCATION,
845             CALENDAR_TIME_ZONE,
846             CALENDAR_ACCESS_LEVEL,
847             DELETED,
848             CAL_SYNC1,
849             CAL_SYNC2,
850             CAL_SYNC3,
851             CAL_SYNC4,
852             CAL_SYNC5,
853             CAL_SYNC6,
854             CAL_SYNC7,
855             CAL_SYNC8,
856             CAL_SYNC9,
857             CAL_SYNC10,
858         };
859     }
860 
861     /**
862      * Columns from the Attendees table that other tables join into themselves.
863      */
864     protected interface AttendeesColumns {
865 
866         /**
867          * The id of the event. Column name.
868          * <P>Type: INTEGER</P>
869          */
870         public static final String EVENT_ID = "event_id";
871 
872         /**
873          * The name of the attendee. Column name.
874          * <P>Type: STRING</P>
875          */
876         public static final String ATTENDEE_NAME = "attendeeName";
877 
878         /**
879          * The email address of the attendee. Column name.
880          * <P>Type: STRING</P>
881          */
882         public static final String ATTENDEE_EMAIL = "attendeeEmail";
883 
884         /**
885          * The relationship of the attendee to the user. Column name.
886          * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.</P>
887          */
888         public static final String ATTENDEE_RELATIONSHIP = "attendeeRelationship";
889 
890         public static final int RELATIONSHIP_NONE = 0;
891         public static final int RELATIONSHIP_ATTENDEE = 1;
892         public static final int RELATIONSHIP_ORGANIZER = 2;
893         public static final int RELATIONSHIP_PERFORMER = 3;
894         public static final int RELATIONSHIP_SPEAKER = 4;
895 
896         /**
897          * The type of attendee. Column name.
898          * <P>
899          * Type: Integer (one of {@link #TYPE_NONE}, {@link #TYPE_REQUIRED},
900          * {@link #TYPE_OPTIONAL}, {@link #TYPE_RESOURCE})
901          * </P>
902          */
903         public static final String ATTENDEE_TYPE = "attendeeType";
904 
905         public static final int TYPE_NONE = 0;
906         public static final int TYPE_REQUIRED = 1;
907         public static final int TYPE_OPTIONAL = 2;
908         /**
909          * This specifies that an attendee is a resource, like a room, a
910          * cabbage, or something and not an actual person.
911          */
912         public static final int TYPE_RESOURCE = 3;
913 
914         /**
915          * The attendance status of the attendee. Column name.
916          * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...).</P>
917          */
918         public static final String ATTENDEE_STATUS = "attendeeStatus";
919 
920         public static final int ATTENDEE_STATUS_NONE = 0;
921         public static final int ATTENDEE_STATUS_ACCEPTED = 1;
922         public static final int ATTENDEE_STATUS_DECLINED = 2;
923         public static final int ATTENDEE_STATUS_INVITED = 3;
924         public static final int ATTENDEE_STATUS_TENTATIVE = 4;
925 
926         /**
927          * The identity of the attendee as referenced in
928          * {@link ContactsContract.CommonDataKinds.Identity#IDENTITY}.
929          * This is required only if {@link #ATTENDEE_ID_NAMESPACE} is present. Column name.
930          * <P>Type: STRING</P>
931          */
932         public static final String ATTENDEE_IDENTITY = "attendeeIdentity";
933 
934         /**
935          * The identity name space of the attendee as referenced in
936          * {@link ContactsContract.CommonDataKinds.Identity#NAMESPACE}.
937          * This is required only if {@link #ATTENDEE_IDENTITY} is present. Column name.
938          * <P>Type: STRING</P>
939          */
940         public static final String ATTENDEE_ID_NAMESPACE = "attendeeIdNamespace";
941     }
942 
943     /**
944      * Fields and helpers for interacting with Attendees. Each row of this table
945      * represents a single attendee or guest of an event. Calling
946      * {@link #query(ContentResolver, long, String[])} will return a list of attendees for
947      * the event with the given eventId. Both apps and sync adapters may write
948      * to this table. There are six writable fields and all of them except
949      * {@link #ATTENDEE_NAME} must be included when inserting a new attendee.
950      * They are:
951      * <ul>
952      * <li>{@link #EVENT_ID}</li>
953      * <li>{@link #ATTENDEE_NAME}</li>
954      * <li>{@link #ATTENDEE_EMAIL}</li>
955      * <li>{@link #ATTENDEE_RELATIONSHIP}</li>
956      * <li>{@link #ATTENDEE_TYPE}</li>
957      * <li>{@link #ATTENDEE_STATUS}</li>
958      * <li>{@link #ATTENDEE_IDENTITY}</li>
959      * <li>{@link #ATTENDEE_ID_NAMESPACE}</li>
960      * </ul>
961      */
962     public static final class Attendees implements BaseColumns, AttendeesColumns, EventsColumns {
963 
964         /**
965          * The content:// style URL for accessing Attendees data
966          */
967         @SuppressWarnings("hiding")
968         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
969         private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
970 
971         /**
972          * This utility class cannot be instantiated
973          */
Attendees()974         private Attendees() {}
975 
976         /**
977          * Queries all attendees associated with the given event. This is a
978          * blocking call and should not be done on the UI thread.
979          *
980          * @param cr The content resolver to use for the query
981          * @param eventId The id of the event to retrieve attendees for
982          * @param projection the columns to return in the cursor
983          * @return A Cursor containing all attendees for the event
984          */
query(ContentResolver cr, long eventId, String[] projection)985         public static final Cursor query(ContentResolver cr, long eventId, String[] projection) {
986             String[] attArgs = {Long.toString(eventId)};
987             return cr.query(CONTENT_URI, projection, ATTENDEES_WHERE, attArgs /* selection args */,
988                     null /* sort order */);
989         }
990     }
991 
992     /**
993      * Columns from the Events table that other tables join into themselves.
994      */
995     protected interface EventsColumns {
996 
997         /**
998          * The {@link Calendars#_ID} of the calendar the event belongs to.
999          * Column name.
1000          * <P>Type: INTEGER</P>
1001          */
1002         public static final String CALENDAR_ID = "calendar_id";
1003 
1004         /**
1005          * The title of the event. Column name.
1006          * <P>Type: TEXT</P>
1007          */
1008         public static final String TITLE = "title";
1009 
1010         /**
1011          * The description of the event. Column name.
1012          * <P>Type: TEXT</P>
1013          */
1014         public static final String DESCRIPTION = "description";
1015 
1016         /**
1017          * Where the event takes place. Column name.
1018          * <P>Type: TEXT</P>
1019          */
1020         public static final String EVENT_LOCATION = "eventLocation";
1021 
1022         /**
1023          * A secondary color for the individual event. This should only be
1024          * updated by the sync adapter for a given account.
1025          * <P>Type: INTEGER</P>
1026          */
1027         public static final String EVENT_COLOR = "eventColor";
1028 
1029         /**
1030          * A secondary color key for the individual event. NULL or an empty
1031          * string are reserved for indicating that the event does not use a key
1032          * for looking up the color. The provider will update
1033          * {@link #EVENT_COLOR} automatically when a valid key is written to
1034          * this column. The key must reference an existing row of the
1035          * {@link Colors} table. @see Colors
1036          * <P>
1037          * Type: TEXT
1038          * </P>
1039          */
1040         public static final String EVENT_COLOR_KEY = "eventColor_index";
1041 
1042         /**
1043          * This will be {@link #EVENT_COLOR} if it is not null; otherwise, this will be
1044          * {@link Calendars#CALENDAR_COLOR}.
1045          * Read-only value. To modify, write to {@link #EVENT_COLOR} or
1046          * {@link Calendars#CALENDAR_COLOR} directly.
1047          *<P>
1048          *     Type: INTEGER
1049          *</P>
1050          */
1051         public static final String DISPLAY_COLOR = "displayColor";
1052 
1053         /**
1054          * The event status. Column name.
1055          * <P>Type: INTEGER (one of {@link #STATUS_TENTATIVE}...)</P>
1056          */
1057         public static final String STATUS = "eventStatus";
1058 
1059         public static final int STATUS_TENTATIVE = 0;
1060         public static final int STATUS_CONFIRMED = 1;
1061         public static final int STATUS_CANCELED = 2;
1062 
1063         /**
1064          * This is a copy of the attendee status for the owner of this event.
1065          * This field is copied here so that we can efficiently filter out
1066          * events that are declined without having to look in the Attendees
1067          * table. Column name.
1068          *
1069          * <P>Type: INTEGER (int)</P>
1070          */
1071         public static final String SELF_ATTENDEE_STATUS = "selfAttendeeStatus";
1072 
1073         /**
1074          * This column is available for use by sync adapters. Column name.
1075          * <P>Type: TEXT</P>
1076          */
1077         public static final String SYNC_DATA1 = "sync_data1";
1078 
1079         /**
1080          * This column is available for use by sync adapters. Column name.
1081          * <P>Type: TEXT</P>
1082          */
1083         public static final String SYNC_DATA2 = "sync_data2";
1084 
1085         /**
1086          * This column is available for use by sync adapters. Column name.
1087          * <P>Type: TEXT</P>
1088          */
1089         public static final String SYNC_DATA3 = "sync_data3";
1090 
1091         /**
1092          * This column is available for use by sync adapters. Column name.
1093          * <P>Type: TEXT</P>
1094          */
1095         public static final String SYNC_DATA4 = "sync_data4";
1096 
1097         /**
1098          * This column is available for use by sync adapters. Column name.
1099          * <P>Type: TEXT</P>
1100          */
1101         public static final String SYNC_DATA5 = "sync_data5";
1102 
1103         /**
1104          * This column is available for use by sync adapters. Column name.
1105          * <P>Type: TEXT</P>
1106          */
1107         public static final String SYNC_DATA6 = "sync_data6";
1108 
1109         /**
1110          * This column is available for use by sync adapters. Column name.
1111          * <P>Type: TEXT</P>
1112          */
1113         public static final String SYNC_DATA7 = "sync_data7";
1114 
1115         /**
1116          * This column is available for use by sync adapters. Column name.
1117          * <P>Type: TEXT</P>
1118          */
1119         public static final String SYNC_DATA8 = "sync_data8";
1120 
1121         /**
1122          * This column is available for use by sync adapters. Column name.
1123          * <P>Type: TEXT</P>
1124          */
1125         public static final String SYNC_DATA9 = "sync_data9";
1126 
1127         /**
1128          * This column is available for use by sync adapters. Column name.
1129          * <P>Type: TEXT</P>
1130          */
1131         public static final String SYNC_DATA10 = "sync_data10";
1132 
1133         /**
1134          * Used to indicate that a row is not a real event but an original copy of a locally
1135          * modified event. A copy is made when an event changes from non-dirty to dirty and the
1136          * event is on a calendar with {@link Calendars#CAN_PARTIALLY_UPDATE} set to 1. This copy
1137          * does not get expanded in the instances table and is only visible in queries made by a
1138          * sync adapter. The copy gets removed when the event is changed back to non-dirty by a
1139          * sync adapter.
1140          * <P>Type: INTEGER (boolean)</P>
1141          */
1142         public static final String LAST_SYNCED = "lastSynced";
1143 
1144         /**
1145          * The time the event starts in UTC millis since epoch. Column name.
1146          * <P>Type: INTEGER (long; millis since epoch)</P>
1147          */
1148         public static final String DTSTART = "dtstart";
1149 
1150         /**
1151          * The time the event ends in UTC millis since epoch. Column name.
1152          * <P>Type: INTEGER (long; millis since epoch)</P>
1153          */
1154         public static final String DTEND = "dtend";
1155 
1156         /**
1157          * The duration of the event in RFC2445 format. Column name.
1158          * <P>Type: TEXT (duration in RFC2445 format)</P>
1159          */
1160         public static final String DURATION = "duration";
1161 
1162         /**
1163          * The timezone for the event. Column name.
1164          * <P>Type: TEXT</P>
1165          */
1166         public static final String EVENT_TIMEZONE = "eventTimezone";
1167 
1168         /**
1169          * The timezone for the end time of the event. Column name.
1170          * <P>Type: TEXT</P>
1171          */
1172         public static final String EVENT_END_TIMEZONE = "eventEndTimezone";
1173 
1174         /**
1175          * Is the event all day (time zone independent). Column name.
1176          * <P>Type: INTEGER (boolean)</P>
1177          */
1178         public static final String ALL_DAY = "allDay";
1179 
1180         /**
1181          * Defines how the event shows up for others when the calendar is
1182          * shared. Column name.
1183          * <P>Type: INTEGER (One of {@link #ACCESS_DEFAULT}, ...)</P>
1184          */
1185         public static final String ACCESS_LEVEL = "accessLevel";
1186 
1187         /**
1188          * Default access is controlled by the server and will be treated as
1189          * public on the device.
1190          */
1191         public static final int ACCESS_DEFAULT = 0;
1192         /**
1193          * Confidential is not used by the app.
1194          */
1195         public static final int ACCESS_CONFIDENTIAL = 1;
1196         /**
1197          * Private shares the event as a free/busy slot with no details.
1198          */
1199         public static final int ACCESS_PRIVATE = 2;
1200         /**
1201          * Public makes the contents visible to anyone with access to the
1202          * calendar.
1203          */
1204         public static final int ACCESS_PUBLIC = 3;
1205 
1206         /**
1207          * If this event counts as busy time or is still free time that can be
1208          * scheduled over. Column name.
1209          * <P>
1210          * Type: INTEGER (One of {@link #AVAILABILITY_BUSY},
1211          * {@link #AVAILABILITY_FREE}, {@link #AVAILABILITY_TENTATIVE})
1212          * </P>
1213          */
1214         public static final String AVAILABILITY = "availability";
1215 
1216         /**
1217          * Indicates that this event takes up time and will conflict with other
1218          * events.
1219          */
1220         public static final int AVAILABILITY_BUSY = 0;
1221         /**
1222          * Indicates that this event is free time and will not conflict with
1223          * other events.
1224          */
1225         public static final int AVAILABILITY_FREE = 1;
1226         /**
1227          * Indicates that the owner's availability may change, but should be
1228          * considered busy time that will conflict.
1229          */
1230         public static final int AVAILABILITY_TENTATIVE = 2;
1231 
1232         /**
1233          * Whether the event has an alarm or not. Column name.
1234          * <P>Type: INTEGER (boolean)</P>
1235          */
1236         public static final String HAS_ALARM = "hasAlarm";
1237 
1238         /**
1239          * Whether the event has extended properties or not. Column name.
1240          * <P>Type: INTEGER (boolean)</P>
1241          */
1242         public static final String HAS_EXTENDED_PROPERTIES = "hasExtendedProperties";
1243 
1244         /**
1245          * The recurrence rule for the event. Column name.
1246          * <P>Type: TEXT</P>
1247          */
1248         public static final String RRULE = "rrule";
1249 
1250         /**
1251          * The recurrence dates for the event. Column name.
1252          * <P>Type: TEXT</P>
1253          */
1254         public static final String RDATE = "rdate";
1255 
1256         /**
1257          * The recurrence exception rule for the event. Column name.
1258          * <P>Type: TEXT</P>
1259          */
1260         public static final String EXRULE = "exrule";
1261 
1262         /**
1263          * The recurrence exception dates for the event. Column name.
1264          * <P>Type: TEXT</P>
1265          */
1266         public static final String EXDATE = "exdate";
1267 
1268         /**
1269          * The {@link Events#_ID} of the original recurring event for which this
1270          * event is an exception. Column name.
1271          * <P>Type: TEXT</P>
1272          */
1273         public static final String ORIGINAL_ID = "original_id";
1274 
1275         /**
1276          * The _sync_id of the original recurring event for which this event is
1277          * an exception. The provider should keep the original_id in sync when
1278          * this is updated. Column name.
1279          * <P>Type: TEXT</P>
1280          */
1281         public static final String ORIGINAL_SYNC_ID = "original_sync_id";
1282 
1283         /**
1284          * The original instance time of the recurring event for which this
1285          * event is an exception. Column name.
1286          * <P>Type: INTEGER (long; millis since epoch)</P>
1287          */
1288         public static final String ORIGINAL_INSTANCE_TIME = "originalInstanceTime";
1289 
1290         /**
1291          * The allDay status (true or false) of the original recurring event
1292          * for which this event is an exception. Column name.
1293          * <P>Type: INTEGER (boolean)</P>
1294          */
1295         public static final String ORIGINAL_ALL_DAY = "originalAllDay";
1296 
1297         /**
1298          * The last date this event repeats on, or NULL if it never ends. Column
1299          * name.
1300          * <P>Type: INTEGER (long; millis since epoch)</P>
1301          */
1302         public static final String LAST_DATE = "lastDate";
1303 
1304         /**
1305          * Whether the event has attendee information.  True if the event
1306          * has full attendee data, false if the event has information about
1307          * self only. Column name.
1308          * <P>Type: INTEGER (boolean)</P>
1309          */
1310         public static final String HAS_ATTENDEE_DATA = "hasAttendeeData";
1311 
1312         /**
1313          * Whether guests can modify the event. Column name.
1314          * <P>Type: INTEGER (boolean)</P>
1315          */
1316         public static final String GUESTS_CAN_MODIFY = "guestsCanModify";
1317 
1318         /**
1319          * Whether guests can invite other guests. Column name.
1320          * <P>Type: INTEGER (boolean)</P>
1321          */
1322         public static final String GUESTS_CAN_INVITE_OTHERS = "guestsCanInviteOthers";
1323 
1324         /**
1325          * Whether guests can see the list of attendees. Column name.
1326          * <P>Type: INTEGER (boolean)</P>
1327          */
1328         public static final String GUESTS_CAN_SEE_GUESTS = "guestsCanSeeGuests";
1329 
1330         /**
1331          * Email of the organizer (owner) of the event. Column name.
1332          * <P>Type: STRING</P>
1333          */
1334         public static final String ORGANIZER = "organizer";
1335 
1336         /**
1337          * Are we the organizer of this event. If this column is not explicitly set, the provider
1338          * will return 1 if {@link #ORGANIZER} is equal to {@link Calendars#OWNER_ACCOUNT}.
1339          * Column name.
1340          * <P>Type: STRING</P>
1341          */
1342         public static final String IS_ORGANIZER = "isOrganizer";
1343 
1344         /**
1345          * Whether the user can invite others to the event. The
1346          * GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary
1347          * guest, while CAN_INVITE_OTHERS indicates if the user can invite
1348          * others (either through GUESTS_CAN_INVITE_OTHERS or because the user
1349          * has modify access to the event). Column name.
1350          * <P>Type: INTEGER (boolean, readonly)</P>
1351          */
1352         public static final String CAN_INVITE_OTHERS = "canInviteOthers";
1353 
1354         /**
1355          * The package name of the custom app that can provide a richer
1356          * experience for the event. See the ACTION TYPE
1357          * {@link CalendarContract#ACTION_HANDLE_CUSTOM_EVENT} for details.
1358          * Column name.
1359          * <P> Type: TEXT </P>
1360          */
1361         public static final String CUSTOM_APP_PACKAGE = "customAppPackage";
1362 
1363         /**
1364          * The URI used by the custom app for the event. Column name.
1365          * <P>Type: TEXT</P>
1366          */
1367         public static final String CUSTOM_APP_URI = "customAppUri";
1368 
1369         /**
1370          * The UID for events added from the RFC 2445 iCalendar format.
1371          * Column name.
1372          * <P>Type: TEXT</P>
1373          */
1374         public static final String UID_2445 = "uid2445";
1375     }
1376 
1377     /**
1378      * Class that represents an Event Entity. There is one entry per event.
1379      * Recurring events show up as a single entry. This is a helper class to
1380      * make batch operations easier. A {@link ContentResolver} or
1381      * {@link ContentProviderClient} is required as the helper does additional
1382      * queries to add reminders and attendees to each entry.
1383      */
1384     public static final class EventsEntity implements BaseColumns, SyncColumns, EventsColumns {
1385         /**
1386          * The content:// style URL for this table
1387          */
1388         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
1389                 "/event_entities");
1390 
1391         /**
1392          * This utility class cannot be instantiated
1393          */
EventsEntity()1394         private EventsEntity() {}
1395 
1396         /**
1397          * Creates a new iterator for events
1398          *
1399          * @param cursor An event query
1400          * @param resolver For performing additional queries
1401          * @return an EntityIterator containing one entity per event in the
1402          *         cursor
1403          */
newEntityIterator(Cursor cursor, ContentResolver resolver)1404         public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
1405             return new EntityIteratorImpl(cursor, resolver);
1406         }
1407 
1408         /**
1409          * Creates a new iterator for events
1410          *
1411          * @param cursor An event query
1412          * @param provider For performing additional queries
1413          * @return an EntityIterator containing one entity per event in the
1414          *         cursor
1415          */
newEntityIterator(Cursor cursor, ContentProviderClient provider)1416         public static EntityIterator newEntityIterator(Cursor cursor,
1417                 ContentProviderClient provider) {
1418             return new EntityIteratorImpl(cursor, provider);
1419         }
1420 
1421         private static class EntityIteratorImpl extends CursorEntityIterator {
1422             private final ContentResolver mResolver;
1423             private final ContentProviderClient mProvider;
1424 
1425             private static final String[] REMINDERS_PROJECTION = new String[] {
1426                     Reminders.MINUTES,
1427                     Reminders.METHOD,
1428             };
1429             private static final int COLUMN_MINUTES = 0;
1430             private static final int COLUMN_METHOD = 1;
1431 
1432             private static final String[] ATTENDEES_PROJECTION = new String[] {
1433                     Attendees.ATTENDEE_NAME,
1434                     Attendees.ATTENDEE_EMAIL,
1435                     Attendees.ATTENDEE_RELATIONSHIP,
1436                     Attendees.ATTENDEE_TYPE,
1437                     Attendees.ATTENDEE_STATUS,
1438                     Attendees.ATTENDEE_IDENTITY,
1439                     Attendees.ATTENDEE_ID_NAMESPACE
1440             };
1441             private static final int COLUMN_ATTENDEE_NAME = 0;
1442             private static final int COLUMN_ATTENDEE_EMAIL = 1;
1443             private static final int COLUMN_ATTENDEE_RELATIONSHIP = 2;
1444             private static final int COLUMN_ATTENDEE_TYPE = 3;
1445             private static final int COLUMN_ATTENDEE_STATUS = 4;
1446             private static final int COLUMN_ATTENDEE_IDENTITY = 5;
1447             private static final int COLUMN_ATTENDEE_ID_NAMESPACE = 6;
1448 
1449             private static final String[] EXTENDED_PROJECTION = new String[] {
1450                     ExtendedProperties._ID,
1451                     ExtendedProperties.NAME,
1452                     ExtendedProperties.VALUE
1453             };
1454             private static final int COLUMN_ID = 0;
1455             private static final int COLUMN_NAME = 1;
1456             private static final int COLUMN_VALUE = 2;
1457 
1458             private static final String WHERE_EVENT_ID = "event_id=?";
1459 
EntityIteratorImpl(Cursor cursor, ContentResolver resolver)1460             public EntityIteratorImpl(Cursor cursor, ContentResolver resolver) {
1461                 super(cursor);
1462                 mResolver = resolver;
1463                 mProvider = null;
1464             }
1465 
EntityIteratorImpl(Cursor cursor, ContentProviderClient provider)1466             public EntityIteratorImpl(Cursor cursor, ContentProviderClient provider) {
1467                 super(cursor);
1468                 mResolver = null;
1469                 mProvider = provider;
1470             }
1471 
1472             @Override
getEntityAndIncrementCursor(Cursor cursor)1473             public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException {
1474                 // we expect the cursor is already at the row we need to read from
1475                 final long eventId = cursor.getLong(cursor.getColumnIndexOrThrow(Events._ID));
1476                 ContentValues cv = new ContentValues();
1477                 cv.put(Events._ID, eventId);
1478                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, CALENDAR_ID);
1479                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, TITLE);
1480                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DESCRIPTION);
1481                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_LOCATION);
1482                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, STATUS);
1483                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SELF_ATTENDEE_STATUS);
1484                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTSTART);
1485                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTEND);
1486                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION);
1487                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE);
1488                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_END_TIMEZONE);
1489                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY);
1490                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
1491                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY);
1492                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EVENT_COLOR);
1493                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_COLOR_KEY);
1494                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, HAS_ALARM);
1495                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
1496                         HAS_EXTENDED_PROPERTIES);
1497                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, RRULE);
1498                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, RDATE);
1499                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE);
1500                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE);
1501                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_SYNC_ID);
1502                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_ID);
1503                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv,
1504                         ORIGINAL_INSTANCE_TIME);
1505                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY);
1506                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_DATE);
1507                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, HAS_ATTENDEE_DATA);
1508                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
1509                         GUESTS_CAN_INVITE_OTHERS);
1510                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, GUESTS_CAN_MODIFY);
1511                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, GUESTS_CAN_SEE_GUESTS);
1512                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CUSTOM_APP_PACKAGE);
1513                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CUSTOM_APP_URI);
1514                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, UID_2445);
1515                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER);
1516                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, IS_ORGANIZER);
1517                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
1518                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
1519                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, MUTATORS);
1520                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_SYNCED);
1521                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
1522                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA1);
1523                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA2);
1524                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA3);
1525                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA4);
1526                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA5);
1527                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA6);
1528                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA7);
1529                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA8);
1530                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA9);
1531                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC_DATA10);
1532                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1);
1533                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2);
1534                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC3);
1535                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC4);
1536                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC5);
1537                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC6);
1538                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC7);
1539                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC8);
1540                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC9);
1541                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC10);
1542 
1543                 Entity entity = new Entity(cv);
1544                 Cursor subCursor;
1545                 if (mResolver != null) {
1546                     subCursor = mResolver.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION,
1547                             WHERE_EVENT_ID,
1548                             new String[] { Long.toString(eventId) }  /* selectionArgs */,
1549                             null /* sortOrder */);
1550                 } else {
1551                     subCursor = mProvider.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION,
1552                             WHERE_EVENT_ID,
1553                             new String[] { Long.toString(eventId) }  /* selectionArgs */,
1554                             null /* sortOrder */);
1555                 }
1556                 try {
1557                     while (subCursor.moveToNext()) {
1558                         ContentValues reminderValues = new ContentValues();
1559                         reminderValues.put(Reminders.MINUTES, subCursor.getInt(COLUMN_MINUTES));
1560                         reminderValues.put(Reminders.METHOD, subCursor.getInt(COLUMN_METHOD));
1561                         entity.addSubValue(Reminders.CONTENT_URI, reminderValues);
1562                     }
1563                 } finally {
1564                     subCursor.close();
1565                 }
1566 
1567                 if (mResolver != null) {
1568                     subCursor = mResolver.query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION,
1569                             WHERE_EVENT_ID,
1570                             new String[] { Long.toString(eventId) } /* selectionArgs */,
1571                             null /* sortOrder */);
1572                 } else {
1573                     subCursor = mProvider.query(Attendees.CONTENT_URI, ATTENDEES_PROJECTION,
1574                             WHERE_EVENT_ID,
1575                             new String[] { Long.toString(eventId) } /* selectionArgs */,
1576                             null /* sortOrder */);
1577                 }
1578                 try {
1579                     while (subCursor.moveToNext()) {
1580                         ContentValues attendeeValues = new ContentValues();
1581                         attendeeValues.put(Attendees.ATTENDEE_NAME,
1582                                 subCursor.getString(COLUMN_ATTENDEE_NAME));
1583                         attendeeValues.put(Attendees.ATTENDEE_EMAIL,
1584                                 subCursor.getString(COLUMN_ATTENDEE_EMAIL));
1585                         attendeeValues.put(Attendees.ATTENDEE_RELATIONSHIP,
1586                                 subCursor.getInt(COLUMN_ATTENDEE_RELATIONSHIP));
1587                         attendeeValues.put(Attendees.ATTENDEE_TYPE,
1588                                 subCursor.getInt(COLUMN_ATTENDEE_TYPE));
1589                         attendeeValues.put(Attendees.ATTENDEE_STATUS,
1590                                 subCursor.getInt(COLUMN_ATTENDEE_STATUS));
1591                         attendeeValues.put(Attendees.ATTENDEE_IDENTITY,
1592                                 subCursor.getString(COLUMN_ATTENDEE_IDENTITY));
1593                         attendeeValues.put(Attendees.ATTENDEE_ID_NAMESPACE,
1594                                 subCursor.getString(COLUMN_ATTENDEE_ID_NAMESPACE));
1595                         entity.addSubValue(Attendees.CONTENT_URI, attendeeValues);
1596                     }
1597                 } finally {
1598                     subCursor.close();
1599                 }
1600 
1601                 if (mResolver != null) {
1602                     subCursor = mResolver.query(ExtendedProperties.CONTENT_URI, EXTENDED_PROJECTION,
1603                             WHERE_EVENT_ID,
1604                             new String[] { Long.toString(eventId) } /* selectionArgs */,
1605                             null /* sortOrder */);
1606                 } else {
1607                     subCursor = mProvider.query(ExtendedProperties.CONTENT_URI, EXTENDED_PROJECTION,
1608                             WHERE_EVENT_ID,
1609                             new String[] { Long.toString(eventId) } /* selectionArgs */,
1610                             null /* sortOrder */);
1611                 }
1612                 try {
1613                     while (subCursor.moveToNext()) {
1614                         ContentValues extendedValues = new ContentValues();
1615                         extendedValues.put(ExtendedProperties._ID,
1616                                 subCursor.getString(COLUMN_ID));
1617                         extendedValues.put(ExtendedProperties.NAME,
1618                                 subCursor.getString(COLUMN_NAME));
1619                         extendedValues.put(ExtendedProperties.VALUE,
1620                                 subCursor.getString(COLUMN_VALUE));
1621                         entity.addSubValue(ExtendedProperties.CONTENT_URI, extendedValues);
1622                     }
1623                 } finally {
1624                     subCursor.close();
1625                 }
1626 
1627                 cursor.moveToNext();
1628                 return entity;
1629             }
1630         }
1631     }
1632 
1633     /**
1634      * Constants and helpers for the Events table, which contains details for
1635      * individual events. <h3>Operations</h3> All operations can be done either
1636      * as an app or as a sync adapter. To perform an operation as a sync adapter
1637      * {@link #CALLER_IS_SYNCADAPTER} should be set to true and
1638      * {@link #ACCOUNT_NAME} and {@link #ACCOUNT_TYPE} must be set in the Uri
1639      * parameters. See
1640      * {@link Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)}
1641      * for details on adding parameters. Sync adapters have write access to more
1642      * columns but are restricted to a single account at a time.
1643      * <dl>
1644      * <dt><b>Insert</b></dt>
1645      * <dd>When inserting a new event the following fields must be included:
1646      * <ul>
1647      * <li>dtstart</li>
1648      * <li>dtend if the event is non-recurring</li>
1649      * <li>duration if the event is recurring</li>
1650      * <li>rrule or rdate if the event is recurring</li>
1651      * <li>eventTimezone</li>
1652      * <li>a calendar_id</li>
1653      * </ul>
1654      * There are also further requirements when inserting or updating an event.
1655      * See the section on Writing to Events.</dd>
1656      * <dt><b>Update</b></dt>
1657      * <dd>To perform an update of an Event the {@link Events#_ID} of the event
1658      * should be provided either as an appended id to the Uri (
1659      * {@link ContentUris#withAppendedId}) or as the first selection item--the
1660      * selection should start with "_id=?" and the first selectionArg should be
1661      * the _id of the event. Updates may also be done using a selection and no
1662      * id. Updating an event must respect the same rules as inserting and is
1663      * further restricted in the fields that can be written. See the section on
1664      * Writing to Events.</dd>
1665      * <dt><b>Delete</b></dt>
1666      * <dd>Events can be deleted either by the {@link Events#_ID} as an appended
1667      * id on the Uri or using any standard selection. If an appended id is used
1668      * a selection is not allowed. There are two versions of delete: as an app
1669      * and as a sync adapter. An app delete will set the deleted column on an
1670      * event and remove all instances of that event. A sync adapter delete will
1671      * remove the event from the database and all associated data.</dd>
1672      * <dt><b>Query</b></dt>
1673      * <dd>Querying the Events table will get you all information about a set of
1674      * events except their reminders, attendees, and extended properties. There
1675      * will be one row returned for each event that matches the query selection,
1676      * or at most a single row if the {@link Events#_ID} is appended to the Uri.
1677      * Recurring events will only return a single row regardless of the number
1678      * of times that event repeats.</dd>
1679      * </dl>
1680      * <h3>Writing to Events</h3> There are further restrictions on all Updates
1681      * and Inserts in the Events table:
1682      * <ul>
1683      * <li>If allDay is set to 1 eventTimezone must be {@link Time#TIMEZONE_UTC}
1684      * and the time must correspond to a midnight boundary.</li>
1685      * <li>Exceptions are not allowed to recur. If rrule or rdate is not empty,
1686      * original_id and original_sync_id must be empty.</li>
1687      * <li>In general a calendar_id should not be modified after insertion. This
1688      * is not explicitly forbidden but many sync adapters will not behave in an
1689      * expected way if the calendar_id is modified.</li>
1690      * </ul>
1691      * The following Events columns are writable by both an app and a sync
1692      * adapter.
1693      * <ul>
1694      * <li>{@link #CALENDAR_ID}</li>
1695      * <li>{@link #ORGANIZER}</li>
1696      * <li>{@link #TITLE}</li>
1697      * <li>{@link #EVENT_LOCATION}</li>
1698      * <li>{@link #DESCRIPTION}</li>
1699      * <li>{@link #EVENT_COLOR}</li>
1700      * <li>{@link #DTSTART}</li>
1701      * <li>{@link #DTEND}</li>
1702      * <li>{@link #EVENT_TIMEZONE}</li>
1703      * <li>{@link #EVENT_END_TIMEZONE}</li>
1704      * <li>{@link #DURATION}</li>
1705      * <li>{@link #ALL_DAY}</li>
1706      * <li>{@link #RRULE}</li>
1707      * <li>{@link #RDATE}</li>
1708      * <li>{@link #EXRULE}</li>
1709      * <li>{@link #EXDATE}</li>
1710      * <li>{@link #ORIGINAL_ID}</li>
1711      * <li>{@link #ORIGINAL_SYNC_ID}</li>
1712      * <li>{@link #ORIGINAL_INSTANCE_TIME}</li>
1713      * <li>{@link #ORIGINAL_ALL_DAY}</li>
1714      * <li>{@link #ACCESS_LEVEL}</li>
1715      * <li>{@link #AVAILABILITY}</li>
1716      * <li>{@link #GUESTS_CAN_MODIFY}</li>
1717      * <li>{@link #GUESTS_CAN_INVITE_OTHERS}</li>
1718      * <li>{@link #GUESTS_CAN_SEE_GUESTS}</li>
1719      * <li>{@link #CUSTOM_APP_PACKAGE}</li>
1720      * <li>{@link #CUSTOM_APP_URI}</li>
1721      * <li>{@link #UID_2445}</li>
1722      * </ul>
1723      * The following Events columns are writable only by a sync adapter
1724      * <ul>
1725      * <li>{@link #DIRTY}</li>
1726      * <li>{@link #MUTATORS}</li>
1727      * <li>{@link #_SYNC_ID}</li>
1728      * <li>{@link #SYNC_DATA1}</li>
1729      * <li>{@link #SYNC_DATA2}</li>
1730      * <li>{@link #SYNC_DATA3}</li>
1731      * <li>{@link #SYNC_DATA4}</li>
1732      * <li>{@link #SYNC_DATA5}</li>
1733      * <li>{@link #SYNC_DATA6}</li>
1734      * <li>{@link #SYNC_DATA7}</li>
1735      * <li>{@link #SYNC_DATA8}</li>
1736      * <li>{@link #SYNC_DATA9}</li>
1737      * <li>{@link #SYNC_DATA10}</li>
1738      * </ul>
1739      * The remaining columns are either updated by the provider only or are
1740      * views into other tables and cannot be changed through the Events table.
1741      */
1742     public static final class Events implements BaseColumns, SyncColumns, EventsColumns,
1743             CalendarColumns {
1744 
1745         /**
1746          * The content:// style URL for interacting with events. Appending an
1747          * event id using {@link ContentUris#withAppendedId(Uri, long)} will
1748          * specify a single event.
1749          */
1750         @SuppressWarnings("hiding")
1751         public static final Uri CONTENT_URI =
1752                 Uri.parse("content://" + AUTHORITY + "/events");
1753 
1754         /**
1755          * The content:// style URL for querying Events table in the managed profile. Appending an
1756          * event id using {@link ContentUris#withAppendedId(Uri, long)} specifies a single event.
1757          *
1758          * <p>The following columns are allowed to be queried via this uri:
1759          * <ul>
1760          * <li>{@link #_ID}</li>
1761          * <li>{@link #CALENDAR_ID}</li>
1762          * <li>{@link #TITLE}</li>
1763          * <li>{@link #EVENT_LOCATION}</li>
1764          * <li>{@link #EVENT_COLOR}</li>
1765          * <li>{@link #STATUS}</li>
1766          * <li>{@link #DTSTART}</li>
1767          * <li>{@link #DTEND}</li>
1768          * <li>{@link #EVENT_TIMEZONE}</li>
1769          * <li>{@link #EVENT_END_TIMEZONE}</li>
1770          * <li>{@link #DURATION}</li>
1771          * <li>{@link #ALL_DAY}</li>
1772          * <li>{@link #AVAILABILITY}</li>
1773          * <li>{@link #RRULE}</li>
1774          * <li>{@link #RDATE}</li>
1775          * <li>{@link #LAST_DATE}</li>
1776          * <li>{@link #EXRULE}</li>
1777          * <li>{@link #EXDATE}</li>
1778          * <li>{@link #SELF_ATTENDEE_STATUS}</li>
1779          * <li>{@link #DISPLAY_COLOR}</li>
1780          * <li>{@link #CALENDAR_COLOR}</li>
1781          * <li>{@link #VISIBLE}</li>
1782          * <li>{@link #CALENDAR_TIME_ZONE}</li>
1783          * <li>{@link #IS_PRIMARY}</li>
1784          * </ul>
1785          *
1786          * <p>{@link IllegalArgumentException} is thrown if there exists columns in the
1787          * projection of the query to this uri that are not contained in the above list.
1788          *
1789          * <p>This uri returns an empty cursor if the calling user is not a parent profile
1790          * of a managed profile, or the managed profile is disabled, or cross-profile calendar is
1791          * disabled in Settings, or this uri is queried from a package that is not allowed by
1792          * the profile owner of the managed profile via
1793          * {@link DevicePolicyManager#setCrossProfileCalendarPackages(ComponentName, Set)}.
1794          *
1795          * <p>Apps can register a {@link android.database.ContentObserver} for this URI to listen
1796          * to changes.
1797          *
1798          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
1799          * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
1800          */
1801         @NonNull
1802         public static final Uri ENTERPRISE_CONTENT_URI =
1803                 Uri.parse("content://" + AUTHORITY + "/enterprise/events");
1804 
1805         /**
1806          * The content:// style URI for recurring event exceptions.  Insertions require an
1807          * appended event ID.  Deletion of exceptions requires both the original event ID and
1808          * the exception event ID (see {@link Uri.Builder#appendPath}).
1809          */
1810         public static final Uri CONTENT_EXCEPTION_URI =
1811                 Uri.parse("content://" + AUTHORITY + "/exception");
1812 
1813         /**
1814          * This utility class cannot be instantiated
1815          */
Events()1816         private Events() {}
1817 
1818         /**
1819          * The default sort order for this table
1820          */
1821         private static final String DEFAULT_SORT_ORDER = "";
1822 
1823         /**
1824          * These are columns that should only ever be updated by the provider,
1825          * either because they are views mapped to another table or because they
1826          * are used for provider only functionality. TODO move to provider
1827          *
1828          * @hide
1829          */
1830         @UnsupportedAppUsage
1831         public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
1832                 ACCOUNT_NAME,
1833                 ACCOUNT_TYPE,
1834                 CAL_SYNC1,
1835                 CAL_SYNC2,
1836                 CAL_SYNC3,
1837                 CAL_SYNC4,
1838                 CAL_SYNC5,
1839                 CAL_SYNC6,
1840                 CAL_SYNC7,
1841                 CAL_SYNC8,
1842                 CAL_SYNC9,
1843                 CAL_SYNC10,
1844                 ALLOWED_REMINDERS,
1845                 ALLOWED_ATTENDEE_TYPES,
1846                 ALLOWED_AVAILABILITY,
1847                 CALENDAR_ACCESS_LEVEL,
1848                 CALENDAR_COLOR,
1849                 CALENDAR_TIME_ZONE,
1850                 CAN_MODIFY_TIME_ZONE,
1851                 CAN_ORGANIZER_RESPOND,
1852                 CALENDAR_DISPLAY_NAME,
1853                 CAN_PARTIALLY_UPDATE,
1854                 SYNC_EVENTS,
1855                 VISIBLE,
1856         };
1857 
1858         /**
1859          * These fields are only writable by a sync adapter. To modify them the
1860          * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
1861          * _SYNC_ACCOUNT_TYPE in the query parameters. TODO move to provider.
1862          *
1863          * @hide
1864          */
1865         @TestApi
1866         public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
1867             _SYNC_ID,
1868             DIRTY,
1869             MUTATORS,
1870             SYNC_DATA1,
1871             SYNC_DATA2,
1872             SYNC_DATA3,
1873             SYNC_DATA4,
1874             SYNC_DATA5,
1875             SYNC_DATA6,
1876             SYNC_DATA7,
1877             SYNC_DATA8,
1878             SYNC_DATA9,
1879             SYNC_DATA10,
1880         };
1881     }
1882 
1883     /**
1884      * Fields and helpers for interacting with Instances. An instance is a
1885      * single occurrence of an event including time zone specific start and end
1886      * days and minutes. The instances table is not writable and only provides a
1887      * way to query event occurrences.
1888      */
1889     public static final class Instances implements BaseColumns, EventsColumns, CalendarColumns {
1890 
1891         private static final String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=?";
1892         private static final String[] WHERE_CALENDARS_ARGS = {
1893             "1"
1894         };
1895 
1896         /**
1897          * This utility class cannot be instantiated
1898          */
Instances()1899         private Instances() {}
1900 
1901         /**
1902          * Performs a query to return all visible instances in the given range.
1903          * This is a blocking function and should not be done on the UI thread.
1904          * This will cause an expansion of recurring events to fill this time
1905          * range if they are not already expanded and will slow down for larger
1906          * time ranges with many recurring events.
1907          *
1908          * @param cr The ContentResolver to use for the query
1909          * @param projection The columns to return
1910          * @param begin The start of the time range to query in UTC millis since
1911          *            epoch
1912          * @param end The end of the time range to query in UTC millis since
1913          *            epoch
1914          * @return A Cursor containing all instances in the given range
1915          */
query(ContentResolver cr, String[] projection, long begin, long end)1916         public static final Cursor query(ContentResolver cr, String[] projection,
1917                                          long begin, long end) {
1918             Uri.Builder builder = CONTENT_URI.buildUpon();
1919             ContentUris.appendId(builder, begin);
1920             ContentUris.appendId(builder, end);
1921             return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
1922                     WHERE_CALENDARS_ARGS, DEFAULT_SORT_ORDER);
1923         }
1924 
1925         /**
1926          * Performs a query to return all visible instances in the given range
1927          * that match the given query. This is a blocking function and should
1928          * not be done on the UI thread. This will cause an expansion of
1929          * recurring events to fill this time range if they are not already
1930          * expanded and will slow down for larger time ranges with many
1931          * recurring events.
1932          *
1933          * @param cr The ContentResolver to use for the query
1934          * @param projection The columns to return
1935          * @param begin The start of the time range to query in UTC millis since
1936          *            epoch
1937          * @param end The end of the time range to query in UTC millis since
1938          *            epoch
1939          * @param searchQuery A string of space separated search terms. Segments
1940          *            enclosed by double quotes will be treated as a single
1941          *            term.
1942          * @return A Cursor of instances matching the search terms in the given
1943          *         time range
1944          */
query(ContentResolver cr, String[] projection, long begin, long end, String searchQuery)1945         public static final Cursor query(ContentResolver cr, String[] projection,
1946                                          long begin, long end, String searchQuery) {
1947             Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
1948             ContentUris.appendId(builder, begin);
1949             ContentUris.appendId(builder, end);
1950             builder = builder.appendPath(searchQuery);
1951             return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
1952                     WHERE_CALENDARS_ARGS, DEFAULT_SORT_ORDER);
1953         }
1954 
1955         /**
1956          * The content:// style URL for querying an instance range. The begin
1957          * and end of the range to query should be added as path segments if
1958          * this is used directly.
1959          */
1960         @SuppressWarnings("hiding")
1961         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
1962                 "/instances/when");
1963         /**
1964          * The content:// style URL for querying an instance range by Julian
1965          * Day. The start and end day should be added as path segments if this
1966          * is used directly.
1967          */
1968         public static final Uri CONTENT_BY_DAY_URI =
1969             Uri.parse("content://" + AUTHORITY + "/instances/whenbyday");
1970         /**
1971          * The content:// style URL for querying an instance range with a search
1972          * term. The begin, end, and search string should be appended as path
1973          * segments if this is used directly.
1974          */
1975         public static final Uri CONTENT_SEARCH_URI = Uri.parse("content://" + AUTHORITY +
1976                 "/instances/search");
1977         /**
1978          * The content:// style URL for querying an instance range with a search
1979          * term. The start day, end day, and search string should be appended as
1980          * path segments if this is used directly.
1981          */
1982         public static final Uri CONTENT_SEARCH_BY_DAY_URI =
1983             Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
1984 
1985         /**
1986          * The content:// style URL for querying an instance range in the managed profile.
1987          * It supports similar semantics as {@link #CONTENT_URI}.
1988          *
1989          * <p>The following columns plus the columns that are allowed by
1990          * {@link Events#ENTERPRISE_CONTENT_URI} are allowed to be queried via this uri:
1991          * <ul>
1992          * <li>{@link #_ID}</li>
1993          * <li>{@link #EVENT_ID}</li>
1994          * <li>{@link #BEGIN}</li>
1995          * <li>{@link #END}</li>
1996          * <li>{@link #START_DAY}</li>
1997          * <li>{@link #END_DAY}</li>
1998          * <li>{@link #START_MINUTE}</li>
1999          * <li>{@link #END_MINUTE}</li>
2000          * </ul>
2001          *
2002          * <p>{@link IllegalArgumentException} is thrown if there exists columns in the
2003          * projection of the query to this uri that are not contained in the above list.
2004          *
2005          * <p>This uri returns an empty cursor if the calling user is not a parent profile
2006          * of a managed profile, or the managed profile is disabled, or cross-profile calendar is
2007          * disabled in Settings, or this uri is queried from a package that is not allowed by
2008          * the profile owner of the managed profile via
2009          * {@link DevicePolicyManager#setCrossProfileCalendarPackages(ComponentName, Set)}.
2010          *
2011          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
2012          * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
2013          */
2014         @NonNull
2015         public static final Uri ENTERPRISE_CONTENT_URI =
2016                 Uri.parse("content://" + AUTHORITY + "/enterprise/instances/when");
2017 
2018         /**
2019          * The content:// style URL for querying an instance range by Julian
2020          * Day in the managed profile. It supports similar semantics as {@link #CONTENT_BY_DAY_URI}
2021          * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
2022          */
2023         @NonNull
2024         public static final Uri ENTERPRISE_CONTENT_BY_DAY_URI =
2025                 Uri.parse("content://" + AUTHORITY + "/enterprise/instances/whenbyday");
2026 
2027         /**
2028          * The content:// style URL for querying an instance range with a search
2029          * term in the managed profile. It supports similar semantics as {@link #CONTENT_SEARCH_URI}
2030          * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
2031          */
2032         @NonNull
2033         public static final Uri ENTERPRISE_CONTENT_SEARCH_URI =
2034                 Uri.parse("content://" + AUTHORITY + "/enterprise/instances/search");
2035 
2036         /**
2037          * The content:// style URL for querying an instance range with a search
2038          * term in the managed profile. It supports similar semantics as
2039          * {@link #CONTENT_SEARCH_BY_DAY_URI} and performs similar checks as
2040          * {@link #ENTERPRISE_CONTENT_URI}.
2041          */
2042         @NonNull
2043         public static final Uri ENTERPRISE_CONTENT_SEARCH_BY_DAY_URI =
2044                 Uri.parse("content://" + AUTHORITY + "/enterprise/instances/searchbyday");
2045 
2046         /**
2047          * The default sort order for this table.
2048          */
2049         private static final String DEFAULT_SORT_ORDER = "begin ASC";
2050 
2051         /**
2052          * The beginning time of the instance, in UTC milliseconds. Column name.
2053          * <P>Type: INTEGER (long; millis since epoch)</P>
2054          */
2055         public static final String BEGIN = "begin";
2056 
2057         /**
2058          * The ending time of the instance, in UTC milliseconds. Column name.
2059          * <P>Type: INTEGER (long; millis since epoch)</P>
2060          */
2061         public static final String END = "end";
2062 
2063         /**
2064          * The _id of the event for this instance. Column name.
2065          * <P>Type: INTEGER (long, foreign key to the Events table)</P>
2066          */
2067         public static final String EVENT_ID = "event_id";
2068 
2069         /**
2070          * The Julian start day of the instance, relative to the local time
2071          * zone. Column name.
2072          * <P>Type: INTEGER (int)</P>
2073          */
2074         public static final String START_DAY = "startDay";
2075 
2076         /**
2077          * The Julian end day of the instance, relative to the local time
2078          * zone. Column name.
2079          * <P>Type: INTEGER (int)</P>
2080          */
2081         public static final String END_DAY = "endDay";
2082 
2083         /**
2084          * The start minute of the instance measured from midnight in the
2085          * local time zone. Column name.
2086          * <P>Type: INTEGER (int)</P>
2087          */
2088         public static final String START_MINUTE = "startMinute";
2089 
2090         /**
2091          * The end minute of the instance measured from midnight in the
2092          * local time zone. Column name.
2093          * <P>Type: INTEGER (int)</P>
2094          */
2095         public static final String END_MINUTE = "endMinute";
2096     }
2097 
2098     protected interface CalendarCacheColumns {
2099         /**
2100          * The key for the setting. Keys are defined in {@link CalendarCache}.
2101          */
2102         public static final String KEY = "key";
2103 
2104         /**
2105          * The value of the given setting.
2106          */
2107         public static final String VALUE = "value";
2108     }
2109 
2110     /**
2111      * CalendarCache stores some settings for calendar including the current
2112      * time zone for the instances. These settings are stored using a key/value
2113      * scheme. A {@link #KEY} must be specified when updating these values.
2114      */
2115     public static final class CalendarCache implements CalendarCacheColumns {
2116         /**
2117          * The URI to use for retrieving the properties from the Calendar db.
2118          */
2119         public static final Uri URI =
2120                 Uri.parse("content://" + AUTHORITY + "/properties");
2121 
2122         /**
2123          * This utility class cannot be instantiated
2124          */
CalendarCache()2125         private CalendarCache() {}
2126 
2127         /**
2128          * They key for updating the use of auto/home time zones in Calendar.
2129          * Valid values are {@link #TIMEZONE_TYPE_AUTO} or
2130          * {@link #TIMEZONE_TYPE_HOME}.
2131          */
2132         public static final String KEY_TIMEZONE_TYPE = "timezoneType";
2133 
2134         /**
2135          * The key for updating the time zone used by the provider when it
2136          * generates the instances table. This should only be written if the
2137          * type is set to {@link #TIMEZONE_TYPE_HOME}. A valid time zone id
2138          * should be written to this field.
2139          */
2140         public static final String KEY_TIMEZONE_INSTANCES = "timezoneInstances";
2141 
2142         /**
2143          * The key for reading the last time zone set by the user. This should
2144          * only be read by apps and it will be automatically updated whenever
2145          * {@link #KEY_TIMEZONE_INSTANCES} is updated with
2146          * {@link #TIMEZONE_TYPE_HOME} set.
2147          */
2148         public static final String KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious";
2149 
2150         /**
2151          * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
2152          * should stay in sync with the device's time zone.
2153          */
2154         public static final String TIMEZONE_TYPE_AUTO = "auto";
2155 
2156         /**
2157          * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
2158          * should use a fixed time zone set by the user.
2159          */
2160         public static final String TIMEZONE_TYPE_HOME = "home";
2161     }
2162 
2163     /**
2164      * A few Calendar globals are needed in the CalendarProvider for expanding
2165      * the Instances table and these are all stored in the first (and only) row
2166      * of the CalendarMetaData table.
2167      *
2168      * @hide
2169      */
2170     protected interface CalendarMetaDataColumns {
2171         /**
2172          * The local timezone that was used for precomputing the fields
2173          * in the Instances table.
2174          */
2175         public static final String LOCAL_TIMEZONE = "localTimezone";
2176 
2177         /**
2178          * The minimum time used in expanding the Instances table,
2179          * in UTC milliseconds.
2180          * <P>Type: INTEGER</P>
2181          */
2182         public static final String MIN_INSTANCE = "minInstance";
2183 
2184         /**
2185          * The maximum time used in expanding the Instances table,
2186          * in UTC milliseconds.
2187          * <P>Type: INTEGER</P>
2188          */
2189         public static final String MAX_INSTANCE = "maxInstance";
2190 
2191         /**
2192          * The minimum Julian day in the EventDays table.
2193          * <P>Type: INTEGER</P>
2194          */
2195         public static final String MIN_EVENTDAYS = "minEventDays";
2196 
2197         /**
2198          * The maximum Julian day in the EventDays table.
2199          * <P>Type: INTEGER</P>
2200          */
2201         public static final String MAX_EVENTDAYS = "maxEventDays";
2202     }
2203 
2204     /**
2205      * @hide
2206      */
2207     public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
2208 
2209         /**
2210          * This utility class cannot be instantiated
2211          */
CalendarMetaData()2212         private CalendarMetaData() {}
2213     }
2214 
2215     protected interface EventDaysColumns {
2216         /**
2217          * The Julian starting day number. Column name.
2218          * <P>Type: INTEGER (int)</P>
2219          */
2220         public static final String STARTDAY = "startDay";
2221         /**
2222          * The Julian ending day number. Column name.
2223          * <P>Type: INTEGER (int)</P>
2224          */
2225         public static final String ENDDAY = "endDay";
2226 
2227     }
2228 
2229     /**
2230      * Fields and helpers for querying for a list of days that contain events.
2231      */
2232     public static final class EventDays implements EventDaysColumns {
2233         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
2234                 + "/instances/groupbyday");
2235         private static final String SELECTION = "selected=1";
2236 
2237         /**
2238          * This utility class cannot be instantiated
2239          */
EventDays()2240         private EventDays() {}
2241 
2242         /**
2243          * Retrieves the days with events for the Julian days starting at
2244          * "startDay" for "numDays". It returns a cursor containing startday and
2245          * endday representing the max range of days for all events beginning on
2246          * each startday.This is a blocking function and should not be done on
2247          * the UI thread.
2248          *
2249          * @param cr the ContentResolver
2250          * @param startDay the first Julian day in the range
2251          * @param numDays the number of days to load (must be at least 1)
2252          * @param projection the columns to return in the cursor
2253          * @return a database cursor containing a list of start and end days for
2254          *         events
2255          */
query(ContentResolver cr, int startDay, int numDays, String[] projection)2256         public static final Cursor query(ContentResolver cr, int startDay, int numDays,
2257                 String[] projection) {
2258             if (numDays < 1) {
2259                 return null;
2260             }
2261             int endDay = startDay + numDays - 1;
2262             Uri.Builder builder = CONTENT_URI.buildUpon();
2263             ContentUris.appendId(builder, startDay);
2264             ContentUris.appendId(builder, endDay);
2265             return cr.query(builder.build(), projection, SELECTION,
2266                     null /* selection args */, STARTDAY);
2267         }
2268     }
2269 
2270     protected interface RemindersColumns {
2271         /**
2272          * The event the reminder belongs to. Column name.
2273          * <P>Type: INTEGER (foreign key to the Events table)</P>
2274          */
2275         public static final String EVENT_ID = "event_id";
2276 
2277         /**
2278          * The minutes prior to the event that the alarm should ring.  -1
2279          * specifies that we should use the default value for the system.
2280          * Column name.
2281          * <P>Type: INTEGER</P>
2282          */
2283         public static final String MINUTES = "minutes";
2284 
2285         /**
2286          * Passing this as a minutes value will use the default reminder
2287          * minutes.
2288          */
2289         public static final int MINUTES_DEFAULT = -1;
2290 
2291         /**
2292          * The alarm method, as set on the server. {@link #METHOD_DEFAULT},
2293          * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, {@link #METHOD_SMS} and
2294          * {@link #METHOD_ALARM} are possible values; the device will only
2295          * process {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders
2296          * (the other types are simply stored so we can send the same reminder
2297          * info back to the server when we make changes).
2298          */
2299         public static final String METHOD = "method";
2300 
2301         public static final int METHOD_DEFAULT = 0;
2302         public static final int METHOD_ALERT = 1;
2303         public static final int METHOD_EMAIL = 2;
2304         public static final int METHOD_SMS = 3;
2305         public static final int METHOD_ALARM = 4;
2306     }
2307 
2308     /**
2309      * Fields and helpers for accessing reminders for an event. Each row of this
2310      * table represents a single reminder for an event. Calling
2311      * {@link #query(ContentResolver, long, String[])} will return a list of reminders for
2312      * the event with the given eventId. Both apps and sync adapters may write
2313      * to this table. There are three writable fields and all of them must be
2314      * included when inserting a new reminder. They are:
2315      * <ul>
2316      * <li>{@link #EVENT_ID}</li>
2317      * <li>{@link #MINUTES}</li>
2318      * <li>{@link #METHOD}</li>
2319      * </ul>
2320      */
2321     public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns {
2322         private static final String REMINDERS_WHERE = CalendarContract.Reminders.EVENT_ID + "=?";
2323         @SuppressWarnings("hiding")
2324         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
2325 
2326         /**
2327          * This utility class cannot be instantiated
2328          */
Reminders()2329         private Reminders() {}
2330 
2331         /**
2332          * Queries all reminders associated with the given event. This is a
2333          * blocking call and should not be done on the UI thread.
2334          *
2335          * @param cr The content resolver to use for the query
2336          * @param eventId The id of the event to retrieve reminders for
2337          * @param projection the columns to return in the cursor
2338          * @return A Cursor containing all reminders for the event
2339          */
query(ContentResolver cr, long eventId, String[] projection)2340         public static final Cursor query(ContentResolver cr, long eventId, String[] projection) {
2341             String[] remArgs = {Long.toString(eventId)};
2342             return cr.query(CONTENT_URI, projection, REMINDERS_WHERE, remArgs /*selection args*/,
2343                     null /* sort order */);
2344         }
2345     }
2346 
2347     protected interface CalendarAlertsColumns {
2348         /**
2349          * The event that the alert belongs to. Column name.
2350          * <P>Type: INTEGER (foreign key to the Events table)</P>
2351          */
2352         public static final String EVENT_ID = "event_id";
2353 
2354         /**
2355          * The start time of the event, in UTC. Column name.
2356          * <P>Type: INTEGER (long; millis since epoch)</P>
2357          */
2358         public static final String BEGIN = "begin";
2359 
2360         /**
2361          * The end time of the event, in UTC. Column name.
2362          * <P>Type: INTEGER (long; millis since epoch)</P>
2363          */
2364         public static final String END = "end";
2365 
2366         /**
2367          * The alarm time of the event, in UTC. Column name.
2368          * <P>Type: INTEGER (long; millis since epoch)</P>
2369          */
2370         public static final String ALARM_TIME = "alarmTime";
2371 
2372         /**
2373          * The creation time of this database entry, in UTC.
2374          * Useful for debugging missed reminders. Column name.
2375          * <P>Type: INTEGER (long; millis since epoch)</P>
2376          */
2377         public static final String CREATION_TIME = "creationTime";
2378 
2379         /**
2380          * The time that the alarm broadcast was received by the Calendar app,
2381          * in UTC. Useful for debugging missed reminders. Column name.
2382          * <P>Type: INTEGER (long; millis since epoch)</P>
2383          */
2384         public static final String RECEIVED_TIME = "receivedTime";
2385 
2386         /**
2387          * The time that the notification was created by the Calendar app,
2388          * in UTC. Useful for debugging missed reminders. Column name.
2389          * <P>Type: INTEGER (long; millis since epoch)</P>
2390          */
2391         public static final String NOTIFY_TIME = "notifyTime";
2392 
2393         /**
2394          * The state of this alert. It starts out as {@link #STATE_SCHEDULED}, then
2395          * when the alarm goes off, it changes to {@link #STATE_FIRED}, and then when
2396          * the user dismisses the alarm it changes to {@link #STATE_DISMISSED}. Column
2397          * name.
2398          * <P>Type: INTEGER</P>
2399          */
2400         public static final String STATE = "state";
2401 
2402         /**
2403          * An alert begins in this state when it is first created.
2404          */
2405         public static final int STATE_SCHEDULED = 0;
2406         /**
2407          * After a notification for an alert has been created it should be
2408          * updated to fired.
2409          */
2410         public static final int STATE_FIRED = 1;
2411         /**
2412          * Once the user has dismissed the notification the alert's state should
2413          * be set to dismissed so it is not fired again.
2414          */
2415         public static final int STATE_DISMISSED = 2;
2416 
2417         /**
2418          * The number of minutes that this alarm precedes the start time. Column
2419          * name.
2420          * <P>Type: INTEGER</P>
2421          */
2422         public static final String MINUTES = "minutes";
2423 
2424         /**
2425          * The default sort order for this alerts queries
2426          */
2427         public static final String DEFAULT_SORT_ORDER = "begin ASC,title ASC";
2428     }
2429 
2430     /**
2431      * Fields and helpers for accessing calendar alerts information. These
2432      * fields are for tracking which alerts have been fired. Scheduled alarms
2433      * will generate an intent using {@link #ACTION_EVENT_REMINDER}. Apps that
2434      * receive this action may update the {@link #STATE} for the reminder when
2435      * they have finished handling it. Apps that have their notifications
2436      * disabled should not modify the table to ensure that they do not conflict
2437      * with another app that is generating a notification. In general, apps
2438      * should not need to write to this table directly except to update the
2439      * state of a reminder.
2440      */
2441     public static final class CalendarAlerts implements BaseColumns,
2442             CalendarAlertsColumns, EventsColumns, CalendarColumns {
2443 
2444         /**
2445          * @hide
2446          */
2447         public static final String TABLE_NAME = "CalendarAlerts";
2448         /**
2449          * The Uri for querying calendar alert information
2450          */
2451         @SuppressWarnings("hiding")
2452         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
2453                 "/calendar_alerts");
2454 
2455         /**
2456          * This utility class cannot be instantiated
2457          */
CalendarAlerts()2458         private CalendarAlerts() {}
2459 
2460         private static final String WHERE_ALARM_EXISTS = EVENT_ID + "=?"
2461                 + " AND " + BEGIN + "=?"
2462                 + " AND " + ALARM_TIME + "=?";
2463 
2464         private static final String WHERE_FINDNEXTALARMTIME = ALARM_TIME + ">=?";
2465         private static final String SORT_ORDER_ALARMTIME_ASC = ALARM_TIME + " ASC";
2466 
2467         private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + STATE_SCHEDULED
2468                 + " AND " + ALARM_TIME + "<?"
2469                 + " AND " + ALARM_TIME + ">?"
2470                 + " AND " + END + ">=?";
2471 
2472         /**
2473          * This URI is for grouping the query results by event_id and begin
2474          * time.  This will return one result per instance of an event.  So
2475          * events with multiple alarms will appear just once, but multiple
2476          * instances of a repeating event will show up multiple times.
2477          */
2478         public static final Uri CONTENT_URI_BY_INSTANCE =
2479             Uri.parse("content://" + AUTHORITY + "/calendar_alerts/by_instance");
2480 
2481         private static final boolean DEBUG = false;
2482 
2483         /**
2484          * Helper for inserting an alarm time associated with an event TODO move
2485          * to Provider
2486          *
2487          * @hide
2488          */
insert(ContentResolver cr, long eventId, long begin, long end, long alarmTime, int minutes)2489         public static final Uri insert(ContentResolver cr, long eventId,
2490                 long begin, long end, long alarmTime, int minutes) {
2491             ContentValues values = new ContentValues();
2492             values.put(CalendarAlerts.EVENT_ID, eventId);
2493             values.put(CalendarAlerts.BEGIN, begin);
2494             values.put(CalendarAlerts.END, end);
2495             values.put(CalendarAlerts.ALARM_TIME, alarmTime);
2496             long currentTime = System.currentTimeMillis();
2497             values.put(CalendarAlerts.CREATION_TIME, currentTime);
2498             values.put(CalendarAlerts.RECEIVED_TIME, 0);
2499             values.put(CalendarAlerts.NOTIFY_TIME, 0);
2500             values.put(CalendarAlerts.STATE, STATE_SCHEDULED);
2501             values.put(CalendarAlerts.MINUTES, minutes);
2502             return cr.insert(CONTENT_URI, values);
2503         }
2504 
2505         /**
2506          * Finds the next alarm after (or equal to) the given time and returns
2507          * the time of that alarm or -1 if no such alarm exists. This is a
2508          * blocking call and should not be done on the UI thread. TODO move to
2509          * provider
2510          *
2511          * @param cr the ContentResolver
2512          * @param millis the time in UTC milliseconds
2513          * @return the next alarm time greater than or equal to "millis", or -1
2514          *         if no such alarm exists.
2515          * @hide
2516          */
2517         @UnsupportedAppUsage
findNextAlarmTime(ContentResolver cr, long millis)2518         public static final long findNextAlarmTime(ContentResolver cr, long millis) {
2519             String selection = ALARM_TIME + ">=" + millis;
2520             // TODO: construct an explicit SQL query so that we can add
2521             // "LIMIT 1" to the end and get just one result.
2522             String[] projection = new String[] { ALARM_TIME };
2523             Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_FINDNEXTALARMTIME,
2524                     (new String[] {
2525                         Long.toString(millis)
2526                     }), SORT_ORDER_ALARMTIME_ASC);
2527             long alarmTime = -1;
2528             try {
2529                 if (cursor != null && cursor.moveToFirst()) {
2530                     alarmTime = cursor.getLong(0);
2531                 }
2532             } finally {
2533                 if (cursor != null) {
2534                     cursor.close();
2535                 }
2536             }
2537             return alarmTime;
2538         }
2539 
2540         /**
2541          * Searches the CalendarAlerts table for alarms that should have fired
2542          * but have not and then reschedules them. This method can be called at
2543          * boot time to restore alarms that may have been lost due to a phone
2544          * reboot. TODO move to provider
2545          *
2546          * @param cr the ContentResolver
2547          * @param context the Context
2548          * @param manager the AlarmManager
2549          * @hide
2550          */
2551         @UnsupportedAppUsage
rescheduleMissedAlarms(ContentResolver cr, Context context, AlarmManager manager)2552         public static final void rescheduleMissedAlarms(ContentResolver cr,
2553                 Context context, AlarmManager manager) {
2554             // Get all the alerts that have been scheduled but have not fired
2555             // and should have fired by now and are not too old.
2556             long now = System.currentTimeMillis();
2557             long ancient = now - DateUtils.DAY_IN_MILLIS;
2558             String[] projection = new String[] {
2559                     ALARM_TIME,
2560             };
2561 
2562             // TODO: construct an explicit SQL query so that we can add
2563             // "GROUPBY" instead of doing a sort and de-dup
2564             Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection,
2565                     WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] {
2566                             Long.toString(now), Long.toString(ancient), Long.toString(now)
2567                     }), SORT_ORDER_ALARMTIME_ASC);
2568             if (cursor == null) {
2569                 return;
2570             }
2571 
2572             if (DEBUG) {
2573                 Log.d(TAG, "missed alarms found: " + cursor.getCount());
2574             }
2575 
2576             try {
2577                 long alarmTime = -1;
2578 
2579                 while (cursor.moveToNext()) {
2580                     long newAlarmTime = cursor.getLong(0);
2581                     if (alarmTime != newAlarmTime) {
2582                         if (DEBUG) {
2583                             Log.w(TAG, "rescheduling missed alarm. alarmTime: " + newAlarmTime);
2584                         }
2585                         scheduleAlarm(context, manager, newAlarmTime);
2586                         alarmTime = newAlarmTime;
2587                     }
2588                 }
2589             } finally {
2590                 cursor.close();
2591             }
2592         }
2593 
2594         /**
2595          * Schedules an alarm intent with the system AlarmManager that will
2596          * notify listeners when a reminder should be fired. The provider will
2597          * keep scheduled reminders up to date but apps may use this to
2598          * implement snooze functionality without modifying the reminders table.
2599          * Scheduled alarms will generate an intent using
2600          * {@link #ACTION_EVENT_REMINDER}. TODO Move to provider
2601          *
2602          * @param context A context for referencing system resources
2603          * @param manager The AlarmManager to use or null
2604          * @param alarmTime The time to fire the intent in UTC millis since
2605          *            epoch
2606          * @hide
2607          */
2608         @UnsupportedAppUsage
scheduleAlarm(Context context, AlarmManager manager, long alarmTime)2609         public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) {
2610             if (DEBUG) {
2611                 Time time = new Time();
2612                 time.set(alarmTime);
2613                 String schedTime = time.format(" %a, %b %d, %Y %I:%M%P");
2614                 Log.d(TAG, "Schedule alarm at " + alarmTime + " " + schedTime);
2615             }
2616 
2617             if (manager == null) {
2618                 manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
2619             }
2620 
2621             Intent intent = new Intent(ACTION_EVENT_REMINDER);
2622             intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
2623             intent.putExtra(ALARM_TIME, alarmTime);
2624             intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
2625             PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
2626             manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pi);
2627         }
2628 
2629         /**
2630          * Searches for an entry in the CalendarAlerts table that matches the
2631          * given event id, begin time and alarm time. If one is found then this
2632          * alarm already exists and this method returns true. TODO Move to
2633          * provider
2634          *
2635          * @param cr the ContentResolver
2636          * @param eventId the event id to match
2637          * @param begin the start time of the event in UTC millis
2638          * @param alarmTime the alarm time of the event in UTC millis
2639          * @return true if there is already an alarm for the given event with
2640          *         the same start time and alarm time.
2641          * @hide
2642          */
alarmExists(ContentResolver cr, long eventId, long begin, long alarmTime)2643         public static final boolean alarmExists(ContentResolver cr, long eventId,
2644                 long begin, long alarmTime) {
2645             // TODO: construct an explicit SQL query so that we can add
2646             // "LIMIT 1" to the end and get just one result.
2647             String[] projection = new String[] { ALARM_TIME };
2648             Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_ALARM_EXISTS,
2649                     (new String[] {
2650                             Long.toString(eventId), Long.toString(begin), Long.toString(alarmTime)
2651                     }), null);
2652             boolean found = false;
2653             try {
2654                 if (cursor != null && cursor.getCount() > 0) {
2655                     found = true;
2656                 }
2657             } finally {
2658                 if (cursor != null) {
2659                     cursor.close();
2660                 }
2661             }
2662             return found;
2663         }
2664     }
2665 
2666     protected interface ColorsColumns extends SyncStateContract.Columns {
2667 
2668         /**
2669          * The type of color, which describes how it should be used. Valid types
2670          * are {@link #TYPE_CALENDAR} and {@link #TYPE_EVENT}. Column name.
2671          * <P>
2672          * Type: INTEGER (NOT NULL)
2673          * </P>
2674          */
2675         public static final String COLOR_TYPE = "color_type";
2676 
2677         /**
2678          * This indicateds a color that can be used for calendars.
2679          */
2680         public static final int TYPE_CALENDAR = 0;
2681         /**
2682          * This indicates a color that can be used for events.
2683          */
2684         public static final int TYPE_EVENT = 1;
2685 
2686         /**
2687          * The key used to reference this color. This can be any non-empty
2688          * string, but must be unique for a given {@link #ACCOUNT_TYPE} and
2689          * {@link #ACCOUNT_NAME}. Column name.
2690          * <P>
2691          * Type: TEXT
2692          * </P>
2693          */
2694         public static final String COLOR_KEY = "color_index";
2695 
2696         /**
2697          * The color as an 8-bit ARGB integer value. Colors should specify alpha
2698          * as fully opaque (eg 0xFF993322) as the alpha may be ignored or
2699          * modified for display. It is reccomended that colors be usable with
2700          * light (near white) text. Apps should not depend on that assumption,
2701          * however. Column name.
2702          * <P>
2703          * Type: INTEGER (NOT NULL)
2704          * </P>
2705          */
2706         public static final String COLOR = "color";
2707 
2708     }
2709 
2710     /**
2711      * Fields for accessing colors available for a given account. Colors are
2712      * referenced by {@link #COLOR_KEY} which must be unique for a given
2713      * account name/type. These values can only be updated by the sync
2714      * adapter. Only {@link #COLOR} may be updated after the initial insert. In
2715      * addition, a row can only be deleted once all references to that color
2716      * have been removed from the {@link Calendars} or {@link Events} tables.
2717      */
2718     public static final class Colors implements ColorsColumns {
2719         /**
2720          * @hide
2721          */
2722         public static final String TABLE_NAME = "Colors";
2723         /**
2724          * The Uri for querying color information
2725          */
2726         @SuppressWarnings("hiding")
2727         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/colors");
2728 
2729         /**
2730          * This utility class cannot be instantiated
2731          */
Colors()2732         private Colors() {
2733         }
2734     }
2735 
2736     protected interface ExtendedPropertiesColumns {
2737         /**
2738          * The event the extended property belongs to. Column name.
2739          * <P>Type: INTEGER (foreign key to the Events table)</P>
2740          */
2741         public static final String EVENT_ID = "event_id";
2742 
2743         /**
2744          * The name of the extended property.  This is a uri of the form
2745          * {scheme}#{local-name} convention. Column name.
2746          * <P>Type: TEXT</P>
2747          */
2748         public static final String NAME = "name";
2749 
2750         /**
2751          * The value of the extended property. Column name.
2752          * <P>Type: TEXT</P>
2753          */
2754         public static final String VALUE = "value";
2755     }
2756 
2757     /**
2758      * Fields for accessing the Extended Properties. This is a generic set of
2759      * name/value pairs for use by sync adapters to add extra
2760      * information to events. There are three writable columns and all three
2761      * must be present when inserting a new value. They are:
2762      * <ul>
2763      * <li>{@link #EVENT_ID}</li>
2764      * <li>{@link #NAME}</li>
2765      * <li>{@link #VALUE}</li>
2766      * </ul>
2767      */
2768    public static final class ExtendedProperties implements BaseColumns,
2769             ExtendedPropertiesColumns, EventsColumns {
2770         public static final Uri CONTENT_URI =
2771                 Uri.parse("content://" + AUTHORITY + "/extendedproperties");
2772 
2773         /**
2774          * This utility class cannot be instantiated
2775          */
ExtendedProperties()2776         private ExtendedProperties() {}
2777 
2778         // TODO: fill out this class when we actually start utilizing extendedproperties
2779         // in the calendar application.
2780    }
2781 
2782     /**
2783      * A table provided for sync adapters to use for storing private sync state data.
2784      *
2785      * @see SyncStateContract
2786      */
2787     public static final class SyncState implements SyncStateContract.Columns {
2788         /**
2789          * This utility class cannot be instantiated
2790          */
SyncState()2791         private SyncState() {}
2792 
2793         private static final String CONTENT_DIRECTORY =
2794                 SyncStateContract.Constants.CONTENT_DIRECTORY;
2795 
2796         /**
2797          * The content:// style URI for this table
2798          */
2799         public static final Uri CONTENT_URI =
2800                 Uri.withAppendedPath(CalendarContract.CONTENT_URI, CONTENT_DIRECTORY);
2801     }
2802 
2803     /**
2804      * Columns from the EventsRawTimes table
2805      *
2806      * @hide
2807      */
2808     protected interface EventsRawTimesColumns {
2809         /**
2810          * The corresponding event id. Column name.
2811          * <P>Type: INTEGER (long)</P>
2812          */
2813         public static final String EVENT_ID = "event_id";
2814 
2815         /**
2816          * The RFC2445 compliant time the event starts. Column name.
2817          * <P>Type: TEXT</P>
2818          */
2819         public static final String DTSTART_2445 = "dtstart2445";
2820 
2821         /**
2822          * The RFC2445 compliant time the event ends. Column name.
2823          * <P>Type: TEXT</P>
2824          */
2825         public static final String DTEND_2445 = "dtend2445";
2826 
2827         /**
2828          * The RFC2445 compliant original instance time of the recurring event
2829          * for which this event is an exception. Column name.
2830          * <P>Type: TEXT</P>
2831          */
2832         public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445";
2833 
2834         /**
2835          * The RFC2445 compliant last date this event repeats on, or NULL if it
2836          * never ends. Column name.
2837          * <P>Type: TEXT</P>
2838          */
2839         public static final String LAST_DATE_2445 = "lastDate2445";
2840     }
2841 
2842     /**
2843      * @hide
2844      */
2845     public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns {
2846 
2847         /**
2848          * This utility class cannot be instantiated
2849          */
EventsRawTimes()2850         private EventsRawTimes() {}
2851     }
2852 }
2853