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