• 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.app.Activity;
24 import android.app.AlarmManager;
25 import android.app.PendingIntent;
26 import android.app.admin.DevicePolicyManager;
27 import android.compat.annotation.UnsupportedAppUsage;
28 import android.content.ComponentName;
29 import android.content.ContentProviderClient;
30 import android.content.ContentResolver;
31 import android.content.ContentUris;
32 import android.content.ContentValues;
33 import android.content.Context;
34 import android.content.CursorEntityIterator;
35 import android.content.Entity;
36 import android.content.EntityIterator;
37 import android.content.Intent;
38 import android.database.Cursor;
39 import android.database.DatabaseUtils;
40 import android.net.Uri;
41 import android.os.RemoteException;
42 import android.os.StrictMode;
43 import android.text.format.DateUtils;
44 import android.text.format.TimeMigrationUtils;
45 import android.util.Log;
46 
47 import com.android.internal.util.Preconditions;
48 
49 import java.util.Set;
50 
51 /**
52  * <p>
53  * The contract between the calendar provider and applications. Contains
54  * definitions for the supported URIs and data columns.
55  * </p>
56  * <h3>Overview</h3>
57  * <p>
58  * CalendarContract defines the data model of calendar and event related
59  * information. This data is stored in a number of tables:
60  * </p>
61  * <ul>
62  * <li>The {@link Calendars} table holds the calendar specific information. Each
63  * row in this table contains the details for a single calendar, such as the
64  * name, color, sync info, etc.</li>
65  * <li>The {@link Events} table holds the event specific information. Each row
66  * in this table has the info for a single event. It contains information such
67  * as event title, location, start time, end time, etc. The event can occur
68  * one-time or can recur multiple times. Attendees, reminders, and extended
69  * properties are stored on separate tables and reference the {@link Events#_ID}
70  * to link them with the event.</li>
71  * <li>The {@link Instances} table holds the start and end time for occurrences
72  * of an event. Each row in this table represents a single occurrence. For
73  * one-time events there will be a 1:1 mapping of instances to events. For
74  * recurring events, multiple rows will automatically be generated which
75  * correspond to multiple occurrences of that event.</li>
76  * <li>The {@link Attendees} table holds the event attendee or guest
77  * information. Each row represents a single guest of an event. It specifies the
78  * type of guest they are and their attendance response for the event.</li>
79  * <li>The {@link Reminders} table holds the alert/notification data. Each row
80  * represents a single alert for an event. An event can have multiple reminders.
81  * The number of reminders per event is specified in
82  * {@link Calendars#MAX_REMINDERS} which is set by the Sync Adapter that owns
83  * the given calendar. Reminders are specified in minutes before the event and
84  * have a type.</li>
85  * <li>The {@link ExtendedProperties} table holds opaque data fields used by the
86  * sync adapter. The provider takes no action with items in this table except to
87  * delete them when their related events are deleted.</li>
88  * </ul>
89  * <p>
90  * Other tables include:
91  * </p>
92  * <ul>
93  * <li>
94  * {@link SyncState}, which contains free-form data maintained by the sync
95  * adapters</li>
96  * </ul>
97  *
98  */
99 public final class CalendarContract {
100     private static final String TAG = "Calendar";
101 
102     /**
103      * Broadcast Action: This is the intent that gets fired when an alarm
104      * notification needs to be posted for a reminder.
105      *
106      */
107     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
108     public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER";
109 
110     /**
111      * Activity Action: Display the event to the user in the custom app as
112      * specified in {@link EventsColumns#CUSTOM_APP_PACKAGE}. The custom app
113      * will be started via {@link Activity#startActivityForResult(Intent, int)}
114      * and it should call {@link Activity#setResult(int)} with
115      * {@link Activity#RESULT_OK} or {@link Activity#RESULT_CANCELED} to
116      * acknowledge whether the action was handled or not.
117      *
118      * The custom app should have an intent filter like the following:
119      * <pre>
120      * &lt;intent-filter&gt;
121      *    &lt;action android:name="android.provider.calendar.action.HANDLE_CUSTOM_EVENT" /&gt;
122      *    &lt;category android:name="android.intent.category.DEFAULT" /&gt;
123      *    &lt;data android:mimeType="vnd.android.cursor.item/event" /&gt;
124      * &lt;/intent-filter&gt;</pre>
125      * <p>
126      * Input: {@link Intent#getData} has the event URI. The extra
127      * {@link #EXTRA_EVENT_BEGIN_TIME} has the start time of the instance. The
128      * extra {@link #EXTRA_CUSTOM_APP_URI} will have the
129      * {@link EventsColumns#CUSTOM_APP_URI}.
130      * <p>
131      * Output: {@link Activity#RESULT_OK} if this was handled; otherwise
132      * {@link Activity#RESULT_CANCELED}.
133      */
134     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
135     public static final String ACTION_HANDLE_CUSTOM_EVENT =
136         "android.provider.calendar.action.HANDLE_CUSTOM_EVENT";
137 
138     /**
139      * Action used to help apps show calendar events in the managed profile.
140      */
141     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
142     public static final String ACTION_VIEW_MANAGED_PROFILE_CALENDAR_EVENT =
143             "android.provider.calendar.action.VIEW_MANAGED_PROFILE_CALENDAR_EVENT";
144 
145     /**
146      * Intent Extras key: {@link EventsColumns#CUSTOM_APP_URI} for the event in
147      * the {@link #ACTION_HANDLE_CUSTOM_EVENT} intent
148      */
149     public static final String EXTRA_CUSTOM_APP_URI = "customAppUri";
150 
151     /**
152      * Intent Extras key: The start time of an event or an instance of a
153      * recurring event. (milliseconds since epoch)
154      */
155     public static final String EXTRA_EVENT_BEGIN_TIME = "beginTime";
156 
157     /**
158      * Intent Extras key: The end time of an event or an instance of a recurring
159      * event. (milliseconds since epoch)
160      */
161     public static final String EXTRA_EVENT_END_TIME = "endTime";
162 
163     /**
164      * Intent Extras key: When creating an event, set this to true to create an
165      * all-day event by default
166      */
167     public static final String EXTRA_EVENT_ALL_DAY = "allDay";
168 
169     /**
170      * Intent Extras key: An extra of type {@code long} holding the id of an event.
171      */
172     public static final String EXTRA_EVENT_ID = "id";
173 
174     /**
175      * This authority is used for writing to or querying from the calendar
176      * provider. Note: This is set at first run and cannot be changed without
177      * breaking apps that access the provider.
178      */
179     public static final String AUTHORITY = "com.android.calendar";
180 
181     /**
182      * The content:// style URL for the top-level calendar authority
183      */
184     public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
185 
186     /**
187      * The content:// style URL for the top-level cross-profile calendar uris.
188      * {@link android.database.ContentObserver} for this URL in the primary profile will be
189      * notified when there is a change in the managed profile calendar provider.
190      *
191      * <p>Throw UnsupportedOperationException if another profile doesn't exist or is disabled, or
192      * if the calling package is not whitelisted to access cross-profile calendar, or if the
193      * feature has been disabled by the user in Settings.
194      *
195      * @see Events#ENTERPRISE_CONTENT_URI
196      * @see Calendars#ENTERPRISE_CONTENT_URI
197      * @see Instances#ENTERPRISE_CONTENT_URI
198      * @see Instances#ENTERPRISE_CONTENT_BY_DAY_URI
199      * @see Instances#ENTERPRISE_CONTENT_SEARCH_URI
200      * @see Instances#ENTERPRISE_CONTENT_SEARCH_BY_DAY_URI
201      * @hide
202      */
203     public static final Uri ENTERPRISE_CONTENT_URI = Uri.parse(
204             "content://" + AUTHORITY + "/enterprise");
205 
206     /**
207      * An optional insert, update or delete URI parameter that allows the caller
208      * to specify that it is a sync adapter. The default value is false. If set
209      * to true, the modified row is not marked as "dirty" (needs to be synced)
210      * and when the provider calls
211      * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
212      * , the third parameter "syncToNetwork" is set to false. Furthermore, if
213      * set to true, the caller must also include
214      * {@link Calendars#ACCOUNT_NAME} and {@link Calendars#ACCOUNT_TYPE} as
215      * query parameters.
216      *
217      * @see Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)
218      */
219     public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
220 
221     /**
222      * A special account type for calendars not associated with any account.
223      * Normally calendars that do not match an account on the device will be
224      * removed. Setting the account_type on a calendar to this will prevent it
225      * from being wiped if it does not match an existing account.
226      *
227      * @see SyncColumns#ACCOUNT_TYPE
228      */
229     public static final String ACCOUNT_TYPE_LOCAL = "LOCAL";
230 
231     /**
232      * This utility class cannot be instantiated
233      */
CalendarContract()234     private CalendarContract() {}
235 
236     /**
237      * Starts an activity to view calendar events in the managed profile.
238      *
239      * When this API is called, the system will attempt to start an activity
240      * in the managed profile with an intent targeting the same caller package.
241      * The intent will have its action set to
242      * {@link CalendarContract#ACTION_VIEW_MANAGED_PROFILE_CALENDAR_EVENT} and contain extras
243      * corresponding to the API's arguments. A calendar app intending to support
244      * cross-profile events viewing should handle this intent, parse the arguments
245      * and show the appropriate UI.
246      *
247      * @param context the context.
248      * @param eventId the id of the event to be viewed. Will be put into {@link #EXTRA_EVENT_ID}
249      *                field of the intent.
250      * @param startMs the start time of the event in milliseconds since epoch.
251      *                Will be put into {@link #EXTRA_EVENT_BEGIN_TIME} field of the intent.
252      * @param endMs the end time of the event in milliseconds since epoch.
253      *              Will be put into {@link #EXTRA_EVENT_END_TIME} field of the intent.
254      * @param allDay if the event is an all-day event. Will be put into
255      *               {@link #EXTRA_EVENT_ALL_DAY} field of the intent.
256      * @param flags flags to be set on the intent via {@link Intent#setFlags}
257      * @return {@code true} if the activity is started successfully. {@code false} otherwise.
258      *
259      * @see #EXTRA_EVENT_ID
260      * @see #EXTRA_EVENT_BEGIN_TIME
261      * @see #EXTRA_EVENT_END_TIME
262      * @see #EXTRA_EVENT_ALL_DAY
263      */
startViewCalendarEventInManagedProfile(@onNull Context context, long eventId, long startMs, long endMs, boolean allDay, int flags)264     public static boolean startViewCalendarEventInManagedProfile(@NonNull Context context,
265             long eventId, long startMs, long endMs, boolean allDay, int flags) {
266         Preconditions.checkNotNull(context, "Context is null");
267         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
268                 Context.DEVICE_POLICY_SERVICE);
269         return dpm.startViewCalendarEventInManagedProfile(eventId, startMs,
270                 endMs, allDay, flags);
271     }
272 
273     /**
274      * Generic columns for use by sync adapters. The specific functions of these
275      * columns are private to the sync adapter. Other clients of the API should
276      * not attempt to either read or write this column. These columns are
277      * editable as part of the Calendars Uri, but can only be read if accessed
278      * through any other Uri.
279      */
280     protected interface CalendarSyncColumns {
281 
282 
283         /**
284          * Generic column for use by sync adapters. Column name.
285          * <P>Type: TEXT</P>
286          */
287         public static final String CAL_SYNC1 = "cal_sync1";
288 
289         /**
290          * Generic column for use by sync adapters. Column name.
291          * <P>Type: TEXT</P>
292          */
293         public static final String CAL_SYNC2 = "cal_sync2";
294 
295         /**
296          * Generic column for use by sync adapters. Column name.
297          * <P>Type: TEXT</P>
298          */
299         public static final String CAL_SYNC3 = "cal_sync3";
300 
301         /**
302          * Generic column for use by sync adapters. Column name.
303          * <P>Type: TEXT</P>
304          */
305         public static final String CAL_SYNC4 = "cal_sync4";
306 
307         /**
308          * Generic column for use by sync adapters. Column name.
309          * <P>Type: TEXT</P>
310          */
311         public static final String CAL_SYNC5 = "cal_sync5";
312 
313         /**
314          * Generic column for use by sync adapters. Column name.
315          * <P>Type: TEXT</P>
316          */
317         public static final String CAL_SYNC6 = "cal_sync6";
318 
319         /**
320          * Generic column for use by sync adapters. Column name.
321          * <P>Type: TEXT</P>
322          */
323         public static final String CAL_SYNC7 = "cal_sync7";
324 
325         /**
326          * Generic column for use by sync adapters. Column name.
327          * <P>Type: TEXT</P>
328          */
329         public static final String CAL_SYNC8 = "cal_sync8";
330 
331         /**
332          * Generic column for use by sync adapters. Column name.
333          * <P>Type: TEXT</P>
334          */
335         public static final String CAL_SYNC9 = "cal_sync9";
336 
337         /**
338          * Generic column for use by sync adapters. Column name.
339          * <P>Type: TEXT</P>
340          */
341         public static final String CAL_SYNC10 = "cal_sync10";
342     }
343 
344     /**
345      * Columns for Sync information used by Calendars and Events tables. These
346      * have specific uses which are expected to be consistent by the app and
347      * sync adapter.
348      *
349      */
350     protected interface SyncColumns extends CalendarSyncColumns {
351         /**
352          * The account that was used to sync the entry to the device. If the
353          * account_type is not {@link #ACCOUNT_TYPE_LOCAL} then the name and
354          * type must match an account on the device or the calendar will be
355          * deleted.
356          * <P>Type: TEXT</P>
357          */
358         public static final String ACCOUNT_NAME = "account_name";
359 
360         /**
361          * The type of the account that was used to sync the entry to the
362          * device. A type of {@link #ACCOUNT_TYPE_LOCAL} will keep this event
363          * form being deleted if there are no matching accounts on the device.
364          * <P>Type: TEXT</P>
365          */
366         public static final String ACCOUNT_TYPE = "account_type";
367 
368         /**
369          * The unique ID for a row assigned by the sync source. NULL if the row
370          * has never been synced. This is used as a reference id for exceptions
371          * along with {@link BaseColumns#_ID}.
372          * <P>Type: TEXT</P>
373          */
374         public static final String _SYNC_ID = "_sync_id";
375 
376         /**
377          * Used to indicate that local, unsynced, changes are present.
378          * <P>Type: INTEGER (long)</P>
379          */
380 
381         public static final String DIRTY = "dirty";
382 
383         /**
384          * Used in conjunction with {@link #DIRTY} to indicate what packages wrote local changes.
385          * <P>Type: TEXT</P>
386          */
387         public static final String MUTATORS = "mutators";
388 
389         /**
390          * Whether the row has been deleted but not synced to the server. A
391          * deleted row should be ignored.
392          * <P>
393          * Type: INTEGER (boolean)
394          * </P>
395          */
396         public static final String DELETED = "deleted";
397 
398         /**
399          * If set to 1 this causes events on this calendar to be duplicated with
400          * {@link Events#LAST_SYNCED} set to 1 whenever the event
401          * transitions from non-dirty to dirty. The duplicated event will not be
402          * expanded in the instances table and will only show up in sync adapter
403          * queries of the events table. It will also be deleted when the
404          * originating event has its dirty flag cleared by the sync adapter.
405          * <P>Type: INTEGER (boolean)</P>
406          */
407         public static final String CAN_PARTIALLY_UPDATE = "canPartiallyUpdate";
408     }
409 
410     /**
411      * Columns specific to the Calendars Uri that other Uris can query.
412      */
413     protected interface CalendarColumns {
414         /**
415          * The color of the calendar. This should only be updated by the sync
416          * adapter, not other apps, as changing a calendar's color can adversely
417          * affect its display.
418          * <P>Type: INTEGER (color value)</P>
419          */
420         public static final String CALENDAR_COLOR = "calendar_color";
421 
422         /**
423          * A key for looking up a color from the {@link Colors} table. NULL or
424          * an empty string are reserved for indicating that the calendar does
425          * not use a key for looking up the color. The provider will update
426          * {@link #CALENDAR_COLOR} automatically when a valid key is written to
427          * this column. The key must reference an existing row of the
428          * {@link Colors} table. @see Colors
429          * <P>
430          * Type: TEXT
431          * </P>
432          */
433         public static final String CALENDAR_COLOR_KEY = "calendar_color_index";
434 
435         /**
436          * The display name of the calendar. Column name.
437          * <P>
438          * Type: TEXT
439          * </P>
440          */
441         public static final String CALENDAR_DISPLAY_NAME = "calendar_displayName";
442 
443         /**
444          * The level of access that the user has for the calendar
445          * <P>Type: INTEGER (one of the values below)</P>
446          */
447         public static final String CALENDAR_ACCESS_LEVEL = "calendar_access_level";
448 
449         /** Cannot access the calendar */
450         public static final int CAL_ACCESS_NONE = 0;
451         /** Can only see free/busy information about the calendar */
452         public static final int CAL_ACCESS_FREEBUSY = 100;
453         /** Can read all event details */
454         public static final int CAL_ACCESS_READ = 200;
455         /** Can reply yes/no/maybe to an event */
456         public static final int CAL_ACCESS_RESPOND = 300;
457         /** not used */
458         public static final int CAL_ACCESS_OVERRIDE = 400;
459         /** Full access to modify the calendar, but not the access control
460          * settings
461          */
462         public static final int CAL_ACCESS_CONTRIBUTOR = 500;
463         /** Full access to modify the calendar, but not the access control
464          * settings
465          */
466         public static final int CAL_ACCESS_EDITOR = 600;
467         /** Full access to the calendar */
468         public static final int CAL_ACCESS_OWNER = 700;
469         /** Domain admin */
470         public static final int CAL_ACCESS_ROOT = 800;
471 
472         /**
473          * Is the calendar selected to be displayed?
474          * 0 - do not show events associated with this calendar.
475          * 1 - show events associated with this calendar
476          * <P>Type: INTEGER (boolean)</P>
477          */
478         public static final String VISIBLE = "visible";
479 
480         /**
481          * The time zone the calendar is associated with.
482          * <P>Type: TEXT</P>
483          */
484         public static final String CALENDAR_TIME_ZONE = "calendar_timezone";
485 
486         /**
487          * Is this calendar synced and are its events stored on the device?
488          * 0 - Do not sync this calendar or store events for this calendar.
489          * 1 - Sync down events for this calendar.
490          * <p>Type: INTEGER (boolean)</p>
491          */
492         public static final String SYNC_EVENTS = "sync_events";
493 
494         /**
495          * The owner account for this calendar, based on the calendar feed.
496          * This will be different from the _SYNC_ACCOUNT for delegated calendars.
497          * Column name.
498          * <P>Type: String</P>
499          */
500         public static final String OWNER_ACCOUNT = "ownerAccount";
501 
502         /**
503          * Can the organizer respond to the event?  If no, the status of the
504          * organizer should not be shown by the UI.  Defaults to 1. Column name.
505          * <P>Type: INTEGER (boolean)</P>
506          */
507         public static final String CAN_ORGANIZER_RESPOND = "canOrganizerRespond";
508 
509         /**
510          * Can the organizer modify the time zone of the event? Column name.
511          * <P>Type: INTEGER (boolean)</P>
512         */
513         public static final String CAN_MODIFY_TIME_ZONE = "canModifyTimeZone";
514 
515         /**
516          * The maximum number of reminders allowed for an event. Column name.
517          * <P>Type: INTEGER</P>
518          */
519         public static final String MAX_REMINDERS = "maxReminders";
520 
521         /**
522          * A comma separated list of reminder methods supported for this
523          * calendar in the format "#,#,#". Valid types are
524          * {@link Reminders#METHOD_DEFAULT}, {@link Reminders#METHOD_ALERT},
525          * {@link Reminders#METHOD_EMAIL}, {@link Reminders#METHOD_SMS},
526          * {@link Reminders#METHOD_ALARM}. Column name.
527          * <P>Type: TEXT</P>
528          */
529         public static final String ALLOWED_REMINDERS = "allowedReminders";
530 
531         /**
532          * A comma separated list of availability types supported for this
533          * calendar in the format "#,#,#". Valid types are
534          * {@link Events#AVAILABILITY_BUSY}, {@link Events#AVAILABILITY_FREE},
535          * {@link Events#AVAILABILITY_TENTATIVE}. Setting this field to only
536          * {@link Events#AVAILABILITY_BUSY} should be used to indicate that
537          * changing the availability is not supported.
538          *
539          */
540         public static final String ALLOWED_AVAILABILITY = "allowedAvailability";
541 
542         /**
543          * A comma separated list of attendee types supported for this calendar
544          * in the format "#,#,#". Valid types are {@link Attendees#TYPE_NONE},
545          * {@link Attendees#TYPE_OPTIONAL}, {@link Attendees#TYPE_REQUIRED},
546          * {@link Attendees#TYPE_RESOURCE}. Setting this field to only
547          * {@link Attendees#TYPE_NONE} should be used to indicate that changing
548          * the attendee type is not supported.
549          *
550          */
551         public static final String ALLOWED_ATTENDEE_TYPES = "allowedAttendeeTypes";
552 
553         /**
554          * Is this the primary calendar for this account. If this column is not explicitly set, the
555          * provider will return 1 if {@link Calendars#ACCOUNT_NAME} is equal to
556          * {@link Calendars#OWNER_ACCOUNT}.
557          */
558         public static final String IS_PRIMARY = "isPrimary";
559     }
560 
561     /**
562      * Class that represents a Calendar Entity. There is one entry per calendar.
563      * This is a helper class to make batch operations easier.
564      */
565     public static final class CalendarEntity implements BaseColumns, SyncColumns, CalendarColumns {
566 
567         /**
568          * The default Uri used when creating a new calendar EntityIterator.
569          */
570         @SuppressWarnings("hiding")
571         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
572                 "/calendar_entities");
573 
574         /**
575          * This utility class cannot be instantiated
576          */
CalendarEntity()577         private CalendarEntity() {}
578 
579         /**
580          * Creates an entity iterator for the given cursor. It assumes the
581          * cursor contains a calendars query.
582          *
583          * @param cursor query on {@link #CONTENT_URI}
584          * @return an EntityIterator of calendars
585          */
newEntityIterator(Cursor cursor)586         public static EntityIterator newEntityIterator(Cursor cursor) {
587             return new EntityIteratorImpl(cursor);
588         }
589 
590         private static class EntityIteratorImpl extends CursorEntityIterator {
591 
EntityIteratorImpl(Cursor cursor)592             public EntityIteratorImpl(Cursor cursor) {
593                 super(cursor);
594             }
595 
596             @Override
getEntityAndIncrementCursor(Cursor cursor)597             public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException {
598                 // we expect the cursor is already at the row we need to read from
599                 final long calendarId = cursor.getLong(cursor.getColumnIndexOrThrow(_ID));
600 
601                 // Create the content value
602                 ContentValues cv = new ContentValues();
603                 cv.put(_ID, calendarId);
604 
605                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_NAME);
606                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_TYPE);
607 
608                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
609                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
610                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, MUTATORS);
611 
612                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1);
613                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2);
614                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC3);
615                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC4);
616                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC5);
617                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC6);
618                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC7);
619                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC8);
620                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC9);
621                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC10);
622 
623                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
624                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
625                         Calendars.CALENDAR_DISPLAY_NAME);
626                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
627                         Calendars.CALENDAR_COLOR);
628                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
629                         Calendars.CALENDAR_COLOR_KEY);
630                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, CALENDAR_ACCESS_LEVEL);
631                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE);
632                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
633                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
634                         Calendars.CALENDAR_LOCATION);
635                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CALENDAR_TIME_ZONE);
636                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
637                         Calendars.OWNER_ACCOUNT);
638                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
639                         Calendars.CAN_ORGANIZER_RESPOND);
640                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
641                         Calendars.CAN_MODIFY_TIME_ZONE);
642                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
643                         Calendars.MAX_REMINDERS);
644                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
645                         Calendars.CAN_PARTIALLY_UPDATE);
646                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
647                         Calendars.ALLOWED_REMINDERS);
648 
649                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
650 
651                 // Create the Entity from the ContentValue
652                 Entity entity = new Entity(cv);
653 
654                 // Set cursor to next row
655                 cursor.moveToNext();
656 
657                 // Return the created Entity
658                 return entity;
659             }
660         }
661      }
662 
663     /**
664      * Constants and helpers for the Calendars table, which contains details for
665      * individual calendars. <h3>Operations</h3> All operations can be done
666      * either as an app or as a sync adapter. To perform an operation as a sync
667      * adapter {@link #CALLER_IS_SYNCADAPTER} should be set to true and
668      * {@link #ACCOUNT_NAME} and {@link #ACCOUNT_TYPE} must be set in the Uri
669      * parameters. See
670      * {@link Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)}
671      * for details on adding parameters. Sync adapters have write access to more
672      * columns but are restricted to a single account at a time. Calendars are
673      * designed to be primarily managed by a sync adapter and inserting new
674      * calendars should be done as a sync adapter. For the most part, apps
675      * should only update calendars (such as changing the color or display
676      * name). If a local calendar is required an app can do so by inserting as a
677      * sync adapter and using an {@link #ACCOUNT_TYPE} of
678      * {@link #ACCOUNT_TYPE_LOCAL} .
679      * <dl>
680      * <dt><b>Insert</b></dt>
681      * <dd>When inserting a new calendar the following fields must be included:
682      * <ul>
683      * <li>{@link #ACCOUNT_NAME}</li>
684      * <li>{@link #ACCOUNT_TYPE}</li>
685      * <li>{@link #NAME}</li>
686      * <li>{@link #CALENDAR_DISPLAY_NAME}</li>
687      * <li>{@link #CALENDAR_COLOR}</li>
688      * <li>{@link #CALENDAR_ACCESS_LEVEL}</li>
689      * <li>{@link #OWNER_ACCOUNT}</li>
690      * </ul>
691      * The following fields are not required when inserting a Calendar but are
692      * generally a good idea to include:
693      * <ul>
694      * <li>{@link #SYNC_EVENTS} set to 1</li>
695      * <li>{@link #CALENDAR_TIME_ZONE}</li>
696      * <li>{@link #ALLOWED_REMINDERS}</li>
697      * <li>{@link #ALLOWED_AVAILABILITY}</li>
698      * <li>{@link #ALLOWED_ATTENDEE_TYPES}</li>
699      * </ul>
700      * <dt><b>Update</b></dt>
701      * <dd>To perform an update on a calendar the {@link #_ID} of the calendar
702      * should be provided either as an appended id to the Uri (
703      * {@link ContentUris#withAppendedId}) or as the first selection item--the
704      * selection should start with "_id=?" and the first selectionArg should be
705      * the _id of the calendar. Calendars may also be updated using a selection
706      * without the id. In general, the {@link #ACCOUNT_NAME} and
707      * {@link #ACCOUNT_TYPE} should not be changed after a calendar is created
708      * as this can cause issues for sync adapters.
709      * <dt><b>Delete</b></dt>
710      * <dd>Calendars can be deleted either by the {@link #_ID} as an appended id
711      * on the Uri or using any standard selection. Deleting a calendar should
712      * generally be handled by a sync adapter as it will remove the calendar
713      * from the database and all associated data (aka events).</dd>
714      * <dt><b>Query</b></dt>
715      * <dd>Querying the Calendars table will get you all information about a set
716      * of calendars. There will be one row returned for each calendar that
717      * matches the query selection, or at most a single row if the {@link #_ID}
718      * is appended to the Uri.</dd>
719      * </dl>
720      * <h3>Calendar Columns</h3> The following Calendar columns are writable by
721      * both an app and a sync adapter.
722      * <ul>
723      * <li>{@link #NAME}</li>
724      * <li>{@link #CALENDAR_DISPLAY_NAME}</li>
725      * <li>{@link #VISIBLE}</li>
726      * <li>{@link #SYNC_EVENTS}</li>
727      * </ul>
728      * The following Calendars columns are writable only by a sync adapter
729      * <ul>
730      * <li>{@link #ACCOUNT_NAME}</li>
731      * <li>{@link #ACCOUNT_TYPE}</li>
732      * <li>{@link #CALENDAR_COLOR}</li>
733      * <li>{@link #_SYNC_ID}</li>
734      * <li>{@link #DIRTY}</li>
735      * <li>{@link #MUTATORS}</li>
736      * <li>{@link #OWNER_ACCOUNT}</li>
737      * <li>{@link #MAX_REMINDERS}</li>
738      * <li>{@link #ALLOWED_REMINDERS}</li>
739      * <li>{@link #ALLOWED_AVAILABILITY}</li>
740      * <li>{@link #ALLOWED_ATTENDEE_TYPES}</li>
741      * <li>{@link #CAN_MODIFY_TIME_ZONE}</li>
742      * <li>{@link #CAN_ORGANIZER_RESPOND}</li>
743      * <li>{@link #CAN_PARTIALLY_UPDATE}</li>
744      * <li>{@link #CALENDAR_LOCATION}</li>
745      * <li>{@link #CALENDAR_TIME_ZONE}</li>
746      * <li>{@link #CALENDAR_ACCESS_LEVEL}</li>
747      * <li>{@link #DELETED}</li>
748      * <li>{@link #CAL_SYNC1}</li>
749      * <li>{@link #CAL_SYNC2}</li>
750      * <li>{@link #CAL_SYNC3}</li>
751      * <li>{@link #CAL_SYNC4}</li>
752      * <li>{@link #CAL_SYNC5}</li>
753      * <li>{@link #CAL_SYNC6}</li>
754      * <li>{@link #CAL_SYNC7}</li>
755      * <li>{@link #CAL_SYNC8}</li>
756      * <li>{@link #CAL_SYNC9}</li>
757      * <li>{@link #CAL_SYNC10}</li>
758      * </ul>
759      */
760     public static final class Calendars implements BaseColumns, SyncColumns, CalendarColumns {
761 
762         /**
763          * This utility class cannot be instantiated
764          */
Calendars()765         private Calendars() {}
766 
767         /**
768          * The content:// style URL for accessing Calendars
769          */
770         @SuppressWarnings("hiding")
771         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars");
772 
773         /**
774          * The content:// style URL for querying Calendars table in the managed profile. Appending
775          * a calendar id using {@link ContentUris#withAppendedId(Uri, long)} specifies
776          * a single calendar.
777          *
778          * <p>The following columns are allowed to be queried via this uri:
779          * <ul>
780          * <li>{@link #_ID}</li>
781          * <li>{@link #CALENDAR_COLOR}</li>
782          * <li>{@link #VISIBLE}</li>
783          * <li>{@link #CALENDAR_LOCATION}</li>
784          * <li>{@link #CALENDAR_TIME_ZONE}</li>
785          * <li>{@link #IS_PRIMARY}</li>
786          * </ul>
787          *
788          * <p>{@link IllegalArgumentException} is thrown if there exists columns in the
789          * projection of the query to this uri that are not contained in the above list.
790          *
791          * <p>This uri returns an empty cursor if the calling user is not a parent profile
792          * of a managed profile, or the managed profile is disabled, or cross-profile calendar is
793          * disabled in Settings, or this uri is queried from a package that is not allowed by
794          * the profile owner of the managed profile via
795          * {@link DevicePolicyManager#setCrossProfileCalendarPackages(ComponentName, Set)}.
796          *
797          * <p>Apps can register a {@link android.database.ContentObserver} for this URI to listen
798          * to changes.
799          *
800          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
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 "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          */
1800         @NonNull
1801         public static final Uri ENTERPRISE_CONTENT_URI =
1802                 Uri.parse("content://" + AUTHORITY + "/enterprise/events");
1803 
1804         /**
1805          * The content:// style URI for recurring event exceptions.  Insertions require an
1806          * appended event ID.  Deletion of exceptions requires both the original event ID and
1807          * the exception event ID (see {@link Uri.Builder#appendPath}).
1808          */
1809         public static final Uri CONTENT_EXCEPTION_URI =
1810                 Uri.parse("content://" + AUTHORITY + "/exception");
1811 
1812         /**
1813          * This utility class cannot be instantiated
1814          */
Events()1815         private Events() {}
1816 
1817         /**
1818          * The default sort order for this table
1819          */
1820         private static final String DEFAULT_SORT_ORDER = "";
1821 
1822         /**
1823          * These are columns that should only ever be updated by the provider,
1824          * either because they are views mapped to another table or because they
1825          * are used for provider only functionality. TODO move to provider
1826          *
1827          * @hide
1828          */
1829         @UnsupportedAppUsage
1830         public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
1831                 ACCOUNT_NAME,
1832                 ACCOUNT_TYPE,
1833                 CAL_SYNC1,
1834                 CAL_SYNC2,
1835                 CAL_SYNC3,
1836                 CAL_SYNC4,
1837                 CAL_SYNC5,
1838                 CAL_SYNC6,
1839                 CAL_SYNC7,
1840                 CAL_SYNC8,
1841                 CAL_SYNC9,
1842                 CAL_SYNC10,
1843                 ALLOWED_REMINDERS,
1844                 ALLOWED_ATTENDEE_TYPES,
1845                 ALLOWED_AVAILABILITY,
1846                 CALENDAR_ACCESS_LEVEL,
1847                 CALENDAR_COLOR,
1848                 CALENDAR_TIME_ZONE,
1849                 CAN_MODIFY_TIME_ZONE,
1850                 CAN_ORGANIZER_RESPOND,
1851                 CALENDAR_DISPLAY_NAME,
1852                 CAN_PARTIALLY_UPDATE,
1853                 SYNC_EVENTS,
1854                 VISIBLE,
1855         };
1856 
1857         /**
1858          * These fields are only writable by a sync adapter. To modify them the
1859          * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
1860          * _SYNC_ACCOUNT_TYPE in the query parameters. TODO move to provider.
1861          *
1862          * @hide
1863          */
1864         @UnsupportedAppUsage
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          */
2013         @NonNull
2014         public static final Uri ENTERPRISE_CONTENT_URI =
2015                 Uri.parse("content://" + AUTHORITY + "/enterprise/instances/when");
2016 
2017         /**
2018          * The content:// style URL for querying an instance range by Julian
2019          * Day in the managed profile. It supports similar semantics as {@link #CONTENT_BY_DAY_URI}
2020          * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
2021          */
2022         @NonNull
2023         public static final Uri ENTERPRISE_CONTENT_BY_DAY_URI =
2024                 Uri.parse("content://" + AUTHORITY + "/enterprise/instances/whenbyday");
2025 
2026         /**
2027          * The content:// style URL for querying an instance range with a search
2028          * term in the managed profile. It supports similar semantics as {@link #CONTENT_SEARCH_URI}
2029          * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
2030          */
2031         @NonNull
2032         public static final Uri ENTERPRISE_CONTENT_SEARCH_URI =
2033                 Uri.parse("content://" + AUTHORITY + "/enterprise/instances/search");
2034 
2035         /**
2036          * The content:// style URL for querying an instance range with a search
2037          * term in the managed profile. It supports similar semantics as
2038          * {@link #CONTENT_SEARCH_BY_DAY_URI} and performs similar checks as
2039          * {@link #ENTERPRISE_CONTENT_URI}.
2040          */
2041         @NonNull
2042         public static final Uri ENTERPRISE_CONTENT_SEARCH_BY_DAY_URI =
2043                 Uri.parse("content://" + AUTHORITY + "/enterprise/instances/searchbyday");
2044 
2045         /**
2046          * The default sort order for this table.
2047          */
2048         private static final String DEFAULT_SORT_ORDER = "begin ASC";
2049 
2050         /**
2051          * The beginning time of the instance, in UTC milliseconds. Column name.
2052          * <P>Type: INTEGER (long; millis since epoch)</P>
2053          */
2054         public static final String BEGIN = "begin";
2055 
2056         /**
2057          * The ending time of the instance, in UTC milliseconds. Column name.
2058          * <P>Type: INTEGER (long; millis since epoch)</P>
2059          */
2060         public static final String END = "end";
2061 
2062         /**
2063          * The _id of the event for this instance. Column name.
2064          * <P>Type: INTEGER (long, foreign key to the Events table)</P>
2065          */
2066         public static final String EVENT_ID = "event_id";
2067 
2068         /**
2069          * The Julian start day of the instance, relative to the local time
2070          * zone. Column name.
2071          * <P>Type: INTEGER (int)</P>
2072          */
2073         public static final String START_DAY = "startDay";
2074 
2075         /**
2076          * The Julian end day of the instance, relative to the local time
2077          * zone. Column name.
2078          * <P>Type: INTEGER (int)</P>
2079          */
2080         public static final String END_DAY = "endDay";
2081 
2082         /**
2083          * The start minute of the instance measured from midnight in the
2084          * local time zone. Column name.
2085          * <P>Type: INTEGER (int)</P>
2086          */
2087         public static final String START_MINUTE = "startMinute";
2088 
2089         /**
2090          * The end minute of the instance measured from midnight in the
2091          * local time zone. Column name.
2092          * <P>Type: INTEGER (int)</P>
2093          */
2094         public static final String END_MINUTE = "endMinute";
2095     }
2096 
2097     protected interface CalendarCacheColumns {
2098         /**
2099          * The key for the setting. Keys are defined in {@link CalendarCache}.
2100          */
2101         public static final String KEY = "key";
2102 
2103         /**
2104          * The value of the given setting.
2105          */
2106         public static final String VALUE = "value";
2107     }
2108 
2109     /**
2110      * CalendarCache stores some settings for calendar including the current
2111      * time zone for the instances. These settings are stored using a key/value
2112      * scheme. A {@link #KEY} must be specified when updating these values.
2113      */
2114     public static final class CalendarCache implements CalendarCacheColumns {
2115         /**
2116          * The URI to use for retrieving the properties from the Calendar db.
2117          */
2118         public static final Uri URI =
2119                 Uri.parse("content://" + AUTHORITY + "/properties");
2120 
2121         /**
2122          * This utility class cannot be instantiated
2123          */
CalendarCache()2124         private CalendarCache() {}
2125 
2126         /**
2127          * They key for updating the use of auto/home time zones in Calendar.
2128          * Valid values are {@link #TIMEZONE_TYPE_AUTO} or
2129          * {@link #TIMEZONE_TYPE_HOME}.
2130          */
2131         public static final String KEY_TIMEZONE_TYPE = "timezoneType";
2132 
2133         /**
2134          * The key for updating the time zone used by the provider when it
2135          * generates the instances table. This should only be written if the
2136          * type is set to {@link #TIMEZONE_TYPE_HOME}. A valid time zone id
2137          * should be written to this field.
2138          */
2139         public static final String KEY_TIMEZONE_INSTANCES = "timezoneInstances";
2140 
2141         /**
2142          * The key for reading the last time zone set by the user. This should
2143          * only be read by apps and it will be automatically updated whenever
2144          * {@link #KEY_TIMEZONE_INSTANCES} is updated with
2145          * {@link #TIMEZONE_TYPE_HOME} set.
2146          */
2147         public static final String KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious";
2148 
2149         /**
2150          * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
2151          * should stay in sync with the device's time zone.
2152          */
2153         public static final String TIMEZONE_TYPE_AUTO = "auto";
2154 
2155         /**
2156          * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
2157          * should use a fixed time zone set by the user.
2158          */
2159         public static final String TIMEZONE_TYPE_HOME = "home";
2160     }
2161 
2162     /**
2163      * A few Calendar globals are needed in the CalendarProvider for expanding
2164      * the Instances table and these are all stored in the first (and only) row
2165      * of the CalendarMetaData table.
2166      *
2167      * @hide
2168      */
2169     protected interface CalendarMetaDataColumns {
2170         /**
2171          * The local timezone that was used for precomputing the fields
2172          * in the Instances table.
2173          */
2174         public static final String LOCAL_TIMEZONE = "localTimezone";
2175 
2176         /**
2177          * The minimum time used in expanding the Instances table,
2178          * in UTC milliseconds.
2179          * <P>Type: INTEGER</P>
2180          */
2181         public static final String MIN_INSTANCE = "minInstance";
2182 
2183         /**
2184          * The maximum time used in expanding the Instances table,
2185          * in UTC milliseconds.
2186          * <P>Type: INTEGER</P>
2187          */
2188         public static final String MAX_INSTANCE = "maxInstance";
2189 
2190         /**
2191          * The minimum Julian day in the EventDays table.
2192          * <P>Type: INTEGER</P>
2193          */
2194         public static final String MIN_EVENTDAYS = "minEventDays";
2195 
2196         /**
2197          * The maximum Julian day in the EventDays table.
2198          * <P>Type: INTEGER</P>
2199          */
2200         public static final String MAX_EVENTDAYS = "maxEventDays";
2201     }
2202 
2203     /**
2204      * @hide
2205      */
2206     public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
2207 
2208         /**
2209          * This utility class cannot be instantiated
2210          */
CalendarMetaData()2211         private CalendarMetaData() {}
2212     }
2213 
2214     protected interface EventDaysColumns {
2215         /**
2216          * The Julian starting day number. Column name.
2217          * <P>Type: INTEGER (int)</P>
2218          */
2219         public static final String STARTDAY = "startDay";
2220         /**
2221          * The Julian ending day number. Column name.
2222          * <P>Type: INTEGER (int)</P>
2223          */
2224         public static final String ENDDAY = "endDay";
2225 
2226     }
2227 
2228     /**
2229      * Fields and helpers for querying for a list of days that contain events.
2230      */
2231     public static final class EventDays implements EventDaysColumns {
2232         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
2233                 + "/instances/groupbyday");
2234         private static final String SELECTION = "selected=1";
2235 
2236         /**
2237          * This utility class cannot be instantiated
2238          */
EventDays()2239         private EventDays() {}
2240 
2241         /**
2242          * Retrieves the days with events for the Julian days starting at
2243          * "startDay" for "numDays". It returns a cursor containing startday and
2244          * endday representing the max range of days for all events beginning on
2245          * each startday.This is a blocking function and should not be done on
2246          * the UI thread.
2247          *
2248          * @param cr the ContentResolver
2249          * @param startDay the first Julian day in the range
2250          * @param numDays the number of days to load (must be at least 1)
2251          * @param projection the columns to return in the cursor
2252          * @return a database cursor containing a list of start and end days for
2253          *         events
2254          */
query(ContentResolver cr, int startDay, int numDays, String[] projection)2255         public static final Cursor query(ContentResolver cr, int startDay, int numDays,
2256                 String[] projection) {
2257             if (numDays < 1) {
2258                 return null;
2259             }
2260             int endDay = startDay + numDays - 1;
2261             Uri.Builder builder = CONTENT_URI.buildUpon();
2262             ContentUris.appendId(builder, startDay);
2263             ContentUris.appendId(builder, endDay);
2264             return cr.query(builder.build(), projection, SELECTION,
2265                     null /* selection args */, STARTDAY);
2266         }
2267     }
2268 
2269     protected interface RemindersColumns {
2270         /**
2271          * The event the reminder belongs to. Column name.
2272          * <P>Type: INTEGER (foreign key to the Events table)</P>
2273          */
2274         public static final String EVENT_ID = "event_id";
2275 
2276         /**
2277          * The minutes prior to the event that the alarm should ring.  -1
2278          * specifies that we should use the default value for the system.
2279          * Column name.
2280          * <P>Type: INTEGER</P>
2281          */
2282         public static final String MINUTES = "minutes";
2283 
2284         /**
2285          * Passing this as a minutes value will use the default reminder
2286          * minutes.
2287          */
2288         public static final int MINUTES_DEFAULT = -1;
2289 
2290         /**
2291          * The alarm method, as set on the server. {@link #METHOD_DEFAULT},
2292          * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, {@link #METHOD_SMS} and
2293          * {@link #METHOD_ALARM} are possible values; the device will only
2294          * process {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders
2295          * (the other types are simply stored so we can send the same reminder
2296          * info back to the server when we make changes).
2297          */
2298         public static final String METHOD = "method";
2299 
2300         public static final int METHOD_DEFAULT = 0;
2301         public static final int METHOD_ALERT = 1;
2302         public static final int METHOD_EMAIL = 2;
2303         public static final int METHOD_SMS = 3;
2304         public static final int METHOD_ALARM = 4;
2305     }
2306 
2307     /**
2308      * Fields and helpers for accessing reminders for an event. Each row of this
2309      * table represents a single reminder for an event. Calling
2310      * {@link #query(ContentResolver, long, String[])} will return a list of reminders for
2311      * the event with the given eventId. Both apps and sync adapters may write
2312      * to this table. There are three writable fields and all of them must be
2313      * included when inserting a new reminder. They are:
2314      * <ul>
2315      * <li>{@link #EVENT_ID}</li>
2316      * <li>{@link #MINUTES}</li>
2317      * <li>{@link #METHOD}</li>
2318      * </ul>
2319      */
2320     public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns {
2321         private static final String REMINDERS_WHERE = CalendarContract.Reminders.EVENT_ID + "=?";
2322         @SuppressWarnings("hiding")
2323         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
2324 
2325         /**
2326          * This utility class cannot be instantiated
2327          */
Reminders()2328         private Reminders() {}
2329 
2330         /**
2331          * Queries all reminders associated with the given event. This is a
2332          * blocking call and should not be done on the UI thread.
2333          *
2334          * @param cr The content resolver to use for the query
2335          * @param eventId The id of the event to retrieve reminders for
2336          * @param projection the columns to return in the cursor
2337          * @return A Cursor containing all reminders for the event
2338          */
query(ContentResolver cr, long eventId, String[] projection)2339         public static final Cursor query(ContentResolver cr, long eventId, String[] projection) {
2340             String[] remArgs = {Long.toString(eventId)};
2341             return cr.query(CONTENT_URI, projection, REMINDERS_WHERE, remArgs /*selection args*/,
2342                     null /* sort order */);
2343         }
2344     }
2345 
2346     protected interface CalendarAlertsColumns {
2347         /**
2348          * The event that the alert belongs to. Column name.
2349          * <P>Type: INTEGER (foreign key to the Events table)</P>
2350          */
2351         public static final String EVENT_ID = "event_id";
2352 
2353         /**
2354          * The start time of the event, in UTC. Column name.
2355          * <P>Type: INTEGER (long; millis since epoch)</P>
2356          */
2357         public static final String BEGIN = "begin";
2358 
2359         /**
2360          * The end time of the event, in UTC. Column name.
2361          * <P>Type: INTEGER (long; millis since epoch)</P>
2362          */
2363         public static final String END = "end";
2364 
2365         /**
2366          * The alarm time of the event, in UTC. Column name.
2367          * <P>Type: INTEGER (long; millis since epoch)</P>
2368          */
2369         public static final String ALARM_TIME = "alarmTime";
2370 
2371         /**
2372          * The creation time of this database entry, in UTC.
2373          * Useful for debugging missed reminders. Column name.
2374          * <P>Type: INTEGER (long; millis since epoch)</P>
2375          */
2376         public static final String CREATION_TIME = "creationTime";
2377 
2378         /**
2379          * The time that the alarm broadcast was received by the Calendar app,
2380          * in UTC. Useful for debugging missed reminders. Column name.
2381          * <P>Type: INTEGER (long; millis since epoch)</P>
2382          */
2383         public static final String RECEIVED_TIME = "receivedTime";
2384 
2385         /**
2386          * The time that the notification was created by the Calendar app,
2387          * in UTC. Useful for debugging missed reminders. Column name.
2388          * <P>Type: INTEGER (long; millis since epoch)</P>
2389          */
2390         public static final String NOTIFY_TIME = "notifyTime";
2391 
2392         /**
2393          * The state of this alert. It starts out as {@link #STATE_SCHEDULED}, then
2394          * when the alarm goes off, it changes to {@link #STATE_FIRED}, and then when
2395          * the user dismisses the alarm it changes to {@link #STATE_DISMISSED}. Column
2396          * name.
2397          * <P>Type: INTEGER</P>
2398          */
2399         public static final String STATE = "state";
2400 
2401         /**
2402          * An alert begins in this state when it is first created.
2403          */
2404         public static final int STATE_SCHEDULED = 0;
2405         /**
2406          * After a notification for an alert has been created it should be
2407          * updated to fired.
2408          */
2409         public static final int STATE_FIRED = 1;
2410         /**
2411          * Once the user has dismissed the notification the alert's state should
2412          * be set to dismissed so it is not fired again.
2413          */
2414         public static final int STATE_DISMISSED = 2;
2415 
2416         /**
2417          * The number of minutes that this alarm precedes the start time. Column
2418          * name.
2419          * <P>Type: INTEGER</P>
2420          */
2421         public static final String MINUTES = "minutes";
2422 
2423         /**
2424          * The default sort order for this alerts queries
2425          */
2426         public static final String DEFAULT_SORT_ORDER = "begin ASC,title ASC";
2427     }
2428 
2429     /**
2430      * Fields and helpers for accessing calendar alerts information. These
2431      * fields are for tracking which alerts have been fired. Scheduled alarms
2432      * will generate an intent using {@link #ACTION_EVENT_REMINDER}. Apps that
2433      * receive this action may update the {@link #STATE} for the reminder when
2434      * they have finished handling it. Apps that have their notifications
2435      * disabled should not modify the table to ensure that they do not conflict
2436      * with another app that is generating a notification. In general, apps
2437      * should not need to write to this table directly except to update the
2438      * state of a reminder.
2439      */
2440     public static final class CalendarAlerts implements BaseColumns,
2441             CalendarAlertsColumns, EventsColumns, CalendarColumns {
2442 
2443         /**
2444          * @hide
2445          */
2446         public static final String TABLE_NAME = "CalendarAlerts";
2447         /**
2448          * The Uri for querying calendar alert information
2449          */
2450         @SuppressWarnings("hiding")
2451         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
2452                 "/calendar_alerts");
2453 
2454         /**
2455          * This utility class cannot be instantiated
2456          */
CalendarAlerts()2457         private CalendarAlerts() {}
2458 
2459         private static final String WHERE_ALARM_EXISTS = EVENT_ID + "=?"
2460                 + " AND " + BEGIN + "=?"
2461                 + " AND " + ALARM_TIME + "=?";
2462 
2463         private static final String WHERE_FINDNEXTALARMTIME = ALARM_TIME + ">=?";
2464         private static final String SORT_ORDER_ALARMTIME_ASC = ALARM_TIME + " ASC";
2465 
2466         private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + STATE_SCHEDULED
2467                 + " AND " + ALARM_TIME + "<?"
2468                 + " AND " + ALARM_TIME + ">?"
2469                 + " AND " + END + ">=?";
2470 
2471         /**
2472          * This URI is for grouping the query results by event_id and begin
2473          * time.  This will return one result per instance of an event.  So
2474          * events with multiple alarms will appear just once, but multiple
2475          * instances of a repeating event will show up multiple times.
2476          */
2477         public static final Uri CONTENT_URI_BY_INSTANCE =
2478             Uri.parse("content://" + AUTHORITY + "/calendar_alerts/by_instance");
2479 
2480         private static final boolean DEBUG = false;
2481 
2482         /**
2483          * Helper for inserting an alarm time associated with an event TODO move
2484          * to Provider
2485          *
2486          * @hide
2487          */
insert(ContentResolver cr, long eventId, long begin, long end, long alarmTime, int minutes)2488         public static final Uri insert(ContentResolver cr, long eventId,
2489                 long begin, long end, long alarmTime, int minutes) {
2490             ContentValues values = new ContentValues();
2491             values.put(CalendarAlerts.EVENT_ID, eventId);
2492             values.put(CalendarAlerts.BEGIN, begin);
2493             values.put(CalendarAlerts.END, end);
2494             values.put(CalendarAlerts.ALARM_TIME, alarmTime);
2495             long currentTime = System.currentTimeMillis();
2496             values.put(CalendarAlerts.CREATION_TIME, currentTime);
2497             values.put(CalendarAlerts.RECEIVED_TIME, 0);
2498             values.put(CalendarAlerts.NOTIFY_TIME, 0);
2499             values.put(CalendarAlerts.STATE, STATE_SCHEDULED);
2500             values.put(CalendarAlerts.MINUTES, minutes);
2501             return cr.insert(CONTENT_URI, values);
2502         }
2503 
2504         /**
2505          * Finds the next alarm after (or equal to) the given time and returns
2506          * the time of that alarm or -1 if no such alarm exists. This is a
2507          * blocking call and should not be done on the UI thread. TODO move to
2508          * provider
2509          *
2510          * @param cr the ContentResolver
2511          * @param millis the time in UTC milliseconds
2512          * @return the next alarm time greater than or equal to "millis", or -1
2513          *         if no such alarm exists.
2514          * @hide
2515          */
2516         @UnsupportedAppUsage
findNextAlarmTime(ContentResolver cr, long millis)2517         public static final long findNextAlarmTime(ContentResolver cr, long millis) {
2518             String selection = ALARM_TIME + ">=" + millis;
2519             // TODO: construct an explicit SQL query so that we can add
2520             // "LIMIT 1" to the end and get just one result.
2521             String[] projection = new String[] { ALARM_TIME };
2522             Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_FINDNEXTALARMTIME,
2523                     (new String[] {
2524                         Long.toString(millis)
2525                     }), SORT_ORDER_ALARMTIME_ASC);
2526             long alarmTime = -1;
2527             try {
2528                 if (cursor != null && cursor.moveToFirst()) {
2529                     alarmTime = cursor.getLong(0);
2530                 }
2531             } finally {
2532                 if (cursor != null) {
2533                     cursor.close();
2534                 }
2535             }
2536             return alarmTime;
2537         }
2538 
2539         /**
2540          * Searches the CalendarAlerts table for alarms that should have fired
2541          * but have not and then reschedules them. This method can be called at
2542          * boot time to restore alarms that may have been lost due to a phone
2543          * reboot. TODO move to provider
2544          *
2545          * @param cr the ContentResolver
2546          * @param context the Context
2547          * @param manager the AlarmManager
2548          * @hide
2549          */
2550         @UnsupportedAppUsage
rescheduleMissedAlarms(ContentResolver cr, Context context, AlarmManager manager)2551         public static final void rescheduleMissedAlarms(ContentResolver cr,
2552                 Context context, AlarmManager manager) {
2553             // Get all the alerts that have been scheduled but have not fired
2554             // and should have fired by now and are not too old.
2555             long now = System.currentTimeMillis();
2556             long ancient = now - DateUtils.DAY_IN_MILLIS;
2557             String[] projection = new String[] {
2558                     ALARM_TIME,
2559             };
2560 
2561             // TODO: construct an explicit SQL query so that we can add
2562             // "GROUPBY" instead of doing a sort and de-dup
2563             Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection,
2564                     WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] {
2565                             Long.toString(now), Long.toString(ancient), Long.toString(now)
2566                     }), SORT_ORDER_ALARMTIME_ASC);
2567             if (cursor == null) {
2568                 return;
2569             }
2570 
2571             if (DEBUG) {
2572                 Log.d(TAG, "missed alarms found: " + cursor.getCount());
2573             }
2574 
2575             try {
2576                 long alarmTime = -1;
2577 
2578                 while (cursor.moveToNext()) {
2579                     long newAlarmTime = cursor.getLong(0);
2580                     if (alarmTime != newAlarmTime) {
2581                         if (DEBUG) {
2582                             Log.w(TAG, "rescheduling missed alarm. alarmTime: " + newAlarmTime);
2583                         }
2584                         scheduleAlarm(context, manager, newAlarmTime);
2585                         alarmTime = newAlarmTime;
2586                     }
2587                 }
2588             } finally {
2589                 cursor.close();
2590             }
2591         }
2592 
2593         /**
2594          * Schedules an alarm intent with the system AlarmManager that will
2595          * notify listeners when a reminder should be fired. The provider will
2596          * keep scheduled reminders up to date but apps may use this to
2597          * implement snooze functionality without modifying the reminders table.
2598          * Scheduled alarms will generate an intent using
2599          * {@link #ACTION_EVENT_REMINDER}. TODO Move to provider
2600          *
2601          * @param context A context for referencing system resources
2602          * @param manager The AlarmManager to use or null
2603          * @param alarmTime The time to fire the intent in UTC millis since
2604          *            epoch
2605          * @hide
2606          */
2607         @UnsupportedAppUsage
scheduleAlarm(Context context, AlarmManager manager, long alarmTime)2608         public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) {
2609             if (DEBUG) {
2610                 String schedTime = TimeMigrationUtils.formatMillisWithFixedFormat(alarmTime);
2611                 Log.d(TAG, "Schedule alarm at " + alarmTime + " " + schedTime);
2612             }
2613 
2614             if (manager == null) {
2615                 manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
2616             }
2617 
2618             Intent intent = new Intent(ACTION_EVENT_REMINDER);
2619             intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
2620             intent.putExtra(ALARM_TIME, alarmTime);
2621             intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
2622 
2623             // Disable strict mode VM policy violations temporarily for intents that contain a
2624             // content URI but don't have FLAG_GRANT_READ_URI_PERMISSION.
2625             StrictMode.VmPolicy oldVmPolicy = StrictMode.allowVmViolations();
2626             PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
2627             StrictMode.setVmPolicy(oldVmPolicy);
2628 
2629             manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pi);
2630         }
2631 
2632         /**
2633          * Searches for an entry in the CalendarAlerts table that matches the
2634          * given event id, begin time and alarm time. If one is found then this
2635          * alarm already exists and this method returns true. TODO Move to
2636          * provider
2637          *
2638          * @param cr the ContentResolver
2639          * @param eventId the event id to match
2640          * @param begin the start time of the event in UTC millis
2641          * @param alarmTime the alarm time of the event in UTC millis
2642          * @return true if there is already an alarm for the given event with
2643          *         the same start time and alarm time.
2644          * @hide
2645          */
alarmExists(ContentResolver cr, long eventId, long begin, long alarmTime)2646         public static final boolean alarmExists(ContentResolver cr, long eventId,
2647                 long begin, long alarmTime) {
2648             // TODO: construct an explicit SQL query so that we can add
2649             // "LIMIT 1" to the end and get just one result.
2650             String[] projection = new String[] { ALARM_TIME };
2651             Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_ALARM_EXISTS,
2652                     (new String[] {
2653                             Long.toString(eventId), Long.toString(begin), Long.toString(alarmTime)
2654                     }), null);
2655             boolean found = false;
2656             try {
2657                 if (cursor != null && cursor.getCount() > 0) {
2658                     found = true;
2659                 }
2660             } finally {
2661                 if (cursor != null) {
2662                     cursor.close();
2663                 }
2664             }
2665             return found;
2666         }
2667     }
2668 
2669     protected interface ColorsColumns extends SyncStateContract.Columns {
2670 
2671         /**
2672          * The type of color, which describes how it should be used. Valid types
2673          * are {@link #TYPE_CALENDAR} and {@link #TYPE_EVENT}. Column name.
2674          * <P>
2675          * Type: INTEGER (NOT NULL)
2676          * </P>
2677          */
2678         public static final String COLOR_TYPE = "color_type";
2679 
2680         /**
2681          * This indicateds a color that can be used for calendars.
2682          */
2683         public static final int TYPE_CALENDAR = 0;
2684         /**
2685          * This indicates a color that can be used for events.
2686          */
2687         public static final int TYPE_EVENT = 1;
2688 
2689         /**
2690          * The key used to reference this color. This can be any non-empty
2691          * string, but must be unique for a given {@link #ACCOUNT_TYPE} and
2692          * {@link #ACCOUNT_NAME}. Column name.
2693          * <P>
2694          * Type: TEXT
2695          * </P>
2696          */
2697         public static final String COLOR_KEY = "color_index";
2698 
2699         /**
2700          * The color as an 8-bit ARGB integer value. Colors should specify alpha
2701          * as fully opaque (eg 0xFF993322) as the alpha may be ignored or
2702          * modified for display. It is reccomended that colors be usable with
2703          * light (near white) text. Apps should not depend on that assumption,
2704          * however. Column name.
2705          * <P>
2706          * Type: INTEGER (NOT NULL)
2707          * </P>
2708          */
2709         public static final String COLOR = "color";
2710 
2711     }
2712 
2713     /**
2714      * Fields for accessing colors available for a given account. Colors are
2715      * referenced by {@link #COLOR_KEY} which must be unique for a given
2716      * account name/type. These values can only be updated by the sync
2717      * adapter. Only {@link #COLOR} may be updated after the initial insert. In
2718      * addition, a row can only be deleted once all references to that color
2719      * have been removed from the {@link Calendars} or {@link Events} tables.
2720      */
2721     public static final class Colors implements ColorsColumns {
2722         /**
2723          * @hide
2724          */
2725         public static final String TABLE_NAME = "Colors";
2726         /**
2727          * The Uri for querying color information
2728          */
2729         @SuppressWarnings("hiding")
2730         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/colors");
2731 
2732         /**
2733          * This utility class cannot be instantiated
2734          */
Colors()2735         private Colors() {
2736         }
2737     }
2738 
2739     protected interface ExtendedPropertiesColumns {
2740         /**
2741          * The event the extended property belongs to. Column name.
2742          * <P>Type: INTEGER (foreign key to the Events table)</P>
2743          */
2744         public static final String EVENT_ID = "event_id";
2745 
2746         /**
2747          * The name of the extended property.  This is a uri of the form
2748          * {scheme}#{local-name} convention. Column name.
2749          * <P>Type: TEXT</P>
2750          */
2751         public static final String NAME = "name";
2752 
2753         /**
2754          * The value of the extended property. Column name.
2755          * <P>Type: TEXT</P>
2756          */
2757         public static final String VALUE = "value";
2758     }
2759 
2760     /**
2761      * Fields for accessing the Extended Properties. This is a generic set of
2762      * name/value pairs for use by sync adapters to add extra
2763      * information to events. There are three writable columns and all three
2764      * must be present when inserting a new value. They are:
2765      * <ul>
2766      * <li>{@link #EVENT_ID}</li>
2767      * <li>{@link #NAME}</li>
2768      * <li>{@link #VALUE}</li>
2769      * </ul>
2770      */
2771    public static final class ExtendedProperties implements BaseColumns,
2772             ExtendedPropertiesColumns, EventsColumns {
2773         public static final Uri CONTENT_URI =
2774                 Uri.parse("content://" + AUTHORITY + "/extendedproperties");
2775 
2776         /**
2777          * This utility class cannot be instantiated
2778          */
ExtendedProperties()2779         private ExtendedProperties() {}
2780 
2781         // TODO: fill out this class when we actually start utilizing extendedproperties
2782         // in the calendar application.
2783    }
2784 
2785     /**
2786      * A table provided for sync adapters to use for storing private sync state data.
2787      *
2788      * @see SyncStateContract
2789      */
2790     public static final class SyncState implements SyncStateContract.Columns {
2791         /**
2792          * This utility class cannot be instantiated
2793          */
SyncState()2794         private SyncState() {}
2795 
2796         private static final String CONTENT_DIRECTORY =
2797                 SyncStateContract.Constants.CONTENT_DIRECTORY;
2798 
2799         /**
2800          * The content:// style URI for this table
2801          */
2802         public static final Uri CONTENT_URI =
2803                 Uri.withAppendedPath(CalendarContract.CONTENT_URI, CONTENT_DIRECTORY);
2804     }
2805 
2806     /**
2807      * Columns from the EventsRawTimes table
2808      *
2809      * @hide
2810      */
2811     protected interface EventsRawTimesColumns {
2812         /**
2813          * The corresponding event id. Column name.
2814          * <P>Type: INTEGER (long)</P>
2815          */
2816         public static final String EVENT_ID = "event_id";
2817 
2818         /**
2819          * The RFC2445 compliant time the event starts. Column name.
2820          * <P>Type: TEXT</P>
2821          */
2822         public static final String DTSTART_2445 = "dtstart2445";
2823 
2824         /**
2825          * The RFC2445 compliant time the event ends. Column name.
2826          * <P>Type: TEXT</P>
2827          */
2828         public static final String DTEND_2445 = "dtend2445";
2829 
2830         /**
2831          * The RFC2445 compliant original instance time of the recurring event
2832          * for which this event is an exception. Column name.
2833          * <P>Type: TEXT</P>
2834          */
2835         public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445";
2836 
2837         /**
2838          * The RFC2445 compliant last date this event repeats on, or NULL if it
2839          * never ends. Column name.
2840          * <P>Type: TEXT</P>
2841          */
2842         public static final String LAST_DATE_2445 = "lastDate2445";
2843     }
2844 
2845     /**
2846      * @hide
2847      */
2848     public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns {
2849 
2850         /**
2851          * This utility class cannot be instantiated
2852          */
EventsRawTimes()2853         private EventsRawTimes() {}
2854     }
2855 }
2856