1/* 2 * Copyright (C) 2012 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 android.webkit; 18 19import android.content.ContentResolver; 20import android.database.Cursor; 21import android.graphics.Bitmap; 22import android.os.Handler; 23import android.os.Message; 24import android.provider.Browser; 25import android.util.Log; 26 27import java.io.File; 28import java.util.HashMap; 29import java.util.Vector; 30 31class WebIconDatabaseClassic extends WebIconDatabase { 32 private static final String LOGTAG = "WebIconDatabase"; 33 // Global instance of a WebIconDatabase 34 private static WebIconDatabaseClassic sIconDatabase; 35 // EventHandler for handling messages before and after the WebCore thread is 36 // ready. 37 private final EventHandler mEventHandler = new EventHandler(); 38 39 // Class to handle messages before WebCore is ready 40 private static class EventHandler extends Handler { 41 // Message ids 42 static final int OPEN = 0; 43 static final int CLOSE = 1; 44 static final int REMOVE_ALL = 2; 45 static final int REQUEST_ICON = 3; 46 static final int RETAIN_ICON = 4; 47 static final int RELEASE_ICON = 5; 48 static final int BULK_REQUEST_ICON = 6; 49 // Message for dispatching icon request results 50 private static final int ICON_RESULT = 10; 51 // Actual handler that runs in WebCore thread 52 private Handler mHandler; 53 // Vector of messages before the WebCore thread is ready 54 private Vector<Message> mMessages = new Vector<Message>(); 55 // Class to handle a result dispatch 56 private class IconResult { 57 private final String mUrl; 58 private final Bitmap mIcon; 59 private final IconListener mListener; 60 IconResult(String url, Bitmap icon, IconListener l) { 61 mUrl = url; 62 mIcon = icon; 63 mListener = l; 64 } 65 void dispatch() { 66 mListener.onReceivedIcon(mUrl, mIcon); 67 } 68 } 69 70 @Override 71 public void handleMessage(Message msg) { 72 // Note: This is the message handler for the UI thread. 73 switch (msg.what) { 74 case ICON_RESULT: 75 ((IconResult) msg.obj).dispatch(); 76 break; 77 } 78 } 79 80 // Called by WebCore thread to create the actual handler 81 private synchronized void createHandler() { 82 if (mHandler == null) { 83 mHandler = new Handler() { 84 @Override 85 public void handleMessage(Message msg) { 86 // Note: This is the message handler for the WebCore 87 // thread. 88 switch (msg.what) { 89 case OPEN: 90 nativeOpen((String) msg.obj); 91 break; 92 93 case CLOSE: 94 nativeClose(); 95 break; 96 97 case REMOVE_ALL: 98 nativeRemoveAllIcons(); 99 break; 100 101 case REQUEST_ICON: 102 IconListener l = (IconListener) msg.obj; 103 String url = msg.getData().getString("url"); 104 requestIconAndSendResult(url, l); 105 break; 106 107 case BULK_REQUEST_ICON: 108 bulkRequestIcons(msg); 109 break; 110 111 case RETAIN_ICON: 112 nativeRetainIconForPageUrl((String) msg.obj); 113 break; 114 115 case RELEASE_ICON: 116 nativeReleaseIconForPageUrl((String) msg.obj); 117 break; 118 } 119 } 120 }; 121 // Transfer all pending messages 122 for (int size = mMessages.size(); size > 0; size--) { 123 mHandler.sendMessage(mMessages.remove(0)); 124 } 125 mMessages = null; 126 } 127 } 128 129 private synchronized boolean hasHandler() { 130 return mHandler != null; 131 } 132 133 private synchronized void postMessage(Message msg) { 134 if (mMessages != null) { 135 mMessages.add(msg); 136 } else { 137 mHandler.sendMessage(msg); 138 } 139 } 140 141 private void bulkRequestIcons(Message msg) { 142 HashMap map = (HashMap) msg.obj; 143 IconListener listener = (IconListener) map.get("listener"); 144 ContentResolver cr = (ContentResolver) map.get("contentResolver"); 145 String where = (String) map.get("where"); 146 147 Cursor c = null; 148 try { 149 c = cr.query( 150 Browser.BOOKMARKS_URI, 151 new String[] { Browser.BookmarkColumns.URL }, 152 where, null, null); 153 if (c.moveToFirst()) { 154 do { 155 String url = c.getString(0); 156 requestIconAndSendResult(url, listener); 157 } while (c.moveToNext()); 158 } 159 } catch (IllegalStateException e) { 160 Log.e(LOGTAG, "BulkRequestIcons", e); 161 } finally { 162 if (c != null) c.close(); 163 } 164 } 165 166 private void requestIconAndSendResult(String url, IconListener listener) { 167 Bitmap icon = nativeIconForPageUrl(url); 168 if (icon != null) { 169 sendMessage(obtainMessage(ICON_RESULT, 170 new IconResult(url, icon, listener))); 171 } 172 } 173 } 174 175 @Override 176 public void open(String path) { 177 if (path != null) { 178 // Make the directories and parents if they don't exist 179 File db = new File(path); 180 if (!db.exists()) { 181 db.mkdirs(); 182 } 183 mEventHandler.postMessage( 184 Message.obtain(null, EventHandler.OPEN, db.getAbsolutePath())); 185 } 186 } 187 188 @Override 189 public void close() { 190 mEventHandler.postMessage( 191 Message.obtain(null, EventHandler.CLOSE)); 192 } 193 194 @Override 195 public void removeAllIcons() { 196 mEventHandler.postMessage( 197 Message.obtain(null, EventHandler.REMOVE_ALL)); 198 } 199 200 /** 201 * Request the Bitmap representing the icon for the given page 202 * url. If the icon exists, the listener will be called with the result. 203 * @param url The page's url. 204 * @param listener An implementation on IconListener to receive the result. 205 */ 206 public void requestIconForPageUrl(String url, IconListener listener) { 207 if (listener == null || url == null) { 208 return; 209 } 210 Message msg = Message.obtain(null, EventHandler.REQUEST_ICON, listener); 211 msg.getData().putString("url", url); 212 mEventHandler.postMessage(msg); 213 } 214 215 /** {@hide} 216 */ 217 public void bulkRequestIconForPageUrl(ContentResolver cr, String where, 218 IconListener listener) { 219 if (listener == null) { 220 return; 221 } 222 223 // Special case situation: we don't want to add this message to the 224 // queue if there is no handler because we may never have a real 225 // handler to service the messages and the cursor will never get 226 // closed. 227 if (mEventHandler.hasHandler()) { 228 // Don't use Bundle as it is parcelable. 229 HashMap<String, Object> map = new HashMap<String, Object>(); 230 map.put("contentResolver", cr); 231 map.put("where", where); 232 map.put("listener", listener); 233 Message msg = 234 Message.obtain(null, EventHandler.BULK_REQUEST_ICON, map); 235 mEventHandler.postMessage(msg); 236 } 237 } 238 239 @Override 240 public void retainIconForPageUrl(String url) { 241 if (url != null) { 242 mEventHandler.postMessage( 243 Message.obtain(null, EventHandler.RETAIN_ICON, url)); 244 } 245 } 246 247 @Override 248 public void releaseIconForPageUrl(String url) { 249 if (url != null) { 250 mEventHandler.postMessage( 251 Message.obtain(null, EventHandler.RELEASE_ICON, url)); 252 } 253 } 254 255 /** 256 * Get the global instance of WebIconDatabase. 257 * @return A single instance of WebIconDatabase. It will be the same 258 * instance for the current process each time this method is 259 * called. 260 */ 261 public static WebIconDatabaseClassic getInstance() { 262 // XXX: Must be created in the UI thread. 263 if (sIconDatabase == null) { 264 sIconDatabase = new WebIconDatabaseClassic(); 265 } 266 return sIconDatabase; 267 } 268 269 /** 270 * Create the internal handler and transfer all pending messages. 271 * XXX: Called by WebCore thread only! 272 */ 273 /*package*/ void createHandler() { 274 mEventHandler.createHandler(); 275 } 276 277 /** 278 * Private constructor to avoid anyone else creating an instance. 279 */ 280 private WebIconDatabaseClassic() {} 281 282 // Native functions 283 private static native void nativeOpen(String path); 284 private static native void nativeClose(); 285 private static native void nativeRemoveAllIcons(); 286 private static native Bitmap nativeIconForPageUrl(String url); 287 private static native void nativeRetainIconForPageUrl(String url); 288 private static native void nativeReleaseIconForPageUrl(String url); 289} 290