1 /* 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 17 package com.android.browser; 18 19 import android.content.ContentResolver; 20 import android.content.ContentUris; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.content.SharedPreferences; 24 import android.database.Cursor; 25 import android.graphics.Bitmap; 26 import android.net.Uri; 27 import android.os.AsyncTask; 28 import android.preference.PreferenceManager; 29 import android.provider.BrowserContract; 30 import android.provider.BrowserContract.Combined; 31 import android.provider.BrowserContract.Images; 32 import android.text.TextUtils; 33 import android.util.Log; 34 import android.webkit.WebIconDatabase; 35 import android.widget.Toast; 36 37 import java.io.ByteArrayOutputStream; 38 39 /** 40 * This class is purely to have a common place for adding/deleting bookmarks. 41 */ 42 public class Bookmarks { 43 // 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 55 private final static String LOGTAG = "Bookmarks"; 56 /** 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. 61 * @param url URL of the website to be bookmarked. 62 * @param name Provided name for the bookmark. 63 * @param thumbnail A thumbnail for the bookmark. 64 * @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. 67 * @param parent ID of the parent folder. 68 */ addBookmark(Context context, boolean showToast, String url, String name, Bitmap thumbnail, long parent)69 /* package */ static void addBookmark(Context context, boolean showToast, String url, 70 String name, Bitmap thumbnail, long parent) { 71 // Want to append to the beginning of the list 72 ContentValues values = new ContentValues(); 73 try { 74 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 75 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)); 80 values.put(BrowserContract.Bookmarks.PARENT, parent); 81 context.getContentResolver().insert(BrowserContract.Bookmarks.CONTENT_URI, values); 82 } catch (IllegalStateException e) { 83 Log.e(LOGTAG, "addBookmark", e); 84 } 85 if (showToast) { 86 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 96 * Toast confirming that the bookmark has been removed and to 97 * lookup the correct content uri. It must not be null. 98 * @param cr The ContentResolver being used to remove the bookmark. 99 * @param url URL of the website to be removed. 100 */ removeFromBookmarks(Context context, ContentResolver cr, String url, String title)101 /* package */ static void removeFromBookmarks(Context context, 102 ContentResolver cr, String url, String title) { 103 Cursor cursor = null; 104 try { 105 Uri uri = BookmarkUtils.getBookmarksUri(context); 106 cursor = cr.query(uri, 107 new String[] { BrowserContract.Bookmarks._ID }, 108 BrowserContract.Bookmarks.URL + " = ? AND " + 109 BrowserContract.Bookmarks.TITLE + " = ?", 110 new String[] { url, title }, 111 null); 112 113 if (!cursor.moveToFirst()) { 114 return; 115 } 116 117 // Remove from bookmarks 118 WebIconDatabase.getInstance().releaseIconForPageUrl(url); 119 uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, 120 cursor.getLong(0)); 121 cr.delete(uri, null, null); 122 if (context != null) { 123 Toast.makeText(context, R.string.removed_from_bookmarks, 124 Toast.LENGTH_LONG).show(); 125 } 126 } catch (IllegalStateException e) { 127 Log.e(LOGTAG, "removeFromBookmarks", e); 128 } finally { 129 if (cursor != null) cursor.close(); 130 } 131 } 132 bitmapToBytes(Bitmap bm)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 } 142 urlHasAcceptableScheme(String url)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 } 155 156 static final String QUERY_BOOKMARKS_WHERE = 157 Combined.URL + " == ? OR " + 158 Combined.URL + " == ?"; 159 queryCombinedForUrl(ContentResolver cr, String originalUrl, String url)160 public static Cursor queryCombinedForUrl(ContentResolver cr, 161 String originalUrl, String url) { 162 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. 173 174 final String[] selArgs = new String[] { originalUrl, url }; 175 final String[] projection = new String[] { Combined.URL }; 176 return cr.query(Combined.CONTENT_URI, projection, QUERY_BOOKMARKS_WHERE, selArgs, null); 177 } 178 179 // Strip the query from the given url. removeQuery(String 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 */ updateFavicon(final ContentResolver cr, final String originalUrl, final String url, final Bitmap favicon)200 /* package */ static void updateFavicon(final ContentResolver cr, 201 final String originalUrl, final String url, final Bitmap favicon) { 202 new AsyncTask<Void, Void, Void>() { 203 @Override 204 protected Void doInBackground(Void... unused) { 205 final ByteArrayOutputStream os = new ByteArrayOutputStream(); 206 favicon.compress(Bitmap.CompressFormat.PNG, 100, os); 207 208 // 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); 213 return null; 214 } 215 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 } 224 }.execute(); 225 } 226 } 227