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