blob: 383ae7fc50e9b89e2c2ad6b6ec21cbd7b1a6d0ee [file] [log] [blame]
Leon Scrogginse372c022009-06-12 17:07:29 -04001/*
2 * Copyright (C) 2009 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
19import android.content.ContentResolver;
20import android.content.ContentUris;
21import android.content.ContentValues;
22import android.content.Context;
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050023import android.content.SharedPreferences;
Leon Scrogginse372c022009-06-12 17:07:29 -040024import android.database.Cursor;
Ben Murdochaac7aa62009-09-17 16:57:40 +010025import android.graphics.Bitmap;
Leon Scrogginse372c022009-06-12 17:07:29 -040026import android.net.Uri;
Jeff Hamilton84029622010-08-05 14:29:28 -050027import android.os.AsyncTask;
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050028import android.preference.PreferenceManager;
Jeff Hamilton69bd7072010-08-17 12:38:22 -050029import android.provider.BrowserContract;
Jeff Hamilton1a805652010-09-07 12:36:30 -070030import android.provider.BrowserContract.Combined;
31import android.provider.BrowserContract.Images;
Leon Scrogginse372c022009-06-12 17:07:29 -040032import android.util.Log;
33import android.webkit.WebIconDatabase;
34import android.widget.Toast;
35
Ben Murdochaac7aa62009-09-17 16:57:40 +010036import java.io.ByteArrayOutputStream;
Leon Scrogginse372c022009-06-12 17:07:29 -040037
38/**
39 * This class is purely to have a common place for adding/deleting bookmarks.
40 */
41/* package */ class Bookmarks {
Ben Murdochde353622009-10-12 10:29:00 +010042 // We only want the user to be able to bookmark content that
43 // the browser can handle directly.
44 private static final String acceptableBookmarkSchemes[] = {
45 "http:",
46 "https:",
47 "about:",
48 "data:",
49 "javascript:",
50 "file:",
51 "content:"
52 };
53
Leon Scroggins2c0f6112010-03-12 18:09:39 -050054 private final static String LOGTAG = "Bookmarks";
Leon Scrogginse372c022009-06-12 17:07:29 -040055 /**
56 * Add a bookmark to the database.
57 * @param context Context of the calling Activity. This is used to make
58 * Toast confirming that the bookmark has been added. If the
59 * caller provides null, the Toast will not be shown.
Leon Scrogginse372c022009-06-12 17:07:29 -040060 * @param url URL of the website to be bookmarked.
61 * @param name Provided name for the bookmark.
Ben Murdochaac7aa62009-09-17 16:57:40 +010062 * @param thumbnail A thumbnail for the bookmark.
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070063 * @param retainIcon Whether to retain the page's icon in the icon database.
64 * This will usually be <code>true</code> except when bookmarks are
65 * added by a settings restore agent.
Leon Scroggins III052ce662010-09-13 14:44:16 -040066 * @param parent ID of the parent folder.
Leon Scrogginse372c022009-06-12 17:07:29 -040067 */
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050068 /* package */ static void addBookmark(Context context, boolean showToast, String url,
Leon Scroggins III052ce662010-09-13 14:44:16 -040069 String name, Bitmap thumbnail, boolean retainIcon, long parent) {
Leon Scrogginse372c022009-06-12 17:07:29 -040070 // Want to append to the beginning of the list
Jeff Hamilton84029622010-08-05 14:29:28 -050071 ContentValues values = new ContentValues();
Leon Scroggins2c0f6112010-03-12 18:09:39 -050072 try {
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050073 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
74 String accountType = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, null);
75 String accountName = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, null);
76 values.put(BrowserContract.Bookmarks.ACCOUNT_TYPE, accountType);
77 values.put(BrowserContract.Bookmarks.ACCOUNT_NAME, accountName);
Jeff Hamilton84029622010-08-05 14:29:28 -050078 values.put(BrowserContract.Bookmarks.TITLE, name);
79 values.put(BrowserContract.Bookmarks.URL, url);
80 values.put(BrowserContract.Bookmarks.IS_FOLDER, 0);
81 values.put(BrowserContract.Bookmarks.THUMBNAIL,
82 bitmapToBytes(thumbnail));
Leon Scroggins III052ce662010-09-13 14:44:16 -040083 values.put(BrowserContract.Bookmarks.PARENT, parent);
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050084 context.getContentResolver().insert(BrowserContract.Bookmarks.CONTENT_URI, values);
Leon Scroggins2c0f6112010-03-12 18:09:39 -050085 } catch (IllegalStateException e) {
86 Log.e(LOGTAG, "addBookmark", e);
Leon Scrogginse372c022009-06-12 17:07:29 -040087 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070088 if (retainIcon) {
89 WebIconDatabase.getInstance().retainIconForPageUrl(url);
90 }
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050091 if (showToast) {
Leon Scrogginse372c022009-06-12 17:07:29 -040092 Toast.makeText(context, R.string.added_to_bookmarks,
93 Toast.LENGTH_LONG).show();
94 }
95 }
96
97 /**
98 * Remove a bookmark from the database. If the url is a visited site, it
99 * will remain in the database, but only as a history item, and not as a
100 * bookmarked site.
101 * @param context Context of the calling Activity. This is used to make
102 * Toast confirming that the bookmark has been removed. If the
103 * caller provides null, the Toast will not be shown.
104 * @param cr The ContentResolver being used to remove the bookmark.
105 * @param url URL of the website to be removed.
106 */
107 /* package */ static void removeFromBookmarks(Context context,
Andrei Popescuc9526192009-09-23 15:52:16 +0100108 ContentResolver cr, String url, String title) {
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500109 Cursor cursor = null;
110 try {
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500111 cursor = cr.query(BrowserContract.Bookmarks.CONTENT_URI,
112 new String[] { BrowserContract.Bookmarks._ID },
113 BrowserContract.Bookmarks.URL + " = ? AND " +
114 BrowserContract.Bookmarks.TITLE + " = ?",
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500115 new String[] { url, title },
116 null);
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500117
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500118 // Should be in the database no matter what
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500119 if (!cursor.moveToFirst()) {
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500120 throw new AssertionError("URL is not in the database! " + url
121 + " " + title);
Leon Scrogginse372c022009-06-12 17:07:29 -0400122 }
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500123
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500124 // Remove from bookmarks
125 WebIconDatabase.getInstance().releaseIconForPageUrl(url);
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500126 Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI,
127 cursor.getLong(0));
128 cr.delete(uri, null, null);
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500129 if (context != null) {
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500130 Toast.makeText(context, R.string.removed_from_bookmarks, Toast.LENGTH_LONG).show();
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500131 }
132 } catch (IllegalStateException e) {
133 Log.e(LOGTAG, "removeFromBookmarks", e);
134 } finally {
135 if (cursor != null) cursor.close();
Leon Scrogginse372c022009-06-12 17:07:29 -0400136 }
Leon Scrogginse372c022009-06-12 17:07:29 -0400137 }
Ben Murdochaac7aa62009-09-17 16:57:40 +0100138
139 private static byte[] bitmapToBytes(Bitmap bm) {
140 if (bm == null) {
141 return null;
142 }
143
144 final ByteArrayOutputStream os = new ByteArrayOutputStream();
145 bm.compress(Bitmap.CompressFormat.PNG, 100, os);
146 return os.toByteArray();
147 }
Ben Murdochde353622009-10-12 10:29:00 +0100148
149 /* package */ static boolean urlHasAcceptableScheme(String url) {
150 if (url == null) {
151 return false;
152 }
153
154 for (int i = 0; i < acceptableBookmarkSchemes.length; i++) {
155 if (url.startsWith(acceptableBookmarkSchemes[i])) {
156 return true;
157 }
158 }
159 return false;
160 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500161
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500162 static final String QUERY_BOOKMARKS_WHERE =
Jeff Hamilton1a805652010-09-07 12:36:30 -0700163 Combined.URL + " == ? OR " +
164 Combined.URL + " == ? OR " +
165 Combined.URL + " LIKE ? || '%' OR " +
166 Combined.URL + " LIKE ? || '%'";
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500167
Jeff Hamilton1a805652010-09-07 12:36:30 -0700168 /* package */ static Cursor queryCombinedForUrl(ContentResolver cr,
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500169 String originalUrl, String url) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500170 if (cr == null || url == null) {
171 return null;
172 }
173
174 // If originalUrl is null, just set it to url.
175 if (originalUrl == null) {
176 originalUrl = url;
177 }
178
179 // Look for both the original url and the actual url. This takes in to
180 // account redirects.
Jeff Hamilton1a805652010-09-07 12:36:30 -0700181 String originalUrlNoQuery = removeQuery(originalUrl);
182 String urlNoQuery = removeQuery(url);
Jeff Hamilton84029622010-08-05 14:29:28 -0500183 originalUrl = originalUrlNoQuery + '?';
184 url = urlNoQuery + '?';
185
186 // Use NoQuery to search for the base url (i.e. if the url is
187 // http://www.yahoo.com/?rs=1, search for http://www.yahoo.com)
188 // Use url to match the base url with other queries (i.e. if the url is
189 // http://www.google.com/m, search for
190 // http://www.google.com/m?some_query)
Jeff Hamilton1a805652010-09-07 12:36:30 -0700191 final String[] selArgs = new String[] { originalUrlNoQuery, urlNoQuery, originalUrl, url };
192 final String[] projection = new String[] { Combined.URL };
193 return cr.query(Combined.CONTENT_URI, projection, QUERY_BOOKMARKS_WHERE, selArgs, null);
Jeff Hamilton84029622010-08-05 14:29:28 -0500194 }
195
196 // Strip the query from the given url.
197 static String removeQuery(String url) {
198 if (url == null) {
199 return null;
200 }
201 int query = url.indexOf('?');
202 String noQuery = url;
203 if (query != -1) {
204 noQuery = url.substring(0, query);
205 }
206 return noQuery;
207 }
208
209 /**
210 * Update the bookmark's favicon. This is a convenience method for updating
211 * a bookmark favicon for the originalUrl and url of the passed in WebView.
212 * @param cr The ContentResolver to use.
213 * @param originalUrl The original url before any redirects.
214 * @param url The current url.
215 * @param favicon The favicon bitmap to write to the db.
216 */
Jeff Hamilton1a805652010-09-07 12:36:30 -0700217 /* package */ static void updateFavicon(final ContentResolver cr,
Jeff Hamilton84029622010-08-05 14:29:28 -0500218 final String originalUrl, final String url, final Bitmap favicon) {
219 new AsyncTask<Void, Void, Void>() {
220 @Override
221 protected Void doInBackground(Void... unused) {
Jeff Hamilton1a805652010-09-07 12:36:30 -0700222 Cursor cursor = queryCombinedForUrl(cr, originalUrl, url);
223 try {
224 if (cursor.moveToFirst()) {
225 final ByteArrayOutputStream os = new ByteArrayOutputStream();
226 favicon.compress(Bitmap.CompressFormat.PNG, 100, os);
227
228 ContentValues values = new ContentValues();
229 values.put(Images.FAVICON, os.toByteArray());
230 values.put(Images.URL, cursor.getString(0));
231
232 do {
233 cr.update(Images.CONTENT_URI, values, null, null);
234 } while (cursor.moveToNext());
235 }
236 } finally {
237 if (cursor != null) cursor.close();
Jeff Hamilton84029622010-08-05 14:29:28 -0500238 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500239 return null;
240 }
241 }.execute();
242 }
Ben Murdochaac7aa62009-09-17 16:57:40 +0100243}