blob: 190ff9dd76706e5897aeb7618a63aec9c05c5118 [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;
John Reck568467e2010-12-21 14:15:50 -080032import android.text.TextUtils;
Leon Scrogginse372c022009-06-12 17:07:29 -040033import android.util.Log;
34import android.webkit.WebIconDatabase;
35import android.widget.Toast;
36
Ben Murdochaac7aa62009-09-17 16:57:40 +010037import java.io.ByteArrayOutputStream;
Leon Scrogginse372c022009-06-12 17:07:29 -040038
39/**
40 * This class is purely to have a common place for adding/deleting bookmarks.
41 */
John Reck57928452011-02-17 12:44:07 -080042public class Bookmarks {
Ben Murdochde353622009-10-12 10:29:00 +010043 // We only want the user to be able to bookmark content that
44 // the browser can handle directly.
45 private static final String acceptableBookmarkSchemes[] = {
46 "http:",
47 "https:",
48 "about:",
49 "data:",
50 "javascript:",
51 "file:",
52 "content:"
53 };
54
Leon Scroggins2c0f6112010-03-12 18:09:39 -050055 private final static String LOGTAG = "Bookmarks";
Leon Scrogginse372c022009-06-12 17:07:29 -040056 /**
57 * Add a bookmark to the database.
58 * @param context Context of the calling Activity. This is used to make
59 * Toast confirming that the bookmark has been added. If the
60 * caller provides null, the Toast will not be shown.
Leon Scrogginse372c022009-06-12 17:07:29 -040061 * @param url URL of the website to be bookmarked.
62 * @param name Provided name for the bookmark.
Ben Murdochaac7aa62009-09-17 16:57:40 +010063 * @param thumbnail A thumbnail for the bookmark.
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070064 * @param retainIcon Whether to retain the page's icon in the icon database.
65 * This will usually be <code>true</code> except when bookmarks are
66 * added by a settings restore agent.
Leon Scroggins III052ce662010-09-13 14:44:16 -040067 * @param parent ID of the parent folder.
Leon Scrogginse372c022009-06-12 17:07:29 -040068 */
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050069 /* package */ static void addBookmark(Context context, boolean showToast, String url,
John Reckaf262e72011-07-25 13:55:44 -070070 String name, Bitmap thumbnail, long parent) {
Leon Scrogginse372c022009-06-12 17:07:29 -040071 // Want to append to the beginning of the list
Jeff Hamilton84029622010-08-05 14:29:28 -050072 ContentValues values = new ContentValues();
Leon Scroggins2c0f6112010-03-12 18:09:39 -050073 try {
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050074 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
Jeff Hamilton84029622010-08-05 14:29:28 -050075 values.put(BrowserContract.Bookmarks.TITLE, name);
76 values.put(BrowserContract.Bookmarks.URL, url);
77 values.put(BrowserContract.Bookmarks.IS_FOLDER, 0);
78 values.put(BrowserContract.Bookmarks.THUMBNAIL,
79 bitmapToBytes(thumbnail));
Leon Scroggins III052ce662010-09-13 14:44:16 -040080 values.put(BrowserContract.Bookmarks.PARENT, parent);
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050081 context.getContentResolver().insert(BrowserContract.Bookmarks.CONTENT_URI, values);
Leon Scroggins2c0f6112010-03-12 18:09:39 -050082 } catch (IllegalStateException e) {
83 Log.e(LOGTAG, "addBookmark", e);
Leon Scrogginse372c022009-06-12 17:07:29 -040084 }
Jeff Hamilton7f6cf3e2010-09-17 17:22:21 -050085 if (showToast) {
Leon Scrogginse372c022009-06-12 17:07:29 -040086 Toast.makeText(context, R.string.added_to_bookmarks,
87 Toast.LENGTH_LONG).show();
88 }
89 }
90
91 /**
92 * Remove a bookmark from the database. If the url is a visited site, it
93 * will remain in the database, but only as a history item, and not as a
94 * bookmarked site.
95 * @param context Context of the calling Activity. This is used to make
John Reck03b20542011-01-06 16:22:48 -080096 * Toast confirming that the bookmark has been removed and to
97 * lookup the correct content uri. It must not be null.
Leon Scrogginse372c022009-06-12 17:07:29 -040098 * @param cr The ContentResolver being used to remove the bookmark.
99 * @param url URL of the website to be removed.
100 */
101 /* package */ static void removeFromBookmarks(Context context,
Andrei Popescuc9526192009-09-23 15:52:16 +0100102 ContentResolver cr, String url, String title) {
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500103 Cursor cursor = null;
104 try {
John Reck4b59db82010-11-16 15:40:34 -0800105 Uri uri = BookmarkUtils.getBookmarksUri(context);
106 cursor = cr.query(uri,
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500107 new String[] { BrowserContract.Bookmarks._ID },
108 BrowserContract.Bookmarks.URL + " = ? AND " +
109 BrowserContract.Bookmarks.TITLE + " = ?",
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500110 new String[] { url, title },
111 null);
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500112
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500113 if (!cursor.moveToFirst()) {
John Reck4b59db82010-11-16 15:40:34 -0800114 return;
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);
John Reck4b59db82010-11-16 15:40:34 -0800119 uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI,
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500120 cursor.getLong(0));
121 cr.delete(uri, null, null);
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500122 if (context != null) {
John Reck03b20542011-01-06 16:22:48 -0800123 Toast.makeText(context, R.string.removed_from_bookmarks,
124 Toast.LENGTH_LONG).show();
Leon Scroggins2c0f6112010-03-12 18:09:39 -0500125 }
126 } catch (IllegalStateException e) {
127 Log.e(LOGTAG, "removeFromBookmarks", e);
128 } finally {
129 if (cursor != null) cursor.close();
Leon Scrogginse372c022009-06-12 17:07:29 -0400130 }
Leon Scrogginse372c022009-06-12 17:07:29 -0400131 }
Ben Murdochaac7aa62009-09-17 16:57:40 +0100132
133 private static byte[] bitmapToBytes(Bitmap bm) {
134 if (bm == null) {
135 return null;
136 }
137
138 final ByteArrayOutputStream os = new ByteArrayOutputStream();
139 bm.compress(Bitmap.CompressFormat.PNG, 100, os);
140 return os.toByteArray();
141 }
Ben Murdochde353622009-10-12 10:29:00 +0100142
143 /* package */ static boolean urlHasAcceptableScheme(String url) {
144 if (url == null) {
145 return false;
146 }
147
148 for (int i = 0; i < acceptableBookmarkSchemes.length; i++) {
149 if (url.startsWith(acceptableBookmarkSchemes[i])) {
150 return true;
151 }
152 }
153 return false;
154 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500155
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500156 static final String QUERY_BOOKMARKS_WHERE =
Jeff Hamilton1a805652010-09-07 12:36:30 -0700157 Combined.URL + " == ? OR " +
John Reck57928452011-02-17 12:44:07 -0800158 Combined.URL + " == ?";
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500159
John Reck57928452011-02-17 12:44:07 -0800160 public static Cursor queryCombinedForUrl(ContentResolver cr,
Jeff Hamilton8ce956c2010-08-17 11:13:53 -0500161 String originalUrl, String url) {
Jeff Hamilton84029622010-08-05 14:29:28 -0500162 if (cr == null || url == null) {
163 return null;
164 }
165
166 // If originalUrl is null, just set it to url.
167 if (originalUrl == null) {
168 originalUrl = url;
169 }
170
171 // Look for both the original url and the actual url. This takes in to
172 // account redirects.
Jeff Hamilton84029622010-08-05 14:29:28 -0500173
John Reck57928452011-02-17 12:44:07 -0800174 final String[] selArgs = new String[] { originalUrl, url };
Jeff Hamilton1a805652010-09-07 12:36:30 -0700175 final String[] projection = new String[] { Combined.URL };
176 return cr.query(Combined.CONTENT_URI, projection, QUERY_BOOKMARKS_WHERE, selArgs, null);
Jeff Hamilton84029622010-08-05 14:29:28 -0500177 }
178
179 // Strip the query from the given url.
180 static String removeQuery(String url) {
181 if (url == null) {
182 return null;
183 }
184 int query = url.indexOf('?');
185 String noQuery = url;
186 if (query != -1) {
187 noQuery = url.substring(0, query);
188 }
189 return noQuery;
190 }
191
192 /**
193 * Update the bookmark's favicon. This is a convenience method for updating
194 * a bookmark favicon for the originalUrl and url of the passed in WebView.
195 * @param cr The ContentResolver to use.
196 * @param originalUrl The original url before any redirects.
197 * @param url The current url.
198 * @param favicon The favicon bitmap to write to the db.
199 */
Jeff Hamilton1a805652010-09-07 12:36:30 -0700200 /* package */ static void updateFavicon(final ContentResolver cr,
Jeff Hamilton84029622010-08-05 14:29:28 -0500201 final String originalUrl, final String url, final Bitmap favicon) {
202 new AsyncTask<Void, Void, Void>() {
203 @Override
204 protected Void doInBackground(Void... unused) {
John Reck568467e2010-12-21 14:15:50 -0800205 final ByteArrayOutputStream os = new ByteArrayOutputStream();
206 favicon.compress(Bitmap.CompressFormat.PNG, 100, os);
Jeff Hamilton1a805652010-09-07 12:36:30 -0700207
John Reck568467e2010-12-21 14:15:50 -0800208 // The Images update will insert if it doesn't exist
209 ContentValues values = new ContentValues();
210 values.put(Images.FAVICON, os.toByteArray());
211 updateImages(cr, originalUrl, values);
212 updateImages(cr, url, values);
Jeff Hamilton84029622010-08-05 14:29:28 -0500213 return null;
214 }
John Reck568467e2010-12-21 14:15:50 -0800215
216 private void updateImages(final ContentResolver cr,
217 final String url, ContentValues values) {
218 String iurl = removeQuery(url);
219 if (!TextUtils.isEmpty(iurl)) {
220 values.put(Images.URL, iurl);
221 cr.update(BrowserContract.Images.CONTENT_URI, values, null, null);
222 }
223 }
Jeff Hamilton84029622010-08-05 14:29:28 -0500224 }.execute();
225 }
Ben Murdochaac7aa62009-09-17 16:57:40 +0100226}