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