BrowserProvider2.java revision b0de9ba4fe3acdcd0766eb07175044c12a54bd82
1/* 2 * Copyright (C) 2010 he 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.browser.provider; 18 19import com.android.browser.R; 20import com.android.internal.content.SyncStateContentProviderHelper; 21 22import android.content.ContentResolver; 23import android.content.ContentUris; 24import android.content.ContentValues; 25import android.content.Context; 26import android.content.UriMatcher; 27import android.database.Cursor; 28import android.database.DatabaseUtils; 29import android.database.sqlite.SQLiteDatabase; 30import android.database.sqlite.SQLiteOpenHelper; 31import android.database.sqlite.SQLiteQueryBuilder; 32import android.net.Uri; 33import android.provider.BrowserContract; 34import android.provider.BrowserContract.Bookmarks; 35import android.provider.BrowserContract.ChromeSyncColumns; 36import android.provider.BrowserContract.History; 37import android.provider.BrowserContract.Searches; 38import android.provider.BrowserContract.SyncState; 39import android.provider.ContactsContract.RawContacts; 40import android.provider.SyncStateContract; 41import android.text.TextUtils; 42 43import java.util.HashMap; 44 45public class BrowserProvider2 extends SQLiteContentProvider { 46 47 static final Uri LEGACY_BROWSER_AUTHORITY_URI = Uri.parse("browser"); 48 49 static final String TABLE_BOOKMARKS = "bookmarks"; 50 static final String TABLE_HISTORY = "history"; 51 static final String TABLE_SEARCHES = "searches"; 52 static final String TABLE_SYNC_STATE = "syncstate"; 53 54 static final String HISTORY_JOIN_BOOKMARKS = 55 "history LEFT OUTER JOIN bookmarks ON (history.url = bookmarks.url)"; 56 57 static final int BOOKMARKS = 1000; 58 static final int BOOKMARKS_ID = 1001; 59 static final int BOOKMARKS_FOLDER = 1002; 60 static final int BOOKMARKS_FOLDER_ID = 1003; 61 62 static final int HISTORY = 2000; 63 static final int HISTORY_ID = 2001; 64 65 static final int SEARCHES = 3000; 66 static final int SEARCHES_ID = 3001; 67 68 static final int SYNCSTATE = 4000; 69 static final int SYNCSTATE_ID = 4001; 70 71 static final long FIXED_ID_CHROME_ROOT = 1; 72 static final long FIXED_ID_BOOKMARKS = 2; 73 static final long FIXED_ID_BOOKMARKS_BAR = 3; 74 static final long FIXED_ID_OTHER_BOOKMARKS = 4; 75 76 static final String DEFAULT_BOOKMARKS_SORT_ORDER = "position ASC, _id ASC"; 77 78 static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); 79 80 static final HashMap<String, String> BOOKMARKS_PROJECTION_MAP = new HashMap<String, String>(); 81 static final HashMap<String, String> OTHER_BOOKMARKS_PROJECTION_MAP = new HashMap<String, String>(); 82 static final HashMap<String, String> HISTORY_PROJECTION_MAP = new HashMap<String, String>(); 83 static final HashMap<String, String> SEARCHES_PROJECTION_MAP = new HashMap<String, String>(); 84 static final HashMap<String, String> SYNC_STATE_PROJECTION_MAP = new HashMap<String, String>(); 85 86 static { 87 final UriMatcher matcher = URI_MATCHER; 88 matcher.addURI(BrowserContract.AUTHORITY, "bookmarks", BOOKMARKS); 89 matcher.addURI(BrowserContract.AUTHORITY, "bookmarks/#", BOOKMARKS_ID); 90 matcher.addURI(BrowserContract.AUTHORITY, "bookmarks/folder", BOOKMARKS_FOLDER); 91 matcher.addURI(BrowserContract.AUTHORITY, "bookmarks/folder/#", BOOKMARKS_FOLDER_ID); 92 matcher.addURI(BrowserContract.AUTHORITY, "history", HISTORY); 93 matcher.addURI(BrowserContract.AUTHORITY, "history/#", HISTORY_ID); 94 matcher.addURI(BrowserContract.AUTHORITY, "searches", SEARCHES); 95 matcher.addURI(BrowserContract.AUTHORITY, "searches/#", SEARCHES_ID); 96 matcher.addURI(BrowserContract.AUTHORITY, "syncstate", SYNCSTATE); 97 matcher.addURI(BrowserContract.AUTHORITY, "syncstate/#", SYNCSTATE_ID); 98 99 // Common BookmarkColumns 100 HashMap<String, String> bookmarksColumns = new HashMap(); 101 bookmarksColumns.put(Bookmarks.TITLE, Bookmarks.TITLE); 102 bookmarksColumns.put(Bookmarks.URL, Bookmarks.URL); 103 bookmarksColumns.put(Bookmarks.FAVICON, Bookmarks.FAVICON); 104 bookmarksColumns.put(Bookmarks.THUMBNAIL, Bookmarks.THUMBNAIL); 105 bookmarksColumns.put(Bookmarks.TOUCH_ICON, Bookmarks.TOUCH_ICON); 106 107 // Bookmarks 108 HashMap<String, String> map = BOOKMARKS_PROJECTION_MAP; 109 map.putAll(bookmarksColumns); 110 map.put(Bookmarks._ID, TABLE_BOOKMARKS + "._id AS _id"); 111 map.put(Bookmarks.IS_FOLDER, Bookmarks.IS_FOLDER); 112 map.put(Bookmarks.PARENT, Bookmarks.PARENT); 113 map.put(Bookmarks.POSITION, Bookmarks.POSITION); 114 map.put(Bookmarks.IS_DELETED, Bookmarks.IS_DELETED); 115 map.put(Bookmarks.ACCOUNT_NAME, Bookmarks.ACCOUNT_NAME); 116 map.put(Bookmarks.ACCOUNT_TYPE, Bookmarks.ACCOUNT_TYPE); 117 map.put(Bookmarks.SOURCE_ID, Bookmarks.SOURCE_ID); 118 map.put(Bookmarks.VERSION, Bookmarks.VERSION); 119 map.put(Bookmarks.DIRTY, Bookmarks.DIRTY); 120 map.put(Bookmarks.SYNC1, Bookmarks.SYNC1); 121 map.put(Bookmarks.SYNC2, Bookmarks.SYNC2); 122 map.put(Bookmarks.SYNC3, Bookmarks.SYNC3); 123 map.put(Bookmarks.SYNC4, Bookmarks.SYNC4); 124 map.put(Bookmarks.SYNC5, Bookmarks.SYNC5); 125 126 // Other bookmarks 127 OTHER_BOOKMARKS_PROJECTION_MAP.putAll(BOOKMARKS_PROJECTION_MAP); 128 OTHER_BOOKMARKS_PROJECTION_MAP.put(Bookmarks.POSITION, 129 Long.toString(Long.MAX_VALUE) + " AS " + Bookmarks.POSITION); 130 131 // History 132 map = HISTORY_PROJECTION_MAP; 133 map.putAll(bookmarksColumns); 134 map.put(History._ID, TABLE_HISTORY + "._id AS _id"); 135 map.put(History.DATE_CREATED, History.DATE_CREATED); 136 map.put(History.DATE_LAST_VISITED, History.DATE_LAST_VISITED); 137 map.put(History.VISITS, History.VISITS); 138 139 // Sync state 140 map = SYNC_STATE_PROJECTION_MAP; 141 map.put(SyncState._ID, SyncState._ID); 142 map.put(SyncState.ACCOUNT_NAME, SyncState.ACCOUNT_NAME); 143 map.put(SyncState.ACCOUNT_TYPE, SyncState.ACCOUNT_TYPE); 144 map.put(SyncState.DATA, SyncState.DATA); 145 } 146 147 DatabaseHelper mOpenHelper; 148 SyncStateContentProviderHelper mSyncHelper = new SyncStateContentProviderHelper(); 149 150 final class DatabaseHelper extends SQLiteOpenHelper { 151 static final String DATABASE_NAME = "browser2.db"; 152 static final int DATABASE_VERSION = 11; 153 public DatabaseHelper(Context context) { 154 super(context, DATABASE_NAME, null, DATABASE_VERSION); 155 } 156 157 @Override 158 public void onCreate(SQLiteDatabase db) { 159 db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" + 160 Bookmarks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 161 Bookmarks.TITLE + " TEXT," + 162 Bookmarks.URL + " TEXT," + 163 Bookmarks.FAVICON + " BLOB," + 164 Bookmarks.THUMBNAIL + " BLOB," + 165 Bookmarks.TOUCH_ICON + " BLOB," + 166 Bookmarks.IS_FOLDER + " INTEGER NOT NULL DEFAULT 0," + 167 Bookmarks.PARENT + " INTEGER NOT NULL DEFAULT 0," + 168 Bookmarks.POSITION + " INTEGER NOT NULL," + 169 Bookmarks.INSERT_AFTER + " INTEGER," + 170 Bookmarks.IS_DELETED + " INTEGER NOT NULL DEFAULT 0," + 171 Bookmarks.ACCOUNT_NAME + " TEXT," + 172 Bookmarks.ACCOUNT_TYPE + " TEXT," + 173 Bookmarks.SOURCE_ID + " TEXT," + 174 Bookmarks.VERSION + " INTEGER NOT NULL DEFAULT 1," + 175 Bookmarks.DIRTY + " INTEGER NOT NULL DEFAULT 0," + 176 Bookmarks.SYNC1 + " TEXT," + 177 Bookmarks.SYNC2 + " TEXT," + 178 Bookmarks.SYNC3 + " TEXT," + 179 Bookmarks.SYNC4 + " TEXT," + 180 Bookmarks.SYNC5 + " TEXT" + 181 ");"); 182 183 // TODO indices 184 185 createDefaultBookmarks(db); 186 187 db.execSQL("CREATE TABLE " + TABLE_HISTORY + "(" + 188 History._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 189 History.URL + " TEXT NOT NULL," + 190 History.DATE_CREATED + " INTEGER," + 191 History.DATE_LAST_VISITED + " INTEGER," + 192 History.VISITS + " INTEGER NOT NULL DEFAULT 0" + 193 ");"); 194 195 db.execSQL("CREATE TABLE " + TABLE_SEARCHES + " (" + 196 Searches._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 197 Searches.SEARCH + " TEXT," + 198 Searches.DATE + " LONG" + 199 ");"); 200 201 mSyncHelper.createDatabase(db); 202 } 203 204 @Override 205 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 206 // TODO write upgrade logic 207 db.execSQL("DROP TABLE IF EXISTS " + TABLE_BOOKMARKS); 208 db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY); 209 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SEARCHES); 210 onCreate(db); 211 } 212 213 @Override 214 public void onOpen(SQLiteDatabase db) { 215 mSyncHelper.onDatabaseOpened(db); 216 } 217 218 private void createDefaultBookmarks(SQLiteDatabase db) { 219 ContentValues values = new ContentValues(); 220 // TODO figure out how to deal with localization for the defaults 221 222 // Chrome sync root folder 223 values.put(Bookmarks._ID, FIXED_ID_CHROME_ROOT); 224 values.put(ChromeSyncColumns.SERVER_UNIQUE, ChromeSyncColumns.FOLDER_NAME_ROOT); 225 values.put(Bookmarks.TITLE, "Google Chrome"); 226 values.put(Bookmarks.PARENT, 0); 227 values.put(Bookmarks.POSITION, 0); 228 values.put(Bookmarks.IS_FOLDER, true); 229 values.put(Bookmarks.DIRTY, true); 230 db.insertOrThrow(TABLE_BOOKMARKS, null, values); 231 232 // Bookmarks folder 233 values.put(Bookmarks._ID, FIXED_ID_BOOKMARKS); 234 values.put(ChromeSyncColumns.SERVER_UNIQUE, ChromeSyncColumns.FOLDER_NAME_BOOKMARKS); 235 values.put(Bookmarks.TITLE, "Bookmarks"); 236 values.put(Bookmarks.PARENT, FIXED_ID_CHROME_ROOT); 237 values.put(Bookmarks.POSITION, 0); 238 values.put(Bookmarks.IS_FOLDER, true); 239 values.put(Bookmarks.DIRTY, true); 240 db.insertOrThrow(TABLE_BOOKMARKS, null, values); 241 242 // Bookmarks Bar folder 243 values.clear(); 244 values.put(Bookmarks._ID, FIXED_ID_BOOKMARKS_BAR); 245 values.put(ChromeSyncColumns.SERVER_UNIQUE, 246 ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR); 247 values.put(Bookmarks.TITLE, "Bookmarks Bar"); 248 values.put(Bookmarks.PARENT, FIXED_ID_BOOKMARKS); 249 values.put(Bookmarks.POSITION, 0); 250 values.put(Bookmarks.IS_FOLDER, true); 251 values.put(Bookmarks.DIRTY, true); 252 db.insertOrThrow(TABLE_BOOKMARKS, null, values); 253 254 // Other Bookmarks folder 255 values.clear(); 256 values.put(Bookmarks._ID, FIXED_ID_OTHER_BOOKMARKS); 257 values.put(ChromeSyncColumns.SERVER_UNIQUE, 258 ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS); 259 values.put(Bookmarks.TITLE, "Other Bookmarks"); 260 values.put(Bookmarks.PARENT, FIXED_ID_BOOKMARKS); 261 values.put(Bookmarks.POSITION, 1000); 262 values.put(Bookmarks.IS_FOLDER, true); 263 values.put(Bookmarks.DIRTY, true); 264 db.insertOrThrow(TABLE_BOOKMARKS, null, values); 265 266 addDefaultBookmarks(db, FIXED_ID_BOOKMARKS_BAR); 267 268 // TODO remove this testing code 269 db.execSQL("INSERT INTO bookmarks (" + 270 Bookmarks.TITLE + ", " + 271 Bookmarks.URL + ", " + 272 Bookmarks.IS_FOLDER + "," + 273 Bookmarks.PARENT + "," + 274 Bookmarks.POSITION + 275 ") VALUES (" + 276 "'Google Reader', " + 277 "'http://reader.google.com', " + 278 "0," + 279 Long.toString(FIXED_ID_OTHER_BOOKMARKS) + "," + 280 0 + 281 ");"); 282 } 283 284 private void addDefaultBookmarks(SQLiteDatabase db, long parentId) { 285 final CharSequence[] bookmarks = getContext().getResources().getTextArray( 286 R.array.bookmarks); 287 int size = bookmarks.length; 288 try { 289 for (int i = 0; i < size; i = i + 2) { 290 CharSequence bookmarkDestination = replaceSystemPropertyInString(getContext(), 291 bookmarks[i + 1]); 292 db.execSQL("INSERT INTO bookmarks (" + 293 Bookmarks.TITLE + ", " + 294 Bookmarks.URL + ", " + 295 Bookmarks.IS_FOLDER + "," + 296 Bookmarks.PARENT + "," + 297 Bookmarks.POSITION + 298 ") VALUES (" + 299 "'" + bookmarks[i] + "', " + 300 "'" + bookmarkDestination + "', " + 301 "0," + 302 Long.toString(parentId) + "," + 303 Integer.toString(i) + 304 ");"); 305 } 306 } catch (ArrayIndexOutOfBoundsException e) { 307 } 308 } 309 310 // XXX: This is a major hack to remove our dependency on gsf constants and 311 // its content provider. http://b/issue?id=2425179 312 private String getClientId(ContentResolver cr) { 313 String ret = "android-google"; 314 Cursor c = null; 315 try { 316 c = cr.query(Uri.parse("content://com.google.settings/partner"), 317 new String[] { "value" }, "name='client_id'", null, null); 318 if (c != null && c.moveToNext()) { 319 ret = c.getString(0); 320 } 321 } catch (RuntimeException ex) { 322 // fall through to return the default 323 } finally { 324 if (c != null) { 325 c.close(); 326 } 327 } 328 return ret; 329 } 330 331 private CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) { 332 StringBuffer sb = new StringBuffer(); 333 int lastCharLoc = 0; 334 335 final String client_id = getClientId(context.getContentResolver()); 336 337 for (int i = 0; i < srcString.length(); ++i) { 338 char c = srcString.charAt(i); 339 if (c == '{') { 340 sb.append(srcString.subSequence(lastCharLoc, i)); 341 lastCharLoc = i; 342 inner: 343 for (int j = i; j < srcString.length(); ++j) { 344 char k = srcString.charAt(j); 345 if (k == '}') { 346 String propertyKeyValue = srcString.subSequence(i + 1, j).toString(); 347 if (propertyKeyValue.equals("CLIENT_ID")) { 348 sb.append(client_id); 349 } else { 350 sb.append("unknown"); 351 } 352 lastCharLoc = j + 1; 353 i = j; 354 break inner; 355 } 356 } 357 } 358 } 359 if (srcString.length() - lastCharLoc > 0) { 360 // Put on the tail, if there is one 361 sb.append(srcString.subSequence(lastCharLoc, srcString.length())); 362 } 363 return sb; 364 } 365 } 366 367 @Override 368 public SQLiteOpenHelper getDatabaseHelper(Context context) { 369 synchronized (this) { 370 if (mOpenHelper == null) { 371 mOpenHelper = new DatabaseHelper(context); 372 } 373 return mOpenHelper; 374 } 375 } 376 377 @Override 378 public boolean isCallerSyncAdapter(Uri uri) { 379 return uri.getBooleanQueryParameter(BrowserContract.CALLER_IS_SYNCADAPTER, false); 380 } 381 382 @Override 383 public void notifyChange(boolean callerIsSyncAdapter) { 384 ContentResolver resolver = getContext().getContentResolver(); 385 resolver.notifyChange(BrowserContract.AUTHORITY_URI, null, !callerIsSyncAdapter); 386 resolver.notifyChange(LEGACY_BROWSER_AUTHORITY_URI, null, !callerIsSyncAdapter); 387 } 388 389 @Override 390 public String getType(Uri uri) { 391 final int match = URI_MATCHER.match(uri); 392 switch (match) { 393 case BOOKMARKS: 394 return Bookmarks.CONTENT_TYPE; 395 case BOOKMARKS_ID: 396 return Bookmarks.CONTENT_ITEM_TYPE; 397 case HISTORY: 398 return History.CONTENT_TYPE; 399 case HISTORY_ID: 400 return History.CONTENT_ITEM_TYPE; 401 case SEARCHES: 402 return Searches.CONTENT_TYPE; 403 case SEARCHES_ID: 404 return Searches.CONTENT_ITEM_TYPE; 405// case SUGGEST: 406// return SearchManager.SUGGEST_MIME_TYPE; 407 } 408 return null; 409 } 410 411 @Override 412 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 413 String sortOrder) { 414 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 415 final int match = URI_MATCHER.match(uri); 416 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 417 switch (match) { 418 case BOOKMARKS_FOLDER_ID: 419 case BOOKMARKS_ID: 420 case BOOKMARKS: { 421 // Only show deleted bookmarks if requested to do so 422 if (!uri.getBooleanQueryParameter(Bookmarks.QUERY_PARAMETER_SHOW_DELETED, false)) { 423 selection = DatabaseUtils.concatenateWhere( 424 Bookmarks.IS_DELETED + "=0", selection); 425 } 426 427 if (match == BOOKMARKS_ID) { 428 // Tack on the ID of the specific bookmark requested 429 selection = DatabaseUtils.concatenateWhere( 430 TABLE_BOOKMARKS + "." + Bookmarks._ID + "=?", selection); 431 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 432 new String[] { Long.toString(ContentUris.parseId(uri)) }); 433 } else if (match == BOOKMARKS_FOLDER_ID) { 434 // Tack on the ID of the specific folder requested 435 selection = DatabaseUtils.concatenateWhere( 436 TABLE_BOOKMARKS + "." + Bookmarks.PARENT + "=?", selection); 437 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 438 new String[] { Long.toString(ContentUris.parseId(uri)) }); 439 } 440 441 if (TextUtils.isEmpty(sortOrder)) { 442 sortOrder = DEFAULT_BOOKMARKS_SORT_ORDER; 443 } 444 445 qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP); 446 qb.setTables(TABLE_BOOKMARKS); 447 break; 448 } 449 450 case BOOKMARKS_FOLDER: { 451 // Don't allow selections to be applied to the default folder 452 if (!TextUtils.isEmpty(selection) || selectionArgs != null) { 453 throw new UnsupportedOperationException( 454 "selections aren't supported on this URI"); 455 } 456 457 qb.setTables(TABLE_BOOKMARKS); 458 qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP); 459 String bookmarksBarQuery = qb.buildQuery(projection, 460 Bookmarks.PARENT + "=?", 461 null, null, null, null, null); 462 463 qb.setProjectionMap(OTHER_BOOKMARKS_PROJECTION_MAP); 464 String otherBookmarksQuery = qb.buildQuery(projection, 465 Bookmarks._ID + "=?", 466 null, null, null, null, null); 467 468 String query = qb.buildUnionQuery( 469 new String[] { bookmarksBarQuery, otherBookmarksQuery }, 470 DEFAULT_BOOKMARKS_SORT_ORDER, null); 471 472 return db.rawQuery(query, new String[] { 473 Long.toString(FIXED_ID_BOOKMARKS_BAR), 474 Long.toString(FIXED_ID_OTHER_BOOKMARKS)}); 475 } 476 477 case HISTORY_ID: { 478 selection = DatabaseUtils.concatenateWhere( 479 TABLE_HISTORY + "._id=?", selection); 480 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 481 new String[] { Long.toString(ContentUris.parseId(uri)) }); 482 // fall through 483 } 484 case HISTORY: { 485 qb.setProjectionMap(HISTORY_PROJECTION_MAP); 486 qb.setTables(HISTORY_JOIN_BOOKMARKS); 487 break; 488 } 489 490 case SYNCSTATE: { 491 return mSyncHelper.query(db, projection, selection, selectionArgs, sortOrder); 492 } 493 494 case SYNCSTATE_ID: { 495 selection = appendAccountToSelection(uri, selection); 496 String selectionWithId = 497 (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ") 498 + (selection == null ? "" : " AND (" + selection + ")"); 499 return mSyncHelper.query(db, projection, selectionWithId, selectionArgs, sortOrder); 500 } 501 502 default: { 503 throw new UnsupportedOperationException("Unknown URL " + uri.toString()); 504 } 505 } 506 507 Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); 508 cursor.setNotificationUri(getContext().getContentResolver(), BrowserContract.AUTHORITY_URI); 509 return cursor; 510 } 511 512 @Override 513 public int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, 514 boolean callerIsSyncAdapter) { 515 final int match = URI_MATCHER.match(uri); 516 final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 517 switch (match) { 518 case BOOKMARKS_ID: 519 case BOOKMARKS: { 520 //TODO cascade deletes down from folders 521 if (!callerIsSyncAdapter) { 522 // If the caller isn't a sync adapter just go through and update all the 523 // bookmarks to have the deleted flag set. 524 ContentValues values = new ContentValues(); 525 values.put(Bookmarks.IS_DELETED, 1); 526 return updateInTransaction(uri, values, selection, selectionArgs, 527 callerIsSyncAdapter); 528 } else { 529 // Sync adapters are allowed to actually delete things 530 if (match == BOOKMARKS_ID) { 531 selection = DatabaseUtils.concatenateWhere(selection, 532 TABLE_BOOKMARKS + "._id=?"); 533 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 534 new String[] { Long.toString(ContentUris.parseId(uri)) }); 535 } 536 return db.delete(TABLE_BOOKMARKS, selection, selectionArgs); 537 } 538 } 539 540 case HISTORY_ID: { 541 selection = DatabaseUtils.concatenateWhere(selection, TABLE_HISTORY + "._id=?"); 542 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 543 new String[] { Long.toString(ContentUris.parseId(uri)) }); 544 // fall through 545 } 546 case HISTORY: { 547 return db.delete(TABLE_HISTORY, selection, selectionArgs); 548 } 549 550 case SEARCHES_ID: { 551 selection = DatabaseUtils.concatenateWhere(selection, TABLE_SEARCHES + "._id=?"); 552 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 553 new String[] { Long.toString(ContentUris.parseId(uri)) }); 554 // fall through 555 } 556 case SEARCHES: { 557 return db.delete(TABLE_SEARCHES, selection, selectionArgs); 558 } 559 560 case SYNCSTATE: { 561 return mSyncHelper.delete(db, selection, selectionArgs); 562 } 563 case SYNCSTATE_ID: { 564 String selectionWithId = 565 (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ") 566 + (selection == null ? "" : " AND (" + selection + ")"); 567 return mSyncHelper.delete(db, selectionWithId, selectionArgs); 568 } 569 } 570 throw new UnsupportedOperationException("Unknown update URI " + uri); 571 } 572 573 @Override 574 public Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { 575 final int match = URI_MATCHER.match(uri); 576 final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 577 long id = -1; 578 switch (match) { 579 case BOOKMARKS: { 580 // Mark rows dirty if they're not coming from a sync adapater 581 if (!callerIsSyncAdapter) { 582 values.put(Bookmarks.DIRTY, 1); 583 } 584 585 // If no parent is set default to the "Bookmarks Bar" folder 586 if (!values.containsKey(Bookmarks.PARENT)) { 587 values.put(Bookmarks.PARENT, FIXED_ID_BOOKMARKS_BAR); 588 } 589 590 // If no position is requested put the bookmark at the beginning of the list 591 if (!values.containsKey(Bookmarks.POSITION)) { 592 values.put(Bookmarks.POSITION, Long.toString(Long.MIN_VALUE)); 593 } 594 595 id = db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.DIRTY, values); 596 break; 597 } 598 599 case HISTORY: { 600 id = db.insertOrThrow(TABLE_HISTORY, History.VISITS, values); 601 break; 602 } 603 604 case SEARCHES: { 605 id = db.insertOrThrow(TABLE_SEARCHES, Searches.SEARCH, values); 606 break; 607 } 608 609 case SYNCSTATE: { 610 id = mSyncHelper.insert(mDb, values); 611 break; 612 } 613 614 default: { 615 throw new UnsupportedOperationException("Unknown insert URI " + uri); 616 } 617 } 618 619 if (id >= 0) { 620 return ContentUris.withAppendedId(uri, id); 621 } else { 622 return null; 623 } 624 } 625 626 @Override 627 public int updateInTransaction(Uri uri, ContentValues values, String selection, 628 String[] selectionArgs, boolean callerIsSyncAdapter) { 629 final int match = URI_MATCHER.match(uri); 630 final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 631 switch (match) { 632 case BOOKMARKS_ID: { 633 // Mark the bookmark dirty if the caller isn't a sync adapter 634 if (!callerIsSyncAdapter) { 635 values = new ContentValues(values); 636 values.put(Bookmarks.DIRTY, 1); 637 } 638 selection = DatabaseUtils.concatenateWhere(selection, 639 TABLE_BOOKMARKS + "._id=?"); 640 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 641 new String[] { Long.toString(ContentUris.parseId(uri)) }); 642 return db.update(TABLE_BOOKMARKS, values, selection, selectionArgs); 643 } 644 645 case BOOKMARKS: { 646 if (!callerIsSyncAdapter) { 647 values = new ContentValues(values); 648 values.put(Bookmarks.DIRTY, 1); 649 } 650 return updateBookmarksInTransaction(values, selection, selectionArgs); 651 } 652 653 case HISTORY_ID: { 654 selection = DatabaseUtils.concatenateWhere(selection, TABLE_HISTORY + "._id=?"); 655 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 656 new String[] { Long.toString(ContentUris.parseId(uri)) }); 657 // fall through 658 } 659 case HISTORY: { 660 return db.update(TABLE_HISTORY, values, selection, selectionArgs); 661 } 662 663 case SEARCHES_ID: { 664 selection = DatabaseUtils.concatenateWhere(selection, TABLE_SEARCHES + "._id=?"); 665 selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, 666 new String[] { Long.toString(ContentUris.parseId(uri)) }); 667 // fall through 668 } 669 case SEARCHES: { 670 return db.update(TABLE_SEARCHES, values, selection, selectionArgs); 671 } 672 673 case SYNCSTATE: { 674 return mSyncHelper.update(mDb, values, 675 appendAccountToSelection(uri, selection), selectionArgs); 676 } 677 678 case SYNCSTATE_ID: { 679 selection = appendAccountToSelection(uri, selection); 680 String selectionWithId = 681 (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ") 682 + (selection == null ? "" : " AND (" + selection + ")"); 683 return mSyncHelper.update(mDb, values, 684 selectionWithId, selectionArgs); 685 } 686 } 687 throw new UnsupportedOperationException("Unknown update URI " + uri); 688 } 689 690 /** 691 * Does a query to find the matching bookmarks and updates each one with the provided values. 692 */ 693 private int updateBookmarksInTransaction(ContentValues values, String selection, 694 String[] selectionArgs) { 695 int count = 0; 696 final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 697 Cursor cursor = query(Bookmarks.CONTENT_URI, new String[] { Bookmarks._ID }, 698 selection, selectionArgs, null); 699 try { 700 String[] args = new String[1]; 701 while (cursor.moveToNext()) { 702 args[0] = cursor.getString(0); 703 count += db.update(TABLE_BOOKMARKS, values, "_id=?", args); 704 } 705 } finally { 706 if (cursor != null) cursor.close(); 707 } 708 return count; 709 } 710 711 private String appendAccountToSelection(Uri uri, String selection) { 712 final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME); 713 final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE); 714 715 final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType); 716 if (partialUri) { 717 // Throw when either account is incomplete 718 throw new IllegalArgumentException( 719 "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE for " + uri); 720 } 721 722 // Accounts are valid by only checking one parameter, since we've 723 // already ruled out partial accounts. 724 final boolean validAccount = !TextUtils.isEmpty(accountName); 725 if (validAccount) { 726 StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "=" 727 + DatabaseUtils.sqlEscapeString(accountName) + " AND " 728 + RawContacts.ACCOUNT_TYPE + "=" 729 + DatabaseUtils.sqlEscapeString(accountType)); 730 if (!TextUtils.isEmpty(selection)) { 731 selectionSb.append(" AND ("); 732 selectionSb.append(selection); 733 selectionSb.append(')'); 734 } 735 return selectionSb.toString(); 736 } else { 737 return selection; 738 } 739 } 740} 741