blob: 33f300647860b2e5f8f8756464c4d99acc534969 [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 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;
Leon Scroggins62b71f72009-06-12 17:51:22 -040034import android.database.ContentObserver;
The Android Open Source Project0c908882009-03-03 19:32:16 -080035import android.database.Cursor;
Jeff Hamilton8b139742010-07-29 16:06:53 -050036import android.database.DatabaseUtils;
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;
Christopher Tatef0c36f72009-07-28 15:24:05 -070044import android.provider.Browser.BookmarkColumns;
Jeff Hamilton84029622010-08-05 14:29:28 -050045import android.provider.Settings;
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;
Dan Egnor5ee906c2009-11-18 12:11:49 -080050
Andrei Popescu1b20b9d2009-09-21 18:49:42 +010051import java.io.File;
52import java.io.FilenameFilter;
Leon Scroggins58d56c62010-01-28 15:12:40 -050053import java.util.ArrayList;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010054import java.util.Date;
Mike LeBeauc42f81b2009-05-14 15:04:19 -070055import java.util.regex.Matcher;
56import java.util.regex.Pattern;
The Android Open Source Project0c908882009-03-03 19:32:16 -080057
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -070058
The Android Open Source Project0c908882009-03-03 19:32:16 -080059public class BrowserProvider extends ContentProvider {
60
61 private SQLiteOpenHelper mOpenHelper;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070062 private BackupManager mBackupManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080063 private static final String sDatabaseName = "browser.db";
64 private static final String TAG = "BrowserProvider";
65 private static final String ORDER_BY = "visits DESC, date DESC";
66
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070067 private static final String PICASA_URL = "http://picasaweb.google.com/m/" +
68 "viewer?source=androidclient";
69
The Android Open Source Project0c908882009-03-03 19:32:16 -080070 private static final String[] TABLE_NAMES = new String[] {
Bjorn Bringerta7611812010-03-24 11:12:02 +000071 "bookmarks", "searches"
The Android Open Source Project0c908882009-03-03 19:32:16 -080072 };
73 private static final String[] SUGGEST_PROJECTION = new String[] {
Leon Scrogginsb4464432009-11-25 12:37:50 -050074 "_id", "url", "title", "bookmark", "user_entered"
The Android Open Source Project0c908882009-03-03 19:32:16 -080075 };
Satish Sampath565505b2009-05-29 15:37:27 +010076 private static final String SUGGEST_SELECTION =
Leon Scrogginsb4464432009-11-25 12:37:50 -050077 "(url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?"
78 + " OR title LIKE ?) AND (bookmark = 1 OR user_entered = 1)";
Leon Scrogginsbd359cc2009-05-26 15:57:35 -040079 private String[] SUGGEST_ARGS = new String[5];
The Android Open Source Project0c908882009-03-03 19:32:16 -080080
81 // shared suggestion array index, make sure to match COLUMNS
82 private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1;
83 private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2;
84 private static final int SUGGEST_COLUMN_TEXT_1_ID = 3;
85 private static final int SUGGEST_COLUMN_TEXT_2_ID = 4;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +000086 private static final int SUGGEST_COLUMN_TEXT_2_URL_ID = 5;
87 private static final int SUGGEST_COLUMN_ICON_1_ID = 6;
88 private static final int SUGGEST_COLUMN_ICON_2_ID = 7;
89 private static final int SUGGEST_COLUMN_QUERY_ID = 8;
Bjorn Bringert04851702009-09-22 10:36:01 +010090 private static final int SUGGEST_COLUMN_INTENT_EXTRA_DATA = 9;
The Android Open Source Project0c908882009-03-03 19:32:16 -080091
Michael Kolbfe251992010-07-08 15:41:55 -070092 // how many suggestions will be shown in dropdown
93 // 0..SHORT: filled by browser db
94 private static final int MAX_SUGGEST_SHORT_SMALL = 3;
95 // SHORT..LONG: filled by search suggestions
96 private static final int MAX_SUGGEST_LONG_SMALL = 6;
97
98 // large screen size shows more
99 private static final int MAX_SUGGEST_SHORT_LARGE = 6;
100 private static final int MAX_SUGGEST_LONG_LARGE = 9;
101
102
The Android Open Source Project0c908882009-03-03 19:32:16 -0800103 // shared suggestion columns
104 private static final String[] COLUMNS = new String[] {
105 "_id",
106 SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
107 SearchManager.SUGGEST_COLUMN_INTENT_DATA,
108 SearchManager.SUGGEST_COLUMN_TEXT_1,
109 SearchManager.SUGGEST_COLUMN_TEXT_2,
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000110 SearchManager.SUGGEST_COLUMN_TEXT_2_URL,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800111 SearchManager.SUGGEST_COLUMN_ICON_1,
112 SearchManager.SUGGEST_COLUMN_ICON_2,
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700113 SearchManager.SUGGEST_COLUMN_QUERY,
Bjorn Bringert04851702009-09-22 10:36:01 +0100114 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA};
The Android Open Source Project0c908882009-03-03 19:32:16 -0800115
The Android Open Source Project0c908882009-03-03 19:32:16 -0800116
117 // make sure that these match the index of TABLE_NAMES
118 private static final int URI_MATCH_BOOKMARKS = 0;
119 private static final int URI_MATCH_SEARCHES = 1;
120 // (id % 10) should match the table name index
121 private static final int URI_MATCH_BOOKMARKS_ID = 10;
122 private static final int URI_MATCH_SEARCHES_ID = 11;
123 //
124 private static final int URI_MATCH_SUGGEST = 20;
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100125 private static final int URI_MATCH_BOOKMARKS_SUGGEST = 21;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800126
127 private static final UriMatcher URI_MATCHER;
128
129 static {
130 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
131 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS],
132 URI_MATCH_BOOKMARKS);
133 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/#",
134 URI_MATCH_BOOKMARKS_ID);
135 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES],
136 URI_MATCH_SEARCHES);
137 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES] + "/#",
138 URI_MATCH_SEARCHES_ID);
139 URI_MATCHER.addURI("browser", SearchManager.SUGGEST_URI_PATH_QUERY,
140 URI_MATCH_SUGGEST);
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100141 URI_MATCHER.addURI("browser",
142 TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/" + SearchManager.SUGGEST_URI_PATH_QUERY,
143 URI_MATCH_BOOKMARKS_SUGGEST);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800144 }
145
146 // 1 -> 2 add cache table
147 // 2 -> 3 update history table
148 // 3 -> 4 add passwords table
149 // 4 -> 5 add settings table
150 // 5 -> 6 ?
151 // 6 -> 7 ?
152 // 7 -> 8 drop proxy table
153 // 8 -> 9 drop settings table
154 // 9 -> 10 add form_urls and form_data
155 // 10 -> 11 add searches table
156 // 11 -> 12 modify cache table
157 // 12 -> 13 modify cache table
158 // 13 -> 14 correspond with Google Bookmarks schema
159 // 14 -> 15 move couple of tables to either browser private database or webview database
160 // 15 -> 17 Set it up for the SearchManager
161 // 17 -> 18 Added favicon in bookmarks table for Home shortcuts
162 // 18 -> 19 Remove labels table
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400163 // 19 -> 20 Added thumbnail
Patrick Scott3918d442009-08-04 13:22:29 -0400164 // 20 -> 21 Added touch_icon
Grace Kloba6b52a552009-09-03 16:29:56 -0700165 // 21 -> 22 Remove "clientid"
Leon Scrogginsb4464432009-11-25 12:37:50 -0500166 // 22 -> 23 Added user_entered
167 private static final int DATABASE_VERSION = 23;
Satish Sampath565505b2009-05-29 15:37:27 +0100168
Mike LeBeauc42f81b2009-05-14 15:04:19 -0700169 // Regular expression which matches http://, followed by some stuff, followed by
170 // optionally a trailing slash, all matched as separate groups.
171 private static final Pattern STRIP_URL_PATTERN = Pattern.compile("^(http://)(.*?)(/$)?");
Satish Sampath565505b2009-05-29 15:37:27 +0100172
Bjorn Bringertd8b0ad22009-06-22 10:36:29 +0100173 private SearchManager mSearchManager;
174
Michael Kolbfe251992010-07-08 15:41:55 -0700175 private int mMaxSuggestionShortSize;
176 private int mMaxSuggestionLongSize;
177
The Android Open Source Project0c908882009-03-03 19:32:16 -0800178 public BrowserProvider() {
179 }
Satish Sampath565505b2009-05-29 15:37:27 +0100180
Patrick Scott43914692010-02-19 10:10:10 -0500181 // XXX: This is a major hack to remove our dependency on gsf constants and
182 // its content provider. http://b/issue?id=2425179
183 static String getClientId(ContentResolver cr) {
184 String ret = "android-google";
185 Cursor c = null;
186 try {
187 c = cr.query(Uri.parse("content://com.google.settings/partner"),
188 new String[] { "value" }, "name='client_id'", null, null);
189 if (c != null && c.moveToNext()) {
190 ret = c.getString(0);
191 }
192 } catch (RuntimeException ex) {
193 // fall through to return the default
194 } finally {
195 if (c != null) {
196 c.close();
197 }
198 }
199 return ret;
200 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800201
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700202 private static CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800203 StringBuffer sb = new StringBuffer();
204 int lastCharLoc = 0;
Satish Sampath565505b2009-05-29 15:37:27 +0100205
Patrick Scott43914692010-02-19 10:10:10 -0500206 final String client_id = getClientId(context.getContentResolver());
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700207
The Android Open Source Project0c908882009-03-03 19:32:16 -0800208 for (int i = 0; i < srcString.length(); ++i) {
209 char c = srcString.charAt(i);
210 if (c == '{') {
211 sb.append(srcString.subSequence(lastCharLoc, i));
212 lastCharLoc = i;
213 inner:
214 for (int j = i; j < srcString.length(); ++j) {
215 char k = srcString.charAt(j);
216 if (k == '}') {
217 String propertyKeyValue = srcString.subSequence(i + 1, j).toString();
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700218 if (propertyKeyValue.equals("CLIENT_ID")) {
219 sb.append(client_id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800220 } else {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700221 sb.append("unknown");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800222 }
223 lastCharLoc = j + 1;
224 i = j;
225 break inner;
226 }
227 }
228 }
229 }
230 if (srcString.length() - lastCharLoc > 0) {
231 // Put on the tail, if there is one
232 sb.append(srcString.subSequence(lastCharLoc, srcString.length()));
233 }
234 return sb;
235 }
236
237 private static class DatabaseHelper extends SQLiteOpenHelper {
238 private Context mContext;
239
240 public DatabaseHelper(Context context) {
241 super(context, sDatabaseName, null, DATABASE_VERSION);
242 mContext = context;
243 }
244
245 @Override
246 public void onCreate(SQLiteDatabase db) {
247 db.execSQL("CREATE TABLE bookmarks (" +
248 "_id INTEGER PRIMARY KEY," +
249 "title TEXT," +
250 "url TEXT," +
251 "visits INTEGER," +
252 "date LONG," +
253 "created LONG," +
254 "description TEXT," +
255 "bookmark INTEGER," +
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400256 "favicon BLOB DEFAULT NULL," +
Patrick Scott3918d442009-08-04 13:22:29 -0400257 "thumbnail BLOB DEFAULT NULL," +
Leon Scrogginsb4464432009-11-25 12:37:50 -0500258 "touch_icon BLOB DEFAULT NULL," +
259 "user_entered INTEGER" +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800260 ");");
261
262 final CharSequence[] bookmarks = mContext.getResources()
263 .getTextArray(R.array.bookmarks);
264 int size = bookmarks.length;
265 try {
266 for (int i = 0; i < size; i = i + 2) {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700267 CharSequence bookmarkDestination = replaceSystemPropertyInString(mContext, bookmarks[i + 1]);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800268 db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
269 "date, created, bookmark)" + " VALUES('" +
Satish Sampath565505b2009-05-29 15:37:27 +0100270 bookmarks[i] + "', '" + bookmarkDestination +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800271 "', 0, 0, 0, 1);");
272 }
273 } catch (ArrayIndexOutOfBoundsException e) {
274 }
275
276 db.execSQL("CREATE TABLE searches (" +
277 "_id INTEGER PRIMARY KEY," +
278 "search TEXT," +
279 "date LONG" +
280 ");");
281 }
282
283 @Override
284 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
285 Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400286 + newVersion);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800287 if (oldVersion == 18) {
288 db.execSQL("DROP TABLE IF EXISTS labels");
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400289 }
290 if (oldVersion <= 19) {
291 db.execSQL("ALTER TABLE bookmarks ADD COLUMN thumbnail BLOB DEFAULT NULL;");
Patrick Scott3918d442009-08-04 13:22:29 -0400292 }
293 if (oldVersion < 21) {
294 db.execSQL("ALTER TABLE bookmarks ADD COLUMN touch_icon BLOB DEFAULT NULL;");
Grace Kloba6b52a552009-09-03 16:29:56 -0700295 }
296 if (oldVersion < 22) {
297 db.execSQL("DELETE FROM bookmarks WHERE (bookmark = 0 AND url LIKE \"%.google.%client=ms-%\")");
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100298 removeGears();
Leon Scrogginsb4464432009-11-25 12:37:50 -0500299 }
300 if (oldVersion < 23) {
301 db.execSQL("ALTER TABLE bookmarks ADD COLUMN user_entered INTEGER;");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800302 } else {
303 db.execSQL("DROP TABLE IF EXISTS bookmarks");
304 db.execSQL("DROP TABLE IF EXISTS searches");
305 onCreate(db);
306 }
307 }
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100308
309 private void removeGears() {
Andrei Popescu93bea962010-03-23 15:04:36 +0000310 new Thread() {
311 @Override
312 public void run() {
313 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100314 String browserDataDirString = mContext.getApplicationInfo().dataDir;
315 final String appPluginsDirString = "app_plugins";
316 final String gearsPrefix = "gears";
317 File appPluginsDir = new File(browserDataDirString + File.separator
318 + appPluginsDirString);
319 if (!appPluginsDir.exists()) {
Andrei Popescu93bea962010-03-23 15:04:36 +0000320 return;
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100321 }
322 // Delete the Gears plugin files
323 File[] gearsFiles = appPluginsDir.listFiles(new FilenameFilter() {
324 public boolean accept(File dir, String filename) {
325 return filename.startsWith(gearsPrefix);
326 }
327 });
328 for (int i = 0; i < gearsFiles.length; ++i) {
329 if (gearsFiles[i].isDirectory()) {
330 deleteDirectory(gearsFiles[i]);
331 } else {
332 gearsFiles[i].delete();
333 }
334 }
335 // Delete the Gears data files
336 File gearsDataDir = new File(browserDataDirString + File.separator
337 + gearsPrefix);
338 if (!gearsDataDir.exists()) {
Andrei Popescu93bea962010-03-23 15:04:36 +0000339 return;
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100340 }
341 deleteDirectory(gearsDataDir);
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100342 }
343
344 private void deleteDirectory(File currentDir) {
345 File[] files = currentDir.listFiles();
346 for (int i = 0; i < files.length; ++i) {
347 if (files[i].isDirectory()) {
348 deleteDirectory(files[i]);
349 }
350 files[i].delete();
351 }
352 currentDir.delete();
353 }
Andrei Popescu93bea962010-03-23 15:04:36 +0000354 }.start();
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100355 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800356 }
357
358 @Override
359 public boolean onCreate() {
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700360 final Context context = getContext();
Michael Kolbfe251992010-07-08 15:41:55 -0700361 boolean xlargeScreenSize = (context.getResources().getConfiguration().screenLayout
362 & Configuration.SCREENLAYOUT_SIZE_MASK)
363 == Configuration.SCREENLAYOUT_SIZE_XLARGE;
364 boolean isPortrait = (context.getResources().getConfiguration().orientation
365 == Configuration.ORIENTATION_PORTRAIT);
366
367
368 if (xlargeScreenSize && isPortrait) {
369 mMaxSuggestionLongSize = MAX_SUGGEST_LONG_LARGE;
370 mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_LARGE;
371 } else {
372 mMaxSuggestionLongSize = MAX_SUGGEST_LONG_SMALL;
373 mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_SMALL;
374 }
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700375 mOpenHelper = new DatabaseHelper(context);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700376 mBackupManager = new BackupManager(context);
Satish Sampath565505b2009-05-29 15:37:27 +0100377 // we added "picasa web album" into default bookmarks for version 19.
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700378 // To avoid erasing the bookmark table, we added it explicitly for
379 // version 18 and 19 as in the other cases, we will erase the table.
380 if (DATABASE_VERSION == 18 || DATABASE_VERSION == 19) {
381 SharedPreferences p = PreferenceManager
382 .getDefaultSharedPreferences(context);
383 boolean fix = p.getBoolean("fix_picasa", true);
384 if (fix) {
385 fixPicasaBookmark();
386 Editor ed = p.edit();
387 ed.putBoolean("fix_picasa", false);
Brad Fitzpatrick1a6e0e82010-09-01 16:29:03 -0700388 ed.apply();
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700389 }
390 }
Bjorn Bringertd8b0ad22009-06-22 10:36:29 +0100391 mSearchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
Leon Scroggins62b71f72009-06-12 17:51:22 -0400392 mShowWebSuggestionsSettingChangeObserver
393 = new ShowWebSuggestionsSettingChangeObserver();
394 context.getContentResolver().registerContentObserver(
395 Settings.System.getUriFor(
396 Settings.System.SHOW_WEB_SUGGESTIONS),
397 true, mShowWebSuggestionsSettingChangeObserver);
398 updateShowWebSuggestions();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800399 return true;
400 }
401
Leon Scroggins62b71f72009-06-12 17:51:22 -0400402 /**
403 * This Observer will ensure that if the user changes the system
404 * setting of whether to display web suggestions, we will
405 * change accordingly.
406 */
407 /* package */ class ShowWebSuggestionsSettingChangeObserver
408 extends ContentObserver {
409 public ShowWebSuggestionsSettingChangeObserver() {
410 super(new Handler());
411 }
412
413 @Override
414 public void onChange(boolean selfChange) {
415 updateShowWebSuggestions();
416 }
417 }
418
419 private ShowWebSuggestionsSettingChangeObserver
420 mShowWebSuggestionsSettingChangeObserver;
421
422 // If non-null, then the system is set to show web suggestions,
423 // and this is the SearchableInfo to use to get them.
424 private SearchableInfo mSearchableInfo;
425
426 /**
427 * Check the system settings to see whether web suggestions are
428 * allowed. If so, store the SearchableInfo to grab suggestions
429 * while the user is typing.
430 */
431 private void updateShowWebSuggestions() {
432 mSearchableInfo = null;
433 Context context = getContext();
434 if (Settings.System.getInt(context.getContentResolver(),
435 Settings.System.SHOW_WEB_SUGGESTIONS,
436 1 /* default on */) == 1) {
Bjorn Bringert32747542010-02-18 21:59:21 +0000437 ComponentName webSearchComponent = mSearchManager.getWebSearchActivity();
438 if (webSearchComponent != null) {
439 mSearchableInfo = mSearchManager.getSearchableInfo(webSearchComponent);
Leon Scroggins62b71f72009-06-12 17:51:22 -0400440 }
441 }
442 }
443
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700444 private void fixPicasaBookmark() {
445 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
446 Cursor cursor = db.rawQuery("SELECT _id FROM bookmarks WHERE " +
447 "bookmark = 1 AND url = ?", new String[] { PICASA_URL });
448 try {
449 if (!cursor.moveToFirst()) {
450 // set "created" so that it will be on the top of the list
451 db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
452 "date, created, bookmark)" + " VALUES('" +
453 getContext().getString(R.string.picasa) + "', '"
454 + PICASA_URL + "', 0, 0, " + new Date().getTime()
455 + ", 1);");
456 }
457 } finally {
458 if (cursor != null) {
459 cursor.close();
460 }
461 }
462 }
463
The Android Open Source Project0c908882009-03-03 19:32:16 -0800464 /*
465 * Subclass AbstractCursor so we can combine multiple Cursors and add
Grace Kloba391df7c2010-03-01 19:51:49 -0800466 * "Search the web".
The Android Open Source Project0c908882009-03-03 19:32:16 -0800467 * Here are the rules.
Satish Sampath565505b2009-05-29 15:37:27 +0100468 * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
Grace Kloba391df7c2010-03-01 19:51:49 -0800469 * "Search the web";
470 * 2. If bookmark/history entries has a match, "Search the web" shows up at
471 * the second place. Otherwise, "Search the web" shows up at the first
472 * place.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800473 */
474 private class MySuggestionCursor extends AbstractCursor {
475 private Cursor mHistoryCursor;
476 private Cursor mSuggestCursor;
477 private int mHistoryCount;
478 private int mSuggestionCount;
Grace Klobad3992d42010-01-28 11:44:38 -0800479 private boolean mIncludeWebSearch;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800480 private String mString;
Satish Sampath565505b2009-05-29 15:37:27 +0100481 private int mSuggestText1Id;
482 private int mSuggestText2Id;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000483 private int mSuggestText2UrlId;
Satish Sampath565505b2009-05-29 15:37:27 +0100484 private int mSuggestQueryId;
Bjorn Bringert04851702009-09-22 10:36:01 +0100485 private int mSuggestIntentExtraDataId;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800486
487 public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
488 mHistoryCursor = hc;
489 mSuggestCursor = sc;
Grace Klobaf9b04272010-06-15 13:36:22 -0700490 mHistoryCount = hc != null ? hc.getCount() : 0;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800491 mSuggestionCount = sc != null ? sc.getCount() : 0;
Michael Kolbfe251992010-07-08 15:41:55 -0700492 if (mSuggestionCount > (mMaxSuggestionLongSize - mHistoryCount)) {
493 mSuggestionCount = mMaxSuggestionLongSize - mHistoryCount;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800494 }
495 mString = string;
Grace Klobad3992d42010-01-28 11:44:38 -0800496 mIncludeWebSearch = string.length() > 0;
Satish Sampath565505b2009-05-29 15:37:27 +0100497
498 // Some web suggest providers only give suggestions and have no description string for
499 // items. The order of the result columns may be different as well. So retrieve the
500 // column indices for the fields we need now and check before using below.
501 if (mSuggestCursor == null) {
502 mSuggestText1Id = -1;
503 mSuggestText2Id = -1;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000504 mSuggestText2UrlId = -1;
Satish Sampath565505b2009-05-29 15:37:27 +0100505 mSuggestQueryId = -1;
Bjorn Bringert04851702009-09-22 10:36:01 +0100506 mSuggestIntentExtraDataId = -1;
Satish Sampath565505b2009-05-29 15:37:27 +0100507 } else {
508 mSuggestText1Id = mSuggestCursor.getColumnIndex(
509 SearchManager.SUGGEST_COLUMN_TEXT_1);
510 mSuggestText2Id = mSuggestCursor.getColumnIndex(
511 SearchManager.SUGGEST_COLUMN_TEXT_2);
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000512 mSuggestText2UrlId = mSuggestCursor.getColumnIndex(
513 SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
Satish Sampath565505b2009-05-29 15:37:27 +0100514 mSuggestQueryId = mSuggestCursor.getColumnIndex(
515 SearchManager.SUGGEST_COLUMN_QUERY);
Bjorn Bringert04851702009-09-22 10:36:01 +0100516 mSuggestIntentExtraDataId = mSuggestCursor.getColumnIndex(
517 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
Satish Sampath565505b2009-05-29 15:37:27 +0100518 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800519 }
520
521 @Override
522 public boolean onMove(int oldPosition, int newPosition) {
523 if (mHistoryCursor == null) {
524 return false;
525 }
Grace Klobad3992d42010-01-28 11:44:38 -0800526 if (mIncludeWebSearch) {
Grace Kloba391df7c2010-03-01 19:51:49 -0800527 if (mHistoryCount == 0 && newPosition == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800528 return true;
Grace Kloba391df7c2010-03-01 19:51:49 -0800529 } else if (mHistoryCount > 0) {
530 if (newPosition == 0) {
531 mHistoryCursor.moveToPosition(0);
532 return true;
533 } else if (newPosition == 1) {
534 return true;
535 }
Grace Klobad3992d42010-01-28 11:44:38 -0800536 }
Grace Kloba391df7c2010-03-01 19:51:49 -0800537 newPosition--;
Grace Klobad3992d42010-01-28 11:44:38 -0800538 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800539 if (mHistoryCount > newPosition) {
540 mHistoryCursor.moveToPosition(newPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800541 } else {
Grace Klobad3992d42010-01-28 11:44:38 -0800542 mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800543 }
544 return true;
545 }
546
547 @Override
548 public int getCount() {
Grace Klobad3992d42010-01-28 11:44:38 -0800549 if (mIncludeWebSearch) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800550 return mHistoryCount + mSuggestionCount + 1;
551 } else {
552 return mHistoryCount + mSuggestionCount;
553 }
554 }
555
556 @Override
557 public String[] getColumnNames() {
558 return COLUMNS;
559 }
Satish Sampath565505b2009-05-29 15:37:27 +0100560
The Android Open Source Project0c908882009-03-03 19:32:16 -0800561 @Override
562 public String getString(int columnIndex) {
563 if ((mPos != -1 && mHistoryCursor != null)) {
Grace Kloba391df7c2010-03-01 19:51:49 -0800564 int type = -1; // 0: web search; 1: history; 2: suggestion
565 if (mIncludeWebSearch) {
566 if (mHistoryCount == 0 && mPos == 0) {
567 type = 0;
568 } else if (mHistoryCount > 0) {
569 if (mPos == 0) {
570 type = 1;
571 } else if (mPos == 1) {
572 type = 0;
573 }
574 }
575 if (type == -1) type = (mPos - 1) < mHistoryCount ? 1 : 2;
576 } else {
577 type = mPos < mHistoryCount ? 1 : 2;
578 }
579
The Android Open Source Project0c908882009-03-03 19:32:16 -0800580 switch(columnIndex) {
581 case SUGGEST_COLUMN_INTENT_ACTION_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800582 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800583 return Intent.ACTION_VIEW;
584 } else {
585 return Intent.ACTION_SEARCH;
586 }
587
588 case SUGGEST_COLUMN_INTENT_DATA_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800589 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800590 return mHistoryCursor.getString(1);
591 } else {
592 return null;
593 }
594
595 case SUGGEST_COLUMN_TEXT_1_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800596 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800597 return mString;
Grace Kloba391df7c2010-03-01 19:51:49 -0800598 } else if (type == 1) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700599 return getHistoryTitle();
Grace Klobad3992d42010-01-28 11:44:38 -0800600 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100601 if (mSuggestText1Id == -1) return null;
602 return mSuggestCursor.getString(mSuggestText1Id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800603 }
604
605 case SUGGEST_COLUMN_TEXT_2_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800606 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800607 return getContext().getString(R.string.search_the_web);
Grace Kloba391df7c2010-03-01 19:51:49 -0800608 } else if (type == 1) {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000609 return null; // Use TEXT_2_URL instead
Grace Klobad3992d42010-01-28 11:44:38 -0800610 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100611 if (mSuggestText2Id == -1) return null;
612 return mSuggestCursor.getString(mSuggestText2Id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800613 }
614
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000615 case SUGGEST_COLUMN_TEXT_2_URL_ID:
616 if (type == 0) {
617 return null;
618 } else if (type == 1) {
619 return getHistoryUrl();
620 } else {
621 if (mSuggestText2UrlId == -1) return null;
622 return mSuggestCursor.getString(mSuggestText2UrlId);
623 }
624
The Android Open Source Project0c908882009-03-03 19:32:16 -0800625 case SUGGEST_COLUMN_ICON_1_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800626 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800627 if (mHistoryCursor.getInt(3) == 1) {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400628 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800629 R.drawable.ic_search_category_bookmark)
630 .toString();
631 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400632 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800633 R.drawable.ic_search_category_history)
634 .toString();
635 }
636 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400637 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800638 R.drawable.ic_search_category_suggest)
639 .toString();
640 }
641
642 case SUGGEST_COLUMN_ICON_2_ID:
Leon Scroggins31887fd2009-05-18 16:58:08 -0400643 return "0";
The Android Open Source Project0c908882009-03-03 19:32:16 -0800644
645 case SUGGEST_COLUMN_QUERY_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800646 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800647 return mString;
Grace Kloba391df7c2010-03-01 19:51:49 -0800648 } else if (type == 1) {
Mike LeBeau2af73052009-06-23 17:36:59 -0700649 // Return the url in the intent query column. This is ignored
650 // within the browser because our searchable is set to
651 // android:searchMode="queryRewriteFromData", but it is used by
652 // global search for query rewriting.
653 return mHistoryCursor.getString(1);
Grace Klobad3992d42010-01-28 11:44:38 -0800654 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100655 if (mSuggestQueryId == -1) return null;
656 return mSuggestCursor.getString(mSuggestQueryId);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800657 }
Satish Sampath565505b2009-05-29 15:37:27 +0100658
Bjorn Bringert04851702009-09-22 10:36:01 +0100659 case SUGGEST_COLUMN_INTENT_EXTRA_DATA:
Grace Kloba391df7c2010-03-01 19:51:49 -0800660 if (type == 0) {
Bjorn Bringert04851702009-09-22 10:36:01 +0100661 return null;
Grace Kloba391df7c2010-03-01 19:51:49 -0800662 } else if (type == 1) {
Grace Klobad3992d42010-01-28 11:44:38 -0800663 return null;
664 } else {
Bjorn Bringert04851702009-09-22 10:36:01 +0100665 if (mSuggestIntentExtraDataId == -1) return null;
666 return mSuggestCursor.getString(mSuggestIntentExtraDataId);
Bjorn Bringert04851702009-09-22 10:36:01 +0100667 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800668 }
669 }
670 return null;
671 }
672
673 @Override
674 public double getDouble(int column) {
675 throw new UnsupportedOperationException();
676 }
677
678 @Override
679 public float getFloat(int column) {
680 throw new UnsupportedOperationException();
681 }
682
683 @Override
684 public int getInt(int column) {
685 throw new UnsupportedOperationException();
686 }
687
688 @Override
689 public long getLong(int column) {
690 if ((mPos != -1) && column == 0) {
691 return mPos; // use row# as the _Id
692 }
693 throw new UnsupportedOperationException();
694 }
695
696 @Override
697 public short getShort(int column) {
698 throw new UnsupportedOperationException();
699 }
700
701 @Override
702 public boolean isNull(int column) {
703 throw new UnsupportedOperationException();
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 deactivate() {
709 if (mHistoryCursor != null) {
710 mHistoryCursor.deactivate();
711 }
712 if (mSuggestCursor != null) {
713 mSuggestCursor.deactivate();
714 }
715 super.deactivate();
716 }
717
Michael Kolbfe251992010-07-08 15:41:55 -0700718 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800719 public boolean requery() {
720 return (mHistoryCursor != null ? mHistoryCursor.requery() : false) |
721 (mSuggestCursor != null ? mSuggestCursor.requery() : false);
722 }
723
724 // TODO Temporary change, finalize after jq's changes go in
Michael Kolbfe251992010-07-08 15:41:55 -0700725 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800726 public void close() {
727 super.close();
728 if (mHistoryCursor != null) {
729 mHistoryCursor.close();
730 mHistoryCursor = null;
731 }
732 if (mSuggestCursor != null) {
733 mSuggestCursor.close();
734 mSuggestCursor = null;
735 }
736 }
Satish Sampath565505b2009-05-29 15:37:27 +0100737
Mike LeBeau21beb132009-05-13 14:57:50 -0700738 /**
739 * Provides the title (text line 1) for a browser suggestion, which should be the
740 * webpage title. If the webpage title is empty, returns the stripped url instead.
Satish Sampath565505b2009-05-29 15:37:27 +0100741 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700742 * @return the title string to use
743 */
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700744 private String getHistoryTitle() {
745 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700746 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000747 title = stripUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700748 }
749 return title;
750 }
Satish Sampath565505b2009-05-29 15:37:27 +0100751
Mike LeBeau21beb132009-05-13 14:57:50 -0700752 /**
753 * Provides the subtitle (text line 2) for a browser suggestion, which should be the
754 * webpage url. If the webpage title is empty, then the url should go in the title
755 * instead, and the subtitle should be empty, so this would return null.
Satish Sampath565505b2009-05-29 15:37:27 +0100756 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700757 * @return the subtitle string to use, or null if none
758 */
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000759 private String getHistoryUrl() {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700760 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700761 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
762 return null;
763 } else {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000764 return stripUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700765 }
766 }
Satish Sampath565505b2009-05-29 15:37:27 +0100767
The Android Open Source Project0c908882009-03-03 19:32:16 -0800768 }
769
Leon Scroggins58d56c62010-01-28 15:12:40 -0500770 private static class ResultsCursor extends AbstractCursor {
771 // Array indices for RESULTS_COLUMNS
772 private static final int RESULT_ACTION_ID = 1;
773 private static final int RESULT_DATA_ID = 2;
774 private static final int RESULT_TEXT_ID = 3;
775 private static final int RESULT_ICON_ID = 4;
776 private static final int RESULT_EXTRA_ID = 5;
777
778 private static final String[] RESULTS_COLUMNS = new String[] {
779 "_id",
780 SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
781 SearchManager.SUGGEST_COLUMN_INTENT_DATA,
782 SearchManager.SUGGEST_COLUMN_TEXT_1,
783 SearchManager.SUGGEST_COLUMN_ICON_1,
784 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA
785 };
786 private final ArrayList<String> mResults;
787 public ResultsCursor(ArrayList<String> results) {
788 mResults = results;
789 }
Michael Kolbfe251992010-07-08 15:41:55 -0700790 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500791 public int getCount() { return mResults.size(); }
792
Michael Kolbfe251992010-07-08 15:41:55 -0700793 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500794 public String[] getColumnNames() {
795 return RESULTS_COLUMNS;
796 }
797
Michael Kolbfe251992010-07-08 15:41:55 -0700798 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500799 public String getString(int column) {
800 switch (column) {
801 case RESULT_ACTION_ID:
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -0500802 return RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500803 case RESULT_TEXT_ID:
804 // The data is used when the phone is in landscape mode. We
805 // still want to show the result string.
806 case RESULT_DATA_ID:
807 return mResults.get(mPos);
808 case RESULT_EXTRA_ID:
809 // The Intent's extra data will store the index into
810 // mResults so the BrowserActivity will know which result to
811 // use.
812 return Integer.toString(mPos);
813 case RESULT_ICON_ID:
814 return Integer.valueOf(R.drawable.magnifying_glass)
815 .toString();
816 default:
817 return null;
818 }
819 }
Michael Kolbfe251992010-07-08 15:41:55 -0700820 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500821 public short getShort(int column) {
822 throw new UnsupportedOperationException();
823 }
Michael Kolbfe251992010-07-08 15:41:55 -0700824 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500825 public int getInt(int column) {
826 throw new UnsupportedOperationException();
827 }
Michael Kolbfe251992010-07-08 15:41:55 -0700828 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500829 public long getLong(int column) {
830 if ((mPos != -1) && column == 0) {
831 return mPos; // use row# as the _id
832 }
833 throw new UnsupportedOperationException();
834 }
Michael Kolbfe251992010-07-08 15:41:55 -0700835 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500836 public float getFloat(int column) {
837 throw new UnsupportedOperationException();
838 }
Michael Kolbfe251992010-07-08 15:41:55 -0700839 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500840 public double getDouble(int column) {
841 throw new UnsupportedOperationException();
842 }
Michael Kolbfe251992010-07-08 15:41:55 -0700843 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500844 public boolean isNull(int column) {
845 throw new UnsupportedOperationException();
846 }
847 }
848
Jeff Hamilton8b139742010-07-29 16:06:53 -0500849 /** Contains custom suggestions results set by the UI */
Leon Scroggins58d56c62010-01-28 15:12:40 -0500850 private ResultsCursor mResultsCursor;
Jeff Hamilton8b139742010-07-29 16:06:53 -0500851 /** Locks access to {@link #mResultsCursor} */
852 private Object mResultsCursorLock = new Object();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500853
854 /**
855 * Provide a set of results to be returned to query, intended to be used
856 * by the SearchDialog when the BrowserActivity is in voice search mode.
857 * @param results Strings to display in the dropdown from the SearchDialog
858 */
859 /* package */ void setQueryResults(ArrayList<String> results) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500860 synchronized (mResultsCursorLock) {
861 if (results == null) {
862 mResultsCursor = null;
863 } else {
864 mResultsCursor = new ResultsCursor(results);
865 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500866 }
867 }
868
The Android Open Source Project0c908882009-03-03 19:32:16 -0800869 @Override
870 public Cursor query(Uri url, String[] projectionIn, String selection,
Satish Sampath565505b2009-05-29 15:37:27 +0100871 String[] selectionArgs, String sortOrder)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800872 throws IllegalStateException {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800873 int match = URI_MATCHER.match(url);
874 if (match == -1) {
875 throw new IllegalArgumentException("Unknown URL");
876 }
Jeff Hamilton8b139742010-07-29 16:06:53 -0500877
878 // If results for the suggestion are already ready just return them directly
879 synchronized (mResultsCursorLock) {
880 if (match == URI_MATCH_SUGGEST && mResultsCursor != null) {
881 Cursor results = mResultsCursor;
882 mResultsCursor = null;
883 return results;
884 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500885 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800886
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100887 if (match == URI_MATCH_SUGGEST || match == URI_MATCH_BOOKMARKS_SUGGEST) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500888 // Handle suggestions
889 return doSuggestQuery(selection, selectionArgs, match == URI_MATCH_BOOKMARKS_SUGGEST);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800890 }
891
892 String[] projection = null;
893 if (projectionIn != null && projectionIn.length > 0) {
894 projection = new String[projectionIn.length + 1];
895 System.arraycopy(projectionIn, 0, projection, 0, projectionIn.length);
896 projection[projectionIn.length] = "_id AS _id";
897 }
898
Jeff Hamilton8b139742010-07-29 16:06:53 -0500899 String whereClause = null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800900 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500901 whereClause = "_id = " + url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800902 }
903
Jeff Hamilton8b139742010-07-29 16:06:53 -0500904 Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_NAMES[match % 10], projection,
905 DatabaseUtils.concatenateWhere(whereClause, selection), selectionArgs,
906 null, null, sortOrder, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800907 c.setNotificationUri(getContext().getContentResolver(), url);
908 return c;
909 }
910
Jeff Hamilton8b139742010-07-29 16:06:53 -0500911 private Cursor doSuggestQuery(String selection, String[] selectionArgs, boolean bookmarksOnly) {
912 String suggestSelection;
913 String [] myArgs;
914 if (selectionArgs[0] == null || selectionArgs[0].equals("")) {
915 return new MySuggestionCursor(null, null, "");
916 } else {
917 String like = selectionArgs[0] + "%";
918 if (selectionArgs[0].startsWith("http")
919 || selectionArgs[0].startsWith("file")) {
920 myArgs = new String[1];
921 myArgs[0] = like;
922 suggestSelection = selection;
923 } else {
924 SUGGEST_ARGS[0] = "http://" + like;
925 SUGGEST_ARGS[1] = "http://www." + like;
926 SUGGEST_ARGS[2] = "https://" + like;
927 SUGGEST_ARGS[3] = "https://www." + like;
928 // To match against titles.
929 SUGGEST_ARGS[4] = like;
930 myArgs = SUGGEST_ARGS;
931 suggestSelection = SUGGEST_SELECTION;
932 }
933 }
934
935 Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
936 SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
937 ORDER_BY, Integer.toString(mMaxSuggestionLongSize));
938
939 if (bookmarksOnly || Patterns.WEB_URL.matcher(selectionArgs[0]).matches()) {
940 return new MySuggestionCursor(c, null, "");
941 } else {
942 // get Google suggest if there is still space in the list
943 if (myArgs != null && myArgs.length > 1 && mSearchableInfo != null
944 && c.getCount() < (mMaxSuggestionShortSize - 1)) {
945 Cursor sc = mSearchManager.getSuggestions(mSearchableInfo, selectionArgs[0]);
946 return new MySuggestionCursor(c, sc, selectionArgs[0]);
947 }
948 return new MySuggestionCursor(c, null, selectionArgs[0]);
949 }
950 }
951
The Android Open Source Project0c908882009-03-03 19:32:16 -0800952 @Override
953 public String getType(Uri url) {
954 int match = URI_MATCHER.match(url);
955 switch (match) {
956 case URI_MATCH_BOOKMARKS:
957 return "vnd.android.cursor.dir/bookmark";
958
959 case URI_MATCH_BOOKMARKS_ID:
960 return "vnd.android.cursor.item/bookmark";
961
962 case URI_MATCH_SEARCHES:
963 return "vnd.android.cursor.dir/searches";
964
965 case URI_MATCH_SEARCHES_ID:
966 return "vnd.android.cursor.item/searches";
967
968 case URI_MATCH_SUGGEST:
969 return SearchManager.SUGGEST_MIME_TYPE;
970
971 default:
972 throw new IllegalArgumentException("Unknown URL");
973 }
974 }
975
976 @Override
977 public Uri insert(Uri url, ContentValues initialValues) {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700978 boolean isBookmarkTable = false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800979 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
980
981 int match = URI_MATCHER.match(url);
982 Uri uri = null;
983 switch (match) {
984 case URI_MATCH_BOOKMARKS: {
985 // Insert into the bookmarks table
986 long rowID = db.insert(TABLE_NAMES[URI_MATCH_BOOKMARKS], "url",
987 initialValues);
988 if (rowID > 0) {
989 uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
990 rowID);
991 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700992 isBookmarkTable = true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800993 break;
994 }
995
996 case URI_MATCH_SEARCHES: {
997 // Insert into the searches table
998 long rowID = db.insert(TABLE_NAMES[URI_MATCH_SEARCHES], "url",
999 initialValues);
1000 if (rowID > 0) {
1001 uri = ContentUris.withAppendedId(Browser.SEARCHES_URI,
1002 rowID);
1003 }
1004 break;
1005 }
1006
1007 default:
1008 throw new IllegalArgumentException("Unknown URL");
1009 }
1010
1011 if (uri == null) {
1012 throw new IllegalArgumentException("Unknown URL");
1013 }
1014 getContext().getContentResolver().notifyChange(uri, null);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001015
Christopher Tatef0c36f72009-07-28 15:24:05 -07001016 // Back up the new bookmark set if we just inserted one.
1017 // A row created when bookmarks are added from scratch will have
1018 // bookmark=1 in the initial value set.
1019 if (isBookmarkTable
1020 && initialValues.containsKey(BookmarkColumns.BOOKMARK)
1021 && initialValues.getAsInteger(BookmarkColumns.BOOKMARK) != 0) {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001022 mBackupManager.dataChanged();
1023 }
The Android Open Source Project0c908882009-03-03 19:32:16 -08001024 return uri;
1025 }
1026
1027 @Override
1028 public int delete(Uri url, String where, String[] whereArgs) {
1029 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1030
1031 int match = URI_MATCHER.match(url);
1032 if (match == -1 || match == URI_MATCH_SUGGEST) {
1033 throw new IllegalArgumentException("Unknown URL");
1034 }
1035
Christopher Tatef0c36f72009-07-28 15:24:05 -07001036 // need to know whether it's the bookmarks table for a couple of reasons
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001037 boolean isBookmarkTable = (match == URI_MATCH_BOOKMARKS_ID);
Christopher Tatef0c36f72009-07-28 15:24:05 -07001038 String id = null;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001039
1040 if (isBookmarkTable || match == URI_MATCH_SEARCHES_ID) {
The Android Open Source Project0c908882009-03-03 19:32:16 -08001041 StringBuilder sb = new StringBuilder();
1042 if (where != null && where.length() > 0) {
1043 sb.append("( ");
1044 sb.append(where);
1045 sb.append(" ) AND ");
1046 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001047 id = url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001048 sb.append("_id = ");
Christopher Tatef0c36f72009-07-28 15:24:05 -07001049 sb.append(id);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001050 where = sb.toString();
1051 }
1052
Christopher Tatef0c36f72009-07-28 15:24:05 -07001053 ContentResolver cr = getContext().getContentResolver();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001054
Christopher Tatef0c36f72009-07-28 15:24:05 -07001055 // we'lll need to back up the bookmark set if we are about to delete one
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001056 if (isBookmarkTable) {
Christopher Tatef0c36f72009-07-28 15:24:05 -07001057 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
1058 new String[] { BookmarkColumns.BOOKMARK },
1059 "_id = " + id, null, null);
1060 if (cursor.moveToNext()) {
1061 if (cursor.getInt(0) != 0) {
1062 // yep, this record is a bookmark
1063 mBackupManager.dataChanged();
1064 }
1065 }
1066 cursor.close();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001067 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001068
1069 int count = db.delete(TABLE_NAMES[match % 10], where, whereArgs);
1070 cr.notifyChange(url, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001071 return count;
1072 }
1073
1074 @Override
1075 public int update(Uri url, ContentValues values, String where,
1076 String[] whereArgs) {
1077 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1078
1079 int match = URI_MATCHER.match(url);
1080 if (match == -1 || match == URI_MATCH_SUGGEST) {
1081 throw new IllegalArgumentException("Unknown URL");
1082 }
1083
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001084 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
The Android Open Source Project0c908882009-03-03 19:32:16 -08001085 StringBuilder sb = new StringBuilder();
1086 if (where != null && where.length() > 0) {
1087 sb.append("( ");
1088 sb.append(where);
1089 sb.append(" ) AND ");
1090 }
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001091 String id = url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001092 sb.append("_id = ");
Christopher Tatef0c36f72009-07-28 15:24:05 -07001093 sb.append(id);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001094 where = sb.toString();
1095 }
1096
Christopher Tatef0c36f72009-07-28 15:24:05 -07001097 ContentResolver cr = getContext().getContentResolver();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001098
Christopher Tatef0c36f72009-07-28 15:24:05 -07001099 // Not all bookmark-table updates should be backed up. Look to see
1100 // whether we changed the title, url, or "is a bookmark" state, and
1101 // request a backup if so.
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001102 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_BOOKMARKS) {
1103 boolean changingBookmarks = false;
Christopher Tatef0c36f72009-07-28 15:24:05 -07001104 // Alterations to the bookmark field inherently change the bookmark
1105 // set, so we don't need to query the record; we know a priori that
1106 // we will need to back up this change.
1107 if (values.containsKey(BookmarkColumns.BOOKMARK)) {
1108 changingBookmarks = true;
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001109 } else if ((values.containsKey(BookmarkColumns.TITLE)
1110 || values.containsKey(BookmarkColumns.URL))
1111 && values.containsKey(BookmarkColumns._ID)) {
1112 // If a title or URL has been changed, check to see if it is to
1113 // a bookmark. The ID should have been included in the update,
1114 // so use it.
Christopher Tatef0c36f72009-07-28 15:24:05 -07001115 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
1116 new String[] { BookmarkColumns.BOOKMARK },
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001117 BookmarkColumns._ID + " = "
1118 + values.getAsString(BookmarkColumns._ID), null, null);
Christopher Tatef0c36f72009-07-28 15:24:05 -07001119 if (cursor.moveToNext()) {
1120 changingBookmarks = (cursor.getInt(0) != 0);
1121 }
1122 cursor.close();
1123 }
1124
1125 // if this *is* a bookmark row we're altering, we need to back it up.
1126 if (changingBookmarks) {
1127 mBackupManager.dataChanged();
1128 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001129 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001130
1131 int ret = db.update(TABLE_NAMES[match % 10], values, where, whereArgs);
1132 cr.notifyChange(url, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001133 return ret;
1134 }
Satish Sampath565505b2009-05-29 15:37:27 +01001135
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001136 /**
1137 * Strips the provided url of preceding "http://" and any trailing "/". Does not
1138 * strip "https://". If the provided string cannot be stripped, the original string
1139 * is returned.
Satish Sampath565505b2009-05-29 15:37:27 +01001140 *
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001141 * TODO: Put this in TextUtils to be used by other packages doing something similar.
Satish Sampath565505b2009-05-29 15:37:27 +01001142 *
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001143 * @param url a url to strip, like "http://www.google.com/"
1144 * @return a stripped url like "www.google.com", or the original string if it could
1145 * not be stripped
1146 */
1147 private static String stripUrl(String url) {
1148 if (url == null) return null;
1149 Matcher m = STRIP_URL_PATTERN.matcher(url);
1150 if (m.matches() && m.groupCount() == 3) {
1151 return m.group(2);
1152 } else {
1153 return url;
1154 }
1155 }
1156
Michael Kolbfe251992010-07-08 15:41:55 -07001157 public static Cursor getBookmarksSuggestions(ContentResolver cr, String constraint) {
1158 Uri uri = Uri.parse("content://browser/" + SearchManager.SUGGEST_URI_PATH_QUERY);
1159 return cr.query(uri, SUGGEST_PROJECTION, SUGGEST_SELECTION,
1160 new String[] { constraint }, ORDER_BY);
1161 }
1162
The Android Open Source Project0c908882009-03-03 19:32:16 -08001163}