blob: 6ebc2c26d31418dd61fb5186bff8762a6f4b2f6d [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
Bjorn Bringertd69f51d2010-09-13 14:06:41 +010019import com.android.browser.search.SearchEngine;
20
The Android Open Source Project0c908882009-03-03 19:32:16 -080021import android.app.SearchManager;
Christopher Tateb6a65442010-03-05 15:47:48 -080022import android.app.backup.BackupManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080023import 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 Projecta3c0aab2009-03-18 17:39:48 -070030import android.content.SharedPreferences.Editor;
Jeff Hamilton84029622010-08-05 14:29:28 -050031import android.content.UriMatcher;
Michael Kolbfe251992010-07-08 15:41:55 -070032import android.content.res.Configuration;
The Android Open Source Project0c908882009-03-03 19:32:16 -080033import android.database.AbstractCursor;
34import android.database.Cursor;
Jeff Hamilton8b139742010-07-29 16:06:53 -050035import android.database.DatabaseUtils;
The Android Open Source Project0c908882009-03-03 19:32:16 -080036import android.database.sqlite.SQLiteDatabase;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010037import android.database.sqlite.SQLiteOpenHelper;
The Android Open Source Project0c908882009-03-03 19:32:16 -080038import android.net.Uri;
Andrei Popescu93bea962010-03-23 15:04:36 +000039import android.os.Process;
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070040import android.preference.PreferenceManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080041import android.provider.Browser;
Christopher Tatef0c36f72009-07-28 15:24:05 -070042import android.provider.Browser.BookmarkColumns;
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -050043import android.speech.RecognizerResultsIntent;
Mike LeBeau21beb132009-05-13 14:57:50 -070044import android.text.TextUtils;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010045import android.util.Log;
Dianne Hackborn385effd2010-02-24 20:03:04 -080046import android.util.Patterns;
Dan Egnor5ee906c2009-11-18 12:11:49 -080047
Andrei Popescu1b20b9d2009-09-21 18:49:42 +010048import java.io.File;
49import java.io.FilenameFilter;
Leon Scroggins58d56c62010-01-28 15:12:40 -050050import java.util.ArrayList;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010051import java.util.Date;
Mike LeBeauc42f81b2009-05-14 15:04:19 -070052import java.util.regex.Matcher;
53import java.util.regex.Pattern;
The Android Open Source Project0c908882009-03-03 19:32:16 -080054
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -070055
The Android Open Source Project0c908882009-03-03 19:32:16 -080056public class BrowserProvider extends ContentProvider {
57
58 private SQLiteOpenHelper mOpenHelper;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070059 private BackupManager mBackupManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080060 private static final String sDatabaseName = "browser.db";
61 private static final String TAG = "BrowserProvider";
62 private static final String ORDER_BY = "visits DESC, date DESC";
63
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070064 private static final String PICASA_URL = "http://picasaweb.google.com/m/" +
65 "viewer?source=androidclient";
66
The Android Open Source Project0c908882009-03-03 19:32:16 -080067 private static final String[] TABLE_NAMES = new String[] {
Bjorn Bringerta7611812010-03-24 11:12:02 +000068 "bookmarks", "searches"
The Android Open Source Project0c908882009-03-03 19:32:16 -080069 };
70 private static final String[] SUGGEST_PROJECTION = new String[] {
Leon Scrogginsb4464432009-11-25 12:37:50 -050071 "_id", "url", "title", "bookmark", "user_entered"
The Android Open Source Project0c908882009-03-03 19:32:16 -080072 };
Satish Sampath565505b2009-05-29 15:37:27 +010073 private static final String SUGGEST_SELECTION =
Leon Scrogginsb4464432009-11-25 12:37:50 -050074 "(url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?"
75 + " OR title LIKE ?) AND (bookmark = 1 OR user_entered = 1)";
Leon Scrogginsbd359cc2009-05-26 15:57:35 -040076 private String[] SUGGEST_ARGS = new String[5];
The Android Open Source Project0c908882009-03-03 19:32:16 -080077
78 // shared suggestion array index, make sure to match COLUMNS
79 private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1;
80 private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2;
81 private static final int SUGGEST_COLUMN_TEXT_1_ID = 3;
82 private static final int SUGGEST_COLUMN_TEXT_2_ID = 4;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +000083 private static final int SUGGEST_COLUMN_TEXT_2_URL_ID = 5;
84 private static final int SUGGEST_COLUMN_ICON_1_ID = 6;
85 private static final int SUGGEST_COLUMN_ICON_2_ID = 7;
86 private static final int SUGGEST_COLUMN_QUERY_ID = 8;
Bjorn Bringert04851702009-09-22 10:36:01 +010087 private static final int SUGGEST_COLUMN_INTENT_EXTRA_DATA = 9;
The Android Open Source Project0c908882009-03-03 19:32:16 -080088
Michael Kolbfe251992010-07-08 15:41:55 -070089 // how many suggestions will be shown in dropdown
90 // 0..SHORT: filled by browser db
91 private static final int MAX_SUGGEST_SHORT_SMALL = 3;
92 // SHORT..LONG: filled by search suggestions
93 private static final int MAX_SUGGEST_LONG_SMALL = 6;
94
95 // large screen size shows more
96 private static final int MAX_SUGGEST_SHORT_LARGE = 6;
97 private static final int MAX_SUGGEST_LONG_LARGE = 9;
98
99
The Android Open Source Project0c908882009-03-03 19:32:16 -0800100 // shared suggestion columns
101 private static final String[] COLUMNS = new String[] {
102 "_id",
103 SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
104 SearchManager.SUGGEST_COLUMN_INTENT_DATA,
105 SearchManager.SUGGEST_COLUMN_TEXT_1,
106 SearchManager.SUGGEST_COLUMN_TEXT_2,
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000107 SearchManager.SUGGEST_COLUMN_TEXT_2_URL,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800108 SearchManager.SUGGEST_COLUMN_ICON_1,
109 SearchManager.SUGGEST_COLUMN_ICON_2,
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700110 SearchManager.SUGGEST_COLUMN_QUERY,
Bjorn Bringert04851702009-09-22 10:36:01 +0100111 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA};
The Android Open Source Project0c908882009-03-03 19:32:16 -0800112
The Android Open Source Project0c908882009-03-03 19:32:16 -0800113
114 // make sure that these match the index of TABLE_NAMES
115 private static final int URI_MATCH_BOOKMARKS = 0;
116 private static final int URI_MATCH_SEARCHES = 1;
117 // (id % 10) should match the table name index
118 private static final int URI_MATCH_BOOKMARKS_ID = 10;
119 private static final int URI_MATCH_SEARCHES_ID = 11;
120 //
121 private static final int URI_MATCH_SUGGEST = 20;
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100122 private static final int URI_MATCH_BOOKMARKS_SUGGEST = 21;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800123
124 private static final UriMatcher URI_MATCHER;
125
126 static {
127 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
128 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS],
129 URI_MATCH_BOOKMARKS);
130 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/#",
131 URI_MATCH_BOOKMARKS_ID);
132 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES],
133 URI_MATCH_SEARCHES);
134 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES] + "/#",
135 URI_MATCH_SEARCHES_ID);
136 URI_MATCHER.addURI("browser", SearchManager.SUGGEST_URI_PATH_QUERY,
137 URI_MATCH_SUGGEST);
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100138 URI_MATCHER.addURI("browser",
139 TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/" + SearchManager.SUGGEST_URI_PATH_QUERY,
140 URI_MATCH_BOOKMARKS_SUGGEST);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800141 }
142
143 // 1 -> 2 add cache table
144 // 2 -> 3 update history table
145 // 3 -> 4 add passwords table
146 // 4 -> 5 add settings table
147 // 5 -> 6 ?
148 // 6 -> 7 ?
149 // 7 -> 8 drop proxy table
150 // 8 -> 9 drop settings table
151 // 9 -> 10 add form_urls and form_data
152 // 10 -> 11 add searches table
153 // 11 -> 12 modify cache table
154 // 12 -> 13 modify cache table
155 // 13 -> 14 correspond with Google Bookmarks schema
156 // 14 -> 15 move couple of tables to either browser private database or webview database
157 // 15 -> 17 Set it up for the SearchManager
158 // 17 -> 18 Added favicon in bookmarks table for Home shortcuts
159 // 18 -> 19 Remove labels table
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400160 // 19 -> 20 Added thumbnail
Patrick Scott3918d442009-08-04 13:22:29 -0400161 // 20 -> 21 Added touch_icon
Grace Kloba6b52a552009-09-03 16:29:56 -0700162 // 21 -> 22 Remove "clientid"
Leon Scrogginsb4464432009-11-25 12:37:50 -0500163 // 22 -> 23 Added user_entered
Magnus Lindhult655e8672010-09-29 18:36:07 +0200164 // 23 -> 24 Url not allowed to be null anymore.
165 private static final int DATABASE_VERSION = 24;
Satish Sampath565505b2009-05-29 15:37:27 +0100166
Mike LeBeauc42f81b2009-05-14 15:04:19 -0700167 // Regular expression which matches http://, followed by some stuff, followed by
168 // optionally a trailing slash, all matched as separate groups.
169 private static final Pattern STRIP_URL_PATTERN = Pattern.compile("^(http://)(.*?)(/$)?");
Satish Sampath565505b2009-05-29 15:37:27 +0100170
Bjorn Bringertd69f51d2010-09-13 14:06:41 +0100171 private BrowserSettings mSettings;
Bjorn Bringertd8b0ad22009-06-22 10:36:29 +0100172
Michael Kolbfe251992010-07-08 15:41:55 -0700173 private int mMaxSuggestionShortSize;
174 private int mMaxSuggestionLongSize;
175
The Android Open Source Project0c908882009-03-03 19:32:16 -0800176 public BrowserProvider() {
177 }
Satish Sampath565505b2009-05-29 15:37:27 +0100178
Patrick Scott43914692010-02-19 10:10:10 -0500179 // XXX: This is a major hack to remove our dependency on gsf constants and
180 // its content provider. http://b/issue?id=2425179
181 static String getClientId(ContentResolver cr) {
182 String ret = "android-google";
Reena Leef59fc9b2011-03-04 16:44:54 -0800183 Cursor legacyClientIdCursor = null;
184 Cursor searchClientIdCursor = null;
185
186 // search_client_id includes search prefix, legacy client_id does not include prefix
Patrick Scott43914692010-02-19 10:10:10 -0500187 try {
Reena Leef59fc9b2011-03-04 16:44:54 -0800188 searchClientIdCursor = cr.query(Uri.parse("content://com.google.settings/partner"),
189 new String[] { "value" }, "name='search_client_id'", null, null);
190 if (searchClientIdCursor != null && searchClientIdCursor.moveToNext()) {
191 ret = searchClientIdCursor.getString(0);
192 } else {
193 legacyClientIdCursor = cr.query(Uri.parse("content://com.google.settings/partner"),
Patrick Scott43914692010-02-19 10:10:10 -0500194 new String[] { "value" }, "name='client_id'", null, null);
Reena Leef59fc9b2011-03-04 16:44:54 -0800195 if (legacyClientIdCursor != null && legacyClientIdCursor.moveToNext()) {
196 ret = "ms-" + legacyClientIdCursor.getString(0);
197 }
Patrick Scott43914692010-02-19 10:10:10 -0500198 }
199 } catch (RuntimeException ex) {
200 // fall through to return the default
201 } finally {
Reena Leef59fc9b2011-03-04 16:44:54 -0800202 if (legacyClientIdCursor != null) {
203 legacyClientIdCursor.close();
204 }
205 if (searchClientIdCursor != null) {
206 searchClientIdCursor.close();
Patrick Scott43914692010-02-19 10:10:10 -0500207 }
208 }
209 return ret;
210 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800211
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700212 private static CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800213 StringBuffer sb = new StringBuffer();
214 int lastCharLoc = 0;
Satish Sampath565505b2009-05-29 15:37:27 +0100215
Patrick Scott43914692010-02-19 10:10:10 -0500216 final String client_id = getClientId(context.getContentResolver());
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700217
The Android Open Source Project0c908882009-03-03 19:32:16 -0800218 for (int i = 0; i < srcString.length(); ++i) {
219 char c = srcString.charAt(i);
220 if (c == '{') {
221 sb.append(srcString.subSequence(lastCharLoc, i));
222 lastCharLoc = i;
223 inner:
224 for (int j = i; j < srcString.length(); ++j) {
225 char k = srcString.charAt(j);
226 if (k == '}') {
227 String propertyKeyValue = srcString.subSequence(i + 1, j).toString();
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700228 if (propertyKeyValue.equals("CLIENT_ID")) {
229 sb.append(client_id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800230 } else {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700231 sb.append("unknown");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800232 }
233 lastCharLoc = j + 1;
234 i = j;
235 break inner;
236 }
237 }
238 }
239 }
240 if (srcString.length() - lastCharLoc > 0) {
241 // Put on the tail, if there is one
242 sb.append(srcString.subSequence(lastCharLoc, srcString.length()));
243 }
244 return sb;
245 }
246
247 private static class DatabaseHelper extends SQLiteOpenHelper {
248 private Context mContext;
249
250 public DatabaseHelper(Context context) {
251 super(context, sDatabaseName, null, DATABASE_VERSION);
252 mContext = context;
253 }
254
255 @Override
256 public void onCreate(SQLiteDatabase db) {
257 db.execSQL("CREATE TABLE bookmarks (" +
258 "_id INTEGER PRIMARY KEY," +
259 "title TEXT," +
Magnus Lindhult655e8672010-09-29 18:36:07 +0200260 "url TEXT NOT NULL," +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800261 "visits INTEGER," +
262 "date LONG," +
263 "created LONG," +
264 "description TEXT," +
265 "bookmark INTEGER," +
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400266 "favicon BLOB DEFAULT NULL," +
Patrick Scott3918d442009-08-04 13:22:29 -0400267 "thumbnail BLOB DEFAULT NULL," +
Leon Scrogginsb4464432009-11-25 12:37:50 -0500268 "touch_icon BLOB DEFAULT NULL," +
269 "user_entered INTEGER" +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800270 ");");
271
272 final CharSequence[] bookmarks = mContext.getResources()
273 .getTextArray(R.array.bookmarks);
274 int size = bookmarks.length;
275 try {
276 for (int i = 0; i < size; i = i + 2) {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700277 CharSequence bookmarkDestination = replaceSystemPropertyInString(mContext, bookmarks[i + 1]);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800278 db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
279 "date, created, bookmark)" + " VALUES('" +
Satish Sampath565505b2009-05-29 15:37:27 +0100280 bookmarks[i] + "', '" + bookmarkDestination +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800281 "', 0, 0, 0, 1);");
282 }
283 } catch (ArrayIndexOutOfBoundsException e) {
284 }
285
286 db.execSQL("CREATE TABLE searches (" +
287 "_id INTEGER PRIMARY KEY," +
288 "search TEXT," +
289 "date LONG" +
290 ");");
291 }
292
293 @Override
294 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
295 Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400296 + newVersion);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800297 if (oldVersion == 18) {
298 db.execSQL("DROP TABLE IF EXISTS labels");
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400299 }
300 if (oldVersion <= 19) {
301 db.execSQL("ALTER TABLE bookmarks ADD COLUMN thumbnail BLOB DEFAULT NULL;");
Patrick Scott3918d442009-08-04 13:22:29 -0400302 }
303 if (oldVersion < 21) {
304 db.execSQL("ALTER TABLE bookmarks ADD COLUMN touch_icon BLOB DEFAULT NULL;");
Grace Kloba6b52a552009-09-03 16:29:56 -0700305 }
306 if (oldVersion < 22) {
307 db.execSQL("DELETE FROM bookmarks WHERE (bookmark = 0 AND url LIKE \"%.google.%client=ms-%\")");
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100308 removeGears();
Leon Scrogginsb4464432009-11-25 12:37:50 -0500309 }
310 if (oldVersion < 23) {
311 db.execSQL("ALTER TABLE bookmarks ADD COLUMN user_entered INTEGER;");
Magnus Lindhult655e8672010-09-29 18:36:07 +0200312 }
313 if (oldVersion < 24) {
314 /* SQLite does not support ALTER COLUMN, hence the lengthy code. */
315 db.execSQL("DELETE FROM bookmarks WHERE url IS NULL;");
316 db.execSQL("ALTER TABLE bookmarks RENAME TO bookmarks_temp;");
317 db.execSQL("CREATE TABLE bookmarks (" +
318 "_id INTEGER PRIMARY KEY," +
319 "title TEXT," +
320 "url TEXT NOT NULL," +
321 "visits INTEGER," +
322 "date LONG," +
323 "created LONG," +
324 "description TEXT," +
325 "bookmark INTEGER," +
326 "favicon BLOB DEFAULT NULL," +
327 "thumbnail BLOB DEFAULT NULL," +
328 "touch_icon BLOB DEFAULT NULL," +
329 "user_entered INTEGER" +
330 ");");
331 db.execSQL("INSERT INTO bookmarks SELECT * FROM bookmarks_temp;");
332 db.execSQL("DROP TABLE bookmarks_temp;");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800333 } else {
334 db.execSQL("DROP TABLE IF EXISTS bookmarks");
335 db.execSQL("DROP TABLE IF EXISTS searches");
336 onCreate(db);
337 }
338 }
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100339
340 private void removeGears() {
Andrei Popescu93bea962010-03-23 15:04:36 +0000341 new Thread() {
342 @Override
343 public void run() {
344 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100345 String browserDataDirString = mContext.getApplicationInfo().dataDir;
346 final String appPluginsDirString = "app_plugins";
347 final String gearsPrefix = "gears";
348 File appPluginsDir = new File(browserDataDirString + File.separator
349 + appPluginsDirString);
350 if (!appPluginsDir.exists()) {
Andrei Popescu93bea962010-03-23 15:04:36 +0000351 return;
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100352 }
353 // Delete the Gears plugin files
354 File[] gearsFiles = appPluginsDir.listFiles(new FilenameFilter() {
355 public boolean accept(File dir, String filename) {
356 return filename.startsWith(gearsPrefix);
357 }
358 });
359 for (int i = 0; i < gearsFiles.length; ++i) {
360 if (gearsFiles[i].isDirectory()) {
361 deleteDirectory(gearsFiles[i]);
362 } else {
363 gearsFiles[i].delete();
364 }
365 }
366 // Delete the Gears data files
367 File gearsDataDir = new File(browserDataDirString + File.separator
368 + gearsPrefix);
369 if (!gearsDataDir.exists()) {
Andrei Popescu93bea962010-03-23 15:04:36 +0000370 return;
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100371 }
372 deleteDirectory(gearsDataDir);
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100373 }
374
375 private void deleteDirectory(File currentDir) {
376 File[] files = currentDir.listFiles();
377 for (int i = 0; i < files.length; ++i) {
378 if (files[i].isDirectory()) {
379 deleteDirectory(files[i]);
380 }
381 files[i].delete();
382 }
383 currentDir.delete();
384 }
Andrei Popescu93bea962010-03-23 15:04:36 +0000385 }.start();
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100386 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800387 }
388
389 @Override
390 public boolean onCreate() {
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700391 final Context context = getContext();
Michael Kolbfe251992010-07-08 15:41:55 -0700392 boolean xlargeScreenSize = (context.getResources().getConfiguration().screenLayout
393 & Configuration.SCREENLAYOUT_SIZE_MASK)
394 == Configuration.SCREENLAYOUT_SIZE_XLARGE;
395 boolean isPortrait = (context.getResources().getConfiguration().orientation
396 == Configuration.ORIENTATION_PORTRAIT);
397
398
399 if (xlargeScreenSize && isPortrait) {
400 mMaxSuggestionLongSize = MAX_SUGGEST_LONG_LARGE;
401 mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_LARGE;
402 } else {
403 mMaxSuggestionLongSize = MAX_SUGGEST_LONG_SMALL;
404 mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_SMALL;
405 }
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700406 mOpenHelper = new DatabaseHelper(context);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700407 mBackupManager = new BackupManager(context);
Satish Sampath565505b2009-05-29 15:37:27 +0100408 // we added "picasa web album" into default bookmarks for version 19.
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700409 // To avoid erasing the bookmark table, we added it explicitly for
410 // version 18 and 19 as in the other cases, we will erase the table.
411 if (DATABASE_VERSION == 18 || DATABASE_VERSION == 19) {
412 SharedPreferences p = PreferenceManager
413 .getDefaultSharedPreferences(context);
414 boolean fix = p.getBoolean("fix_picasa", true);
415 if (fix) {
416 fixPicasaBookmark();
417 Editor ed = p.edit();
418 ed.putBoolean("fix_picasa", false);
Brad Fitzpatrick1a6e0e82010-09-01 16:29:03 -0700419 ed.apply();
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700420 }
421 }
Bjorn Bringertd69f51d2010-09-13 14:06:41 +0100422 mSettings = BrowserSettings.getInstance();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800423 return true;
424 }
425
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700426 private void fixPicasaBookmark() {
427 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
428 Cursor cursor = db.rawQuery("SELECT _id FROM bookmarks WHERE " +
429 "bookmark = 1 AND url = ?", new String[] { PICASA_URL });
430 try {
431 if (!cursor.moveToFirst()) {
432 // set "created" so that it will be on the top of the list
433 db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
434 "date, created, bookmark)" + " VALUES('" +
435 getContext().getString(R.string.picasa) + "', '"
436 + PICASA_URL + "', 0, 0, " + new Date().getTime()
437 + ", 1);");
438 }
439 } finally {
440 if (cursor != null) {
441 cursor.close();
442 }
443 }
444 }
445
The Android Open Source Project0c908882009-03-03 19:32:16 -0800446 /*
447 * Subclass AbstractCursor so we can combine multiple Cursors and add
Grace Kloba391df7c2010-03-01 19:51:49 -0800448 * "Search the web".
The Android Open Source Project0c908882009-03-03 19:32:16 -0800449 * Here are the rules.
Satish Sampath565505b2009-05-29 15:37:27 +0100450 * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
Grace Kloba391df7c2010-03-01 19:51:49 -0800451 * "Search the web";
452 * 2. If bookmark/history entries has a match, "Search the web" shows up at
453 * the second place. Otherwise, "Search the web" shows up at the first
454 * place.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800455 */
456 private class MySuggestionCursor extends AbstractCursor {
457 private Cursor mHistoryCursor;
458 private Cursor mSuggestCursor;
459 private int mHistoryCount;
460 private int mSuggestionCount;
Grace Klobad3992d42010-01-28 11:44:38 -0800461 private boolean mIncludeWebSearch;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800462 private String mString;
Satish Sampath565505b2009-05-29 15:37:27 +0100463 private int mSuggestText1Id;
464 private int mSuggestText2Id;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000465 private int mSuggestText2UrlId;
Satish Sampath565505b2009-05-29 15:37:27 +0100466 private int mSuggestQueryId;
Bjorn Bringert04851702009-09-22 10:36:01 +0100467 private int mSuggestIntentExtraDataId;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800468
469 public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
470 mHistoryCursor = hc;
471 mSuggestCursor = sc;
Grace Klobaf9b04272010-06-15 13:36:22 -0700472 mHistoryCount = hc != null ? hc.getCount() : 0;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800473 mSuggestionCount = sc != null ? sc.getCount() : 0;
Michael Kolbfe251992010-07-08 15:41:55 -0700474 if (mSuggestionCount > (mMaxSuggestionLongSize - mHistoryCount)) {
475 mSuggestionCount = mMaxSuggestionLongSize - mHistoryCount;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800476 }
477 mString = string;
Grace Klobad3992d42010-01-28 11:44:38 -0800478 mIncludeWebSearch = string.length() > 0;
Satish Sampath565505b2009-05-29 15:37:27 +0100479
480 // Some web suggest providers only give suggestions and have no description string for
481 // items. The order of the result columns may be different as well. So retrieve the
482 // column indices for the fields we need now and check before using below.
483 if (mSuggestCursor == null) {
484 mSuggestText1Id = -1;
485 mSuggestText2Id = -1;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000486 mSuggestText2UrlId = -1;
Satish Sampath565505b2009-05-29 15:37:27 +0100487 mSuggestQueryId = -1;
Bjorn Bringert04851702009-09-22 10:36:01 +0100488 mSuggestIntentExtraDataId = -1;
Satish Sampath565505b2009-05-29 15:37:27 +0100489 } else {
490 mSuggestText1Id = mSuggestCursor.getColumnIndex(
491 SearchManager.SUGGEST_COLUMN_TEXT_1);
492 mSuggestText2Id = mSuggestCursor.getColumnIndex(
493 SearchManager.SUGGEST_COLUMN_TEXT_2);
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000494 mSuggestText2UrlId = mSuggestCursor.getColumnIndex(
495 SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
Satish Sampath565505b2009-05-29 15:37:27 +0100496 mSuggestQueryId = mSuggestCursor.getColumnIndex(
497 SearchManager.SUGGEST_COLUMN_QUERY);
Bjorn Bringert04851702009-09-22 10:36:01 +0100498 mSuggestIntentExtraDataId = mSuggestCursor.getColumnIndex(
499 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
Satish Sampath565505b2009-05-29 15:37:27 +0100500 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800501 }
502
503 @Override
504 public boolean onMove(int oldPosition, int newPosition) {
505 if (mHistoryCursor == null) {
506 return false;
507 }
Grace Klobad3992d42010-01-28 11:44:38 -0800508 if (mIncludeWebSearch) {
Grace Kloba391df7c2010-03-01 19:51:49 -0800509 if (mHistoryCount == 0 && newPosition == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800510 return true;
Grace Kloba391df7c2010-03-01 19:51:49 -0800511 } else if (mHistoryCount > 0) {
512 if (newPosition == 0) {
513 mHistoryCursor.moveToPosition(0);
514 return true;
515 } else if (newPosition == 1) {
516 return true;
517 }
Grace Klobad3992d42010-01-28 11:44:38 -0800518 }
Grace Kloba391df7c2010-03-01 19:51:49 -0800519 newPosition--;
Grace Klobad3992d42010-01-28 11:44:38 -0800520 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800521 if (mHistoryCount > newPosition) {
522 mHistoryCursor.moveToPosition(newPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800523 } else {
Grace Klobad3992d42010-01-28 11:44:38 -0800524 mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800525 }
526 return true;
527 }
528
529 @Override
530 public int getCount() {
Grace Klobad3992d42010-01-28 11:44:38 -0800531 if (mIncludeWebSearch) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800532 return mHistoryCount + mSuggestionCount + 1;
533 } else {
534 return mHistoryCount + mSuggestionCount;
535 }
536 }
537
538 @Override
539 public String[] getColumnNames() {
540 return COLUMNS;
541 }
Satish Sampath565505b2009-05-29 15:37:27 +0100542
The Android Open Source Project0c908882009-03-03 19:32:16 -0800543 @Override
544 public String getString(int columnIndex) {
545 if ((mPos != -1 && mHistoryCursor != null)) {
Grace Kloba391df7c2010-03-01 19:51:49 -0800546 int type = -1; // 0: web search; 1: history; 2: suggestion
547 if (mIncludeWebSearch) {
548 if (mHistoryCount == 0 && mPos == 0) {
549 type = 0;
550 } else if (mHistoryCount > 0) {
551 if (mPos == 0) {
552 type = 1;
553 } else if (mPos == 1) {
554 type = 0;
555 }
556 }
557 if (type == -1) type = (mPos - 1) < mHistoryCount ? 1 : 2;
558 } else {
559 type = mPos < mHistoryCount ? 1 : 2;
560 }
561
The Android Open Source Project0c908882009-03-03 19:32:16 -0800562 switch(columnIndex) {
563 case SUGGEST_COLUMN_INTENT_ACTION_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800564 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800565 return Intent.ACTION_VIEW;
566 } else {
567 return Intent.ACTION_SEARCH;
568 }
569
570 case SUGGEST_COLUMN_INTENT_DATA_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800571 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800572 return mHistoryCursor.getString(1);
573 } else {
574 return null;
575 }
576
577 case SUGGEST_COLUMN_TEXT_1_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800578 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800579 return mString;
Grace Kloba391df7c2010-03-01 19:51:49 -0800580 } else if (type == 1) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700581 return getHistoryTitle();
Grace Klobad3992d42010-01-28 11:44:38 -0800582 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100583 if (mSuggestText1Id == -1) return null;
584 return mSuggestCursor.getString(mSuggestText1Id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800585 }
586
587 case SUGGEST_COLUMN_TEXT_2_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800588 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800589 return getContext().getString(R.string.search_the_web);
Grace Kloba391df7c2010-03-01 19:51:49 -0800590 } else if (type == 1) {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000591 return null; // Use TEXT_2_URL instead
Grace Klobad3992d42010-01-28 11:44:38 -0800592 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100593 if (mSuggestText2Id == -1) return null;
594 return mSuggestCursor.getString(mSuggestText2Id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800595 }
596
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000597 case SUGGEST_COLUMN_TEXT_2_URL_ID:
598 if (type == 0) {
599 return null;
600 } else if (type == 1) {
601 return getHistoryUrl();
602 } else {
603 if (mSuggestText2UrlId == -1) return null;
604 return mSuggestCursor.getString(mSuggestText2UrlId);
605 }
606
The Android Open Source Project0c908882009-03-03 19:32:16 -0800607 case SUGGEST_COLUMN_ICON_1_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800608 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800609 if (mHistoryCursor.getInt(3) == 1) {
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_bookmark)
612 .toString();
613 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400614 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800615 R.drawable.ic_search_category_history)
616 .toString();
617 }
618 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400619 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800620 R.drawable.ic_search_category_suggest)
621 .toString();
622 }
623
624 case SUGGEST_COLUMN_ICON_2_ID:
Leon Scroggins31887fd2009-05-18 16:58:08 -0400625 return "0";
The Android Open Source Project0c908882009-03-03 19:32:16 -0800626
627 case SUGGEST_COLUMN_QUERY_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800628 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800629 return mString;
Grace Kloba391df7c2010-03-01 19:51:49 -0800630 } else if (type == 1) {
Mike LeBeau2af73052009-06-23 17:36:59 -0700631 // Return the url in the intent query column. This is ignored
632 // within the browser because our searchable is set to
633 // android:searchMode="queryRewriteFromData", but it is used by
634 // global search for query rewriting.
635 return mHistoryCursor.getString(1);
Grace Klobad3992d42010-01-28 11:44:38 -0800636 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100637 if (mSuggestQueryId == -1) return null;
638 return mSuggestCursor.getString(mSuggestQueryId);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800639 }
Satish Sampath565505b2009-05-29 15:37:27 +0100640
Bjorn Bringert04851702009-09-22 10:36:01 +0100641 case SUGGEST_COLUMN_INTENT_EXTRA_DATA:
Grace Kloba391df7c2010-03-01 19:51:49 -0800642 if (type == 0) {
Bjorn Bringert04851702009-09-22 10:36:01 +0100643 return null;
Grace Kloba391df7c2010-03-01 19:51:49 -0800644 } else if (type == 1) {
Grace Klobad3992d42010-01-28 11:44:38 -0800645 return null;
646 } else {
Bjorn Bringert04851702009-09-22 10:36:01 +0100647 if (mSuggestIntentExtraDataId == -1) return null;
648 return mSuggestCursor.getString(mSuggestIntentExtraDataId);
Bjorn Bringert04851702009-09-22 10:36:01 +0100649 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800650 }
651 }
652 return null;
653 }
654
655 @Override
656 public double getDouble(int column) {
657 throw new UnsupportedOperationException();
658 }
659
660 @Override
661 public float getFloat(int column) {
662 throw new UnsupportedOperationException();
663 }
664
665 @Override
666 public int getInt(int column) {
667 throw new UnsupportedOperationException();
668 }
669
670 @Override
671 public long getLong(int column) {
672 if ((mPos != -1) && column == 0) {
673 return mPos; // use row# as the _Id
674 }
675 throw new UnsupportedOperationException();
676 }
677
678 @Override
679 public short getShort(int column) {
680 throw new UnsupportedOperationException();
681 }
682
683 @Override
684 public boolean isNull(int column) {
685 throw new UnsupportedOperationException();
686 }
687
688 // TODO Temporary change, finalize after jq's changes go in
Michael Kolbfe251992010-07-08 15:41:55 -0700689 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800690 public void deactivate() {
691 if (mHistoryCursor != null) {
692 mHistoryCursor.deactivate();
693 }
694 if (mSuggestCursor != null) {
695 mSuggestCursor.deactivate();
696 }
697 super.deactivate();
698 }
699
Michael Kolbfe251992010-07-08 15:41:55 -0700700 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800701 public boolean requery() {
702 return (mHistoryCursor != null ? mHistoryCursor.requery() : false) |
703 (mSuggestCursor != null ? mSuggestCursor.requery() : false);
704 }
705
706 // TODO Temporary change, finalize after jq's changes go in
Michael Kolbfe251992010-07-08 15:41:55 -0700707 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800708 public void close() {
709 super.close();
710 if (mHistoryCursor != null) {
711 mHistoryCursor.close();
712 mHistoryCursor = null;
713 }
714 if (mSuggestCursor != null) {
715 mSuggestCursor.close();
716 mSuggestCursor = null;
717 }
718 }
Satish Sampath565505b2009-05-29 15:37:27 +0100719
Mike LeBeau21beb132009-05-13 14:57:50 -0700720 /**
721 * Provides the title (text line 1) for a browser suggestion, which should be the
722 * webpage title. If the webpage title is empty, returns the stripped url instead.
Satish Sampath565505b2009-05-29 15:37:27 +0100723 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700724 * @return the title string to use
725 */
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700726 private String getHistoryTitle() {
727 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700728 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000729 title = stripUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700730 }
731 return title;
732 }
Satish Sampath565505b2009-05-29 15:37:27 +0100733
Mike LeBeau21beb132009-05-13 14:57:50 -0700734 /**
735 * Provides the subtitle (text line 2) for a browser suggestion, which should be the
736 * webpage url. If the webpage title is empty, then the url should go in the title
737 * instead, and the subtitle should be empty, so this would return null.
Satish Sampath565505b2009-05-29 15:37:27 +0100738 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700739 * @return the subtitle string to use, or null if none
740 */
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000741 private String getHistoryUrl() {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700742 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700743 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
744 return null;
745 } else {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000746 return stripUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700747 }
748 }
Satish Sampath565505b2009-05-29 15:37:27 +0100749
The Android Open Source Project0c908882009-03-03 19:32:16 -0800750 }
751
Leon Scroggins58d56c62010-01-28 15:12:40 -0500752 private static class ResultsCursor extends AbstractCursor {
753 // Array indices for RESULTS_COLUMNS
754 private static final int RESULT_ACTION_ID = 1;
755 private static final int RESULT_DATA_ID = 2;
756 private static final int RESULT_TEXT_ID = 3;
757 private static final int RESULT_ICON_ID = 4;
758 private static final int RESULT_EXTRA_ID = 5;
759
760 private static final String[] RESULTS_COLUMNS = new String[] {
761 "_id",
762 SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
763 SearchManager.SUGGEST_COLUMN_INTENT_DATA,
764 SearchManager.SUGGEST_COLUMN_TEXT_1,
765 SearchManager.SUGGEST_COLUMN_ICON_1,
766 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA
767 };
768 private final ArrayList<String> mResults;
769 public ResultsCursor(ArrayList<String> results) {
770 mResults = results;
771 }
Michael Kolbfe251992010-07-08 15:41:55 -0700772 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500773 public int getCount() { return mResults.size(); }
774
Michael Kolbfe251992010-07-08 15:41:55 -0700775 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500776 public String[] getColumnNames() {
777 return RESULTS_COLUMNS;
778 }
779
Michael Kolbfe251992010-07-08 15:41:55 -0700780 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500781 public String getString(int column) {
782 switch (column) {
783 case RESULT_ACTION_ID:
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -0500784 return RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500785 case RESULT_TEXT_ID:
786 // The data is used when the phone is in landscape mode. We
787 // still want to show the result string.
788 case RESULT_DATA_ID:
789 return mResults.get(mPos);
790 case RESULT_EXTRA_ID:
791 // The Intent's extra data will store the index into
792 // mResults so the BrowserActivity will know which result to
793 // use.
794 return Integer.toString(mPos);
795 case RESULT_ICON_ID:
796 return Integer.valueOf(R.drawable.magnifying_glass)
797 .toString();
798 default:
799 return null;
800 }
801 }
Michael Kolbfe251992010-07-08 15:41:55 -0700802 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500803 public short getShort(int column) {
804 throw new UnsupportedOperationException();
805 }
Michael Kolbfe251992010-07-08 15:41:55 -0700806 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500807 public int getInt(int column) {
808 throw new UnsupportedOperationException();
809 }
Michael Kolbfe251992010-07-08 15:41:55 -0700810 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500811 public long getLong(int column) {
812 if ((mPos != -1) && column == 0) {
813 return mPos; // use row# as the _id
814 }
815 throw new UnsupportedOperationException();
816 }
Michael Kolbfe251992010-07-08 15:41:55 -0700817 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500818 public float getFloat(int column) {
819 throw new UnsupportedOperationException();
820 }
Michael Kolbfe251992010-07-08 15:41:55 -0700821 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500822 public double getDouble(int column) {
823 throw new UnsupportedOperationException();
824 }
Michael Kolbfe251992010-07-08 15:41:55 -0700825 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500826 public boolean isNull(int column) {
827 throw new UnsupportedOperationException();
828 }
829 }
830
Jeff Hamilton8b139742010-07-29 16:06:53 -0500831 /** Contains custom suggestions results set by the UI */
Leon Scroggins58d56c62010-01-28 15:12:40 -0500832 private ResultsCursor mResultsCursor;
Jeff Hamilton8b139742010-07-29 16:06:53 -0500833 /** Locks access to {@link #mResultsCursor} */
834 private Object mResultsCursorLock = new Object();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500835
836 /**
837 * Provide a set of results to be returned to query, intended to be used
838 * by the SearchDialog when the BrowserActivity is in voice search mode.
839 * @param results Strings to display in the dropdown from the SearchDialog
840 */
841 /* package */ void setQueryResults(ArrayList<String> results) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500842 synchronized (mResultsCursorLock) {
843 if (results == null) {
844 mResultsCursor = null;
845 } else {
846 mResultsCursor = new ResultsCursor(results);
847 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500848 }
849 }
850
The Android Open Source Project0c908882009-03-03 19:32:16 -0800851 @Override
852 public Cursor query(Uri url, String[] projectionIn, String selection,
Satish Sampath565505b2009-05-29 15:37:27 +0100853 String[] selectionArgs, String sortOrder)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800854 throws IllegalStateException {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800855 int match = URI_MATCHER.match(url);
856 if (match == -1) {
857 throw new IllegalArgumentException("Unknown URL");
858 }
Jeff Hamilton8b139742010-07-29 16:06:53 -0500859
860 // If results for the suggestion are already ready just return them directly
861 synchronized (mResultsCursorLock) {
862 if (match == URI_MATCH_SUGGEST && mResultsCursor != null) {
863 Cursor results = mResultsCursor;
864 mResultsCursor = null;
865 return results;
866 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500867 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800868
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100869 if (match == URI_MATCH_SUGGEST || match == URI_MATCH_BOOKMARKS_SUGGEST) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500870 // Handle suggestions
871 return doSuggestQuery(selection, selectionArgs, match == URI_MATCH_BOOKMARKS_SUGGEST);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800872 }
873
874 String[] projection = null;
875 if (projectionIn != null && projectionIn.length > 0) {
876 projection = new String[projectionIn.length + 1];
877 System.arraycopy(projectionIn, 0, projection, 0, projectionIn.length);
878 projection[projectionIn.length] = "_id AS _id";
879 }
880
Jeff Hamilton8b139742010-07-29 16:06:53 -0500881 String whereClause = null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800882 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500883 whereClause = "_id = " + url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800884 }
885
Jeff Hamilton8b139742010-07-29 16:06:53 -0500886 Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_NAMES[match % 10], projection,
887 DatabaseUtils.concatenateWhere(whereClause, selection), selectionArgs,
888 null, null, sortOrder, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800889 c.setNotificationUri(getContext().getContentResolver(), url);
890 return c;
891 }
892
Jeff Hamilton8b139742010-07-29 16:06:53 -0500893 private Cursor doSuggestQuery(String selection, String[] selectionArgs, boolean bookmarksOnly) {
894 String suggestSelection;
895 String [] myArgs;
896 if (selectionArgs[0] == null || selectionArgs[0].equals("")) {
897 return new MySuggestionCursor(null, null, "");
898 } else {
899 String like = selectionArgs[0] + "%";
900 if (selectionArgs[0].startsWith("http")
901 || selectionArgs[0].startsWith("file")) {
902 myArgs = new String[1];
903 myArgs[0] = like;
904 suggestSelection = selection;
905 } else {
906 SUGGEST_ARGS[0] = "http://" + like;
907 SUGGEST_ARGS[1] = "http://www." + like;
908 SUGGEST_ARGS[2] = "https://" + like;
909 SUGGEST_ARGS[3] = "https://www." + like;
910 // To match against titles.
911 SUGGEST_ARGS[4] = like;
912 myArgs = SUGGEST_ARGS;
913 suggestSelection = SUGGEST_SELECTION;
914 }
915 }
916
917 Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
918 SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
919 ORDER_BY, Integer.toString(mMaxSuggestionLongSize));
920
921 if (bookmarksOnly || Patterns.WEB_URL.matcher(selectionArgs[0]).matches()) {
922 return new MySuggestionCursor(c, null, "");
923 } else {
Bjorn Bringertd69f51d2010-09-13 14:06:41 +0100924 // get search suggestions if there is still space in the list
925 if (myArgs != null && myArgs.length > 1
Leon Scroggins III4fb6cc92010-09-17 15:01:28 -0400926 && c.getCount() < (MAX_SUGGEST_SHORT_SMALL - 1)) {
Bjorn Bringertd69f51d2010-09-13 14:06:41 +0100927 SearchEngine searchEngine = mSettings.getSearchEngine();
928 if (searchEngine != null && searchEngine.supportsSuggestions()) {
929 Cursor sc = searchEngine.getSuggestions(getContext(), selectionArgs[0]);
930 return new MySuggestionCursor(c, sc, selectionArgs[0]);
931 }
Jeff Hamilton8b139742010-07-29 16:06:53 -0500932 }
933 return new MySuggestionCursor(c, null, selectionArgs[0]);
934 }
935 }
936
The Android Open Source Project0c908882009-03-03 19:32:16 -0800937 @Override
938 public String getType(Uri url) {
939 int match = URI_MATCHER.match(url);
940 switch (match) {
941 case URI_MATCH_BOOKMARKS:
942 return "vnd.android.cursor.dir/bookmark";
943
944 case URI_MATCH_BOOKMARKS_ID:
945 return "vnd.android.cursor.item/bookmark";
946
947 case URI_MATCH_SEARCHES:
948 return "vnd.android.cursor.dir/searches";
949
950 case URI_MATCH_SEARCHES_ID:
951 return "vnd.android.cursor.item/searches";
952
953 case URI_MATCH_SUGGEST:
954 return SearchManager.SUGGEST_MIME_TYPE;
955
956 default:
957 throw new IllegalArgumentException("Unknown URL");
958 }
959 }
960
961 @Override
962 public Uri insert(Uri url, ContentValues initialValues) {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700963 boolean isBookmarkTable = false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800964 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
965
966 int match = URI_MATCHER.match(url);
967 Uri uri = null;
968 switch (match) {
969 case URI_MATCH_BOOKMARKS: {
970 // Insert into the bookmarks table
971 long rowID = db.insert(TABLE_NAMES[URI_MATCH_BOOKMARKS], "url",
972 initialValues);
973 if (rowID > 0) {
974 uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
975 rowID);
976 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700977 isBookmarkTable = true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800978 break;
979 }
980
981 case URI_MATCH_SEARCHES: {
982 // Insert into the searches table
983 long rowID = db.insert(TABLE_NAMES[URI_MATCH_SEARCHES], "url",
984 initialValues);
985 if (rowID > 0) {
986 uri = ContentUris.withAppendedId(Browser.SEARCHES_URI,
987 rowID);
988 }
989 break;
990 }
991
992 default:
993 throw new IllegalArgumentException("Unknown URL");
994 }
995
996 if (uri == null) {
997 throw new IllegalArgumentException("Unknown URL");
998 }
999 getContext().getContentResolver().notifyChange(uri, null);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001000
Christopher Tatef0c36f72009-07-28 15:24:05 -07001001 // Back up the new bookmark set if we just inserted one.
1002 // A row created when bookmarks are added from scratch will have
1003 // bookmark=1 in the initial value set.
1004 if (isBookmarkTable
1005 && initialValues.containsKey(BookmarkColumns.BOOKMARK)
1006 && initialValues.getAsInteger(BookmarkColumns.BOOKMARK) != 0) {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001007 mBackupManager.dataChanged();
1008 }
The Android Open Source Project0c908882009-03-03 19:32:16 -08001009 return uri;
1010 }
1011
1012 @Override
1013 public int delete(Uri url, String where, String[] whereArgs) {
1014 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1015
1016 int match = URI_MATCHER.match(url);
1017 if (match == -1 || match == URI_MATCH_SUGGEST) {
1018 throw new IllegalArgumentException("Unknown URL");
1019 }
1020
Christopher Tatef0c36f72009-07-28 15:24:05 -07001021 // need to know whether it's the bookmarks table for a couple of reasons
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001022 boolean isBookmarkTable = (match == URI_MATCH_BOOKMARKS_ID);
Christopher Tatef0c36f72009-07-28 15:24:05 -07001023 String id = null;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001024
1025 if (isBookmarkTable || match == URI_MATCH_SEARCHES_ID) {
The Android Open Source Project0c908882009-03-03 19:32:16 -08001026 StringBuilder sb = new StringBuilder();
1027 if (where != null && where.length() > 0) {
1028 sb.append("( ");
1029 sb.append(where);
1030 sb.append(" ) AND ");
1031 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001032 id = url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001033 sb.append("_id = ");
Christopher Tatef0c36f72009-07-28 15:24:05 -07001034 sb.append(id);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001035 where = sb.toString();
1036 }
1037
Christopher Tatef0c36f72009-07-28 15:24:05 -07001038 ContentResolver cr = getContext().getContentResolver();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001039
Christopher Tatef0c36f72009-07-28 15:24:05 -07001040 // we'lll need to back up the bookmark set if we are about to delete one
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001041 if (isBookmarkTable) {
Christopher Tatef0c36f72009-07-28 15:24:05 -07001042 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
1043 new String[] { BookmarkColumns.BOOKMARK },
1044 "_id = " + id, null, null);
1045 if (cursor.moveToNext()) {
1046 if (cursor.getInt(0) != 0) {
1047 // yep, this record is a bookmark
1048 mBackupManager.dataChanged();
1049 }
1050 }
1051 cursor.close();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001052 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001053
1054 int count = db.delete(TABLE_NAMES[match % 10], where, whereArgs);
1055 cr.notifyChange(url, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001056 return count;
1057 }
1058
1059 @Override
1060 public int update(Uri url, ContentValues values, String where,
1061 String[] whereArgs) {
1062 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1063
1064 int match = URI_MATCHER.match(url);
1065 if (match == -1 || match == URI_MATCH_SUGGEST) {
1066 throw new IllegalArgumentException("Unknown URL");
1067 }
1068
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001069 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
The Android Open Source Project0c908882009-03-03 19:32:16 -08001070 StringBuilder sb = new StringBuilder();
1071 if (where != null && where.length() > 0) {
1072 sb.append("( ");
1073 sb.append(where);
1074 sb.append(" ) AND ");
1075 }
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001076 String id = url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001077 sb.append("_id = ");
Christopher Tatef0c36f72009-07-28 15:24:05 -07001078 sb.append(id);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001079 where = sb.toString();
1080 }
1081
Christopher Tatef0c36f72009-07-28 15:24:05 -07001082 ContentResolver cr = getContext().getContentResolver();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001083
Christopher Tatef0c36f72009-07-28 15:24:05 -07001084 // Not all bookmark-table updates should be backed up. Look to see
1085 // whether we changed the title, url, or "is a bookmark" state, and
1086 // request a backup if so.
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001087 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_BOOKMARKS) {
1088 boolean changingBookmarks = false;
Christopher Tatef0c36f72009-07-28 15:24:05 -07001089 // Alterations to the bookmark field inherently change the bookmark
1090 // set, so we don't need to query the record; we know a priori that
1091 // we will need to back up this change.
1092 if (values.containsKey(BookmarkColumns.BOOKMARK)) {
1093 changingBookmarks = true;
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001094 } else if ((values.containsKey(BookmarkColumns.TITLE)
1095 || values.containsKey(BookmarkColumns.URL))
1096 && values.containsKey(BookmarkColumns._ID)) {
1097 // If a title or URL has been changed, check to see if it is to
1098 // a bookmark. The ID should have been included in the update,
1099 // so use it.
Christopher Tatef0c36f72009-07-28 15:24:05 -07001100 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
1101 new String[] { BookmarkColumns.BOOKMARK },
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001102 BookmarkColumns._ID + " = "
1103 + values.getAsString(BookmarkColumns._ID), null, null);
Christopher Tatef0c36f72009-07-28 15:24:05 -07001104 if (cursor.moveToNext()) {
1105 changingBookmarks = (cursor.getInt(0) != 0);
1106 }
1107 cursor.close();
1108 }
1109
1110 // if this *is* a bookmark row we're altering, we need to back it up.
1111 if (changingBookmarks) {
1112 mBackupManager.dataChanged();
1113 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001114 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001115
1116 int ret = db.update(TABLE_NAMES[match % 10], values, where, whereArgs);
1117 cr.notifyChange(url, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001118 return ret;
1119 }
Satish Sampath565505b2009-05-29 15:37:27 +01001120
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001121 /**
1122 * Strips the provided url of preceding "http://" and any trailing "/". Does not
1123 * strip "https://". If the provided string cannot be stripped, the original string
1124 * is returned.
Satish Sampath565505b2009-05-29 15:37:27 +01001125 *
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001126 * TODO: Put this in TextUtils to be used by other packages doing something similar.
Satish Sampath565505b2009-05-29 15:37:27 +01001127 *
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001128 * @param url a url to strip, like "http://www.google.com/"
1129 * @return a stripped url like "www.google.com", or the original string if it could
1130 * not be stripped
1131 */
1132 private static String stripUrl(String url) {
1133 if (url == null) return null;
1134 Matcher m = STRIP_URL_PATTERN.matcher(url);
1135 if (m.matches() && m.groupCount() == 3) {
1136 return m.group(2);
1137 } else {
1138 return url;
1139 }
1140 }
1141
Michael Kolbfe251992010-07-08 15:41:55 -07001142 public static Cursor getBookmarksSuggestions(ContentResolver cr, String constraint) {
1143 Uri uri = Uri.parse("content://browser/" + SearchManager.SUGGEST_URI_PATH_QUERY);
1144 return cr.query(uri, SUGGEST_PROJECTION, SUGGEST_SELECTION,
1145 new String[] { constraint }, ORDER_BY);
1146 }
1147
The Android Open Source Project0c908882009-03-03 19:32:16 -08001148}