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