blob: df52d80e5b9c057f27eaef4e9ed694bd1658d1fe [file] [log] [blame]
The Android Open Source Project0c908882009-03-03 19:32:16 -08001/*
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
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -070019import com.google.android.providers.GoogleSettings.Partner;
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070020
The Android Open Source Project0c908882009-03-03 19:32:16 -080021import 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;
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070028import android.content.SharedPreferences;
The Android Open Source Project0c908882009-03-03 19:32:16 -080029import android.content.UriMatcher;
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070030import android.content.SharedPreferences.Editor;
The Android Open Source Project0c908882009-03-03 19:32:16 -080031import android.database.AbstractCursor;
32import android.database.Cursor;
The Android Open Source Project0c908882009-03-03 19:32:16 -080033import android.database.sqlite.SQLiteDatabase;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010034import android.database.sqlite.SQLiteOpenHelper;
The Android Open Source Project0c908882009-03-03 19:32:16 -080035import android.net.Uri;
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070036import android.preference.PreferenceManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080037import android.provider.Browser;
The Android Open Source Project0c908882009-03-03 19:32:16 -080038import android.server.search.SearchableInfo;
Mike LeBeau21beb132009-05-13 14:57:50 -070039import android.text.TextUtils;
The Android Open Source Project0c908882009-03-03 19:32:16 -080040import android.text.util.Regex;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010041import android.util.Log;
Mike LeBeauc42f81b2009-05-14 15:04:19 -070042import android.util.TypedValue;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010043
44import java.util.Date;
Mike LeBeauc42f81b2009-05-14 15:04:19 -070045import java.util.regex.Matcher;
46import java.util.regex.Pattern;
The Android Open Source Project0c908882009-03-03 19:32:16 -080047
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -070048
The Android Open Source Project0c908882009-03-03 19:32:16 -080049public 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
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070056 private static final String PICASA_URL = "http://picasaweb.google.com/m/" +
57 "viewer?source=androidclient";
58
The Android Open Source Project0c908882009-03-03 19:32:16 -080059 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 =
Leon Scrogginsbd359cc2009-05-26 15:57:35 -040066 "url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?"
67 + " OR title LIKE ?";
68 private String[] SUGGEST_ARGS = new String[5];
The Android Open Source Project0c908882009-03-03 19:32:16 -080069
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;
Mike LeBeau1ef26a32009-05-13 20:11:00 -070078 private static final int SUGGEST_COLUMN_FORMAT = 8;
The Android Open Source Project0c908882009-03-03 19:32:16 -080079
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,
Mike LeBeau1ef26a32009-05-13 20:11:00 -070089 SearchManager.SUGGEST_COLUMN_QUERY,
90 SearchManager.SUGGEST_COLUMN_FORMAT};
The Android Open Source Project0c908882009-03-03 19:32:16 -080091
92 private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3;
93 private static final int MAX_SUGGESTION_LONG_ENTRIES = 6;
Leon Scroggins31887fd2009-05-18 16:58:08 -040094 private static final String MAX_SUGGESTION_LONG_ENTRIES_STRING =
95 Integer.valueOf(MAX_SUGGESTION_LONG_ENTRIES).toString();
The Android Open Source Project0c908882009-03-03 19:32:16 -080096
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;
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100105 private static final int URI_MATCH_BOOKMARKS_SUGGEST = 21;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800106
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);
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100121 URI_MATCHER.addURI("browser",
122 TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/" + SearchManager.SUGGEST_URI_PATH_QUERY,
123 URI_MATCH_BOOKMARKS_SUGGEST);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800124 }
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;
Mike LeBeauc42f81b2009-05-14 15:04:19 -0700144
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;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800153
154 public BrowserProvider() {
155 }
156
157
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700158 private static CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800159 StringBuffer sb = new StringBuffer();
160 int lastCharLoc = 0;
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700161
162 final String client_id = Partner.getString(context.getContentResolver(), Partner.CLIENT_ID);
163
The Android Open Source Project0c908882009-03-03 19:32:16 -0800164 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();
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700174 if (propertyKeyValue.equals("CLIENT_ID")) {
175 sb.append(client_id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800176 } else {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700177 sb.append("unknown");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800178 }
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) {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700220 CharSequence bookmarkDestination = replaceSystemPropertyInString(mContext, bookmarks[i + 1]);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800221 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() {
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700252 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 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800268 return true;
269 }
270
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700271 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
The Android Open Source Project0c908882009-03-03 19:32:16 -0800291 /*
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 }
Mike LeBeau21beb132009-05-13 14:57:50 -0700350
The Android Open Source Project0c908882009-03-03 19:32:16 -0800351 @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) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700371 return getHistoryTitle();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800372 } else if (!mBeyondCursor) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700373 return mSuggestCursor.getString(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800374 } else {
375 return mString;
376 }
377
378 case SUGGEST_COLUMN_TEXT_2_ID:
379 if (mHistoryCount > mPos) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700380 return getHistorySubtitle();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800381 } else if (!mBeyondCursor) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700382 return mSuggestCursor.getString(2);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800383 } else {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700384 return getContext().getString(R.string.search_the_web);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800385 }
386
387 case SUGGEST_COLUMN_ICON_1_ID:
388 if (mHistoryCount > mPos) {
389 if (mHistoryCursor.getInt(3) == 1) {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400390 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800391 R.drawable.ic_search_category_bookmark)
392 .toString();
393 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400394 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800395 R.drawable.ic_search_category_history)
396 .toString();
397 }
398 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400399 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800400 R.drawable.ic_search_category_suggest)
401 .toString();
402 }
403
404 case SUGGEST_COLUMN_ICON_2_ID:
Leon Scroggins31887fd2009-05-18 16:58:08 -0400405 return "0";
The Android Open Source Project0c908882009-03-03 19:32:16 -0800406
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 }
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700415
416 case SUGGEST_COLUMN_FORMAT:
417 return "html";
The Android Open Source Project0c908882009-03-03 19:32:16 -0800418 }
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 }
Mike LeBeau21beb132009-05-13 14:57:50 -0700484
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 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700489 * @return the title string to use
490 */
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700491 private String getHistoryTitle() {
492 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700493 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700494 title = beautifyUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700495 }
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 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700504 * @return the subtitle string to use, or null if none
505 */
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700506 private String getHistorySubtitle() {
507 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700508 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
509 return null;
510 } else {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700511 return beautifyUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700512 }
513 }
514
515 /**
Mike LeBeauc42f81b2009-05-14 15:04:19 -0700516 * Strips "http://" from the beginning of a url and "/" from the end,
517 * and adds html formatting to make it green.
Mike LeBeau21beb132009-05-13 14:57:50 -0700518 */
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700519 private String beautifyUrl(String url) {
Mike LeBeauc42f81b2009-05-14 15:04:19 -0700520 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);
Mike LeBeau21beb132009-05-13 14:57:50 -0700530 }
Mike LeBeauc42f81b2009-05-14 15:04:19 -0700531
532 return "<font color=\"#" + mSearchUrlColorHex + "\">" + stripUrl(url) + "</font>";
Mike LeBeau21beb132009-05-13 14:57:50 -0700533 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800534 }
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
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100547 if (match == URI_MATCH_SUGGEST || match == URI_MATCH_BOOKMARKS_SUGGEST) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800548 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] + "%";
Leon Scrogginsfaa15db2009-04-03 10:16:06 -0700555 if (selectionArgs[0].startsWith("http")
556 || selectionArgs[0].startsWith("file")) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800557 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;
Leon Scrogginsbd359cc2009-05-26 15:57:35 -0400565 // To match against titles.
566 SUGGEST_ARGS[4] = like;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800567 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,
Leon Scroggins31887fd2009-05-18 16:58:08 -0400574 ORDER_BY, MAX_SUGGESTION_LONG_ENTRIES_STRING);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800575
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100576 if (match == URI_MATCH_BOOKMARKS_SUGGEST
577 || Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800578 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)) {
Bjorn Bringert24e1ec62009-04-29 16:10:43 +0100583 // 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]);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800592 }
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 }
Mike LeBeauc42f81b2009-05-14 15:04:19 -0700743
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
The Android Open Source Project0c908882009-03-03 19:32:16 -0800765}