blob: 1320e83295d26f37ffb4649b51e4f70f98d3a4b5 [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;
Jeff Hamilton84029622010-08-05 14:29:28 -050043import android.provider.Settings;
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -050044import android.speech.RecognizerResultsIntent;
Mike LeBeau21beb132009-05-13 14:57:50 -070045import android.text.TextUtils;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010046import android.util.Log;
Dianne Hackborn385effd2010-02-24 20:03:04 -080047import android.util.Patterns;
Dan Egnor5ee906c2009-11-18 12:11:49 -080048
Andrei Popescu1b20b9d2009-09-21 18:49:42 +010049import java.io.File;
50import java.io.FilenameFilter;
Leon Scroggins58d56c62010-01-28 15:12:40 -050051import java.util.ArrayList;
Bjorn Bringertbcd20b32009-04-29 21:52:09 +010052import java.util.Date;
Mike LeBeauc42f81b2009-05-14 15:04:19 -070053import java.util.regex.Matcher;
54import java.util.regex.Pattern;
The Android Open Source Project0c908882009-03-03 19:32:16 -080055
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -070056
The Android Open Source Project0c908882009-03-03 19:32:16 -080057public class BrowserProvider extends ContentProvider {
58
59 private SQLiteOpenHelper mOpenHelper;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070060 private BackupManager mBackupManager;
The Android Open Source Project0c908882009-03-03 19:32:16 -080061 private static final String sDatabaseName = "browser.db";
62 private static final String TAG = "BrowserProvider";
63 private static final String ORDER_BY = "visits DESC, date DESC";
64
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -070065 private static final String PICASA_URL = "http://picasaweb.google.com/m/" +
66 "viewer?source=androidclient";
67
The Android Open Source Project0c908882009-03-03 19:32:16 -080068 private static final String[] TABLE_NAMES = new String[] {
Bjorn Bringerta7611812010-03-24 11:12:02 +000069 "bookmarks", "searches"
The Android Open Source Project0c908882009-03-03 19:32:16 -080070 };
71 private static final String[] SUGGEST_PROJECTION = new String[] {
Leon Scrogginsb4464432009-11-25 12:37:50 -050072 "_id", "url", "title", "bookmark", "user_entered"
The Android Open Source Project0c908882009-03-03 19:32:16 -080073 };
Satish Sampath565505b2009-05-29 15:37:27 +010074 private static final String SUGGEST_SELECTION =
Leon Scrogginsb4464432009-11-25 12:37:50 -050075 "(url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?"
76 + " OR title LIKE ?) AND (bookmark = 1 OR user_entered = 1)";
Leon Scrogginsbd359cc2009-05-26 15:57:35 -040077 private String[] SUGGEST_ARGS = new String[5];
The Android Open Source Project0c908882009-03-03 19:32:16 -080078
79 // shared suggestion array index, make sure to match COLUMNS
80 private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1;
81 private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2;
82 private static final int SUGGEST_COLUMN_TEXT_1_ID = 3;
83 private static final int SUGGEST_COLUMN_TEXT_2_ID = 4;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +000084 private static final int SUGGEST_COLUMN_TEXT_2_URL_ID = 5;
85 private static final int SUGGEST_COLUMN_ICON_1_ID = 6;
86 private static final int SUGGEST_COLUMN_ICON_2_ID = 7;
87 private static final int SUGGEST_COLUMN_QUERY_ID = 8;
Bjorn Bringert04851702009-09-22 10:36:01 +010088 private static final int SUGGEST_COLUMN_INTENT_EXTRA_DATA = 9;
The Android Open Source Project0c908882009-03-03 19:32:16 -080089
Michael Kolbfe251992010-07-08 15:41:55 -070090 // how many suggestions will be shown in dropdown
91 // 0..SHORT: filled by browser db
92 private static final int MAX_SUGGEST_SHORT_SMALL = 3;
93 // SHORT..LONG: filled by search suggestions
94 private static final int MAX_SUGGEST_LONG_SMALL = 6;
95
96 // large screen size shows more
97 private static final int MAX_SUGGEST_SHORT_LARGE = 6;
98 private static final int MAX_SUGGEST_LONG_LARGE = 9;
99
100
The Android Open Source Project0c908882009-03-03 19:32:16 -0800101 // shared suggestion columns
102 private static final String[] COLUMNS = new String[] {
103 "_id",
104 SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
105 SearchManager.SUGGEST_COLUMN_INTENT_DATA,
106 SearchManager.SUGGEST_COLUMN_TEXT_1,
107 SearchManager.SUGGEST_COLUMN_TEXT_2,
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000108 SearchManager.SUGGEST_COLUMN_TEXT_2_URL,
The Android Open Source Project0c908882009-03-03 19:32:16 -0800109 SearchManager.SUGGEST_COLUMN_ICON_1,
110 SearchManager.SUGGEST_COLUMN_ICON_2,
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700111 SearchManager.SUGGEST_COLUMN_QUERY,
Bjorn Bringert04851702009-09-22 10:36:01 +0100112 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA};
The Android Open Source Project0c908882009-03-03 19:32:16 -0800113
The Android Open Source Project0c908882009-03-03 19:32:16 -0800114
115 // make sure that these match the index of TABLE_NAMES
116 private static final int URI_MATCH_BOOKMARKS = 0;
117 private static final int URI_MATCH_SEARCHES = 1;
118 // (id % 10) should match the table name index
119 private static final int URI_MATCH_BOOKMARKS_ID = 10;
120 private static final int URI_MATCH_SEARCHES_ID = 11;
121 //
122 private static final int URI_MATCH_SUGGEST = 20;
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100123 private static final int URI_MATCH_BOOKMARKS_SUGGEST = 21;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800124
125 private static final UriMatcher URI_MATCHER;
126
127 static {
128 URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
129 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS],
130 URI_MATCH_BOOKMARKS);
131 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/#",
132 URI_MATCH_BOOKMARKS_ID);
133 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES],
134 URI_MATCH_SEARCHES);
135 URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES] + "/#",
136 URI_MATCH_SEARCHES_ID);
137 URI_MATCHER.addURI("browser", SearchManager.SUGGEST_URI_PATH_QUERY,
138 URI_MATCH_SUGGEST);
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100139 URI_MATCHER.addURI("browser",
140 TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/" + SearchManager.SUGGEST_URI_PATH_QUERY,
141 URI_MATCH_BOOKMARKS_SUGGEST);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800142 }
143
144 // 1 -> 2 add cache table
145 // 2 -> 3 update history table
146 // 3 -> 4 add passwords table
147 // 4 -> 5 add settings table
148 // 5 -> 6 ?
149 // 6 -> 7 ?
150 // 7 -> 8 drop proxy table
151 // 8 -> 9 drop settings table
152 // 9 -> 10 add form_urls and form_data
153 // 10 -> 11 add searches table
154 // 11 -> 12 modify cache table
155 // 12 -> 13 modify cache table
156 // 13 -> 14 correspond with Google Bookmarks schema
157 // 14 -> 15 move couple of tables to either browser private database or webview database
158 // 15 -> 17 Set it up for the SearchManager
159 // 17 -> 18 Added favicon in bookmarks table for Home shortcuts
160 // 18 -> 19 Remove labels table
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400161 // 19 -> 20 Added thumbnail
Patrick Scott3918d442009-08-04 13:22:29 -0400162 // 20 -> 21 Added touch_icon
Grace Kloba6b52a552009-09-03 16:29:56 -0700163 // 21 -> 22 Remove "clientid"
Leon Scrogginsb4464432009-11-25 12:37:50 -0500164 // 22 -> 23 Added user_entered
165 private static final int DATABASE_VERSION = 23;
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";
183 Cursor c = null;
184 try {
185 c = cr.query(Uri.parse("content://com.google.settings/partner"),
186 new String[] { "value" }, "name='client_id'", null, null);
187 if (c != null && c.moveToNext()) {
188 ret = c.getString(0);
189 }
190 } catch (RuntimeException ex) {
191 // fall through to return the default
192 } finally {
193 if (c != null) {
194 c.close();
195 }
196 }
197 return ret;
198 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800199
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700200 private static CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800201 StringBuffer sb = new StringBuffer();
202 int lastCharLoc = 0;
Satish Sampath565505b2009-05-29 15:37:27 +0100203
Patrick Scott43914692010-02-19 10:10:10 -0500204 final String client_id = getClientId(context.getContentResolver());
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700205
The Android Open Source Project0c908882009-03-03 19:32:16 -0800206 for (int i = 0; i < srcString.length(); ++i) {
207 char c = srcString.charAt(i);
208 if (c == '{') {
209 sb.append(srcString.subSequence(lastCharLoc, i));
210 lastCharLoc = i;
211 inner:
212 for (int j = i; j < srcString.length(); ++j) {
213 char k = srcString.charAt(j);
214 if (k == '}') {
215 String propertyKeyValue = srcString.subSequence(i + 1, j).toString();
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700216 if (propertyKeyValue.equals("CLIENT_ID")) {
217 sb.append(client_id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800218 } else {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700219 sb.append("unknown");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800220 }
221 lastCharLoc = j + 1;
222 i = j;
223 break inner;
224 }
225 }
226 }
227 }
228 if (srcString.length() - lastCharLoc > 0) {
229 // Put on the tail, if there is one
230 sb.append(srcString.subSequence(lastCharLoc, srcString.length()));
231 }
232 return sb;
233 }
234
235 private static class DatabaseHelper extends SQLiteOpenHelper {
236 private Context mContext;
237
238 public DatabaseHelper(Context context) {
239 super(context, sDatabaseName, null, DATABASE_VERSION);
240 mContext = context;
241 }
242
243 @Override
244 public void onCreate(SQLiteDatabase db) {
245 db.execSQL("CREATE TABLE bookmarks (" +
246 "_id INTEGER PRIMARY KEY," +
247 "title TEXT," +
248 "url TEXT," +
249 "visits INTEGER," +
250 "date LONG," +
251 "created LONG," +
252 "description TEXT," +
253 "bookmark INTEGER," +
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400254 "favicon BLOB DEFAULT NULL," +
Patrick Scott3918d442009-08-04 13:22:29 -0400255 "thumbnail BLOB DEFAULT NULL," +
Leon Scrogginsb4464432009-11-25 12:37:50 -0500256 "touch_icon BLOB DEFAULT NULL," +
257 "user_entered INTEGER" +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800258 ");");
259
260 final CharSequence[] bookmarks = mContext.getResources()
261 .getTextArray(R.array.bookmarks);
262 int size = bookmarks.length;
263 try {
264 for (int i = 0; i < size; i = i + 2) {
Ramanan Rajeswarandd4f4292009-03-24 20:41:19 -0700265 CharSequence bookmarkDestination = replaceSystemPropertyInString(mContext, bookmarks[i + 1]);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800266 db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
267 "date, created, bookmark)" + " VALUES('" +
Satish Sampath565505b2009-05-29 15:37:27 +0100268 bookmarks[i] + "', '" + bookmarkDestination +
The Android Open Source Project0c908882009-03-03 19:32:16 -0800269 "', 0, 0, 0, 1);");
270 }
271 } catch (ArrayIndexOutOfBoundsException e) {
272 }
273
274 db.execSQL("CREATE TABLE searches (" +
275 "_id INTEGER PRIMARY KEY," +
276 "search TEXT," +
277 "date LONG" +
278 ");");
279 }
280
281 @Override
282 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
283 Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400284 + newVersion);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800285 if (oldVersion == 18) {
286 db.execSQL("DROP TABLE IF EXISTS labels");
Leon Scrogginsb6b7f9e2009-06-18 12:05:28 -0400287 }
288 if (oldVersion <= 19) {
289 db.execSQL("ALTER TABLE bookmarks ADD COLUMN thumbnail BLOB DEFAULT NULL;");
Patrick Scott3918d442009-08-04 13:22:29 -0400290 }
291 if (oldVersion < 21) {
292 db.execSQL("ALTER TABLE bookmarks ADD COLUMN touch_icon BLOB DEFAULT NULL;");
Grace Kloba6b52a552009-09-03 16:29:56 -0700293 }
294 if (oldVersion < 22) {
295 db.execSQL("DELETE FROM bookmarks WHERE (bookmark = 0 AND url LIKE \"%.google.%client=ms-%\")");
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100296 removeGears();
Leon Scrogginsb4464432009-11-25 12:37:50 -0500297 }
298 if (oldVersion < 23) {
299 db.execSQL("ALTER TABLE bookmarks ADD COLUMN user_entered INTEGER;");
The Android Open Source Project0c908882009-03-03 19:32:16 -0800300 } else {
301 db.execSQL("DROP TABLE IF EXISTS bookmarks");
302 db.execSQL("DROP TABLE IF EXISTS searches");
303 onCreate(db);
304 }
305 }
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100306
307 private void removeGears() {
Andrei Popescu93bea962010-03-23 15:04:36 +0000308 new Thread() {
309 @Override
310 public void run() {
311 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100312 String browserDataDirString = mContext.getApplicationInfo().dataDir;
313 final String appPluginsDirString = "app_plugins";
314 final String gearsPrefix = "gears";
315 File appPluginsDir = new File(browserDataDirString + File.separator
316 + appPluginsDirString);
317 if (!appPluginsDir.exists()) {
Andrei Popescu93bea962010-03-23 15:04:36 +0000318 return;
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100319 }
320 // Delete the Gears plugin files
321 File[] gearsFiles = appPluginsDir.listFiles(new FilenameFilter() {
322 public boolean accept(File dir, String filename) {
323 return filename.startsWith(gearsPrefix);
324 }
325 });
326 for (int i = 0; i < gearsFiles.length; ++i) {
327 if (gearsFiles[i].isDirectory()) {
328 deleteDirectory(gearsFiles[i]);
329 } else {
330 gearsFiles[i].delete();
331 }
332 }
333 // Delete the Gears data files
334 File gearsDataDir = new File(browserDataDirString + File.separator
335 + gearsPrefix);
336 if (!gearsDataDir.exists()) {
Andrei Popescu93bea962010-03-23 15:04:36 +0000337 return;
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100338 }
339 deleteDirectory(gearsDataDir);
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100340 }
341
342 private void deleteDirectory(File currentDir) {
343 File[] files = currentDir.listFiles();
344 for (int i = 0; i < files.length; ++i) {
345 if (files[i].isDirectory()) {
346 deleteDirectory(files[i]);
347 }
348 files[i].delete();
349 }
350 currentDir.delete();
351 }
Andrei Popescu93bea962010-03-23 15:04:36 +0000352 }.start();
Andrei Popescu1b20b9d2009-09-21 18:49:42 +0100353 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800354 }
355
356 @Override
357 public boolean onCreate() {
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700358 final Context context = getContext();
Michael Kolbfe251992010-07-08 15:41:55 -0700359 boolean xlargeScreenSize = (context.getResources().getConfiguration().screenLayout
360 & Configuration.SCREENLAYOUT_SIZE_MASK)
361 == Configuration.SCREENLAYOUT_SIZE_XLARGE;
362 boolean isPortrait = (context.getResources().getConfiguration().orientation
363 == Configuration.ORIENTATION_PORTRAIT);
364
365
366 if (xlargeScreenSize && isPortrait) {
367 mMaxSuggestionLongSize = MAX_SUGGEST_LONG_LARGE;
368 mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_LARGE;
369 } else {
370 mMaxSuggestionLongSize = MAX_SUGGEST_LONG_SMALL;
371 mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_SMALL;
372 }
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700373 mOpenHelper = new DatabaseHelper(context);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700374 mBackupManager = new BackupManager(context);
Satish Sampath565505b2009-05-29 15:37:27 +0100375 // we added "picasa web album" into default bookmarks for version 19.
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700376 // To avoid erasing the bookmark table, we added it explicitly for
377 // version 18 and 19 as in the other cases, we will erase the table.
378 if (DATABASE_VERSION == 18 || DATABASE_VERSION == 19) {
379 SharedPreferences p = PreferenceManager
380 .getDefaultSharedPreferences(context);
381 boolean fix = p.getBoolean("fix_picasa", true);
382 if (fix) {
383 fixPicasaBookmark();
384 Editor ed = p.edit();
385 ed.putBoolean("fix_picasa", false);
Brad Fitzpatrick1a6e0e82010-09-01 16:29:03 -0700386 ed.apply();
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700387 }
388 }
Bjorn Bringertd69f51d2010-09-13 14:06:41 +0100389 mSettings = BrowserSettings.getInstance();
The Android Open Source Project0c908882009-03-03 19:32:16 -0800390 return true;
391 }
392
The Android Open Source Projecta3c0aab2009-03-18 17:39:48 -0700393 private void fixPicasaBookmark() {
394 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
395 Cursor cursor = db.rawQuery("SELECT _id FROM bookmarks WHERE " +
396 "bookmark = 1 AND url = ?", new String[] { PICASA_URL });
397 try {
398 if (!cursor.moveToFirst()) {
399 // set "created" so that it will be on the top of the list
400 db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
401 "date, created, bookmark)" + " VALUES('" +
402 getContext().getString(R.string.picasa) + "', '"
403 + PICASA_URL + "', 0, 0, " + new Date().getTime()
404 + ", 1);");
405 }
406 } finally {
407 if (cursor != null) {
408 cursor.close();
409 }
410 }
411 }
412
The Android Open Source Project0c908882009-03-03 19:32:16 -0800413 /*
414 * Subclass AbstractCursor so we can combine multiple Cursors and add
Grace Kloba391df7c2010-03-01 19:51:49 -0800415 * "Search the web".
The Android Open Source Project0c908882009-03-03 19:32:16 -0800416 * Here are the rules.
Satish Sampath565505b2009-05-29 15:37:27 +0100417 * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
Grace Kloba391df7c2010-03-01 19:51:49 -0800418 * "Search the web";
419 * 2. If bookmark/history entries has a match, "Search the web" shows up at
420 * the second place. Otherwise, "Search the web" shows up at the first
421 * place.
The Android Open Source Project0c908882009-03-03 19:32:16 -0800422 */
423 private class MySuggestionCursor extends AbstractCursor {
424 private Cursor mHistoryCursor;
425 private Cursor mSuggestCursor;
426 private int mHistoryCount;
427 private int mSuggestionCount;
Grace Klobad3992d42010-01-28 11:44:38 -0800428 private boolean mIncludeWebSearch;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800429 private String mString;
Satish Sampath565505b2009-05-29 15:37:27 +0100430 private int mSuggestText1Id;
431 private int mSuggestText2Id;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000432 private int mSuggestText2UrlId;
Satish Sampath565505b2009-05-29 15:37:27 +0100433 private int mSuggestQueryId;
Bjorn Bringert04851702009-09-22 10:36:01 +0100434 private int mSuggestIntentExtraDataId;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800435
436 public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
437 mHistoryCursor = hc;
438 mSuggestCursor = sc;
Grace Klobaf9b04272010-06-15 13:36:22 -0700439 mHistoryCount = hc != null ? hc.getCount() : 0;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800440 mSuggestionCount = sc != null ? sc.getCount() : 0;
Michael Kolbfe251992010-07-08 15:41:55 -0700441 if (mSuggestionCount > (mMaxSuggestionLongSize - mHistoryCount)) {
442 mSuggestionCount = mMaxSuggestionLongSize - mHistoryCount;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800443 }
444 mString = string;
Grace Klobad3992d42010-01-28 11:44:38 -0800445 mIncludeWebSearch = string.length() > 0;
Satish Sampath565505b2009-05-29 15:37:27 +0100446
447 // Some web suggest providers only give suggestions and have no description string for
448 // items. The order of the result columns may be different as well. So retrieve the
449 // column indices for the fields we need now and check before using below.
450 if (mSuggestCursor == null) {
451 mSuggestText1Id = -1;
452 mSuggestText2Id = -1;
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000453 mSuggestText2UrlId = -1;
Satish Sampath565505b2009-05-29 15:37:27 +0100454 mSuggestQueryId = -1;
Bjorn Bringert04851702009-09-22 10:36:01 +0100455 mSuggestIntentExtraDataId = -1;
Satish Sampath565505b2009-05-29 15:37:27 +0100456 } else {
457 mSuggestText1Id = mSuggestCursor.getColumnIndex(
458 SearchManager.SUGGEST_COLUMN_TEXT_1);
459 mSuggestText2Id = mSuggestCursor.getColumnIndex(
460 SearchManager.SUGGEST_COLUMN_TEXT_2);
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000461 mSuggestText2UrlId = mSuggestCursor.getColumnIndex(
462 SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
Satish Sampath565505b2009-05-29 15:37:27 +0100463 mSuggestQueryId = mSuggestCursor.getColumnIndex(
464 SearchManager.SUGGEST_COLUMN_QUERY);
Bjorn Bringert04851702009-09-22 10:36:01 +0100465 mSuggestIntentExtraDataId = mSuggestCursor.getColumnIndex(
466 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
Satish Sampath565505b2009-05-29 15:37:27 +0100467 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800468 }
469
470 @Override
471 public boolean onMove(int oldPosition, int newPosition) {
472 if (mHistoryCursor == null) {
473 return false;
474 }
Grace Klobad3992d42010-01-28 11:44:38 -0800475 if (mIncludeWebSearch) {
Grace Kloba391df7c2010-03-01 19:51:49 -0800476 if (mHistoryCount == 0 && newPosition == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800477 return true;
Grace Kloba391df7c2010-03-01 19:51:49 -0800478 } else if (mHistoryCount > 0) {
479 if (newPosition == 0) {
480 mHistoryCursor.moveToPosition(0);
481 return true;
482 } else if (newPosition == 1) {
483 return true;
484 }
Grace Klobad3992d42010-01-28 11:44:38 -0800485 }
Grace Kloba391df7c2010-03-01 19:51:49 -0800486 newPosition--;
Grace Klobad3992d42010-01-28 11:44:38 -0800487 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800488 if (mHistoryCount > newPosition) {
489 mHistoryCursor.moveToPosition(newPosition);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800490 } else {
Grace Klobad3992d42010-01-28 11:44:38 -0800491 mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800492 }
493 return true;
494 }
495
496 @Override
497 public int getCount() {
Grace Klobad3992d42010-01-28 11:44:38 -0800498 if (mIncludeWebSearch) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800499 return mHistoryCount + mSuggestionCount + 1;
500 } else {
501 return mHistoryCount + mSuggestionCount;
502 }
503 }
504
505 @Override
506 public String[] getColumnNames() {
507 return COLUMNS;
508 }
Satish Sampath565505b2009-05-29 15:37:27 +0100509
The Android Open Source Project0c908882009-03-03 19:32:16 -0800510 @Override
511 public String getString(int columnIndex) {
512 if ((mPos != -1 && mHistoryCursor != null)) {
Grace Kloba391df7c2010-03-01 19:51:49 -0800513 int type = -1; // 0: web search; 1: history; 2: suggestion
514 if (mIncludeWebSearch) {
515 if (mHistoryCount == 0 && mPos == 0) {
516 type = 0;
517 } else if (mHistoryCount > 0) {
518 if (mPos == 0) {
519 type = 1;
520 } else if (mPos == 1) {
521 type = 0;
522 }
523 }
524 if (type == -1) type = (mPos - 1) < mHistoryCount ? 1 : 2;
525 } else {
526 type = mPos < mHistoryCount ? 1 : 2;
527 }
528
The Android Open Source Project0c908882009-03-03 19:32:16 -0800529 switch(columnIndex) {
530 case SUGGEST_COLUMN_INTENT_ACTION_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800531 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800532 return Intent.ACTION_VIEW;
533 } else {
534 return Intent.ACTION_SEARCH;
535 }
536
537 case SUGGEST_COLUMN_INTENT_DATA_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800538 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800539 return mHistoryCursor.getString(1);
540 } else {
541 return null;
542 }
543
544 case SUGGEST_COLUMN_TEXT_1_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800545 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800546 return mString;
Grace Kloba391df7c2010-03-01 19:51:49 -0800547 } else if (type == 1) {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700548 return getHistoryTitle();
Grace Klobad3992d42010-01-28 11:44:38 -0800549 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100550 if (mSuggestText1Id == -1) return null;
551 return mSuggestCursor.getString(mSuggestText1Id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800552 }
553
554 case SUGGEST_COLUMN_TEXT_2_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800555 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800556 return getContext().getString(R.string.search_the_web);
Grace Kloba391df7c2010-03-01 19:51:49 -0800557 } else if (type == 1) {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000558 return null; // Use TEXT_2_URL instead
Grace Klobad3992d42010-01-28 11:44:38 -0800559 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100560 if (mSuggestText2Id == -1) return null;
561 return mSuggestCursor.getString(mSuggestText2Id);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800562 }
563
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000564 case SUGGEST_COLUMN_TEXT_2_URL_ID:
565 if (type == 0) {
566 return null;
567 } else if (type == 1) {
568 return getHistoryUrl();
569 } else {
570 if (mSuggestText2UrlId == -1) return null;
571 return mSuggestCursor.getString(mSuggestText2UrlId);
572 }
573
The Android Open Source Project0c908882009-03-03 19:32:16 -0800574 case SUGGEST_COLUMN_ICON_1_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800575 if (type == 1) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800576 if (mHistoryCursor.getInt(3) == 1) {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400577 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800578 R.drawable.ic_search_category_bookmark)
579 .toString();
580 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400581 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800582 R.drawable.ic_search_category_history)
583 .toString();
584 }
585 } else {
Leon Scroggins31887fd2009-05-18 16:58:08 -0400586 return Integer.valueOf(
The Android Open Source Project0c908882009-03-03 19:32:16 -0800587 R.drawable.ic_search_category_suggest)
588 .toString();
589 }
590
591 case SUGGEST_COLUMN_ICON_2_ID:
Leon Scroggins31887fd2009-05-18 16:58:08 -0400592 return "0";
The Android Open Source Project0c908882009-03-03 19:32:16 -0800593
594 case SUGGEST_COLUMN_QUERY_ID:
Grace Kloba391df7c2010-03-01 19:51:49 -0800595 if (type == 0) {
Grace Klobad3992d42010-01-28 11:44:38 -0800596 return mString;
Grace Kloba391df7c2010-03-01 19:51:49 -0800597 } else if (type == 1) {
Mike LeBeau2af73052009-06-23 17:36:59 -0700598 // Return the url in the intent query column. This is ignored
599 // within the browser because our searchable is set to
600 // android:searchMode="queryRewriteFromData", but it is used by
601 // global search for query rewriting.
602 return mHistoryCursor.getString(1);
Grace Klobad3992d42010-01-28 11:44:38 -0800603 } else {
Satish Sampath565505b2009-05-29 15:37:27 +0100604 if (mSuggestQueryId == -1) return null;
605 return mSuggestCursor.getString(mSuggestQueryId);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800606 }
Satish Sampath565505b2009-05-29 15:37:27 +0100607
Bjorn Bringert04851702009-09-22 10:36:01 +0100608 case SUGGEST_COLUMN_INTENT_EXTRA_DATA:
Grace Kloba391df7c2010-03-01 19:51:49 -0800609 if (type == 0) {
Bjorn Bringert04851702009-09-22 10:36:01 +0100610 return null;
Grace Kloba391df7c2010-03-01 19:51:49 -0800611 } else if (type == 1) {
Grace Klobad3992d42010-01-28 11:44:38 -0800612 return null;
613 } else {
Bjorn Bringert04851702009-09-22 10:36:01 +0100614 if (mSuggestIntentExtraDataId == -1) return null;
615 return mSuggestCursor.getString(mSuggestIntentExtraDataId);
Bjorn Bringert04851702009-09-22 10:36:01 +0100616 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800617 }
618 }
619 return null;
620 }
621
622 @Override
623 public double getDouble(int column) {
624 throw new UnsupportedOperationException();
625 }
626
627 @Override
628 public float getFloat(int column) {
629 throw new UnsupportedOperationException();
630 }
631
632 @Override
633 public int getInt(int column) {
634 throw new UnsupportedOperationException();
635 }
636
637 @Override
638 public long getLong(int column) {
639 if ((mPos != -1) && column == 0) {
640 return mPos; // use row# as the _Id
641 }
642 throw new UnsupportedOperationException();
643 }
644
645 @Override
646 public short getShort(int column) {
647 throw new UnsupportedOperationException();
648 }
649
650 @Override
651 public boolean isNull(int column) {
652 throw new UnsupportedOperationException();
653 }
654
655 // TODO Temporary change, finalize after jq's changes go in
Michael Kolbfe251992010-07-08 15:41:55 -0700656 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800657 public void deactivate() {
658 if (mHistoryCursor != null) {
659 mHistoryCursor.deactivate();
660 }
661 if (mSuggestCursor != null) {
662 mSuggestCursor.deactivate();
663 }
664 super.deactivate();
665 }
666
Michael Kolbfe251992010-07-08 15:41:55 -0700667 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800668 public boolean requery() {
669 return (mHistoryCursor != null ? mHistoryCursor.requery() : false) |
670 (mSuggestCursor != null ? mSuggestCursor.requery() : false);
671 }
672
673 // TODO Temporary change, finalize after jq's changes go in
Michael Kolbfe251992010-07-08 15:41:55 -0700674 @Override
The Android Open Source Project0c908882009-03-03 19:32:16 -0800675 public void close() {
676 super.close();
677 if (mHistoryCursor != null) {
678 mHistoryCursor.close();
679 mHistoryCursor = null;
680 }
681 if (mSuggestCursor != null) {
682 mSuggestCursor.close();
683 mSuggestCursor = null;
684 }
685 }
Satish Sampath565505b2009-05-29 15:37:27 +0100686
Mike LeBeau21beb132009-05-13 14:57:50 -0700687 /**
688 * Provides the title (text line 1) for a browser suggestion, which should be the
689 * webpage title. If the webpage title is empty, returns the stripped url instead.
Satish Sampath565505b2009-05-29 15:37:27 +0100690 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700691 * @return the title string to use
692 */
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700693 private String getHistoryTitle() {
694 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700695 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000696 title = stripUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700697 }
698 return title;
699 }
Satish Sampath565505b2009-05-29 15:37:27 +0100700
Mike LeBeau21beb132009-05-13 14:57:50 -0700701 /**
702 * Provides the subtitle (text line 2) for a browser suggestion, which should be the
703 * webpage url. If the webpage title is empty, then the url should go in the title
704 * instead, and the subtitle should be empty, so this would return null.
Satish Sampath565505b2009-05-29 15:37:27 +0100705 *
Mike LeBeau21beb132009-05-13 14:57:50 -0700706 * @return the subtitle string to use, or null if none
707 */
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000708 private String getHistoryUrl() {
Mike LeBeau1ef26a32009-05-13 20:11:00 -0700709 String title = mHistoryCursor.getString(2 /* webpage title */);
Mike LeBeau21beb132009-05-13 14:57:50 -0700710 if (TextUtils.isEmpty(title) || TextUtils.getTrimmedLength(title) == 0) {
711 return null;
712 } else {
Bjorn Bringertc7c0fce2010-03-02 11:20:29 +0000713 return stripUrl(mHistoryCursor.getString(1 /* url */));
Mike LeBeau21beb132009-05-13 14:57:50 -0700714 }
715 }
Satish Sampath565505b2009-05-29 15:37:27 +0100716
The Android Open Source Project0c908882009-03-03 19:32:16 -0800717 }
718
Leon Scroggins58d56c62010-01-28 15:12:40 -0500719 private static class ResultsCursor extends AbstractCursor {
720 // Array indices for RESULTS_COLUMNS
721 private static final int RESULT_ACTION_ID = 1;
722 private static final int RESULT_DATA_ID = 2;
723 private static final int RESULT_TEXT_ID = 3;
724 private static final int RESULT_ICON_ID = 4;
725 private static final int RESULT_EXTRA_ID = 5;
726
727 private static final String[] RESULTS_COLUMNS = new String[] {
728 "_id",
729 SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
730 SearchManager.SUGGEST_COLUMN_INTENT_DATA,
731 SearchManager.SUGGEST_COLUMN_TEXT_1,
732 SearchManager.SUGGEST_COLUMN_ICON_1,
733 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA
734 };
735 private final ArrayList<String> mResults;
736 public ResultsCursor(ArrayList<String> results) {
737 mResults = results;
738 }
Michael Kolbfe251992010-07-08 15:41:55 -0700739 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500740 public int getCount() { return mResults.size(); }
741
Michael Kolbfe251992010-07-08 15:41:55 -0700742 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500743 public String[] getColumnNames() {
744 return RESULTS_COLUMNS;
745 }
746
Michael Kolbfe251992010-07-08 15:41:55 -0700747 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500748 public String getString(int column) {
749 switch (column) {
750 case RESULT_ACTION_ID:
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -0500751 return RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500752 case RESULT_TEXT_ID:
753 // The data is used when the phone is in landscape mode. We
754 // still want to show the result string.
755 case RESULT_DATA_ID:
756 return mResults.get(mPos);
757 case RESULT_EXTRA_ID:
758 // The Intent's extra data will store the index into
759 // mResults so the BrowserActivity will know which result to
760 // use.
761 return Integer.toString(mPos);
762 case RESULT_ICON_ID:
763 return Integer.valueOf(R.drawable.magnifying_glass)
764 .toString();
765 default:
766 return null;
767 }
768 }
Michael Kolbfe251992010-07-08 15:41:55 -0700769 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500770 public short getShort(int column) {
771 throw new UnsupportedOperationException();
772 }
Michael Kolbfe251992010-07-08 15:41:55 -0700773 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500774 public int getInt(int column) {
775 throw new UnsupportedOperationException();
776 }
Michael Kolbfe251992010-07-08 15:41:55 -0700777 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500778 public long getLong(int column) {
779 if ((mPos != -1) && column == 0) {
780 return mPos; // use row# as the _id
781 }
782 throw new UnsupportedOperationException();
783 }
Michael Kolbfe251992010-07-08 15:41:55 -0700784 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500785 public float getFloat(int column) {
786 throw new UnsupportedOperationException();
787 }
Michael Kolbfe251992010-07-08 15:41:55 -0700788 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500789 public double getDouble(int column) {
790 throw new UnsupportedOperationException();
791 }
Michael Kolbfe251992010-07-08 15:41:55 -0700792 @Override
Leon Scroggins58d56c62010-01-28 15:12:40 -0500793 public boolean isNull(int column) {
794 throw new UnsupportedOperationException();
795 }
796 }
797
Jeff Hamilton8b139742010-07-29 16:06:53 -0500798 /** Contains custom suggestions results set by the UI */
Leon Scroggins58d56c62010-01-28 15:12:40 -0500799 private ResultsCursor mResultsCursor;
Jeff Hamilton8b139742010-07-29 16:06:53 -0500800 /** Locks access to {@link #mResultsCursor} */
801 private Object mResultsCursorLock = new Object();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500802
803 /**
804 * Provide a set of results to be returned to query, intended to be used
805 * by the SearchDialog when the BrowserActivity is in voice search mode.
806 * @param results Strings to display in the dropdown from the SearchDialog
807 */
808 /* package */ void setQueryResults(ArrayList<String> results) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500809 synchronized (mResultsCursorLock) {
810 if (results == null) {
811 mResultsCursor = null;
812 } else {
813 mResultsCursor = new ResultsCursor(results);
814 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500815 }
816 }
817
The Android Open Source Project0c908882009-03-03 19:32:16 -0800818 @Override
819 public Cursor query(Uri url, String[] projectionIn, String selection,
Satish Sampath565505b2009-05-29 15:37:27 +0100820 String[] selectionArgs, String sortOrder)
The Android Open Source Project0c908882009-03-03 19:32:16 -0800821 throws IllegalStateException {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800822 int match = URI_MATCHER.match(url);
823 if (match == -1) {
824 throw new IllegalArgumentException("Unknown URL");
825 }
Jeff Hamilton8b139742010-07-29 16:06:53 -0500826
827 // If results for the suggestion are already ready just return them directly
828 synchronized (mResultsCursorLock) {
829 if (match == URI_MATCH_SUGGEST && mResultsCursor != null) {
830 Cursor results = mResultsCursor;
831 mResultsCursor = null;
832 return results;
833 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500834 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800835
Bjorn Bringert346dafb2009-04-29 21:41:47 +0100836 if (match == URI_MATCH_SUGGEST || match == URI_MATCH_BOOKMARKS_SUGGEST) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500837 // Handle suggestions
838 return doSuggestQuery(selection, selectionArgs, match == URI_MATCH_BOOKMARKS_SUGGEST);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800839 }
840
841 String[] projection = null;
842 if (projectionIn != null && projectionIn.length > 0) {
843 projection = new String[projectionIn.length + 1];
844 System.arraycopy(projectionIn, 0, projection, 0, projectionIn.length);
845 projection[projectionIn.length] = "_id AS _id";
846 }
847
Jeff Hamilton8b139742010-07-29 16:06:53 -0500848 String whereClause = null;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800849 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
Jeff Hamilton8b139742010-07-29 16:06:53 -0500850 whereClause = "_id = " + url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800851 }
852
Jeff Hamilton8b139742010-07-29 16:06:53 -0500853 Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_NAMES[match % 10], projection,
854 DatabaseUtils.concatenateWhere(whereClause, selection), selectionArgs,
855 null, null, sortOrder, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -0800856 c.setNotificationUri(getContext().getContentResolver(), url);
857 return c;
858 }
859
Jeff Hamilton8b139742010-07-29 16:06:53 -0500860 private Cursor doSuggestQuery(String selection, String[] selectionArgs, boolean bookmarksOnly) {
861 String suggestSelection;
862 String [] myArgs;
863 if (selectionArgs[0] == null || selectionArgs[0].equals("")) {
864 return new MySuggestionCursor(null, null, "");
865 } else {
866 String like = selectionArgs[0] + "%";
867 if (selectionArgs[0].startsWith("http")
868 || selectionArgs[0].startsWith("file")) {
869 myArgs = new String[1];
870 myArgs[0] = like;
871 suggestSelection = selection;
872 } else {
873 SUGGEST_ARGS[0] = "http://" + like;
874 SUGGEST_ARGS[1] = "http://www." + like;
875 SUGGEST_ARGS[2] = "https://" + like;
876 SUGGEST_ARGS[3] = "https://www." + like;
877 // To match against titles.
878 SUGGEST_ARGS[4] = like;
879 myArgs = SUGGEST_ARGS;
880 suggestSelection = SUGGEST_SELECTION;
881 }
882 }
883
884 Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
885 SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
886 ORDER_BY, Integer.toString(mMaxSuggestionLongSize));
887
888 if (bookmarksOnly || Patterns.WEB_URL.matcher(selectionArgs[0]).matches()) {
889 return new MySuggestionCursor(c, null, "");
890 } else {
Bjorn Bringertd69f51d2010-09-13 14:06:41 +0100891 // get search suggestions if there is still space in the list
892 if (myArgs != null && myArgs.length > 1
893 && mSettings.getShowSearchSuggestions()
894 && c.getCount() < (MAX_SUGGEST_SHORT_SMALL - 1)) {
895 SearchEngine searchEngine = mSettings.getSearchEngine();
896 if (searchEngine != null && searchEngine.supportsSuggestions()) {
897 Cursor sc = searchEngine.getSuggestions(getContext(), selectionArgs[0]);
898 return new MySuggestionCursor(c, sc, selectionArgs[0]);
899 }
Jeff Hamilton8b139742010-07-29 16:06:53 -0500900 }
901 return new MySuggestionCursor(c, null, selectionArgs[0]);
902 }
903 }
904
The Android Open Source Project0c908882009-03-03 19:32:16 -0800905 @Override
906 public String getType(Uri url) {
907 int match = URI_MATCHER.match(url);
908 switch (match) {
909 case URI_MATCH_BOOKMARKS:
910 return "vnd.android.cursor.dir/bookmark";
911
912 case URI_MATCH_BOOKMARKS_ID:
913 return "vnd.android.cursor.item/bookmark";
914
915 case URI_MATCH_SEARCHES:
916 return "vnd.android.cursor.dir/searches";
917
918 case URI_MATCH_SEARCHES_ID:
919 return "vnd.android.cursor.item/searches";
920
921 case URI_MATCH_SUGGEST:
922 return SearchManager.SUGGEST_MIME_TYPE;
923
924 default:
925 throw new IllegalArgumentException("Unknown URL");
926 }
927 }
928
929 @Override
930 public Uri insert(Uri url, ContentValues initialValues) {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700931 boolean isBookmarkTable = false;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800932 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
933
934 int match = URI_MATCHER.match(url);
935 Uri uri = null;
936 switch (match) {
937 case URI_MATCH_BOOKMARKS: {
938 // Insert into the bookmarks table
939 long rowID = db.insert(TABLE_NAMES[URI_MATCH_BOOKMARKS], "url",
940 initialValues);
941 if (rowID > 0) {
942 uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
943 rowID);
944 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700945 isBookmarkTable = true;
The Android Open Source Project0c908882009-03-03 19:32:16 -0800946 break;
947 }
948
949 case URI_MATCH_SEARCHES: {
950 // Insert into the searches table
951 long rowID = db.insert(TABLE_NAMES[URI_MATCH_SEARCHES], "url",
952 initialValues);
953 if (rowID > 0) {
954 uri = ContentUris.withAppendedId(Browser.SEARCHES_URI,
955 rowID);
956 }
957 break;
958 }
959
960 default:
961 throw new IllegalArgumentException("Unknown URL");
962 }
963
964 if (uri == null) {
965 throw new IllegalArgumentException("Unknown URL");
966 }
967 getContext().getContentResolver().notifyChange(uri, null);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700968
Christopher Tatef0c36f72009-07-28 15:24:05 -0700969 // Back up the new bookmark set if we just inserted one.
970 // A row created when bookmarks are added from scratch will have
971 // bookmark=1 in the initial value set.
972 if (isBookmarkTable
973 && initialValues.containsKey(BookmarkColumns.BOOKMARK)
974 && initialValues.getAsInteger(BookmarkColumns.BOOKMARK) != 0) {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700975 mBackupManager.dataChanged();
976 }
The Android Open Source Project0c908882009-03-03 19:32:16 -0800977 return uri;
978 }
979
980 @Override
981 public int delete(Uri url, String where, String[] whereArgs) {
982 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
983
984 int match = URI_MATCHER.match(url);
985 if (match == -1 || match == URI_MATCH_SUGGEST) {
986 throw new IllegalArgumentException("Unknown URL");
987 }
988
Christopher Tatef0c36f72009-07-28 15:24:05 -0700989 // need to know whether it's the bookmarks table for a couple of reasons
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700990 boolean isBookmarkTable = (match == URI_MATCH_BOOKMARKS_ID);
Christopher Tatef0c36f72009-07-28 15:24:05 -0700991 String id = null;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700992
993 if (isBookmarkTable || match == URI_MATCH_SEARCHES_ID) {
The Android Open Source Project0c908882009-03-03 19:32:16 -0800994 StringBuilder sb = new StringBuilder();
995 if (where != null && where.length() > 0) {
996 sb.append("( ");
997 sb.append(where);
998 sb.append(" ) AND ");
999 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001000 id = url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001001 sb.append("_id = ");
Christopher Tatef0c36f72009-07-28 15:24:05 -07001002 sb.append(id);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001003 where = sb.toString();
1004 }
1005
Christopher Tatef0c36f72009-07-28 15:24:05 -07001006 ContentResolver cr = getContext().getContentResolver();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001007
Christopher Tatef0c36f72009-07-28 15:24:05 -07001008 // we'lll need to back up the bookmark set if we are about to delete one
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001009 if (isBookmarkTable) {
Christopher Tatef0c36f72009-07-28 15:24:05 -07001010 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
1011 new String[] { BookmarkColumns.BOOKMARK },
1012 "_id = " + id, null, null);
1013 if (cursor.moveToNext()) {
1014 if (cursor.getInt(0) != 0) {
1015 // yep, this record is a bookmark
1016 mBackupManager.dataChanged();
1017 }
1018 }
1019 cursor.close();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001020 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001021
1022 int count = db.delete(TABLE_NAMES[match % 10], where, whereArgs);
1023 cr.notifyChange(url, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001024 return count;
1025 }
1026
1027 @Override
1028 public int update(Uri url, ContentValues values, String where,
1029 String[] whereArgs) {
1030 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1031
1032 int match = URI_MATCHER.match(url);
1033 if (match == -1 || match == URI_MATCH_SUGGEST) {
1034 throw new IllegalArgumentException("Unknown URL");
1035 }
1036
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001037 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
The Android Open Source Project0c908882009-03-03 19:32:16 -08001038 StringBuilder sb = new StringBuilder();
1039 if (where != null && where.length() > 0) {
1040 sb.append("( ");
1041 sb.append(where);
1042 sb.append(" ) AND ");
1043 }
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001044 String id = url.getPathSegments().get(1);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001045 sb.append("_id = ");
Christopher Tatef0c36f72009-07-28 15:24:05 -07001046 sb.append(id);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001047 where = sb.toString();
1048 }
1049
Christopher Tatef0c36f72009-07-28 15:24:05 -07001050 ContentResolver cr = getContext().getContentResolver();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001051
Christopher Tatef0c36f72009-07-28 15:24:05 -07001052 // Not all bookmark-table updates should be backed up. Look to see
1053 // whether we changed the title, url, or "is a bookmark" state, and
1054 // request a backup if so.
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001055 if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_BOOKMARKS) {
1056 boolean changingBookmarks = false;
Christopher Tatef0c36f72009-07-28 15:24:05 -07001057 // Alterations to the bookmark field inherently change the bookmark
1058 // set, so we don't need to query the record; we know a priori that
1059 // we will need to back up this change.
1060 if (values.containsKey(BookmarkColumns.BOOKMARK)) {
1061 changingBookmarks = true;
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001062 } else if ((values.containsKey(BookmarkColumns.TITLE)
1063 || values.containsKey(BookmarkColumns.URL))
1064 && values.containsKey(BookmarkColumns._ID)) {
1065 // If a title or URL has been changed, check to see if it is to
1066 // a bookmark. The ID should have been included in the update,
1067 // so use it.
Christopher Tatef0c36f72009-07-28 15:24:05 -07001068 Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
1069 new String[] { BookmarkColumns.BOOKMARK },
Leon Scrogginsf2463ae2010-02-23 14:28:51 -05001070 BookmarkColumns._ID + " = "
1071 + values.getAsString(BookmarkColumns._ID), null, null);
Christopher Tatef0c36f72009-07-28 15:24:05 -07001072 if (cursor.moveToNext()) {
1073 changingBookmarks = (cursor.getInt(0) != 0);
1074 }
1075 cursor.close();
1076 }
1077
1078 // if this *is* a bookmark row we're altering, we need to back it up.
1079 if (changingBookmarks) {
1080 mBackupManager.dataChanged();
1081 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -07001082 }
Christopher Tatef0c36f72009-07-28 15:24:05 -07001083
1084 int ret = db.update(TABLE_NAMES[match % 10], values, where, whereArgs);
1085 cr.notifyChange(url, null);
The Android Open Source Project0c908882009-03-03 19:32:16 -08001086 return ret;
1087 }
Satish Sampath565505b2009-05-29 15:37:27 +01001088
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001089 /**
1090 * Strips the provided url of preceding "http://" and any trailing "/". Does not
1091 * strip "https://". If the provided string cannot be stripped, the original string
1092 * is returned.
Satish Sampath565505b2009-05-29 15:37:27 +01001093 *
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001094 * TODO: Put this in TextUtils to be used by other packages doing something similar.
Satish Sampath565505b2009-05-29 15:37:27 +01001095 *
Mike LeBeauc42f81b2009-05-14 15:04:19 -07001096 * @param url a url to strip, like "http://www.google.com/"
1097 * @return a stripped url like "www.google.com", or the original string if it could
1098 * not be stripped
1099 */
1100 private static String stripUrl(String url) {
1101 if (url == null) return null;
1102 Matcher m = STRIP_URL_PATTERN.matcher(url);
1103 if (m.matches() && m.groupCount() == 3) {
1104 return m.group(2);
1105 } else {
1106 return url;
1107 }
1108 }
1109
Michael Kolbfe251992010-07-08 15:41:55 -07001110 public static Cursor getBookmarksSuggestions(ContentResolver cr, String constraint) {
1111 Uri uri = Uri.parse("content://browser/" + SearchManager.SUGGEST_URI_PATH_QUERY);
1112 return cr.query(uri, SUGGEST_PROJECTION, SUGGEST_SELECTION,
1113 new String[] { constraint }, ORDER_BY);
1114 }
1115
The Android Open Source Project0c908882009-03-03 19:32:16 -08001116}