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