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