Home | History | Annotate | Download | only in webkit
      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 
     17 package android.webkit;
     18 
     19 import android.content.ContentResolver;
     20 import android.database.Cursor;
     21 import android.graphics.Bitmap;
     22 import android.os.Handler;
     23 import android.os.Message;
     24 import android.provider.Browser;
     25 import android.util.Log;
     26 
     27 import java.io.File;
     28 import java.util.HashMap;
     29 import java.util.Vector;
     30 
     31 class 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