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