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