blob: bf1f9d5b33ced2f5c504842bd6b399a14e0a4c52 [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
The Android Open Source Project0c908882009-03-03 19:32:16 -080019import android.app.SearchManager;
Bjorn Bringertabc3ac82009-12-04 12:59:14 +000020import android.app.SearchableInfo;
Christopher Tateb6a65442010-03-05 15:47:48 -080021import android.app.backup.BackupManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080022import android.content.ComponentName;
23import android.content.ContentProvider;
Christopher Tatef0c36f72009-07-28 15:24:05 -070024import android.content.ContentResolver;
The Android Open Source Project0c908882009-03-03 19:32:16 -080025import android.content.ContentUris;
26import android.content.ContentValues;
27import android.content.Context;
28import android.content.Intent;
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070029import android.content.SharedPreferences;
The Android Open Source Project0c908882009-03-03 19:32:16 -080030import android.content.UriMatcher;
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070031import android.content.SharedPreferences.Editor;
Satish Sampath565505b2009-05-29 15:37:27 +010032import android.content.pm.PackageManager;
33import android.content.pm.ResolveInfo;
The Android Open Source Project0c908882009-03-03 19:32:16 -080034import android.database.AbstractCursor;
Leon Scroggins62b71f72009-06-12 17:51:22 -040035import android.database.ContentObserver;
The Android Open Source Project0c908882009-03-03 19:32:16 -080036import android.database.Cursor;
The Android Open Source Project0c908882009-03-03 19:32:16 -080037import android.database.sqlite.SQLiteDatabase;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010038import android.database.sqlite.SQLiteOpenHelper;
The Android Open Source Project0c908882009-03-03 19:32:16 -080039import android.net.Uri;
Leon Scroggins62b71f72009-06-12 17:51:22 -040040import android.os.Handler;
Andrei Popescu93bea962010-03-23 15:04:36 +000041import android.os.Process;
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070042import android.preference.PreferenceManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080043import android.provider.Browser;
Leon Scroggins62b71f72009-06-12 17:51:22 -040044import android.provider.Settings;
Christopher Tatef0c36f72009-07-28 15:24:05 -070045import android.provider.Browser.BookmarkColumns;
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -050046import android.speech.RecognizerResultsIntent;
Mike LeBeau21beb132009-05-13 14:57:50 -070047import android.text.TextUtils;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010048import android.util.Log;
Dianne Hackborn385effd2010-02-24 20:03:04 -080049import android.util.Patterns;
Mike LeBeauc42f81b2009-05-14 15:04:19 -070050import android.util.TypedValue;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010051
Dan Egnor5ee906c2009-11-18 12:11:49 -080052
Andrei Popescu1b20b9d2009-09-21 18:49:42 +010053import java.io.File;
54import java.io.FilenameFilter;
Leon Scroggins58d56c62010-01-28 15:12:40 -050055import java.util.ArrayList;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010056import java.util.Date;
Mike LeBeauc42f81b2009-05-14 15:04:19 -070057import java.util.regex.Matcher;
58import java.util.regex.Pattern;
The Android Open Source Project0c908882009-03-03 19:32:16 -080059
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -070060
The Android Open Source Project0c908882009-03-03 19:32:16 -080061public class BrowserProvider extends ContentProvider {
62
63 private SQLiteOpenHelper mOpenHelper;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070064 private BackupManager mBackupManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080065 private static final String sDatabaseName = "browser.db";
66 private static final String TAG = "BrowserProvider";
67 private static final String ORDER_BY = "visits DESC, date DESC";
68
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070069 private static final String PICASA_URL = "http://picasaweb.google.com/m/" +
70 "viewer?source=androidclient";
71
The Android Open Source Project0c908882009-03-03 19:32:16 -080072 private static final String[] TABLE_NAMES = new String[] {
Bjorn Bringerta7611812010-03-24 11:12:02 +000073 "bookmarks", "searches"
The Android Open Source Project0c908882009-03-03 19:32:16 -080074 };
75 private static final String[] SUGGEST_PROJECTION = new String[] {
Leon Scrogginsb4464432009-11-25 12:37:50 -050076 "_id", "url", "title", "bookmark", "user_entered"
The Android Open Source Project0c908882009-03-03 19:32:16 -080077 };
Satish Sampath565505b2009-05-29 15:37:27 +010078 private static final String SUGGEST_SELECTION =
Leon Scrogginsb4464432009-11-25 12:37:50 -050079 "(url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?"
80 + " OR title LIKE ?) AND (bookmark = 1 OR user_entered = 1)";
Leon Scrogginsbd359cc2009-05-26 15:57:35 -040081 private String[] SUGGEST_ARGS = new String[5];
The Android Open Source Project0c908882009-03-03 19:32:16 -080082
83 // shared suggestion array index, make sure to match COLUMNS
84 private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1;
85 private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2;
86 private static final int SUGGEST_COLUMN_TEXT_1_ID = 3;
87 private static final int SUGGEST_COLUMN_TEXT_2_ID = 4;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +000088 private static final int SUGGEST_COLUMN_TEXT_2_URL_ID = 5;
89 private static final int SUGGEST_COLUMN_ICON_1_ID = 6;
90 private static final int SUGGEST_COLUMN_ICON_2_ID = 7;
91 private static final int SUGGEST_COLUMN_QUERY_ID = 8;
Bjorn Bringert04851702009-09-22 10:36:01 +010092 private static final int SUGGEST_COLUMN_INTENT_EXTRA_DATA = 9;
The Android Open Source Project0c908882009-03-03 19:32:16 -080093
94 // shared suggestion columns
95 private static final String[] COLUMNS = new String[] {
96 "_id",
97 SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
98 SearchManager.SUGGEST_COLUMN_INTENT_DATA,
99 SearchManager.SUGGEST_COLUMN_TEXT_1,
100 SearchManager.SUGGEST_COLUMN_TEXT_2,
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000101 SearchManager.SUGGEST_COLUMN_TEXT_2_URL,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800102 SearchManager.SUGGEST_COLUMN_ICON_1,
103 SearchManager.SUGGEST_COLUMN_ICON_2,
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700104 SearchManager.SUGGEST_COLUMN_QUERY,
Bjorn Bringert04851702009-09-22 10:36:01 +0100105 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA};
The Android Open Source Project0c908882009-03-03 19:32:16 -0800106
107 private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3;
108 private static final int MAX_SUGGESTION_LONG_ENTRIES = 6;
Leon Scroggins31887fd2009-05-18 16:58:08 -0400109 private static final String MAX_SUGGESTION_LONG_ENTRIES_STRING =
110 Integer.valueOf(MAX_SUGGESTION_LONG_ENTRIES).toString();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800111
112 // make sure that these match the index of TABLE_NAMES
113 private static final int URI_MATCH_BOOKMARKS = 0;
114 private static final int URI_MATCH_SEARCHES = 1;
115 // (id % 10) should match the table name index
116 private static final int URI_MATCH_BOOKMARKS_ID = 10;
117 private static final int URI_MATCH_SEARCHES_ID = 11;
118 //
119 private static final int URI_MATCH_SUGGEST = 20;
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100120 private static final int URI_MATCH_BOOKMARKS_SUGGEST = 21;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800121
122 private static final UriMatcher URI_MATCHER;
123
124 static {
125 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
126 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS],
127 URI_MATCH_BOOKMARKS);
128 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/#",
129 URI_MATCH_BOOKMARKS_ID);
130 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES],
131 URI_MATCH_SEARCHES);
132 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES] + "/#",
133 URI_MATCH_SEARCHES_ID);
134 URI_MATCHER.addURI("browser", SearchManager.SUGGEST_URI_PATH_QUERY,
135 URI_MATCH_SUGGEST);
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100136 URI_MATCHER.addURI("browser",
137 TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/" + SearchManager.SUGGEST_URI_PATH_QUERY,
138 URI_MATCH_BOOKMARKS_SUGGEST);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800139 }
140
141 // 1 -> 2 add cache table
142 // 2 -> 3 update history table
143 // 3 -> 4 add passwords table
144 // 4 -> 5 add settings table
145 // 5 -> 6 ?
146 // 6 -> 7 ?
147 // 7 -> 8 drop proxy table
148 // 8 -> 9 drop settings table
149 // 9 -> 10 add form_urls and form_data
150 // 10 -> 11 add searches table
151 // 11 -> 12 modify cache table
152 // 12 -> 13 modify cache table
153 // 13 -> 14 correspond with Google Bookmarks schema
154 // 14 -> 15 move couple of tables to either browser private database or webview database
155 // 15 -> 17 Set it up for the SearchManager
156 // 17 -> 18 Added favicon in bookmarks table for Home shortcuts
157 // 18 -> 19 Remove labels table
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400158 // 19 -> 20 Added thumbnail
Patrick Scott3918d442009-08-04 13:22:29 -0400159 // 20 -> 21 Added touch_icon
Grace Kloba6b52a552009-09-03 16:29:56 -0700160 // 21 -> 22 Remove "clientid"
Leon Scrogginsb4464432009-11-25 12:37:50 -0500161 // 22 -> 23 Added user_entered
162 private static final int DATABASE_VERSION = 23;
Satish Sampath565505b2009-05-29 15:37:27 +0100163
Mike LeBeauc42f81b2009-05-14 15:04:19 -0700164 // Regular expression which matches http://, followed by some stuff, followed by
165 // optionally a trailing slash, all matched as separate groups.
166 private static final Pattern STRIP_URL_PATTERN = Pattern.compile("^(http://)(.*?)(/$)?");
Satish Sampath565505b2009-05-29 15:37:27 +0100167
Bjorn Bringertd8b0ad22009-06-22 10:36:29 +0100168 private SearchManager mSearchManager;
169
The Android Open Source Project0c908882009-03-03 19:32:16 -0800170 public BrowserProvider() {
171 }
Satish Sampath565505b2009-05-29 15:37:27 +0100172
Patrick Scott43914692010-02-19 10:10:10 -0500173 // XXX: This is a major hack to remove our dependency on gsf constants and
174 // its content provider. http://b/issue?id=2425179
175 static String getClientId(ContentResolver cr) {
176 String ret = "android-google";
177 Cursor c = null;
178 try {
179 c = cr.query(Uri.parse("content://com.google.settings/partner"),
180 new String[] { "value" }, "name='client_id'", null, null);
181 if (c != null && c.moveToNext()) {
182 ret = c.getString(0);
183 }
184 } catch (RuntimeException ex) {
185 // fall through to return the default
186 } finally {
187 if (c != null) {
188 c.close();
189 }
190 }
191 return ret;
192 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800193
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700194 private static CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800195 StringBuffer sb = new StringBuffer();
196 int lastCharLoc = 0;
Satish Sampath565505b2009-05-29 15:37:27 +0100197
Patrick Scott43914692010-02-19 10:10:10 -0500198 final String client_id = getClientId(context.getContentResolver());
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700199
The Android Open Source Project0c908882009-03-03 19:32:16 -0800200 for (int i = 0; i < srcString.length(); ++i) {
201 char c = srcString.charAt(i);
202 if (c == '{') {
203 sb.append(srcString.subSequence(lastCharLoc, i));
204 lastCharLoc = i;
205 inner:
206 for (int j = i; j < srcString.length(); ++j) {
207 char k = srcString.charAt(j);
208 if (k == '}') {
209 String propertyKeyValue = srcString.subSequence(i + 1, j).toString();
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700210 if (propertyKeyValue.equals("CLIENT_ID")) {
211 sb.append(client_id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800212 } else {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700213 sb.append("unknown");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800214 }
215 lastCharLoc = j + 1;
216 i = j;
217 break inner;
218 }
219 }
220 }
221 }
222 if (srcString.length() - lastCharLoc > 0) {
223 // Put on the tail, if there is one
224 sb.append(srcString.subSequence(lastCharLoc, srcString.length()));
225 }
226 return sb;
227 }
228
229 private static class DatabaseHelper extends SQLiteOpenHelper {
230 private Context mContext;
231
232 public DatabaseHelper(Context context) {
233 super(context, sDatabaseName, null, DATABASE_VERSION);
234 mContext = context;
235 }
236
237 @Override
238 public void onCreate(SQLiteDatabase db) {
239 db.execSQL("CREATE TABLE bookmarks (" +
240 "_id INTEGER PRIMARY KEY," +
241 "title TEXT," +
242 "url TEXT," +
243 "visits INTEGER," +
244 "date LONG," +
245 "created LONG," +
246 "description TEXT," +
247 "bookmark INTEGER," +
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400248 "favicon BLOB DEFAULT NULL," +
Patrick Scott3918d442009-08-04 13:22:29 -0400249 "thumbnail BLOB DEFAULT NULL," +
Leon Scrogginsb4464432009-11-25 12:37:50 -0500250 "touch_icon BLOB DEFAULT NULL," +
251 "user_entered INTEGER" +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800252 ");");
253
254 final CharSequence[] bookmarks = mContext.getResources()
255 .getTextArray(R.array.bookmarks);
256 int size = bookmarks.length;
257 try {
258 for (int i = 0; i < size; i = i + 2) {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700259 CharSequence bookmarkDestination = replaceSystemPropertyInString(mContext, bookmarks[i + 1]);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800260 db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
261 "date, created, bookmark)" + " VALUES('" +
Satish Sampath565505b2009-05-29 15:37:27 +0100262 bookmarks[i] + "', '" + bookmarkDestination +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800263 "', 0, 0, 0, 1);");
264 }
265 } catch (ArrayIndexOutOfBoundsException e) {
266 }
267
268 db.execSQL("CREATE TABLE searches (" +
269 "_id INTEGER PRIMARY KEY," +
270 "search TEXT," +
271 "date LONG" +
272 ");");
273 }
274
275 @Override
276 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
277 Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400278 + newVersion);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800279 if (oldVersion == 18) {
280 db.execSQL("DROP TABLE IF EXISTS labels");
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400281 }
282 if (oldVersion <= 19) {
283 db.execSQL("ALTER TABLE bookmarks ADD COLUMN thumbnail BLOB DEFAULT NULL;");
Patrick Scott3918d442009-08-04 13:22:29 -0400284 }
285 if (oldVersion < 21) {
286 db.execSQL("ALTER TABLE bookmarks ADD COLUMN touch_icon BLOB DEFAULT NULL;");
Grace Kloba6b52a552009-09-03 16:29:56 -0700287 }
288 if (oldVersion < 22) {
289 db.execSQL("DELETE FROM bookmarks WHERE (bookmark = 0 AND url LIKE \"%.google.%client=ms-%\")");
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100290 removeGears();
Leon Scrogginsb4464432009-11-25 12:37:50 -0500291 }
292 if (oldVersion < 23) {
293 db.execSQL("ALTER TABLE bookmarks ADD COLUMN user_entered INTEGER;");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800294 } else {
295 db.execSQL("DROP TABLE IF EXISTS bookmarks");
296 db.execSQL("DROP TABLE IF EXISTS searches");
297 onCreate(db);
298 }
299 }
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100300
301 private void removeGears() {
Andrei Popescu93bea962010-03-23 15:04:36 +0000302 new Thread() {
303 @Override
304 public void run() {
305 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100306 String browserDataDirString = mContext.getApplicationInfo().dataDir;
307 final String appPluginsDirString = "app_plugins";
308 final String gearsPrefix = "gears";
309 File appPluginsDir = new File(browserDataDirString + File.separator
310 + appPluginsDirString);
311 if (!appPluginsDir.exists()) {
Andrei Popescu93bea962010-03-23 15:04:36 +0000312 return;
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100313 }
314 // Delete the Gears plugin files
315 File[] gearsFiles = appPluginsDir.listFiles(new FilenameFilter() {
316 public boolean accept(File dir, String filename) {
317 return filename.startsWith(gearsPrefix);
318 }
319 });
320 for (int i = 0; i < gearsFiles.length; ++i) {
321 if (gearsFiles[i].isDirectory()) {
322 deleteDirectory(gearsFiles[i]);
323 } else {
324 gearsFiles[i].delete();
325 }
326 }
327 // Delete the Gears data files
328 File gearsDataDir = new File(browserDataDirString + File.separator
329 + gearsPrefix);
330 if (!gearsDataDir.exists()) {
Andrei Popescu93bea962010-03-23 15:04:36 +0000331 return;
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100332 }
333 deleteDirectory(gearsDataDir);
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100334 }
335
336 private void deleteDirectory(File currentDir) {
337 File[] files = currentDir.listFiles();
338 for (int i = 0; i < files.length; ++i) {
339 if (files[i].isDirectory()) {
340 deleteDirectory(files[i]);
341 }
342 files[i].delete();
343 }
344 currentDir.delete();
345 }
Andrei Popescu93bea962010-03-23 15:04:36 +0000346 }.start();
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100347 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800348 }
349
350 @Override
351 public boolean onCreate() {
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700352 final Context context = getContext();
353 mOpenHelper = new DatabaseHelper(context);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700354 mBackupManager = new BackupManager(context);
Satish Sampath565505b2009-05-29 15:37:27 +0100355 // we added "picasa web album" into default bookmarks for version 19.
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700356 // To avoid erasing the bookmark table, we added it explicitly for
357 // version 18 and 19 as in the other cases, we will erase the table.
358 if (DATABASE_VERSION == 18 || DATABASE_VERSION == 19) {
359 SharedPreferences p = PreferenceManager
360 .getDefaultSharedPreferences(context);
361 boolean fix = p.getBoolean("fix_picasa", true);
362 if (fix) {
363 fixPicasaBookmark();
364 Editor ed = p.edit();
365 ed.putBoolean("fix_picasa", false);
366 ed.commit();
367 }
368 }
Bjorn Bringertd8b0ad22009-06-22 10:36:29 +0100369 mSearchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
Leon Scroggins62b71f72009-06-12 17:51:22 -0400370 mShowWebSuggestionsSettingChangeObserver
371 = new ShowWebSuggestionsSettingChangeObserver();
372 context.getContentResolver().registerContentObserver(
373 Settings.System.getUriFor(
374 Settings.System.SHOW_WEB_SUGGESTIONS),
375 true, mShowWebSuggestionsSettingChangeObserver);
376 updateShowWebSuggestions();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800377 return true;
378 }
379
Leon Scroggins62b71f72009-06-12 17:51:22 -0400380 /**
381 * This Observer will ensure that if the user changes the system
382 * setting of whether to display web suggestions, we will
383 * change accordingly.
384 */
385 /* package */ class ShowWebSuggestionsSettingChangeObserver
386 extends ContentObserver {
387 public ShowWebSuggestionsSettingChangeObserver() {
388 super(new Handler());
389 }
390
391 @Override
392 public void onChange(boolean selfChange) {
393 updateShowWebSuggestions();
394 }
395 }
396
397 private ShowWebSuggestionsSettingChangeObserver
398 mShowWebSuggestionsSettingChangeObserver;
399
400 // If non-null, then the system is set to show web suggestions,
401 // and this is the SearchableInfo to use to get them.
402 private SearchableInfo mSearchableInfo;
403
404 /**
405 * Check the system settings to see whether web suggestions are
406 * allowed. If so, store the SearchableInfo to grab suggestions
407 * while the user is typing.
408 */
409 private void updateShowWebSuggestions() {
410 mSearchableInfo = null;
411 Context context = getContext();
412 if (Settings.System.getInt(context.getContentResolver(),
413 Settings.System.SHOW_WEB_SUGGESTIONS,
414 1 /* default on */) == 1) {
Bjorn Bringert32747542010-02-18 21:59:21 +0000415 ComponentName webSearchComponent = mSearchManager.getWebSearchActivity();
416 if (webSearchComponent != null) {
417 mSearchableInfo = mSearchManager.getSearchableInfo(webSearchComponent);
Leon Scroggins62b71f72009-06-12 17:51:22 -0400418 }
419 }
420 }
421
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700422 private void fixPicasaBookmark() {
423 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
424 Cursor cursor = db.rawQuery("SELECT _id FROM bookmarks WHERE " +
425 "bookmark = 1 AND url = ?", new String[] { PICASA_URL });
426 try {
427 if (!cursor.moveToFirst()) {
428 // set "created" so that it will be on the top of the list
429 db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
430 "date, created, bookmark)" + " VALUES('" +
431 getContext().getString(R.string.picasa) + "', '"
432 + PICASA_URL + "', 0, 0, " + new Date().getTime()
433 + ", 1);");
434 }
435 } finally {
436 if (cursor != null) {
437 cursor.close();
438 }
439 }
440 }
441
The Android Open Source Project0c908882009-03-03 19:32:16 -0800442 /*
443 * Subclass AbstractCursor so we can combine multiple Cursors and add
Grace Kloba391df7c2010-03-01 19:51:49 -0800444 * "Search the web".
The Android Open Source Project0c908882009-03-03 19:32:16 -0800445 * Here are the rules.
Satish Sampath565505b2009-05-29 15:37:27 +0100446 * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
Grace Kloba391df7c2010-03-01 19:51:49 -0800447 * "Search the web";
448 * 2. If bookmark/history entries has a match, "Search the web" shows up at
449 * the second place. Otherwise, "Search the web" shows up at the first
450 * place.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800451 */
452 private class MySuggestionCursor extends AbstractCursor {
453 private Cursor mHistoryCursor;
454 private Cursor mSuggestCursor;
455 private int mHistoryCount;
456 private int mSuggestionCount;
Grace Klobad3992d42010-01-28 11:44:38 -0800457 private boolean mIncludeWebSearch;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800458 private String mString;
Satish Sampath565505b2009-05-29 15:37:27 +0100459 private int mSuggestText1Id;
460 private int mSuggestText2Id;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000461 private int mSuggestText2UrlId;
Satish Sampath565505b2009-05-29 15:37:27 +0100462 private int mSuggestQueryId;
Bjorn Bringert04851702009-09-22 10:36:01 +0100463 private int mSuggestIntentExtraDataId;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800464
465 public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
466 mHistoryCursor = hc;
467 mSuggestCursor = sc;
468 mHistoryCount = hc.getCount();
469 mSuggestionCount = sc != null ? sc.getCount() : 0;
470 if (mSuggestionCount > (MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount)) {
471 mSuggestionCount = MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount;
472 }
473 mString = string;
Grace Klobad3992d42010-01-28 11:44:38 -0800474 mIncludeWebSearch = string.length() > 0;
Satish Sampath565505b2009-05-29 15:37:27 +0100475
476 // Some web suggest providers only give suggestions and have no description string for
477 // items. The order of the result columns may be different as well. So retrieve the
478 // column indices for the fields we need now and check before using below.
479 if (mSuggestCursor == null) {
480 mSuggestText1Id = -1;
481 mSuggestText2Id = -1;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000482 mSuggestText2UrlId = -1;
Satish Sampath565505b2009-05-29 15:37:27 +0100483 mSuggestQueryId = -1;
Bjorn Bringert04851702009-09-22 10:36:01 +0100484 mSuggestIntentExtraDataId = -1;
Satish Sampath565505b2009-05-29 15:37:27 +0100485 } else {
486 mSuggestText1Id = mSuggestCursor.getColumnIndex(
487 SearchManager.SUGGEST_COLUMN_TEXT_1);
488 mSuggestText2Id = mSuggestCursor.getColumnIndex(
489 SearchManager.SUGGEST_COLUMN_TEXT_2);
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000490 mSuggestText2UrlId = mSuggestCursor.getColumnIndex(
491 SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
Satish Sampath565505b2009-05-29 15:37:27 +0100492 mSuggestQueryId = mSuggestCursor.getColumnIndex(
493 SearchManager.SUGGEST_COLUMN_QUERY);
Bjorn Bringert04851702009-09-22 10:36:01 +0100494 mSuggestIntentExtraDataId = mSuggestCursor.getColumnIndex(
495 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
Satish Sampath565505b2009-05-29 15:37:27 +0100496 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800497 }
498
499 @Override
500 public boolean onMove(int oldPosition, int newPosition) {
501 if (mHistoryCursor == null) {
502 return false;
503 }
Grace Klobad3992d42010-01-28 11:44:38 -0800504 if (mIncludeWebSearch) {
Grace Kloba391df7c2010-03-01 19:51:49 -0800505 if (mHistoryCount == 0 && newPosition == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800506 return true;
Grace Kloba391df7c2010-03-01 19:51:49 -0800507 } else if (mHistoryCount > 0) {
508 if (newPosition == 0) {
509 mHistoryCursor.moveToPosition(0);
510 return true;
511 } else if (newPosition == 1) {
512 return true;
513 }
Grace Klobad3992d42010-01-28 11:44:38 -0800514 }
Grace Kloba391df7c2010-03-01 19:51:49 -0800515 newPosition--;
Grace Klobad3992d42010-01-28 11:44:38 -0800516 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800517 if (mHistoryCount > newPosition) {
518 mHistoryCursor.moveToPosition(newPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800519 } else {
Grace Klobad3992d42010-01-28 11:44:38 -0800520 mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800521 }
522 return true;
523 }
524
525 @Override
526 public int getCount() {
Grace Klobad3992d42010-01-28 11:44:38 -0800527 if (mIncludeWebSearch) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800528 return mHistoryCount + mSuggestionCount + 1;
529 } else {
530 return mHistoryCount + mSuggestionCount;
531 }
532 }
533
534 @Override
535 public String[] getColumnNames() {
536 return COLUMNS;
537 }
Satish Sampath565505b2009-05-29 15:37:27 +0100538
The Android Open Source Project0c908882009-03-03 19:32:16 -0800539 @Override
540 public String getString(int columnIndex) {
541 if ((mPos != -1 && mHistoryCursor != null)) {
Grace Kloba391df7c2010-03-01 19:51:49 -0800542 int type = -1; // 0: web search; 1: history; 2: suggestion
543 if (mIncludeWebSearch) {
544 if (mHistoryCount == 0 && mPos == 0) {
545 type = 0;
546 } else if (mHistoryCount > 0) {
547 if (mPos == 0) {
548 type = 1;
549 } else if (mPos == 1) {
550 type = 0;
551 }
552 }
553 if (type == -1) type = (mPos - 1) < mHistoryCount ? 1 : 2;
554 } else {
555 type = mPos < mHistoryCount ? 1 : 2;
556 }
557
The Android Open Source Project0c908882009-03-03 19:32:16 -0800558 switch(columnIndex) {
559 case SUGGEST_COLUMN_INTENT_ACTION_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800560 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800561 return Intent.ACTION_VIEW;
562 } else {
563 return Intent.ACTION_SEARCH;
564 }
565
566 case SUGGEST_COLUMN_INTENT_DATA_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800567 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800568 return mHistoryCursor.getString(1);
569 } else {
570 return null;
571 }
572
573 case SUGGEST_COLUMN_TEXT_1_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800574 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800575 return mString;
Grace Kloba391df7c2010-03-01 19:51:49 -0800576 } else if (type == 1) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700577 return getHistoryTitle();
Grace Klobad3992d42010-01-28 11:44:38 -0800578 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100579 if (mSuggestText1Id == -1) return null;
580 return mSuggestCursor.getString(mSuggestText1Id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800581 }
582
583 case SUGGEST_COLUMN_TEXT_2_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800584 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800585 return getContext().getString(R.string.search_the_web);
Grace Kloba391df7c2010-03-01 19:51:49 -0800586 } else if (type == 1) {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000587 return null; // Use TEXT_2_URL instead
Grace Klobad3992d42010-01-28 11:44:38 -0800588 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100589 if (mSuggestText2Id == -1) return null;
590 return mSuggestCursor.getString(mSuggestText2Id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800591 }
592
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000593 case SUGGEST_COLUMN_TEXT_2_URL_ID:
594 if (type == 0) {
595 return null;
596 } else if (type == 1) {
597 return getHistoryUrl();
598 } else {
599 if (mSuggestText2UrlId == -1) return null;
600 return mSuggestCursor.getString(mSuggestText2UrlId);
601 }
602
The Android Open Source Project0c908882009-03-03 19:32:16 -0800603 case SUGGEST_COLUMN_ICON_1_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800604 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800605 if (mHistoryCursor.getInt(3) == 1) {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400606 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800607 R.drawable.ic_search_category_bookmark)
608 .toString();
609 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400610 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800611 R.drawable.ic_search_category_history)
612 .toString();
613 }
614 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400615 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800616 R.drawable.ic_search_category_suggest)
617 .toString();
618 }
619
620 case SUGGEST_COLUMN_ICON_2_ID:
Leon Scroggins31887fd2009-05-18 16:58:08 -0400621 return "0";
The Android Open Source Project0c908882009-03-03 19:32:16 -0800622
623 case SUGGEST_COLUMN_QUERY_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800624 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800625 return mString;
Grace Kloba391df7c2010-03-01 19:51:49 -0800626 } else if (type == 1) {
Mike LeBeau2af73052009-06-23 17:36:59 -0700627 // Return the url in the intent query column. This is ignored
628 // within the browser because our searchable is set to
629 // android:searchMode="queryRewriteFromData", but it is used by
630 // global search for query rewriting.
631 return mHistoryCursor.getString(1);
Grace Klobad3992d42010-01-28 11:44:38 -0800632 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100633 if (mSuggestQueryId == -1) return null;
634 return mSuggestCursor.getString(mSuggestQueryId);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800635 }
Satish Sampath565505b2009-05-29 15:37:27 +0100636
Bjorn Bringert04851702009-09-22 10:36:01 +0100637 case SUGGEST_COLUMN_INTENT_EXTRA_DATA:
Grace Kloba391df7c2010-03-01 19:51:49 -0800638 if (type == 0) {
Bjorn Bringert04851702009-09-22 10:36:01 +0100639 return null;
Grace Kloba391df7c2010-03-01 19:51:49 -0800640 } else if (type == 1) {
Grace Klobad3992d42010-01-28 11:44:38 -0800641 return null;
642 } else {
Bjorn Bringert04851702009-09-22 10:36:01 +0100643 if (mSuggestIntentExtraDataId == -1) return null;
644 return mSuggestCursor.getString(mSuggestIntentExtraDataId);
Bjorn Bringert04851702009-09-22 10:36:01 +0100645 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800646 }
647 }
648 return null;
649 }
650
651 @Override
652 public double getDouble(int column) {
653 throw new UnsupportedOperationException();
654 }
655
656 @Override
657 public float getFloat(int column) {
658 throw new UnsupportedOperationException();
659 }
660
661 @Override
662 public int getInt(int column) {
663 throw new UnsupportedOperationException();
664 }
665
666 @Override
667 public long getLong(int column) {
668 if ((mPos != -1) && column == 0) {
669 return mPos; // use row# as the _Id
670 }
671 throw new UnsupportedOperationException();
672 }
673
674 @Override
675 public short getShort(int column) {
676 throw new UnsupportedOperationException();
677 }
678
679 @Override
680 public boolean isNull(int column) {
681 throw new UnsupportedOperationException();
682 }
683
684 // TODO Temporary change, finalize after jq's changes go in
685 public void deactivate() {
686 if (mHistoryCursor != null) {
687 mHistoryCursor.deactivate();
688 }
689 if (mSuggestCursor != null) {
690 mSuggestCursor.deactivate();
691 }
692 super.deactivate();
693 }
694
695 public boolean requery() {
696 return (mHistoryCursor != null ? mHistoryCursor.requery() : false) |
697 (mSuggestCursor != null ? mSuggestCursor.requery() : false);
698 }
699
700 // TODO Temporary change, finalize after jq's changes go in
701 public void close() {
702 super.close();
703 if (mHistoryCursor != null) {
704 mHistoryCursor.close();
705 mHistoryCursor = null;
706 }
707 if (mSuggestCursor != null) {
708 mSuggestCursor.close();
709 mSuggestCursor = null;
710 }
711 }
Satish Sampath565505b2009-05-29 15:37:27 +0100712
Mike LeBeau21beb132009-05-13 14:57:50 -0700713 /**
714 * Provides the title (text line 1) for a browser suggestion, which should be the
715 * webpage title. If the webpage title is empty, returns the stripped url instead.
Satish Sampath565505b2009-05-29 15:37:27 +0100716 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700717 * @return the title string to use
718 */
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700719 private String getHistoryTitle() {
720 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700721 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000722 title = stripUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700723 }
724 return title;
725 }
Satish Sampath565505b2009-05-29 15:37:27 +0100726
Mike LeBeau21beb132009-05-13 14:57:50 -0700727 /**
728 * Provides the subtitle (text line 2) for a browser suggestion, which should be the
729 * webpage url. If the webpage title is empty, then the url should go in the title
730 * instead, and the subtitle should be empty, so this would return null.
Satish Sampath565505b2009-05-29 15:37:27 +0100731 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700732 * @return the subtitle string to use, or null if none
733 */
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000734 private String getHistoryUrl() {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700735 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700736 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
737 return null;
738 } else {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000739 return stripUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700740 }
741 }
Satish Sampath565505b2009-05-29 15:37:27 +0100742
The Android Open Source Project0c908882009-03-03 19:32:16 -0800743 }
744
Leon Scroggins58d56c62010-01-28 15:12:40 -0500745 private static class ResultsCursor extends AbstractCursor {
746 // Array indices for RESULTS_COLUMNS
747 private static final int RESULT_ACTION_ID = 1;
748 private static final int RESULT_DATA_ID = 2;
749 private static final int RESULT_TEXT_ID = 3;
750 private static final int RESULT_ICON_ID = 4;
751 private static final int RESULT_EXTRA_ID = 5;
752
753 private static final String[] RESULTS_COLUMNS = new String[] {
754 "_id",
755 SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
756 SearchManager.SUGGEST_COLUMN_INTENT_DATA,
757 SearchManager.SUGGEST_COLUMN_TEXT_1,
758 SearchManager.SUGGEST_COLUMN_ICON_1,
759 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA
760 };
761 private final ArrayList<String> mResults;
762 public ResultsCursor(ArrayList<String> results) {
763 mResults = results;
764 }
765 public int getCount() { return mResults.size(); }
766
767 public String[] getColumnNames() {
768 return RESULTS_COLUMNS;
769 }
770
771 public String getString(int column) {
772 switch (column) {
773 case RESULT_ACTION_ID:
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -0500774 return RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500775 case RESULT_TEXT_ID:
776 // The data is used when the phone is in landscape mode. We
777 // still want to show the result string.
778 case RESULT_DATA_ID:
779 return mResults.get(mPos);
780 case RESULT_EXTRA_ID:
781 // The Intent's extra data will store the index into
782 // mResults so the BrowserActivity will know which result to
783 // use.
784 return Integer.toString(mPos);
785 case RESULT_ICON_ID:
786 return Integer.valueOf(R.drawable.magnifying_glass)
787 .toString();
788 default:
789 return null;
790 }
791 }
792 public short getShort(int column) {
793 throw new UnsupportedOperationException();
794 }
795 public int getInt(int column) {
796 throw new UnsupportedOperationException();
797 }
798 public long getLong(int column) {
799 if ((mPos != -1) && column == 0) {
800 return mPos; // use row# as the _id
801 }
802 throw new UnsupportedOperationException();
803 }
804 public float getFloat(int column) {
805 throw new UnsupportedOperationException();
806 }
807 public double getDouble(int column) {
808 throw new UnsupportedOperationException();
809 }
810 public boolean isNull(int column) {
811 throw new UnsupportedOperationException();
812 }
813 }
814
815 private ResultsCursor mResultsCursor;
816
817 /**
818 * Provide a set of results to be returned to query, intended to be used
819 * by the SearchDialog when the BrowserActivity is in voice search mode.
820 * @param results Strings to display in the dropdown from the SearchDialog
821 */
822 /* package */ void setQueryResults(ArrayList<String> results) {
823 if (results == null) {
824 mResultsCursor = null;
825 } else {
826 mResultsCursor = new ResultsCursor(results);
827 }
828 }
829
The Android Open Source Project0c908882009-03-03 19:32:16 -0800830 @Override
831 public Cursor query(Uri url, String[] projectionIn, String selection,
Satish Sampath565505b2009-05-29 15:37:27 +0100832 String[] selectionArgs, String sortOrder)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800833 throws IllegalStateException {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800834 int match = URI_MATCHER.match(url);
835 if (match == -1) {
836 throw new IllegalArgumentException("Unknown URL");
837 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500838 if (match == URI_MATCH_SUGGEST && mResultsCursor != null) {
839 Cursor results = mResultsCursor;
840 mResultsCursor = null;
841 return results;
842 }
843 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800844
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100845 if (match == URI_MATCH_SUGGEST || match == URI_MATCH_BOOKMARKS_SUGGEST) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800846 String suggestSelection;
847 String [] myArgs;
848 if (selectionArgs[0] == null || selectionArgs[0].equals("")) {
849 suggestSelection = null;
850 myArgs = null;
851 } else {
852 String like = selectionArgs[0] + "%";
Leon Scrogginsfaa15db2009-04-03 10:16:06 -0700853 if (selectionArgs[0].startsWith("http")
854 || selectionArgs[0].startsWith("file")) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800855 myArgs = new String[1];
856 myArgs[0] = like;
857 suggestSelection = selection;
858 } else {
859 SUGGEST_ARGS[0] = "http://" + like;
860 SUGGEST_ARGS[1] = "http://www." + like;
861 SUGGEST_ARGS[2] = "https://" + like;
862 SUGGEST_ARGS[3] = "https://www." + like;
Leon Scrogginsbd359cc2009-05-26 15:57:35 -0400863 // To match against titles.
864 SUGGEST_ARGS[4] = like;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800865 myArgs = SUGGEST_ARGS;
866 suggestSelection = SUGGEST_SELECTION;
867 }
868 }
869
870 Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
871 SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
Leon Scroggins31887fd2009-05-18 16:58:08 -0400872 ORDER_BY, MAX_SUGGESTION_LONG_ENTRIES_STRING);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800873
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100874 if (match == URI_MATCH_BOOKMARKS_SUGGEST
Dan Egnor5ee906c2009-11-18 12:11:49 -0800875 || Patterns.WEB_URL.matcher(selectionArgs[0]).matches()) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800876 return new MySuggestionCursor(c, null, "");
877 } else {
878 // get Google suggest if there is still space in the list
879 if (myArgs != null && myArgs.length > 1
Leon Scroggins62b71f72009-06-12 17:51:22 -0400880 && mSearchableInfo != null
The Android Open Source Project0c908882009-03-03 19:32:16 -0800881 && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) {
Bjorn Bringertd8b0ad22009-06-22 10:36:29 +0100882 Cursor sc = mSearchManager.getSuggestions(mSearchableInfo, selectionArgs[0]);
Leon Scroggins62b71f72009-06-12 17:51:22 -0400883 return new MySuggestionCursor(c, sc, selectionArgs[0]);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800884 }
885 return new MySuggestionCursor(c, null, selectionArgs[0]);
886 }
887 }
888
889 String[] projection = null;
890 if (projectionIn != null && projectionIn.length > 0) {
891 projection = new String[projectionIn.length + 1];
892 System.arraycopy(projectionIn, 0, projection, 0, projectionIn.length);
893 projection[projectionIn.length] = "_id AS _id";
894 }
895
896 StringBuilder whereClause = new StringBuilder(256);
897 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
898 whereClause.append("(_id = ").append(url.getPathSegments().get(1))
899 .append(")");
900 }
901
902 // Tack on the user's selection, if present
903 if (selection != null && selection.length() > 0) {
904 if (whereClause.length() > 0) {
905 whereClause.append(" AND ");
906 }
907
908 whereClause.append('(');
909 whereClause.append(selection);
910 whereClause.append(')');
911 }
912 Cursor c = db.query(TABLE_NAMES[match % 10], projection,
913 whereClause.toString(), selectionArgs, null, null, sortOrder,
914 null);
915 c.setNotificationUri(getContext().getContentResolver(), url);
916 return c;
917 }
918
919 @Override
920 public String getType(Uri url) {
921 int match = URI_MATCHER.match(url);
922 switch (match) {
923 case URI_MATCH_BOOKMARKS:
924 return "vnd.android.cursor.dir/bookmark";
925
926 case URI_MATCH_BOOKMARKS_ID:
927 return "vnd.android.cursor.item/bookmark";
928
929 case URI_MATCH_SEARCHES:
930 return "vnd.android.cursor.dir/searches";
931
932 case URI_MATCH_SEARCHES_ID:
933 return "vnd.android.cursor.item/searches";
934
935 case URI_MATCH_SUGGEST:
936 return SearchManager.SUGGEST_MIME_TYPE;
937
938 default:
939 throw new IllegalArgumentException("Unknown URL");
940 }
941 }
942
943 @Override
944 public Uri insert(Uri url, ContentValues initialValues) {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700945 boolean isBookmarkTable = false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800946 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
947
948 int match = URI_MATCHER.match(url);
949 Uri uri = null;
950 switch (match) {
951 case URI_MATCH_BOOKMARKS: {
952 // Insert into the bookmarks table
953 long rowID = db.insert(TABLE_NAMES[URI_MATCH_BOOKMARKS], "url",
954 initialValues);
955 if (rowID > 0) {
956 uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
957 rowID);
958 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700959 isBookmarkTable = true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800960 break;
961 }
962
963 case URI_MATCH_SEARCHES: {
964 // Insert into the searches table
965 long rowID = db.insert(TABLE_NAMES[URI_MATCH_SEARCHES], "url",
966 initialValues);
967 if (rowID > 0) {
968 uri = ContentUris.withAppendedId(Browser.SEARCHES_URI,
969 rowID);
970 }
971 break;
972 }
973
974 default:
975 throw new IllegalArgumentException("Unknown URL");
976 }
977
978 if (uri == null) {
979 throw new IllegalArgumentException("Unknown URL");
980 }
981 getContext().getContentResolver().notifyChange(uri, null);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700982
Christopher Tatef0c36f72009-07-28 15:24:05 -0700983 // Back up the new bookmark set if we just inserted one.
984 // A row created when bookmarks are added from scratch will have
985 // bookmark=1 in the initial value set.
986 if (isBookmarkTable
987 && initialValues.containsKey(BookmarkColumns.BOOKMARK)
988 && initialValues.getAsInteger(BookmarkColumns.BOOKMARK) != 0) {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700989 mBackupManager.dataChanged();
990 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800991 return uri;
992 }
993
994 @Override
995 public int delete(Uri url, String where, String[] whereArgs) {
996 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
997
998 int match = URI_MATCHER.match(url);
999 if (match == -1 || match == URI_MATCH_SUGGEST) {
1000 throw new IllegalArgumentException("Unknown URL");
1001 }
1002
Christopher Tatef0c36f72009-07-28 15:24:05 -07001003 // need to know whether it's the bookmarks table for a couple of reasons
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001004 boolean isBookmarkTable = (match == URI_MATCH_BOOKMARKS_ID);
Christopher Tatef0c36f72009-07-28 15:24:05 -07001005 String id = null;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001006
1007 if (isBookmarkTable || match == URI_MATCH_SEARCHES_ID) {
The Android Open Source Project0c908882009-03-03 19:32:16 -08001008 StringBuilder sb = new StringBuilder();
1009 if (where != null && where.length() > 0) {
1010 sb.append("( ");
1011 sb.append(where);
1012 sb.append(" ) AND ");
1013 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001014 id = url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001015 sb.append("_id = ");
Christopher Tatef0c36f72009-07-28 15:24:05 -07001016 sb.append(id);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001017 where = sb.toString();
1018 }
1019
Christopher Tatef0c36f72009-07-28 15:24:05 -07001020 ContentResolver cr = getContext().getContentResolver();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001021
Christopher Tatef0c36f72009-07-28 15:24:05 -07001022 // we'lll need to back up the bookmark set if we are about to delete one
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001023 if (isBookmarkTable) {
Christopher Tatef0c36f72009-07-28 15:24:05 -07001024 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
1025 new String[] { BookmarkColumns.BOOKMARK },
1026 "_id = " + id, null, null);
1027 if (cursor.moveToNext()) {
1028 if (cursor.getInt(0) != 0) {
1029 // yep, this record is a bookmark
1030 mBackupManager.dataChanged();
1031 }
1032 }
1033 cursor.close();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001034 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001035
1036 int count = db.delete(TABLE_NAMES[match % 10], where, whereArgs);
1037 cr.notifyChange(url, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001038 return count;
1039 }
1040
1041 @Override
1042 public int update(Uri url, ContentValues values, String where,
1043 String[] whereArgs) {
1044 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1045
1046 int match = URI_MATCHER.match(url);
1047 if (match == -1 || match == URI_MATCH_SUGGEST) {
1048 throw new IllegalArgumentException("Unknown URL");
1049 }
1050
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001051 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
The Android Open Source Project0c908882009-03-03 19:32:16 -08001052 StringBuilder sb = new StringBuilder();
1053 if (where != null && where.length() > 0) {
1054 sb.append("( ");
1055 sb.append(where);
1056 sb.append(" ) AND ");
1057 }
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001058 String id = url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001059 sb.append("_id = ");
Christopher Tatef0c36f72009-07-28 15:24:05 -07001060 sb.append(id);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001061 where = sb.toString();
1062 }
1063
Christopher Tatef0c36f72009-07-28 15:24:05 -07001064 ContentResolver cr = getContext().getContentResolver();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001065
Christopher Tatef0c36f72009-07-28 15:24:05 -07001066 // Not all bookmark-table updates should be backed up. Look to see
1067 // whether we changed the title, url, or "is a bookmark" state, and
1068 // request a backup if so.
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001069 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_BOOKMARKS) {
1070 boolean changingBookmarks = false;
Christopher Tatef0c36f72009-07-28 15:24:05 -07001071 // Alterations to the bookmark field inherently change the bookmark
1072 // set, so we don't need to query the record; we know a priori that
1073 // we will need to back up this change.
1074 if (values.containsKey(BookmarkColumns.BOOKMARK)) {
1075 changingBookmarks = true;
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001076 } else if ((values.containsKey(BookmarkColumns.TITLE)
1077 || values.containsKey(BookmarkColumns.URL))
1078 && values.containsKey(BookmarkColumns._ID)) {
1079 // If a title or URL has been changed, check to see if it is to
1080 // a bookmark. The ID should have been included in the update,
1081 // so use it.
Christopher Tatef0c36f72009-07-28 15:24:05 -07001082 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
1083 new String[] { BookmarkColumns.BOOKMARK },
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001084 BookmarkColumns._ID + " = "
1085 + values.getAsString(BookmarkColumns._ID), null, null);
Christopher Tatef0c36f72009-07-28 15:24:05 -07001086 if (cursor.moveToNext()) {
1087 changingBookmarks = (cursor.getInt(0) != 0);
1088 }
1089 cursor.close();
1090 }
1091
1092 // if this *is* a bookmark row we're altering, we need to back it up.
1093 if (changingBookmarks) {
1094 mBackupManager.dataChanged();
1095 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001096 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001097
1098 int ret = db.update(TABLE_NAMES[match % 10], values, where, whereArgs);
1099 cr.notifyChange(url, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001100 return ret;
1101 }
Satish Sampath565505b2009-05-29 15:37:27 +01001102
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001103 /**
1104 * Strips the provided url of preceding "http://" and any trailing "/". Does not
1105 * strip "https://". If the provided string cannot be stripped, the original string
1106 * is returned.
Satish Sampath565505b2009-05-29 15:37:27 +01001107 *
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001108 * TODO: Put this in TextUtils to be used by other packages doing something similar.
Satish Sampath565505b2009-05-29 15:37:27 +01001109 *
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001110 * @param url a url to strip, like "http://www.google.com/"
1111 * @return a stripped url like "www.google.com", or the original string if it could
1112 * not be stripped
1113 */
1114 private static String stripUrl(String url) {
1115 if (url == null) return null;
1116 Matcher m = STRIP_URL_PATTERN.matcher(url);
1117 if (m.matches() && m.groupCount() == 3) {
1118 return m.group(2);
1119 } else {
1120 return url;
1121 }
1122 }
1123
The Android Open Source Project0c908882009-03-03 19:32:16 -08001124}