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