blob: 3ead2034e47abd67c5ae7ee5c4ab014397af8ab8 [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;
23import android.database.Cursor;
Ben Murdochaac7aa62009-09-17 16:57:40 +010024import android.graphics.Bitmap;
Leon Scrogginse372c022009-06-12 17:07:29 -040025import android.net.Uri;
Jeff Hamilton84029622010-08-05 14:29:28 -050026import android.os.AsyncTask;
Jeff Hamilton69bd7072010-08-17 12:38:22 -050027import android.provider.BrowserContract;
Leon Scrogginse372c022009-06-12 17:07:29 -040028import android.util.Log;
29import android.webkit.WebIconDatabase;
30import android.widget.Toast;
31
Ben Murdochaac7aa62009-09-17 16:57:40 +010032import java.io.ByteArrayOutputStream;
Leon Scrogginse372c022009-06-12 17:07:29 -040033
34/**
35 * This class is purely to have a common place for adding/deleting bookmarks.
36 */
37/* package */ class Bookmarks {
Ben Murdochde353622009-10-12 10:29:00 +010038 // We only want the user to be able to bookmark content that
39 // the browser can handle directly.
40 private static final String acceptableBookmarkSchemes[] = {
41 "http:",
42 "https:",
43 "about:",
44 "data:",
45 "javascript:",
46 "file:",
47 "content:"
48 };
49
Leon Scroggins2c0f6112010-03-12 18:09:39 -050050 private final static String LOGTAG = "Bookmarks";
Leon Scrogginse372c022009-06-12 17:07:29 -040051 /**
52 * Add a bookmark to the database.
53 * @param context Context of the calling Activity. This is used to make
54 * Toast confirming that the bookmark has been added. If the
55 * caller provides null, the Toast will not be shown.
56 * @param cr The ContentResolver being used to add the bookmark to the db.
57 * @param url URL of the website to be bookmarked.
58 * @param name Provided name for the bookmark.
Ben Murdochaac7aa62009-09-17 16:57:40 +010059 * @param thumbnail A thumbnail for the bookmark.
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070060 * @param retainIcon Whether to retain the page's icon in the icon database.
61 * This will usually be <code>true</code> except when bookmarks are
62 * added by a settings restore agent.
Leon Scrogginse372c022009-06-12 17:07:29 -040063 */
Jeff Hamilton84029622010-08-05 14:29:28 -050064 /* package */ static void addBookmark(Context context, ContentResolver cr, String url,
65 String name, Bitmap thumbnail, boolean retainIcon) {
Leon Scrogginse372c022009-06-12 17:07:29 -040066 // Want to append to the beginning of the list
Jeff Hamilton84029622010-08-05 14:29:28 -050067 ContentValues values = new ContentValues();
Leon Scroggins2c0f6112010-03-12 18:09:39 -050068 Cursor cursor = null;
69 try {
Jeff Hamilton84029622010-08-05 14:29:28 -050070 values.put(BrowserContract.Bookmarks.TITLE, name);
71 values.put(BrowserContract.Bookmarks.URL, url);
72 values.put(BrowserContract.Bookmarks.IS_FOLDER, 0);
73 values.put(BrowserContract.Bookmarks.THUMBNAIL,
74 bitmapToBytes(thumbnail));
75 cr.insert(BrowserContract.Bookmarks.CONTENT_URI, values);
Leon Scroggins2c0f6112010-03-12 18:09:39 -050076 } catch (IllegalStateException e) {
77 Log.e(LOGTAG, "addBookmark", e);
78 } finally {
79 if (cursor != null) cursor.close();
Leon Scrogginse372c022009-06-12 17:07:29 -040080 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070081 if (retainIcon) {
82 WebIconDatabase.getInstance().retainIconForPageUrl(url);
83 }
Leon Scrogginse372c022009-06-12 17:07:29 -040084 if (context != null) {
85 Toast.makeText(context, R.string.added_to_bookmarks,
86 Toast.LENGTH_LONG).show();
87 }
88 }
89
90 /**
91 * Remove a bookmark from the database. If the url is a visited site, it
92 * will remain in the database, but only as a history item, and not as a
93 * bookmarked site.
94 * @param context Context of the calling Activity. This is used to make
95 * Toast confirming that the bookmark has been removed. If the
96 * caller provides null, the Toast will not be shown.
97 * @param cr The ContentResolver being used to remove the bookmark.
98 * @param url URL of the website to be removed.
99 */
100 /* package */ static void removeFromBookmarks(Context context,
Andrei Popescuc9526192009-09-23 15:52:16 +0100101 ContentResolver cr, String url, String title) {
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500102 Cursor cursor = null;
103 try {
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500104 cursor = cr.query(BrowserContract.Bookmarks.CONTENT_URI,
105 new String[] { BrowserContract.Bookmarks._ID },
106 BrowserContract.Bookmarks.URL + " = ? AND " +
107 BrowserContract.Bookmarks.TITLE + " = ?",
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500108 new String[] { url, title },
109 null);
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500110
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500111 // Should be in the database no matter what
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500112 if (!cursor.moveToFirst()) {
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500113 throw new AssertionError("URL is not in the database! " + url
114 + " " + title);
Leon Scrogginse372c022009-06-12 17:07:29 -0400115 }
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500116
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500117 // Remove from bookmarks
118 WebIconDatabase.getInstance().releaseIconForPageUrl(url);
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500119 Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI,
120 cursor.getLong(0));
121 cr.delete(uri, null, null);
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500122 if (context != null) {
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500123 Toast.makeText(context, R.string.removed_from_bookmarks, Toast.LENGTH_LONG).show();
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500124 }
125 } catch (IllegalStateException e) {
126 Log.e(LOGTAG, "removeFromBookmarks", e);
127 } finally {
128 if (cursor != null) cursor.close();
Leon Scrogginse372c022009-06-12 17:07:29 -0400129 }
Leon Scrogginse372c022009-06-12 17:07:29 -0400130 }
Ben Murdochaac7aa62009-09-17 16:57:40 +0100131
132 private static byte[] bitmapToBytes(Bitmap bm) {
133 if (bm == null) {
134 return null;
135 }
136
137 final ByteArrayOutputStream os = new ByteArrayOutputStream();
138 bm.compress(Bitmap.CompressFormat.PNG, 100, os);
139 return os.toByteArray();
140 }
Ben Murdochde353622009-10-12 10:29:00 +0100141
142 /* package */ static boolean urlHasAcceptableScheme(String url) {
143 if (url == null) {
144 return false;
145 }
146
147 for (int i = 0; i < acceptableBookmarkSchemes.length; i++) {
148 if (url.startsWith(acceptableBookmarkSchemes[i])) {
149 return true;
150 }
151 }
152 return false;
153 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500154
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500155 static final String QUERY_BOOKMARKS_WHERE =
156 BrowserContract.Bookmarks.IS_FOLDER + " == 0 AND (" +
157 BrowserContract.Bookmarks.URL + " == ? OR " +
158 BrowserContract.Bookmarks.URL + " == ? OR " +
159 BrowserContract.Bookmarks.URL + " LIKE ? || '%' OR " +
160 BrowserContract.Bookmarks.URL + " LIKE ? || '%')";
161
Jeff Hamilton84029622010-08-05 14:29:28 -0500162 /* package */ static Cursor queryBookmarksForUrl(ContentResolver cr,
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500163 String originalUrl, String url) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500164 if (cr == null || url == null) {
165 return null;
166 }
167
168 // If originalUrl is null, just set it to url.
169 if (originalUrl == null) {
170 originalUrl = url;
171 }
172
173 // Look for both the original url and the actual url. This takes in to
174 // account redirects.
175 String originalUrlNoQuery = Bookmarks.removeQuery(originalUrl);
176 String urlNoQuery = Bookmarks.removeQuery(url);
177 originalUrl = originalUrlNoQuery + '?';
178 url = urlNoQuery + '?';
179
180 // Use NoQuery to search for the base url (i.e. if the url is
181 // http://www.yahoo.com/?rs=1, search for http://www.yahoo.com)
182 // Use url to match the base url with other queries (i.e. if the url is
183 // http://www.google.com/m, search for
184 // http://www.google.com/m?some_query)
185 final String[] selArgs = new String[] {
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500186 originalUrlNoQuery, urlNoQuery, originalUrl, url };
187 final String[] projection = new String[] { BrowserContract.Bookmarks._ID };
188 return cr.query(BrowserContract.Bookmarks.CONTENT_URI, projection, QUERY_BOOKMARKS_WHERE,
189 selArgs, null);
Jeff Hamilton84029622010-08-05 14:29:28 -0500190 }
191
192 // Strip the query from the given url.
193 static String removeQuery(String url) {
194 if (url == null) {
195 return null;
196 }
197 int query = url.indexOf('?');
198 String noQuery = url;
199 if (query != -1) {
200 noQuery = url.substring(0, query);
201 }
202 return noQuery;
203 }
204
205 /**
206 * Update the bookmark's favicon. This is a convenience method for updating
207 * a bookmark favicon for the originalUrl and url of the passed in WebView.
208 * @param cr The ContentResolver to use.
209 * @param originalUrl The original url before any redirects.
210 * @param url The current url.
211 * @param favicon The favicon bitmap to write to the db.
212 */
213 /* package */ static void updateBookmarkFavicon(final ContentResolver cr,
214 final String originalUrl, final String url, final Bitmap favicon) {
215 new AsyncTask<Void, Void, Void>() {
216 @Override
217 protected Void doInBackground(Void... unused) {
218 final Cursor c =
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500219 Bookmarks.queryBookmarksForUrl(cr, originalUrl, url);
Jeff Hamilton84029622010-08-05 14:29:28 -0500220 if (c == null) {
221 return null;
222 }
223 if (c.moveToFirst()) {
224 ContentValues values = new ContentValues();
225 final ByteArrayOutputStream os =
226 new ByteArrayOutputStream();
227 favicon.compress(Bitmap.CompressFormat.PNG, 100, os);
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500228 values.put(BrowserContract.Bookmarks.FAVICON,
Jeff Hamilton84029622010-08-05 14:29:28 -0500229 os.toByteArray());
230 do {
231 cr.update(ContentUris.withAppendedId(
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500232 BrowserContract.Bookmarks.CONTENT_URI, c.getLong(0)),
Jeff Hamilton84029622010-08-05 14:29:28 -0500233 values, null, null);
234 } while (c.moveToNext());
235 }
236 c.close();
237 return null;
238 }
239 }.execute();
240 }
Ben Murdochaac7aa62009-09-17 16:57:40 +0100241}