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