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