CalendarDatabaseHelper.java revision 1ae4c22f15c107cd9f9cd8babaa11005e45e4647
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.providers.calendar; 18 19import android.accounts.Account; 20import android.content.ContentResolver; 21import android.content.ContentValues; 22import android.content.Context; 23import android.content.res.Resources; 24import android.database.Cursor; 25import android.database.DatabaseUtils; 26import android.database.sqlite.SQLiteDatabase; 27import android.database.sqlite.SQLiteOpenHelper; 28import android.os.Bundle; 29import android.provider.Calendar; 30import android.provider.ContactsContract; 31import android.util.Log; 32import com.android.internal.content.SyncStateContentProviderHelper; 33 34/** 35 * Database helper for calendar. Designed as a singleton to make sure that all 36 * {@link android.content.ContentProvider} users get the same reference. 37 */ 38/* package */ class CalendarDatabaseHelper extends SQLiteOpenHelper { 39 private static final String TAG = "CalendarDatabaseHelper"; 40 41 private static final String DATABASE_NAME = "calendar2.db"; 42 43 // TODO: change the Calendar contract so these are defined there. 44 static final String ACCOUNT_NAME = "_sync_account"; 45 static final String ACCOUNT_TYPE = "_sync_account_type"; 46 47 // Note: if you update the version number, you must also update the code 48 // in upgradeDatabase() to modify the database (gracefully, if possible). 49 private static final int DATABASE_VERSION = 57; 50 51 private final Context mContext; 52 private final SyncStateContentProviderHelper mSyncState; 53 54 private static CalendarDatabaseHelper sSingleton = null; 55 56 private DatabaseUtils.InsertHelper mCalendarsInserter; 57 private DatabaseUtils.InsertHelper mEventsInserter; 58 private DatabaseUtils.InsertHelper mEventsRawTimesInserter; 59 private DatabaseUtils.InsertHelper mInstancesInserter; 60 private DatabaseUtils.InsertHelper mAttendeesInserter; 61 private DatabaseUtils.InsertHelper mRemindersInserter; 62 private DatabaseUtils.InsertHelper mCalendarAlertsInserter; 63 private DatabaseUtils.InsertHelper mExtendedPropertiesInserter; 64 65 public long calendarsInsert(ContentValues values) { 66 return mCalendarsInserter.insert(values); 67 } 68 69 public long eventsInsert(ContentValues values) { 70 return mEventsInserter.insert(values); 71 } 72 73 public long eventsRawTimesInsert(ContentValues values) { 74 return mEventsRawTimesInserter.insert(values); 75 } 76 77 public long eventsRawTimesReplace(ContentValues values) { 78 return mEventsRawTimesInserter.replace(values); 79 } 80 81 public long instancesInsert(ContentValues values) { 82 return mInstancesInserter.insert(values); 83 } 84 85 public long attendeesInsert(ContentValues values) { 86 return mAttendeesInserter.insert(values); 87 } 88 89 public long remindersInsert(ContentValues values) { 90 return mRemindersInserter.insert(values); 91 } 92 93 public long calendarAlertsInsert(ContentValues values) { 94 return mCalendarAlertsInserter.insert(values); 95 } 96 97 public long extendedPropertiesInsert(ContentValues values) { 98 return mExtendedPropertiesInserter.insert(values); 99 } 100 101 public static synchronized CalendarDatabaseHelper getInstance(Context context) { 102 if (sSingleton == null) { 103 sSingleton = new CalendarDatabaseHelper(context); 104 } 105 return sSingleton; 106 } 107 108 /** 109 * Private constructor, callers except unit tests should obtain an instance through 110 * {@link #getInstance(android.content.Context)} instead. 111 */ 112 /* package */ CalendarDatabaseHelper(Context context) { 113 super(context, DATABASE_NAME, null, DATABASE_VERSION); 114 if (false) Log.i(TAG, "Creating OpenHelper"); 115 Resources resources = context.getResources(); 116 117 mContext = context; 118 mSyncState = new SyncStateContentProviderHelper(); 119 } 120 121 @Override 122 public void onOpen(SQLiteDatabase db) { 123 mSyncState.onDatabaseOpened(db); 124 125 mCalendarsInserter = new DatabaseUtils.InsertHelper(db, "Calendars"); 126 mEventsInserter = new DatabaseUtils.InsertHelper(db, "Events"); 127 mEventsRawTimesInserter = new DatabaseUtils.InsertHelper(db, "EventsRawTimes"); 128 mInstancesInserter = new DatabaseUtils.InsertHelper(db, "Instances"); 129 mAttendeesInserter = new DatabaseUtils.InsertHelper(db, "Attendees"); 130 mRemindersInserter = new DatabaseUtils.InsertHelper(db, "Reminders"); 131 mCalendarAlertsInserter = new DatabaseUtils.InsertHelper(db, "CalendarAlerts"); 132 mExtendedPropertiesInserter = 133 new DatabaseUtils.InsertHelper(db, "ExtendedProperties"); 134 } 135 136 @Override 137 public void onCreate(SQLiteDatabase db) { 138 Log.i(TAG, "Bootstrapping database"); 139 140 mSyncState.createDatabase(db); 141 142 db.execSQL("CREATE TABLE Calendars (" + 143 "_id INTEGER PRIMARY KEY," + 144 ACCOUNT_NAME + " TEXT," + 145 ACCOUNT_TYPE + " TEXT," + 146 "_sync_id TEXT," + 147 "_sync_version TEXT," + 148 "_sync_time TEXT," + // UTC 149 "_sync_local_id INTEGER," + 150 "_sync_dirty INTEGER," + 151 "_sync_mark INTEGER," + // Used to filter out new rows 152 "url TEXT," + 153 "name TEXT," + 154 "displayName TEXT," + 155 "hidden INTEGER NOT NULL DEFAULT 0," + 156 "color INTEGER," + 157 "access_level INTEGER," + 158 "selected INTEGER NOT NULL DEFAULT 1," + 159 "sync_events INTEGER NOT NULL DEFAULT 0," + 160 "location TEXT," + 161 "timezone TEXT," + 162 "ownerAccount TEXT" + 163 ");"); 164 165 // Trigger to remove a calendar's events when we delete the calendar 166 db.execSQL("CREATE TRIGGER calendar_cleanup DELETE ON Calendars " + 167 "BEGIN " + 168 "DELETE FROM Events WHERE calendar_id = old._id;" + 169 "END"); 170 171 // TODO: do we need both dtend and duration? 172 db.execSQL("CREATE TABLE Events (" + 173 "_id INTEGER PRIMARY KEY," + 174 ACCOUNT_NAME + " TEXT," + 175 ACCOUNT_TYPE + " TEXT," + 176 "_sync_id TEXT," + 177 "_sync_version TEXT," + 178 "_sync_time TEXT," + // UTC 179 "_sync_local_id INTEGER," + 180 "_sync_dirty INTEGER," + 181 "_sync_mark INTEGER," + // To filter out new rows 182 "calendar_id INTEGER NOT NULL," + 183 "htmlUri TEXT," + 184 "title TEXT," + 185 "eventLocation TEXT," + 186 "description TEXT," + 187 "eventStatus INTEGER," + 188 "selfAttendeeStatus INTEGER NOT NULL DEFAULT 0," + 189 "commentsUri TEXT," + 190 "dtstart INTEGER," + // millis since epoch 191 "dtend INTEGER," + // millis since epoch 192 "eventTimezone TEXT," + // timezone for event 193 "duration TEXT," + 194 "allDay INTEGER NOT NULL DEFAULT 0," + 195 "visibility INTEGER NOT NULL DEFAULT 0," + 196 "transparency INTEGER NOT NULL DEFAULT 0," + 197 "hasAlarm INTEGER NOT NULL DEFAULT 0," + 198 "hasExtendedProperties INTEGER NOT NULL DEFAULT 0," + 199 "rrule TEXT," + 200 "rdate TEXT," + 201 "exrule TEXT," + 202 "exdate TEXT," + 203 "originalEvent TEXT," + // _sync_id of recurring event 204 "originalInstanceTime INTEGER," + // millis since epoch 205 "originalAllDay INTEGER," + 206 "lastDate INTEGER," + // millis since epoch 207 "hasAttendeeData INTEGER NOT NULL DEFAULT 0," + 208 "guestsCanModify INTEGER NOT NULL DEFAULT 0," + 209 "guestsCanInviteOthers INTEGER NOT NULL DEFAULT 1," + 210 "guestsCanSeeGuests INTEGER NOT NULL DEFAULT 1," + 211 "organizer STRING," + 212 "deleted INTEGER NOT NULL DEFAULT 0" + 213 ");"); 214 215 db.execSQL("CREATE INDEX eventSyncAccountAndIdIndex ON Events (" 216 + Calendar.Events._SYNC_ACCOUNT_TYPE + ", " + Calendar.Events._SYNC_ACCOUNT + ", " 217 + Calendar.Events._SYNC_ID + ");"); 218 219 db.execSQL("CREATE INDEX eventsCalendarIdIndex ON Events (" + 220 Calendar.Events.CALENDAR_ID + 221 ");"); 222 223 db.execSQL("CREATE TABLE EventsRawTimes (" + 224 "_id INTEGER PRIMARY KEY," + 225 "event_id INTEGER NOT NULL," + 226 "dtstart2445 TEXT," + 227 "dtend2445 TEXT," + 228 "originalInstanceTime2445 TEXT," + 229 "lastDate2445 TEXT," + 230 "UNIQUE (event_id)" + 231 ");"); 232 233 db.execSQL("CREATE TABLE Instances (" + 234 "_id INTEGER PRIMARY KEY," + 235 "event_id INTEGER," + 236 "begin INTEGER," + // UTC millis 237 "end INTEGER," + // UTC millis 238 "startDay INTEGER," + // Julian start day 239 "endDay INTEGER," + // Julian end day 240 "startMinute INTEGER," + // minutes from midnight 241 "endMinute INTEGER," + // minutes from midnight 242 "UNIQUE (event_id, begin, end)" + 243 ");"); 244 245 db.execSQL("CREATE INDEX instancesStartDayIndex ON Instances (" + 246 Calendar.Instances.START_DAY + 247 ");"); 248 249 db.execSQL("CREATE TABLE CalendarMetaData (" + 250 "_id INTEGER PRIMARY KEY," + 251 "localTimezone TEXT," + 252 "minInstance INTEGER," + // UTC millis 253 "maxInstance INTEGER," + // UTC millis 254 "minBusyBits INTEGER," + // UTC millis 255 "maxBusyBits INTEGER" + // UTC millis 256 ");"); 257 258 db.execSQL("CREATE TABLE BusyBits(" + 259 "day INTEGER PRIMARY KEY," + // the Julian day 260 "busyBits INTEGER," + // 24 bits for 60-minute intervals 261 "allDayCount INTEGER" + // number of all-day events 262 ");"); 263 264 db.execSQL("CREATE TABLE Attendees (" + 265 "_id INTEGER PRIMARY KEY," + 266 "event_id INTEGER," + 267 "attendeeName TEXT," + 268 "attendeeEmail TEXT," + 269 "attendeeStatus INTEGER," + 270 "attendeeRelationship INTEGER," + 271 "attendeeType INTEGER" + 272 ");"); 273 274 db.execSQL("CREATE INDEX attendeesEventIdIndex ON Attendees (" + 275 Calendar.Attendees.EVENT_ID + 276 ");"); 277 278 db.execSQL("CREATE TABLE Reminders (" + 279 "_id INTEGER PRIMARY KEY," + 280 "event_id INTEGER," + 281 "minutes INTEGER," + 282 "method INTEGER NOT NULL" + 283 " DEFAULT " + Calendar.Reminders.METHOD_DEFAULT + 284 ");"); 285 286 db.execSQL("CREATE INDEX remindersEventIdIndex ON Reminders (" + 287 Calendar.Reminders.EVENT_ID + 288 ");"); 289 290 // This table stores the Calendar notifications that have gone off. 291 db.execSQL("CREATE TABLE CalendarAlerts (" + 292 "_id INTEGER PRIMARY KEY," + 293 "event_id INTEGER," + 294 "begin INTEGER NOT NULL," + // UTC millis 295 "end INTEGER NOT NULL," + // UTC millis 296 "alarmTime INTEGER NOT NULL," + // UTC millis 297 "creationTime INTEGER NOT NULL," + // UTC millis 298 "receivedTime INTEGER NOT NULL," + // UTC millis 299 "notifyTime INTEGER NOT NULL," + // UTC millis 300 "state INTEGER NOT NULL," + 301 "minutes INTEGER," + 302 "UNIQUE (alarmTime, begin, event_id)" + 303 ");"); 304 305 db.execSQL("CREATE INDEX calendarAlertsEventIdIndex ON CalendarAlerts (" + 306 Calendar.CalendarAlerts.EVENT_ID + 307 ");"); 308 309 db.execSQL("CREATE TABLE ExtendedProperties (" + 310 "_id INTEGER PRIMARY KEY," + 311 "event_id INTEGER," + 312 "name TEXT," + 313 "value TEXT" + 314 ");"); 315 316 db.execSQL("CREATE INDEX extendedPropertiesEventIdIndex ON ExtendedProperties (" + 317 Calendar.ExtendedProperties.EVENT_ID + 318 ");"); 319 320 // Trigger to remove data tied to an event when we delete that event. 321 db.execSQL("CREATE TRIGGER events_cleanup_delete DELETE ON Events " + 322 "BEGIN " + 323 "DELETE FROM Instances WHERE event_id = old._id;" + 324 "DELETE FROM EventsRawTimes WHERE event_id = old._id;" + 325 "DELETE FROM Attendees WHERE event_id = old._id;" + 326 "DELETE FROM Reminders WHERE event_id = old._id;" + 327 "DELETE FROM CalendarAlerts WHERE event_id = old._id;" + 328 "DELETE FROM ExtendedProperties WHERE event_id = old._id;" + 329 "END"); 330 331 createEventsView(db); 332 333 ContentResolver.requestSync(null /* all accounts */, 334 ContactsContract.AUTHORITY, new Bundle()); 335 } 336 337 @Override 338 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 339 Log.i(TAG, "Upgrading DB from version " + oldVersion 340 + " to " + newVersion); 341 if (oldVersion < 46) { 342 dropTables(db); 343 mSyncState.createDatabase(db); 344 return; // this was lossy 345 } 346 347 if (oldVersion == 46) { 348 Log.w(TAG, "Upgrading CalendarAlerts table"); 349 db.execSQL("UPDATE CalendarAlerts SET reminder_id=NULL;"); 350 db.execSQL("ALTER TABLE CalendarAlerts ADD COLUMN minutes INTEGER DEFAULT 0;"); 351 oldVersion += 1; 352 } 353 354 if (oldVersion == 47) { 355 // Changing to version 48 was intended to force a data wipe 356 dropTables(db); 357 mSyncState.createDatabase(db); 358 return; // this was lossy 359 } 360 361 if (oldVersion == 48) { 362 // Changing to version 49 was intended to force a data wipe 363 dropTables(db); 364 mSyncState.createDatabase(db); 365 return; // this was lossy 366 } 367 368 if (oldVersion == 49) { 369 Log.w(TAG, "Upgrading DeletedEvents table"); 370 371 // We don't have enough information to fill in the correct 372 // value of the calendar_id for old rows in the DeletedEvents 373 // table, but rows in that table are transient so it is unlikely 374 // that there are any rows. Plus, the calendar_id is used only 375 // when deleting a calendar, which is a rare event. All new rows 376 // will have the correct calendar_id. 377 db.execSQL("ALTER TABLE DeletedEvents ADD COLUMN calendar_id INTEGER;"); 378 379 // Trigger to remove a calendar's events when we delete the calendar 380 db.execSQL("DROP TRIGGER IF EXISTS calendar_cleanup"); 381 db.execSQL("CREATE TRIGGER calendar_cleanup DELETE ON Calendars " + 382 "BEGIN " + 383 "DELETE FROM Events WHERE calendar_id = old._id;" + 384 "DELETE FROM DeletedEvents WHERE calendar_id = old._id;" + 385 "END"); 386 db.execSQL("DROP TRIGGER IF EXISTS event_to_deleted"); 387 oldVersion += 1; 388 } 389 390 if (oldVersion == 50) { 391 // This should have been deleted in the upgrade from version 49 392 // but we missed it. 393 db.execSQL("DROP TRIGGER IF EXISTS event_to_deleted"); 394 oldVersion += 1; 395 } 396 397 if (oldVersion == 51) { 398 // We added "originalAllDay" to the Events table to keep track of 399 // the allDay status of the original recurring event for entries 400 // that are exceptions to that recurring event. We need this so 401 // that we can format the date correctly for the "originalInstanceTime" 402 // column when we make a change to the recurrence exception and 403 // send it to the server. 404 db.execSQL("ALTER TABLE Events ADD COLUMN originalAllDay INTEGER;"); 405 406 // Iterate through the Events table and for each recurrence 407 // exception, fill in the correct value for "originalAllDay", 408 // if possible. The only times where this might not be possible 409 // are (1) the original recurring event no longer exists, or 410 // (2) the original recurring event does not yet have a _sync_id 411 // because it was created on the phone and hasn't been synced to the 412 // server yet. In both cases the originalAllDay field will be set 413 // to null. In the first case we don't care because the recurrence 414 // exception will not be displayed and we won't be able to make 415 // any changes to it (and even if we did, the server should ignore 416 // them, right?). In the second case, the calendar client already 417 // disallows making changes to an instance of a recurring event 418 // until the recurring event has been synced to the server so the 419 // second case should never occur. 420 421 // "cursor" iterates over all the recurrences exceptions. 422 Cursor cursor = db.rawQuery("SELECT _id,originalEvent FROM Events" 423 + " WHERE originalEvent IS NOT NULL", null /* selection args */); 424 if (cursor != null) { 425 try { 426 while (cursor.moveToNext()) { 427 long id = cursor.getLong(0); 428 String originalEvent = cursor.getString(1); 429 430 // Find the original recurring event (if it exists) 431 Cursor recur = db.rawQuery("SELECT allDay FROM Events" 432 + " WHERE _sync_id=?", new String[] {originalEvent}); 433 if (recur == null) { 434 continue; 435 } 436 437 try { 438 // Fill in the "originalAllDay" field of the 439 // recurrence exception with the "allDay" value 440 // from the recurring event. 441 if (recur.moveToNext()) { 442 int allDay = recur.getInt(0); 443 db.execSQL("UPDATE Events SET originalAllDay=" + allDay 444 + " WHERE _id="+id); 445 } 446 } finally { 447 recur.close(); 448 } 449 } 450 } finally { 451 cursor.close(); 452 } 453 } 454 oldVersion += 1; 455 } 456 457 if (oldVersion == 52) { 458 Log.w(TAG, "Upgrading CalendarAlerts table"); 459 db.execSQL("ALTER TABLE CalendarAlerts ADD COLUMN creationTime INTEGER DEFAULT 0;"); 460 db.execSQL("ALTER TABLE CalendarAlerts ADD COLUMN receivedTime INTEGER DEFAULT 0;"); 461 db.execSQL("ALTER TABLE CalendarAlerts ADD COLUMN notifyTime INTEGER DEFAULT 0;"); 462 oldVersion += 1; 463 } 464 465 if (oldVersion == 53) { 466 Log.w(TAG, "adding eventSyncAccountAndIdIndex"); 467 db.execSQL("CREATE INDEX eventSyncAccountAndIdIndex ON Events (" 468 + Calendar.Events._SYNC_ACCOUNT + ", " + Calendar.Events._SYNC_ID + ");"); 469 oldVersion += 1; 470 } 471 472 if (oldVersion == 54) { 473 db.execSQL("ALTER TABLE Calendars ADD COLUMN _sync_account_type TEXT;"); 474 db.execSQL("ALTER TABLE Events ADD COLUMN _sync_account_type TEXT;"); 475 db.execSQL("ALTER TABLE DeletedEvents ADD COLUMN _sync_account_type TEXT;"); 476 db.execSQL("UPDATE Calendars" 477 + " SET _sync_account_type='com.google'" 478 + " WHERE _sync_account IS NOT NULL"); 479 db.execSQL("UPDATE Events" 480 + " SET _sync_account_type='com.google'" 481 + " WHERE _sync_account IS NOT NULL"); 482 db.execSQL("UPDATE DeletedEvents" 483 + " SET _sync_account_type='com.google'" 484 + " WHERE _sync_account IS NOT NULL"); 485 Log.w(TAG, "re-creating eventSyncAccountAndIdIndex"); 486 db.execSQL("DROP INDEX eventSyncAccountAndIdIndex"); 487 db.execSQL("CREATE INDEX eventSyncAccountAndIdIndex ON Events (" 488 + Calendar.Events._SYNC_ACCOUNT_TYPE + ", " 489 + Calendar.Events._SYNC_ACCOUNT + ", " 490 + Calendar.Events._SYNC_ID + ");"); 491 oldVersion += 1; 492 } 493 if (oldVersion == 55 || oldVersion == 56) { // Both require resync 494 // Delete sync state, so all records will be re-synced. 495 db.execSQL("DELETE FROM _sync_state;"); 496 497 // "cursor" iterates over all the calendars 498 Cursor cursor = db.rawQuery("SELECT _sync_account,_sync_account_type,url " 499 + "FROM Calendars", 500 null /* selection args */); 501 if (cursor != null) { 502 try { 503 while (cursor.moveToNext()) { 504 String accountName = cursor.getString(0); 505 String accountType = cursor.getString(1); 506 final Account account = new Account(accountName, accountType); 507 String calendarUrl = cursor.getString(2); 508 scheduleSync(account, false /* two-way sync */, calendarUrl); 509 } 510 } finally { 511 cursor.close(); 512 } 513 } 514 } 515 if (oldVersion == 55) { 516 db.execSQL("ALTER TABLE Calendars ADD COLUMN ownerAccount TEXT;"); 517 db.execSQL("ALTER TABLE Events ADD COLUMN hasAttendeeData INTEGER;"); 518 // Clear _sync_dirty to avoid a client-to-server sync that could blow away 519 // server attendees. 520 // Clear _sync_version to pull down the server's event (with attendees) 521 // Change the URLs from full-selfattendance to full 522 db.execSQL("UPDATE Events" 523 + " SET _sync_dirty=0," 524 + " _sync_version=NULL," 525 + " _sync_id=" 526 + "REPLACE(_sync_id, '/private/full-selfattendance', '/private/full')," 527 + " commentsUri =" 528 + "REPLACE(commentsUri, '/private/full-selfattendance', '/private/full');"); 529 db.execSQL("UPDATE Calendars" 530 + " SET url=" 531 + "REPLACE(url, '/private/full-selfattendance', '/private/full');"); 532 533 // "cursor" iterates over all the calendars 534 Cursor cursor = db.rawQuery("SELECT _id, url FROM Calendars", 535 null /* selection args */); 536 // Add the owner column. 537 if (cursor != null) { 538 try { 539 while (cursor.moveToNext()) { 540 Long id = cursor.getLong(0); 541 String url = cursor.getString(1); 542 String owner = CalendarSyncAdapter.calendarEmailAddressFromFeedUrl(url); 543 db.execSQL("UPDATE Calendars SET ownerAccount=? WHERE _id=?", 544 new Object[] {owner, id}); 545 } 546 } finally { 547 cursor.close(); 548 } 549 } 550 oldVersion += 1; 551 } 552 if (oldVersion == 56) { 553 db.execSQL("ALTER TABLE Events ADD COLUMN guestsCanModify" 554 + " INTEGER NOT NULL DEFAULT 0;"); 555 db.execSQL("ALTER TABLE Events ADD COLUMN guestsCanInviteOthers" 556 + " INTEGER NOT NULL DEFAULT 1;"); 557 db.execSQL("ALTER TABLE Events ADD COLUMN guestsCanSeeGuests" 558 + " INTEGER NOT NULL DEFAULT 1;"); 559 db.execSQL("ALTER TABLE Events ADD COLUMN organizer STRING;"); 560 db.execSQL("UPDATE Events SET organizer=" 561 + "(SELECT attendeeEmail FROM Attendees WHERE " 562 + "Attendees.event_id = Events._id" 563 + " AND Attendees.attendeeRelationship=2);"); 564 oldVersion += 1; 565 } 566 } 567 568 private void dropTables(SQLiteDatabase db) { 569 db.execSQL("DROP TABLE IF EXISTS Calendars;"); 570 db.execSQL("DROP TABLE IF EXISTS Events;"); 571 db.execSQL("DROP TABLE IF EXISTS EventsRawTimes;"); 572 db.execSQL("DROP TABLE IF EXISTS Instances;"); 573 db.execSQL("DROP TABLE IF EXISTS CalendarMetaData;"); 574 db.execSQL("DROP TABLE IF EXISTS BusyBits;"); 575 db.execSQL("DROP TABLE IF EXISTS Attendees;"); 576 db.execSQL("DROP TABLE IF EXISTS Reminders;"); 577 db.execSQL("DROP TABLE IF EXISTS CalendarAlerts;"); 578 db.execSQL("DROP TABLE IF EXISTS ExtendedProperties;"); 579 } 580 581 @Override 582 public synchronized SQLiteDatabase getWritableDatabase() { 583 SQLiteDatabase db = super.getWritableDatabase(); 584 return db; 585 } 586 587 public SyncStateContentProviderHelper getSyncState() { 588 return mSyncState; 589 } 590 591 /** 592 * Schedule a calendar sync for the account. 593 * @param account the account for which to schedule a sync 594 * @param uploadChangesOnly if set, specify that the sync should only send 595 * up local changes. This is typically used for a local sync, a user override of 596 * too many deletions, or a sync after a calendar is unselected. 597 * @param url the url feed for the calendar to sync (may be null, in which case a poll of 598 * all feeds is done.) 599 */ 600 void scheduleSync(Account account, boolean uploadChangesOnly, String url) { 601 Bundle extras = new Bundle(); 602 if (uploadChangesOnly) { 603 extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, uploadChangesOnly); 604 } 605 if (url != null) { 606 extras.putString("feed", url); 607 extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 608 } 609 ContentResolver.requestSync(account, Calendar.Calendars.CONTENT_URI.getAuthority(), extras); 610 } 611 612 public void wipeData() { 613 SQLiteDatabase db = getWritableDatabase(); 614 615 db.execSQL("DELETE FROM Calendars;"); 616 db.execSQL("DELETE FROM Events;"); 617 db.execSQL("DELETE FROM EventsRawTimes;"); 618 db.execSQL("DELETE FROM Instances;"); 619 db.execSQL("DELETE FROM CalendarMetaData;"); 620 db.execSQL("DELETE FROM BusyBits;"); 621 db.execSQL("DELETE FROM Attendees;"); 622 db.execSQL("DELETE FROM Reminders;"); 623 db.execSQL("DELETE FROM CalendarAlerts;"); 624 db.execSQL("DELETE FROM ExtendedProperties;"); 625 } 626 627 public interface Views { 628 public static final String EVENTS = "view_events"; 629 } 630 631 public interface Tables { 632 public static final String EVENTS = "Events"; 633 public static final String CALENDARS = "Calendars"; 634 } 635 636 private static void createEventsView(SQLiteDatabase db) { 637 db.execSQL("DROP VIEW IF EXISTS " + Views.EVENTS + ";"); 638 String eventsSelect = "SELECT " 639 + Tables.EVENTS + "." + Calendar.Events._ID + " AS " + Calendar.Events._ID + "," 640 + Calendar.Events.HTML_URI + "," 641 + Calendar.Events.TITLE + "," 642 + Calendar.Events.DESCRIPTION + "," 643 + Calendar.Events.EVENT_LOCATION + "," 644 + Calendar.Events.STATUS + "," 645 + Calendar.Events.SELF_ATTENDEE_STATUS + "," 646 + Calendar.Events.COMMENTS_URI + "," 647 + Calendar.Events.DTSTART + "," 648 + Calendar.Events.DTEND + "," 649 + Calendar.Events.DURATION + "," 650 + Calendar.Events.EVENT_TIMEZONE + "," 651 + Calendar.Events.ALL_DAY + "," 652 + Calendar.Events.VISIBILITY + "," 653 + Calendar.Events.TIMEZONE + "," 654 + Calendar.Events.SELECTED + "," 655 + Calendar.Events.ACCESS_LEVEL + "," 656 + Calendar.Events.TRANSPARENCY + "," 657 + Calendar.Events.COLOR + "," 658 + Calendar.Events.HAS_ALARM + "," 659 + Calendar.Events.HAS_EXTENDED_PROPERTIES + "," 660 + Calendar.Events.RRULE + "," 661 + Calendar.Events.RDATE + "," 662 + Calendar.Events.EXRULE + "," 663 + Calendar.Events.EXDATE + "," 664 + Calendar.Events.ORIGINAL_EVENT + "," 665 + Calendar.Events.ORIGINAL_INSTANCE_TIME + "," 666 + Calendar.Events.ORIGINAL_ALL_DAY + "," 667 + Calendar.Events.LAST_DATE + "," 668 + Calendar.Events.HAS_ATTENDEE_DATA + "," 669 + Calendar.Events.CALENDAR_ID + "," 670 + Calendar.Events.GUESTS_CAN_INVITE_OTHERS + "," 671 + Calendar.Events.GUESTS_CAN_MODIFY + "," 672 + Calendar.Events.GUESTS_CAN_SEE_GUESTS + "," 673 + Calendar.Events.ORGANIZER + "," 674 + Calendar.Events.DELETED + "," 675 + Tables.EVENTS + "." + Calendar.Events._SYNC_ID 676 + " AS " + Calendar.Events._SYNC_ID + "," 677 + Tables.EVENTS + "." + Calendar.Events._SYNC_VERSION 678 + " AS " + Calendar.Events._SYNC_VERSION + "," 679 + Tables.EVENTS + "." + Calendar.Events._SYNC_DIRTY 680 + " AS " + Calendar.Events._SYNC_DIRTY + "," 681 + Tables.EVENTS + "." + Calendar.Events._SYNC_ACCOUNT 682 + " AS " + Calendar.Events._SYNC_ACCOUNT + "," 683 + Tables.EVENTS + "." + Calendar.Events._SYNC_ACCOUNT_TYPE 684 + " AS " + Calendar.Events._SYNC_ACCOUNT_TYPE + "," 685 + Tables.EVENTS + "." + Calendar.Events._SYNC_TIME 686 + " AS " + Calendar.Events._SYNC_TIME + "," 687 + Tables.EVENTS + "." + Calendar.Events._SYNC_LOCAL_ID 688 + " AS " + Calendar.Events._SYNC_LOCAL_ID + "," 689 + Calendar.Calendars.URL + "," 690 + Calendar.Calendars.OWNER_ACCOUNT 691 + " FROM " + Tables.EVENTS + " JOIN " + Tables.CALENDARS 692 + " ON (" + Tables.EVENTS + "." + Calendar.Events.CALENDAR_ID 693 + "=" + Tables.CALENDARS + "." + Calendar.Calendars._ID 694 + ")"; 695 696 db.execSQL("CREATE VIEW " + Views.EVENTS + " AS " + eventsSelect); 697 } 698} 699