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