BrowserProvider.java revision bd359cc6925f8d06489c93327732251f1fecf425
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.browser; 18 19import com.google.android.providers.GoogleSettings.Partner; 20 21import android.app.SearchManager; 22import android.content.ComponentName; 23import android.content.ContentProvider; 24import android.content.ContentUris; 25import android.content.ContentValues; 26import android.content.Context; 27import android.content.Intent; 28import android.content.SharedPreferences; 29import android.content.UriMatcher; 30import android.content.SharedPreferences.Editor; 31import android.database.AbstractCursor; 32import android.database.Cursor; 33import android.database.sqlite.SQLiteDatabase; 34import android.database.sqlite.SQLiteOpenHelper; 35import android.net.Uri; 36import android.preference.PreferenceManager; 37import android.provider.Browser; 38import android.server.search.SearchableInfo; 39import android.text.TextUtils; 40import android.text.util.Regex; 41import android.util.Log; 42import android.util.TypedValue; 43 44import java.util.Date; 45import java.util.regex.Matcher; 46import java.util.regex.Pattern; 47 48 49public class BrowserProvider extends ContentProvider { 50 51 private SQLiteOpenHelper mOpenHelper; 52 private static final String sDatabaseName = "browser.db"; 53 private static final String TAG = "BrowserProvider"; 54 private static final String ORDER_BY = "visits DESC, date DESC"; 55 56 private static final String PICASA_URL = "http://picasaweb.google.com/m/" + 57 "viewer?source=androidclient"; 58 59 private static final String[] TABLE_NAMES = new String[] { 60 "bookmarks", "searches" 61 }; 62 private static final String[] SUGGEST_PROJECTION = new String[] { 63 "_id", "url", "title", "bookmark" 64 }; 65 private static final String SUGGEST_SELECTION = 66 "url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?" 67 + " OR title LIKE ?"; 68 private String[] SUGGEST_ARGS = new String[5]; 69 70 // shared suggestion array index, make sure to match COLUMNS 71 private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1; 72 private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2; 73 private static final int SUGGEST_COLUMN_TEXT_1_ID = 3; 74 private static final int SUGGEST_COLUMN_TEXT_2_ID = 4; 75 private static final int SUGGEST_COLUMN_ICON_1_ID = 5; 76 private static final int SUGGEST_COLUMN_ICON_2_ID = 6; 77 private static final int SUGGEST_COLUMN_QUERY_ID = 7; 78 private static final int SUGGEST_COLUMN_FORMAT = 8; 79 80 // shared suggestion columns 81 private static final String[] COLUMNS = new String[] { 82 "_id", 83 SearchManager.SUGGEST_COLUMN_INTENT_ACTION, 84 SearchManager.SUGGEST_COLUMN_INTENT_DATA, 85 SearchManager.SUGGEST_COLUMN_TEXT_1, 86 SearchManager.SUGGEST_COLUMN_TEXT_2, 87 SearchManager.SUGGEST_COLUMN_ICON_1, 88 SearchManager.SUGGEST_COLUMN_ICON_2, 89 SearchManager.SUGGEST_COLUMN_QUERY, 90 SearchManager.SUGGEST_COLUMN_FORMAT}; 91 92 private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3; 93 private static final int MAX_SUGGESTION_LONG_ENTRIES = 6; 94 private static final String MAX_SUGGESTION_LONG_ENTRIES_STRING = 95 Integer.valueOf(MAX_SUGGESTION_LONG_ENTRIES).toString(); 96 97 // make sure that these match the index of TABLE_NAMES 98 private static final int URI_MATCH_BOOKMARKS = 0; 99 private static final int URI_MATCH_SEARCHES = 1; 100 // (id % 10) should match the table name index 101 private static final int URI_MATCH_BOOKMARKS_ID = 10; 102 private static final int URI_MATCH_SEARCHES_ID = 11; 103 // 104 private static final int URI_MATCH_SUGGEST = 20; 105 private static final int URI_MATCH_BOOKMARKS_SUGGEST = 21; 106 107 private static final UriMatcher URI_MATCHER; 108 109 static { 110 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); 111 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS], 112 URI_MATCH_BOOKMARKS); 113 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/#", 114 URI_MATCH_BOOKMARKS_ID); 115 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES], 116 URI_MATCH_SEARCHES); 117 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES] + "/#", 118 URI_MATCH_SEARCHES_ID); 119 URI_MATCHER.addURI("browser", SearchManager.SUGGEST_URI_PATH_QUERY, 120 URI_MATCH_SUGGEST); 121 URI_MATCHER.addURI("browser", 122 TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/" + SearchManager.SUGGEST_URI_PATH_QUERY, 123 URI_MATCH_BOOKMARKS_SUGGEST); 124 } 125 126 // 1 -> 2 add cache table 127 // 2 -> 3 update history table 128 // 3 -> 4 add passwords table 129 // 4 -> 5 add settings table 130 // 5 -> 6 ? 131 // 6 -> 7 ? 132 // 7 -> 8 drop proxy table 133 // 8 -> 9 drop settings table 134 // 9 -> 10 add form_urls and form_data 135 // 10 -> 11 add searches table 136 // 11 -> 12 modify cache table 137 // 12 -> 13 modify cache table 138 // 13 -> 14 correspond with Google Bookmarks schema 139 // 14 -> 15 move couple of tables to either browser private database or webview database 140 // 15 -> 17 Set it up for the SearchManager 141 // 17 -> 18 Added favicon in bookmarks table for Home shortcuts 142 // 18 -> 19 Remove labels table 143 private static final int DATABASE_VERSION = 19; 144 145 // Regular expression which matches http://, followed by some stuff, followed by 146 // optionally a trailing slash, all matched as separate groups. 147 private static final Pattern STRIP_URL_PATTERN = Pattern.compile("^(http://)(.*?)(/$)?"); 148 149 // The hex color string to be applied to urls of website suggestions, as derived from 150 // the current theme. This is not set until/unless beautifyUrl is called, at which point 151 // this variable caches the color value. 152 private static String mSearchUrlColorHex; 153 154 public BrowserProvider() { 155 } 156 157 158 private static CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) { 159 StringBuffer sb = new StringBuffer(); 160 int lastCharLoc = 0; 161 162 final String client_id = Partner.getString(context.getContentResolver(), Partner.CLIENT_ID); 163 164 for (int i = 0; i < srcString.length(); ++i) { 165 char c = srcString.charAt(i); 166 if (c == '{') { 167 sb.append(srcString.subSequence(lastCharLoc, i)); 168 lastCharLoc = i; 169 inner: 170 for (int j = i; j < srcString.length(); ++j) { 171 char k = srcString.charAt(j); 172 if (k == '}') { 173 String propertyKeyValue = srcString.subSequence(i + 1, j).toString(); 174 if (propertyKeyValue.equals("CLIENT_ID")) { 175 sb.append(client_id); 176 } else { 177 sb.append("unknown"); 178 } 179 lastCharLoc = j + 1; 180 i = j; 181 break inner; 182 } 183 } 184 } 185 } 186 if (srcString.length() - lastCharLoc > 0) { 187 // Put on the tail, if there is one 188 sb.append(srcString.subSequence(lastCharLoc, srcString.length())); 189 } 190 return sb; 191 } 192 193 private static class DatabaseHelper extends SQLiteOpenHelper { 194 private Context mContext; 195 196 public DatabaseHelper(Context context) { 197 super(context, sDatabaseName, null, DATABASE_VERSION); 198 mContext = context; 199 } 200 201 @Override 202 public void onCreate(SQLiteDatabase db) { 203 db.execSQL("CREATE TABLE bookmarks (" + 204 "_id INTEGER PRIMARY KEY," + 205 "title TEXT," + 206 "url TEXT," + 207 "visits INTEGER," + 208 "date LONG," + 209 "created LONG," + 210 "description TEXT," + 211 "bookmark INTEGER," + 212 "favicon BLOB DEFAULT NULL" + 213 ");"); 214 215 final CharSequence[] bookmarks = mContext.getResources() 216 .getTextArray(R.array.bookmarks); 217 int size = bookmarks.length; 218 try { 219 for (int i = 0; i < size; i = i + 2) { 220 CharSequence bookmarkDestination = replaceSystemPropertyInString(mContext, bookmarks[i + 1]); 221 db.execSQL("INSERT INTO bookmarks (title, url, visits, " + 222 "date, created, bookmark)" + " VALUES('" + 223 bookmarks[i] + "', '" + bookmarkDestination + 224 "', 0, 0, 0, 1);"); 225 } 226 } catch (ArrayIndexOutOfBoundsException e) { 227 } 228 229 db.execSQL("CREATE TABLE searches (" + 230 "_id INTEGER PRIMARY KEY," + 231 "search TEXT," + 232 "date LONG" + 233 ");"); 234 } 235 236 @Override 237 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 238 Log.w(TAG, "Upgrading database from version " + oldVersion + " to " 239 + newVersion + ", which will destroy all old data"); 240 if (oldVersion == 18) { 241 db.execSQL("DROP TABLE IF EXISTS labels"); 242 } else { 243 db.execSQL("DROP TABLE IF EXISTS bookmarks"); 244 db.execSQL("DROP TABLE IF EXISTS searches"); 245 onCreate(db); 246 } 247 } 248 } 249 250 @Override 251 public boolean onCreate() { 252 final Context context = getContext(); 253 mOpenHelper = new DatabaseHelper(context); 254 // we added "picasa web album" into default bookmarks for version 19. 255 // To avoid erasing the bookmark table, we added it explicitly for 256 // version 18 and 19 as in the other cases, we will erase the table. 257 if (DATABASE_VERSION == 18 || DATABASE_VERSION == 19) { 258 SharedPreferences p = PreferenceManager 259 .getDefaultSharedPreferences(context); 260 boolean fix = p.getBoolean("fix_picasa", true); 261 if (fix) { 262 fixPicasaBookmark(); 263 Editor ed = p.edit(); 264 ed.putBoolean("fix_picasa", false); 265 ed.commit(); 266 } 267 } 268 return true; 269 } 270 271 private void fixPicasaBookmark() { 272 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 273 Cursor cursor = db.rawQuery("SELECT _id FROM bookmarks WHERE " + 274 "bookmark = 1 AND url = ?", new String[] { PICASA_URL }); 275 try { 276 if (!cursor.moveToFirst()) { 277 // set "created" so that it will be on the top of the list 278 db.execSQL("INSERT INTO bookmarks (title, url, visits, " + 279 "date, created, bookmark)" + " VALUES('" + 280 getContext().getString(R.string.picasa) + "', '" 281 + PICASA_URL + "', 0, 0, " + new Date().getTime() 282 + ", 1);"); 283 } 284 } finally { 285 if (cursor != null) { 286 cursor.close(); 287 } 288 } 289 } 290 291 /* 292 * Subclass AbstractCursor so we can combine multiple Cursors and add 293 * "Google Search". 294 * Here are the rules. 295 * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus 296 * "Google Search"; 297 * 2. If bookmark/history entries are less than 298 * (MAX_SUGGESTION_SHORT_ENTRIES -1), we include Google suggest. 299 */ 300 private class MySuggestionCursor extends AbstractCursor { 301 private Cursor mHistoryCursor; 302 private Cursor mSuggestCursor; 303 private int mHistoryCount; 304 private int mSuggestionCount; 305 private boolean mBeyondCursor; 306 private String mString; 307 308 public MySuggestionCursor(Cursor hc, Cursor sc, String string) { 309 mHistoryCursor = hc; 310 mSuggestCursor = sc; 311 mHistoryCount = hc.getCount(); 312 mSuggestionCount = sc != null ? sc.getCount() : 0; 313 if (mSuggestionCount > (MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount)) { 314 mSuggestionCount = MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount; 315 } 316 mString = string; 317 mBeyondCursor = false; 318 } 319 320 @Override 321 public boolean onMove(int oldPosition, int newPosition) { 322 if (mHistoryCursor == null) { 323 return false; 324 } 325 if (mHistoryCount > newPosition) { 326 mHistoryCursor.moveToPosition(newPosition); 327 mBeyondCursor = false; 328 } else if (mHistoryCount + mSuggestionCount > newPosition) { 329 mSuggestCursor.moveToPosition(newPosition - mHistoryCount); 330 mBeyondCursor = false; 331 } else { 332 mBeyondCursor = true; 333 } 334 return true; 335 } 336 337 @Override 338 public int getCount() { 339 if (mString.length() > 0) { 340 return mHistoryCount + mSuggestionCount + 1; 341 } else { 342 return mHistoryCount + mSuggestionCount; 343 } 344 } 345 346 @Override 347 public String[] getColumnNames() { 348 return COLUMNS; 349 } 350 351 @Override 352 public String getString(int columnIndex) { 353 if ((mPos != -1 && mHistoryCursor != null)) { 354 switch(columnIndex) { 355 case SUGGEST_COLUMN_INTENT_ACTION_ID: 356 if (mHistoryCount > mPos) { 357 return Intent.ACTION_VIEW; 358 } else { 359 return Intent.ACTION_SEARCH; 360 } 361 362 case SUGGEST_COLUMN_INTENT_DATA_ID: 363 if (mHistoryCount > mPos) { 364 return mHistoryCursor.getString(1); 365 } else { 366 return null; 367 } 368 369 case SUGGEST_COLUMN_TEXT_1_ID: 370 if (mHistoryCount > mPos) { 371 return getHistoryTitle(); 372 } else if (!mBeyondCursor) { 373 return mSuggestCursor.getString(1); 374 } else { 375 return mString; 376 } 377 378 case SUGGEST_COLUMN_TEXT_2_ID: 379 if (mHistoryCount > mPos) { 380 return getHistorySubtitle(); 381 } else if (!mBeyondCursor) { 382 return mSuggestCursor.getString(2); 383 } else { 384 return getContext().getString(R.string.search_the_web); 385 } 386 387 case SUGGEST_COLUMN_ICON_1_ID: 388 if (mHistoryCount > mPos) { 389 if (mHistoryCursor.getInt(3) == 1) { 390 return Integer.valueOf( 391 R.drawable.ic_search_category_bookmark) 392 .toString(); 393 } else { 394 return Integer.valueOf( 395 R.drawable.ic_search_category_history) 396 .toString(); 397 } 398 } else { 399 return Integer.valueOf( 400 R.drawable.ic_search_category_suggest) 401 .toString(); 402 } 403 404 case SUGGEST_COLUMN_ICON_2_ID: 405 return "0"; 406 407 case SUGGEST_COLUMN_QUERY_ID: 408 if (mHistoryCount > mPos) { 409 return null; 410 } else if (!mBeyondCursor) { 411 return mSuggestCursor.getString(3); 412 } else { 413 return mString; 414 } 415 416 case SUGGEST_COLUMN_FORMAT: 417 return "html"; 418 } 419 } 420 return null; 421 } 422 423 @Override 424 public double getDouble(int column) { 425 throw new UnsupportedOperationException(); 426 } 427 428 @Override 429 public float getFloat(int column) { 430 throw new UnsupportedOperationException(); 431 } 432 433 @Override 434 public int getInt(int column) { 435 throw new UnsupportedOperationException(); 436 } 437 438 @Override 439 public long getLong(int column) { 440 if ((mPos != -1) && column == 0) { 441 return mPos; // use row# as the _Id 442 } 443 throw new UnsupportedOperationException(); 444 } 445 446 @Override 447 public short getShort(int column) { 448 throw new UnsupportedOperationException(); 449 } 450 451 @Override 452 public boolean isNull(int column) { 453 throw new UnsupportedOperationException(); 454 } 455 456 // TODO Temporary change, finalize after jq's changes go in 457 public void deactivate() { 458 if (mHistoryCursor != null) { 459 mHistoryCursor.deactivate(); 460 } 461 if (mSuggestCursor != null) { 462 mSuggestCursor.deactivate(); 463 } 464 super.deactivate(); 465 } 466 467 public boolean requery() { 468 return (mHistoryCursor != null ? mHistoryCursor.requery() : false) | 469 (mSuggestCursor != null ? mSuggestCursor.requery() : false); 470 } 471 472 // TODO Temporary change, finalize after jq's changes go in 473 public void close() { 474 super.close(); 475 if (mHistoryCursor != null) { 476 mHistoryCursor.close(); 477 mHistoryCursor = null; 478 } 479 if (mSuggestCursor != null) { 480 mSuggestCursor.close(); 481 mSuggestCursor = null; 482 } 483 } 484 485 /** 486 * Provides the title (text line 1) for a browser suggestion, which should be the 487 * webpage title. If the webpage title is empty, returns the stripped url instead. 488 * 489 * @return the title string to use 490 */ 491 private String getHistoryTitle() { 492 String title = mHistoryCursor.getString(2 /* webpage title */); 493 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) { 494 title = beautifyUrl(mHistoryCursor.getString(1 /* url */)); 495 } 496 return title; 497 } 498 499 /** 500 * Provides the subtitle (text line 2) for a browser suggestion, which should be the 501 * webpage url. If the webpage title is empty, then the url should go in the title 502 * instead, and the subtitle should be empty, so this would return null. 503 * 504 * @return the subtitle string to use, or null if none 505 */ 506 private String getHistorySubtitle() { 507 String title = mHistoryCursor.getString(2 /* webpage title */); 508 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) { 509 return null; 510 } else { 511 return beautifyUrl(mHistoryCursor.getString(1 /* url */)); 512 } 513 } 514 515 /** 516 * Strips "http://" from the beginning of a url and "/" from the end, 517 * and adds html formatting to make it green. 518 */ 519 private String beautifyUrl(String url) { 520 if (mSearchUrlColorHex == null) { 521 // Get the color used for this purpose from the current theme. 522 TypedValue colorValue = new TypedValue(); 523 getContext().getTheme().resolveAttribute( 524 com.android.internal.R.attr.textColorSearchUrl, colorValue, true); 525 int color = getContext().getResources().getColor(colorValue.resourceId); 526 527 // Convert the int color value into a hex string, and strip the first two 528 // characters which will be the alpha transparency (html doesn't want this). 529 mSearchUrlColorHex = Integer.toHexString(color).substring(2); 530 } 531 532 return "<font color=\"#" + mSearchUrlColorHex + "\">" + stripUrl(url) + "</font>"; 533 } 534 } 535 536 @Override 537 public Cursor query(Uri url, String[] projectionIn, String selection, 538 String[] selectionArgs, String sortOrder) 539 throws IllegalStateException { 540 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 541 542 int match = URI_MATCHER.match(url); 543 if (match == -1) { 544 throw new IllegalArgumentException("Unknown URL"); 545 } 546 547 if (match == URI_MATCH_SUGGEST || match == URI_MATCH_BOOKMARKS_SUGGEST) { 548 String suggestSelection; 549 String [] myArgs; 550 if (selectionArgs[0] == null || selectionArgs[0].equals("")) { 551 suggestSelection = null; 552 myArgs = null; 553 } else { 554 String like = selectionArgs[0] + "%"; 555 if (selectionArgs[0].startsWith("http") 556 || selectionArgs[0].startsWith("file")) { 557 myArgs = new String[1]; 558 myArgs[0] = like; 559 suggestSelection = selection; 560 } else { 561 SUGGEST_ARGS[0] = "http://" + like; 562 SUGGEST_ARGS[1] = "http://www." + like; 563 SUGGEST_ARGS[2] = "https://" + like; 564 SUGGEST_ARGS[3] = "https://www." + like; 565 // To match against titles. 566 SUGGEST_ARGS[4] = like; 567 myArgs = SUGGEST_ARGS; 568 suggestSelection = SUGGEST_SELECTION; 569 } 570 } 571 572 Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS], 573 SUGGEST_PROJECTION, suggestSelection, myArgs, null, null, 574 ORDER_BY, MAX_SUGGESTION_LONG_ENTRIES_STRING); 575 576 if (match == URI_MATCH_BOOKMARKS_SUGGEST 577 || Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) { 578 return new MySuggestionCursor(c, null, ""); 579 } else { 580 // get Google suggest if there is still space in the list 581 if (myArgs != null && myArgs.length > 1 582 && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) { 583 // TODO: This shouldn't be hard-coded. Instead, it should use the 584 // default web search provider. But the API for that is not implemented yet. 585 ComponentName googleSearchComponent = 586 new ComponentName("com.android.googlesearch", 587 "com.android.googlesearch.GoogleSearch"); 588 SearchableInfo si = 589 SearchManager.getSearchableInfo(googleSearchComponent, false); 590 Cursor sc = SearchManager.getSuggestions(getContext(), si, selectionArgs[0]); 591 return new MySuggestionCursor(c, sc, selectionArgs[0]); 592 } 593 return new MySuggestionCursor(c, null, selectionArgs[0]); 594 } 595 } 596 597 String[] projection = null; 598 if (projectionIn != null && projectionIn.length > 0) { 599 projection = new String[projectionIn.length + 1]; 600 System.arraycopy(projectionIn, 0, projection, 0, projectionIn.length); 601 projection[projectionIn.length] = "_id AS _id"; 602 } 603 604 StringBuilder whereClause = new StringBuilder(256); 605 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) { 606 whereClause.append("(_id = ").append(url.getPathSegments().get(1)) 607 .append(")"); 608 } 609 610 // Tack on the user's selection, if present 611 if (selection != null && selection.length() > 0) { 612 if (whereClause.length() > 0) { 613 whereClause.append(" AND "); 614 } 615 616 whereClause.append('('); 617 whereClause.append(selection); 618 whereClause.append(')'); 619 } 620 Cursor c = db.query(TABLE_NAMES[match % 10], projection, 621 whereClause.toString(), selectionArgs, null, null, sortOrder, 622 null); 623 c.setNotificationUri(getContext().getContentResolver(), url); 624 return c; 625 } 626 627 @Override 628 public String getType(Uri url) { 629 int match = URI_MATCHER.match(url); 630 switch (match) { 631 case URI_MATCH_BOOKMARKS: 632 return "vnd.android.cursor.dir/bookmark"; 633 634 case URI_MATCH_BOOKMARKS_ID: 635 return "vnd.android.cursor.item/bookmark"; 636 637 case URI_MATCH_SEARCHES: 638 return "vnd.android.cursor.dir/searches"; 639 640 case URI_MATCH_SEARCHES_ID: 641 return "vnd.android.cursor.item/searches"; 642 643 case URI_MATCH_SUGGEST: 644 return SearchManager.SUGGEST_MIME_TYPE; 645 646 default: 647 throw new IllegalArgumentException("Unknown URL"); 648 } 649 } 650 651 @Override 652 public Uri insert(Uri url, ContentValues initialValues) { 653 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 654 655 int match = URI_MATCHER.match(url); 656 Uri uri = null; 657 switch (match) { 658 case URI_MATCH_BOOKMARKS: { 659 // Insert into the bookmarks table 660 long rowID = db.insert(TABLE_NAMES[URI_MATCH_BOOKMARKS], "url", 661 initialValues); 662 if (rowID > 0) { 663 uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI, 664 rowID); 665 } 666 break; 667 } 668 669 case URI_MATCH_SEARCHES: { 670 // Insert into the searches table 671 long rowID = db.insert(TABLE_NAMES[URI_MATCH_SEARCHES], "url", 672 initialValues); 673 if (rowID > 0) { 674 uri = ContentUris.withAppendedId(Browser.SEARCHES_URI, 675 rowID); 676 } 677 break; 678 } 679 680 default: 681 throw new IllegalArgumentException("Unknown URL"); 682 } 683 684 if (uri == null) { 685 throw new IllegalArgumentException("Unknown URL"); 686 } 687 getContext().getContentResolver().notifyChange(uri, null); 688 return uri; 689 } 690 691 @Override 692 public int delete(Uri url, String where, String[] whereArgs) { 693 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 694 695 int match = URI_MATCHER.match(url); 696 if (match == -1 || match == URI_MATCH_SUGGEST) { 697 throw new IllegalArgumentException("Unknown URL"); 698 } 699 700 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) { 701 StringBuilder sb = new StringBuilder(); 702 if (where != null && where.length() > 0) { 703 sb.append("( "); 704 sb.append(where); 705 sb.append(" ) AND "); 706 } 707 sb.append("_id = "); 708 sb.append(url.getPathSegments().get(1)); 709 where = sb.toString(); 710 } 711 712 int count = db.delete(TABLE_NAMES[match % 10], where, whereArgs); 713 getContext().getContentResolver().notifyChange(url, null); 714 return count; 715 } 716 717 @Override 718 public int update(Uri url, ContentValues values, String where, 719 String[] whereArgs) { 720 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 721 722 int match = URI_MATCHER.match(url); 723 if (match == -1 || match == URI_MATCH_SUGGEST) { 724 throw new IllegalArgumentException("Unknown URL"); 725 } 726 727 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) { 728 StringBuilder sb = new StringBuilder(); 729 if (where != null && where.length() > 0) { 730 sb.append("( "); 731 sb.append(where); 732 sb.append(" ) AND "); 733 } 734 sb.append("_id = "); 735 sb.append(url.getPathSegments().get(1)); 736 where = sb.toString(); 737 } 738 739 int ret = db.update(TABLE_NAMES[match % 10], values, where, whereArgs); 740 getContext().getContentResolver().notifyChange(url, null); 741 return ret; 742 } 743 744 /** 745 * Strips the provided url of preceding "http://" and any trailing "/". Does not 746 * strip "https://". If the provided string cannot be stripped, the original string 747 * is returned. 748 * 749 * TODO: Put this in TextUtils to be used by other packages doing something similar. 750 * 751 * @param url a url to strip, like "http://www.google.com/" 752 * @return a stripped url like "www.google.com", or the original string if it could 753 * not be stripped 754 */ 755 private static String stripUrl(String url) { 756 if (url == null) return null; 757 Matcher m = STRIP_URL_PATTERN.matcher(url); 758 if (m.matches() && m.groupCount() == 3) { 759 return m.group(2); 760 } else { 761 return url; 762 } 763 } 764 765} 766