mobile/android/base/favicons/Favicons.java

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/favicons/Favicons.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,483 @@
     1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +package org.mozilla.gecko.favicons;
    1.10 +
    1.11 +import org.mozilla.gecko.AboutPages;
    1.12 +import org.mozilla.gecko.AppConstants;
    1.13 +import org.mozilla.gecko.GeckoAppShell;
    1.14 +import org.mozilla.gecko.R;
    1.15 +import org.mozilla.gecko.Tab;
    1.16 +import org.mozilla.gecko.Tabs;
    1.17 +import org.mozilla.gecko.db.BrowserDB;
    1.18 +import org.mozilla.gecko.favicons.cache.FaviconCache;
    1.19 +import org.mozilla.gecko.util.GeckoJarReader;
    1.20 +import org.mozilla.gecko.util.NonEvictingLruCache;
    1.21 +import org.mozilla.gecko.util.ThreadUtils;
    1.22 +
    1.23 +import android.content.Context;
    1.24 +import android.content.res.Resources;
    1.25 +import android.graphics.Bitmap;
    1.26 +import android.graphics.BitmapFactory;
    1.27 +import android.text.TextUtils;
    1.28 +import android.util.Log;
    1.29 +import android.util.SparseArray;
    1.30 +
    1.31 +import java.io.File;
    1.32 +import java.net.URI;
    1.33 +import java.net.URISyntaxException;
    1.34 +import java.util.Arrays;
    1.35 +import java.util.Iterator;
    1.36 +import java.util.List;
    1.37 +
    1.38 +public class Favicons {
    1.39 +    private static final String LOGTAG = "GeckoFavicons";
    1.40 +
    1.41 +    // A magic URL representing the app's own favicon, used for about: pages.
    1.42 +    private static final String BUILT_IN_FAVICON_URL = "about:favicon";
    1.43 +
    1.44 +    // Size of the favicon bitmap cache, in bytes (Counting payload only).
    1.45 +    public static final int FAVICON_CACHE_SIZE_BYTES = 512 * 1024;
    1.46 +
    1.47 +    // Number of URL mappings from page URL to Favicon URL to cache in memory.
    1.48 +    public static final int NUM_PAGE_URL_MAPPINGS_TO_STORE = 128;
    1.49 +
    1.50 +    public static final int NOT_LOADING  = 0;
    1.51 +    public static final int LOADED       = 1;
    1.52 +    public static final int FLAG_PERSIST = 2;
    1.53 +    public static final int FLAG_SCALE   = 4;
    1.54 +
    1.55 +    protected static Context context;
    1.56 +
    1.57 +    // The default Favicon to show if no other can be found.
    1.58 +    public static Bitmap defaultFavicon;
    1.59 +
    1.60 +    // The density-adjusted default Favicon dimensions.
    1.61 +    public static int defaultFaviconSize;
    1.62 +
    1.63 +    // The density-adjusted maximum Favicon dimensions.
    1.64 +    public static int largestFaviconSize;
    1.65 +
    1.66 +    private static final SparseArray<LoadFaviconTask> loadTasks = new SparseArray<LoadFaviconTask>();
    1.67 +
    1.68 +    // Cache to hold mappings between page URLs and Favicon URLs. Used to avoid going to the DB when
    1.69 +    // doing so is not necessary.
    1.70 +    private static final NonEvictingLruCache<String, String> pageURLMappings = new NonEvictingLruCache<String, String>(NUM_PAGE_URL_MAPPINGS_TO_STORE);
    1.71 +
    1.72 +    public static String getFaviconURLForPageURLFromCache(String pageURL) {
    1.73 +        return pageURLMappings.get(pageURL);
    1.74 +    }
    1.75 +
    1.76 +    /**
    1.77 +     * Insert the given pageUrl->faviconUrl mapping into the memory cache of such mappings.
    1.78 +     * Useful for short-circuiting local database access.
    1.79 +     */
    1.80 +    public static void putFaviconURLForPageURLInCache(String pageURL, String faviconURL) {
    1.81 +        pageURLMappings.put(pageURL, faviconURL);
    1.82 +    }
    1.83 +
    1.84 +    private static FaviconCache faviconsCache;
    1.85 +
    1.86 +    /**
    1.87 +     * Returns either NOT_LOADING, or LOADED if the onFaviconLoaded call could
    1.88 +     * be made on the main thread.
    1.89 +     * If no listener is provided, NOT_LOADING is returned.
    1.90 +     */
    1.91 +    static int dispatchResult(final String pageUrl, final String faviconURL, final Bitmap image,
    1.92 +            final OnFaviconLoadedListener listener) {
    1.93 +        if (listener == null) {
    1.94 +            return NOT_LOADING;
    1.95 +        }
    1.96 +
    1.97 +        if (ThreadUtils.isOnUiThread()) {
    1.98 +            listener.onFaviconLoaded(pageUrl, faviconURL, image);
    1.99 +            return LOADED;
   1.100 +        }
   1.101 +
   1.102 +        // We want to always run the listener on UI thread.
   1.103 +        ThreadUtils.postToUiThread(new Runnable() {
   1.104 +            @Override
   1.105 +            public void run() {
   1.106 +                listener.onFaviconLoaded(pageUrl, faviconURL, image);
   1.107 +            }
   1.108 +        });
   1.109 +        return NOT_LOADING;
   1.110 +    }
   1.111 +
   1.112 +    /**
   1.113 +     * Only returns a non-null Bitmap if the entire path is cached -- the
   1.114 +     * page URL to favicon URL, and the favicon URL to in-memory bitmaps.
   1.115 +     *
   1.116 +     * Returns null otherwise.
   1.117 +     */
   1.118 +    public static Bitmap getSizedFaviconForPageFromCache(final String pageURL, int targetSize) {
   1.119 +        final String faviconURL = pageURLMappings.get(pageURL);
   1.120 +        if (faviconURL == null) {
   1.121 +            return null;
   1.122 +        }
   1.123 +        return getSizedFaviconFromCache(faviconURL, targetSize);
   1.124 +    }
   1.125 +
   1.126 +    /**
   1.127 +     * Get a Favicon as close as possible to the target dimensions for the URL provided.
   1.128 +     * If a result is instantly available from the cache, it is returned and the listener is invoked.
   1.129 +     * Otherwise, the result is drawn from the database or network and the listener invoked when the
   1.130 +     * result becomes available.
   1.131 +     *
   1.132 +     * @param pageURL Page URL for which a Favicon is desired.
   1.133 +     * @param faviconURL URL of the Favicon to be downloaded, if known. If none provided, an educated
   1.134 +     *                    guess is made by the system.
   1.135 +     * @param targetSize Target size of the returned Favicon
   1.136 +     * @param listener Listener to call with the result of the load operation, if the result is not
   1.137 +     *                  immediately available.
   1.138 +     * @return The id of the asynchronous task created, NOT_LOADING if none is created, or
   1.139 +     *         LOADED if the value could be dispatched on the current thread.
   1.140 +     */
   1.141 +    public static int getSizedFavicon(String pageURL, String faviconURL, int targetSize, int flags, OnFaviconLoadedListener listener) {
   1.142 +        // Do we know the favicon URL for this page already?
   1.143 +        String cacheURL = faviconURL;
   1.144 +        if (cacheURL == null) {
   1.145 +            cacheURL = pageURLMappings.get(pageURL);
   1.146 +        }
   1.147 +
   1.148 +        // If there's no favicon URL given, try and hit the cache with the default one.
   1.149 +        if (cacheURL == null)  {
   1.150 +            cacheURL = guessDefaultFaviconURL(pageURL);
   1.151 +        }
   1.152 +
   1.153 +        // If it's something we can't even figure out a default URL for, just give up.
   1.154 +        if (cacheURL == null) {
   1.155 +            return dispatchResult(pageURL, null, defaultFavicon, listener);
   1.156 +        }
   1.157 +
   1.158 +        Bitmap cachedIcon = getSizedFaviconFromCache(cacheURL, targetSize);
   1.159 +        if (cachedIcon != null) {
   1.160 +            return dispatchResult(pageURL, cacheURL, cachedIcon, listener);
   1.161 +        }
   1.162 +
   1.163 +        // Check if favicon has failed.
   1.164 +        if (faviconsCache.isFailedFavicon(cacheURL)) {
   1.165 +            return dispatchResult(pageURL, cacheURL, defaultFavicon, listener);
   1.166 +        }
   1.167 +
   1.168 +        // Failing that, try and get one from the database or internet.
   1.169 +        return loadUncachedFavicon(pageURL, faviconURL, flags, targetSize, listener);
   1.170 +    }
   1.171 +
   1.172 +    /**
   1.173 +     * Returns the cached Favicon closest to the target size if any exists or is coercible. Returns
   1.174 +     * null otherwise. Does not query the database or network for the Favicon is the result is not
   1.175 +     * immediately available.
   1.176 +     *
   1.177 +     * @param faviconURL URL of the Favicon to query for.
   1.178 +     * @param targetSize The desired size of the returned Favicon.
   1.179 +     * @return The cached Favicon, rescaled to be as close as possible to the target size, if any exists.
   1.180 +     *         null if no applicable Favicon exists in the cache.
   1.181 +     */
   1.182 +    public static Bitmap getSizedFaviconFromCache(String faviconURL, int targetSize) {
   1.183 +        return faviconsCache.getFaviconForDimensions(faviconURL, targetSize);
   1.184 +    }
   1.185 +
   1.186 +    /**
   1.187 +     * Attempts to find a Favicon for the provided page URL from either the mem cache or the database.
   1.188 +     * Does not need an explicit favicon URL, since, as we are accessing the database anyway, we
   1.189 +     * can query the history DB for the Favicon URL.
   1.190 +     * Handy for easing the transition from caching with page URLs to caching with Favicon URLs.
   1.191 +     *
   1.192 +     * A null result is passed to the listener if no value is locally available. The Favicon is not
   1.193 +     * added to the failure cache.
   1.194 +     *
   1.195 +     * @param pageURL Page URL for which a Favicon is wanted.
   1.196 +     * @param targetSize Target size of the desired Favicon to pass to the cache query
   1.197 +     * @param callback Callback to fire with the result.
   1.198 +     * @return The job ID of the spawned async task, if any.
   1.199 +     */
   1.200 +    public static int getSizedFaviconForPageFromLocal(final String pageURL, final int targetSize, final OnFaviconLoadedListener callback) {
   1.201 +        // Firstly, try extremely hard to cheat.
   1.202 +        // Have we cached this favicon URL? If we did, we can consult the memcache right away.
   1.203 +        String targetURL = pageURLMappings.get(pageURL);
   1.204 +        if (targetURL != null) {
   1.205 +            // Check if favicon has failed.
   1.206 +            if (faviconsCache.isFailedFavicon(targetURL)) {
   1.207 +                return dispatchResult(pageURL, targetURL, null, callback);
   1.208 +            }
   1.209 +
   1.210 +            // Do we have a Favicon in the cache for this favicon URL?
   1.211 +            Bitmap result = getSizedFaviconFromCache(targetURL, targetSize);
   1.212 +            if (result != null) {
   1.213 +                // Victory - immediate response!
   1.214 +                return dispatchResult(pageURL, targetURL, result, callback);
   1.215 +            }
   1.216 +        }
   1.217 +
   1.218 +        // No joy using in-memory resources. Go to background thread and ask the database.
   1.219 +        LoadFaviconTask task = new LoadFaviconTask(ThreadUtils.getBackgroundHandler(), pageURL, targetURL, 0, callback, targetSize, true);
   1.220 +        int taskId = task.getId();
   1.221 +        synchronized(loadTasks) {
   1.222 +            loadTasks.put(taskId, task);
   1.223 +        }
   1.224 +        task.execute();
   1.225 +        return taskId;
   1.226 +    }
   1.227 +
   1.228 +    public static int getSizedFaviconForPageFromLocal(final String pageURL, final OnFaviconLoadedListener callback) {
   1.229 +        return getSizedFaviconForPageFromLocal(pageURL, defaultFaviconSize, callback);
   1.230 +    }
   1.231 +
   1.232 +    /**
   1.233 +     * Helper method to determine the URL of the Favicon image for a given page URL by querying the
   1.234 +     * history database. Should only be called from the background thread - does database access.
   1.235 +     *
   1.236 +     * @param pageURL The URL of a webpage with a Favicon.
   1.237 +     * @return The URL of the Favicon used by that webpage, according to either the History database
   1.238 +     *         or a somewhat educated guess.
   1.239 +     */
   1.240 +    public static String getFaviconURLForPageURL(String pageURL) {
   1.241 +        // Attempt to determine the Favicon URL from the Tabs datastructure. Can dodge having to use
   1.242 +        // the database sometimes by doing this.
   1.243 +        String targetURL;
   1.244 +        Tab theTab = Tabs.getInstance().getFirstTabForUrl(pageURL);
   1.245 +        if (theTab != null) {
   1.246 +            targetURL = theTab.getFaviconURL();
   1.247 +            if (targetURL != null) {
   1.248 +                return targetURL;
   1.249 +            }
   1.250 +        }
   1.251 +
   1.252 +        targetURL = BrowserDB.getFaviconUrlForHistoryUrl(context.getContentResolver(), pageURL);
   1.253 +        if (targetURL == null) {
   1.254 +            // Nothing in the history database. Fall back to the default URL and hope for the best.
   1.255 +            targetURL = guessDefaultFaviconURL(pageURL);
   1.256 +        }
   1.257 +        return targetURL;
   1.258 +    }
   1.259 +
   1.260 +    /**
   1.261 +     * Helper function to create an async job to load a Favicon which does not exist in the memcache.
   1.262 +     * Contains logic to prevent the repeated loading of Favicons which have previously failed.
   1.263 +     * There is no support for recovery from transient failures.
   1.264 +     *
   1.265 +     * @param pageUrl URL of the page for which to load a Favicon. If null, no job is created.
   1.266 +     * @param faviconUrl The URL of the Favicon to load. If null, an attempt to infer the value from
   1.267 +     *                   the history database will be made, and ultimately an attempt to guess will
   1.268 +     *                   be made.
   1.269 +     * @param flags Flags to be used by the LoadFaviconTask while loading. Currently only one flag
   1.270 +     *              is supported, LoadFaviconTask.FLAG_PERSIST.
   1.271 +     *              If FLAG_PERSIST is set and the Favicon is ultimately loaded from the internet,
   1.272 +     *              the downloaded Favicon is subsequently stored in the local database.
   1.273 +     *              If FLAG_PERSIST is unset, the downloaded Favicon is stored only in the memcache.
   1.274 +     *              FLAG_PERSIST has no effect on loads which come from the database.
   1.275 +     * @param listener The OnFaviconLoadedListener to invoke with the result of this Favicon load.
   1.276 +     * @return The id of the LoadFaviconTask handling this job.
   1.277 +     */
   1.278 +    private static int loadUncachedFavicon(String pageUrl, String faviconUrl, int flags, int targetSize, OnFaviconLoadedListener listener) {
   1.279 +        // Handle the case where we have no page url.
   1.280 +        if (TextUtils.isEmpty(pageUrl)) {
   1.281 +            dispatchResult(null, null, null, listener);
   1.282 +            return NOT_LOADING;
   1.283 +        }
   1.284 +
   1.285 +        LoadFaviconTask task = new LoadFaviconTask(ThreadUtils.getBackgroundHandler(), pageUrl, faviconUrl, flags, listener, targetSize, false);
   1.286 +
   1.287 +        int taskId = task.getId();
   1.288 +        synchronized(loadTasks) {
   1.289 +            loadTasks.put(taskId, task);
   1.290 +        }
   1.291 +
   1.292 +        task.execute();
   1.293 +
   1.294 +        return taskId;
   1.295 +    }
   1.296 +
   1.297 +    public static void putFaviconInMemCache(String pageUrl, Bitmap image) {
   1.298 +        faviconsCache.putSingleFavicon(pageUrl, image);
   1.299 +    }
   1.300 +
   1.301 +    /**
   1.302 +     * Adds the bitmaps given by the specified iterator to the cache associated with the url given.
   1.303 +     * Future requests for images will be able to select the least larger image than the target
   1.304 +     * size from this new set of images.
   1.305 +     *
   1.306 +     * @param pageUrl The URL to associate the new favicons with.
   1.307 +     * @param images An iterator over the new favicons to put in the cache.
   1.308 +     */
   1.309 +    public static void putFaviconsInMemCache(String pageUrl, Iterator<Bitmap> images, boolean permanently) {
   1.310 +        faviconsCache.putFavicons(pageUrl, images, permanently);
   1.311 +    }
   1.312 +
   1.313 +    public static void putFaviconsInMemCache(String pageUrl, Iterator<Bitmap> images) {
   1.314 +        putFaviconsInMemCache(pageUrl, images, false);
   1.315 +    }
   1.316 +
   1.317 +    public static void clearMemCache() {
   1.318 +        faviconsCache.evictAll();
   1.319 +        pageURLMappings.evictAll();
   1.320 +    }
   1.321 +
   1.322 +    public static void putFaviconInFailedCache(String faviconURL) {
   1.323 +        faviconsCache.putFailed(faviconURL);
   1.324 +    }
   1.325 +
   1.326 +    public static boolean cancelFaviconLoad(int taskId) {
   1.327 +        if (taskId == NOT_LOADING) {
   1.328 +            return false;
   1.329 +        }
   1.330 +
   1.331 +        boolean cancelled;
   1.332 +        synchronized (loadTasks) {
   1.333 +            if (loadTasks.indexOfKey(taskId) < 0) {
   1.334 +                return false;
   1.335 +            }
   1.336 +
   1.337 +            Log.v(LOGTAG, "Cancelling favicon load " + taskId + ".");
   1.338 +
   1.339 +            LoadFaviconTask task = loadTasks.get(taskId);
   1.340 +            cancelled = task.cancel(false);
   1.341 +        }
   1.342 +        return cancelled;
   1.343 +    }
   1.344 +
   1.345 +    public static void close() {
   1.346 +        Log.d(LOGTAG, "Closing Favicons database");
   1.347 +
   1.348 +        // Cancel any pending tasks
   1.349 +        synchronized (loadTasks) {
   1.350 +            final int count = loadTasks.size();
   1.351 +            for (int i = 0; i < count; i++) {
   1.352 +                cancelFaviconLoad(loadTasks.keyAt(i));
   1.353 +            }
   1.354 +            loadTasks.clear();
   1.355 +        }
   1.356 +
   1.357 +        LoadFaviconTask.closeHTTPClient();
   1.358 +    }
   1.359 +
   1.360 +    /**
   1.361 +     * Get the dominant colour of the Favicon at the URL given, if any exists in the cache.
   1.362 +     *
   1.363 +     * @param url The URL of the Favicon, to be used as the cache key for the colour value.
   1.364 +     * @return The dominant colour of the provided Favicon.
   1.365 +     */
   1.366 +    public static int getFaviconColor(String url) {
   1.367 +        return faviconsCache.getDominantColor(url);
   1.368 +    }
   1.369 +
   1.370 +    /**
   1.371 +     * Called by GeckoApp on startup to pass this class a reference to the GeckoApp object used as
   1.372 +     * the application's Context.
   1.373 +     * Consider replacing with references to a staticly held reference to the GeckoApp object.
   1.374 +     *
   1.375 +     * @param context A reference to the GeckoApp instance.
   1.376 +     */
   1.377 +    public static void attachToContext(Context context) throws Exception {
   1.378 +        final Resources res = context.getResources();
   1.379 +        Favicons.context = context;
   1.380 +
   1.381 +        // Decode the default Favicon ready for use.
   1.382 +        defaultFavicon = BitmapFactory.decodeResource(res, R.drawable.favicon);
   1.383 +        if (defaultFavicon == null) {
   1.384 +            throw new Exception("Null default favicon was returned from the resources system!");
   1.385 +        }
   1.386 +
   1.387 +        defaultFaviconSize = res.getDimensionPixelSize(R.dimen.favicon_bg);
   1.388 +
   1.389 +        // Screen-density-adjusted upper limit on favicon size. Favicons larger than this are
   1.390 +        // downscaled to this size or discarded.
   1.391 +        largestFaviconSize = context.getResources().getDimensionPixelSize(R.dimen.favicon_largest_interesting_size);
   1.392 +        faviconsCache = new FaviconCache(FAVICON_CACHE_SIZE_BYTES, largestFaviconSize);
   1.393 +
   1.394 +        // Initialize page mappings for each of our special pages.
   1.395 +        for (String url : AboutPages.getDefaultIconPages()) {
   1.396 +            pageURLMappings.putWithoutEviction(url, BUILT_IN_FAVICON_URL);
   1.397 +        }
   1.398 +
   1.399 +        // Load and cache the built-in favicon in each of its sizes.
   1.400 +        // TODO: don't open the zip twice!
   1.401 +        List<Bitmap> toInsert = Arrays.asList(loadBrandingBitmap(context, "favicon64.png"),
   1.402 +                                              loadBrandingBitmap(context, "favicon32.png"));
   1.403 +
   1.404 +        putFaviconsInMemCache(BUILT_IN_FAVICON_URL, toInsert.iterator(), true);
   1.405 +    }
   1.406 +
   1.407 +    /**
   1.408 +     * Compute a string like:
   1.409 +     * "jar:jar:file:///data/app/org.mozilla.firefox-1.apk!/assets/omni.ja!/chrome/chrome/content/branding/favicon64.png"
   1.410 +     */
   1.411 +    private static String getBrandingBitmapPath(Context context, String name) {
   1.412 +        final String apkPath = context.getPackageResourcePath();
   1.413 +        return "jar:jar:" + new File(apkPath).toURI() + "!/" +
   1.414 +               AppConstants.OMNIJAR_NAME + "!/" +
   1.415 +               "chrome/chrome/content/branding/" + name;
   1.416 +    }
   1.417 +
   1.418 +    private static Bitmap loadBrandingBitmap(Context context, String name) {
   1.419 +        Bitmap b = GeckoJarReader.getBitmap(context.getResources(),
   1.420 +                                            getBrandingBitmapPath(context, name));
   1.421 +        if (b == null) {
   1.422 +            throw new IllegalStateException("Bitmap " + name + " missing from JAR!");
   1.423 +        }
   1.424 +        return b;
   1.425 +    }
   1.426 +
   1.427 +    /**
   1.428 +     * Helper method to get the default Favicon URL for a given pageURL. Generally: somewhere.com/favicon.ico
   1.429 +     *
   1.430 +     * @param pageURL Page URL for which a default Favicon URL is requested
   1.431 +     * @return The default Favicon URL.
   1.432 +     */
   1.433 +    public static String guessDefaultFaviconURL(String pageURL) {
   1.434 +        // Special-casing for about: pages. The favicon for about:pages which don't provide a link tag
   1.435 +        // is bundled in the database, keyed only by page URL, hence the need to return the page URL
   1.436 +        // here. If the database ever migrates to stop being silly in this way, this can plausibly
   1.437 +        // be removed.
   1.438 +        if (AboutPages.isAboutPage(pageURL) || pageURL.startsWith("jar:")) {
   1.439 +            return pageURL;
   1.440 +        }
   1.441 +
   1.442 +        try {
   1.443 +            // Fall back to trying "someScheme:someDomain.someExtension/favicon.ico".
   1.444 +            URI u = new URI(pageURL);
   1.445 +            return new URI(u.getScheme(),
   1.446 +                           u.getAuthority(),
   1.447 +                           "/favicon.ico", null,
   1.448 +                           null).toString();
   1.449 +        } catch (URISyntaxException e) {
   1.450 +            Log.e(LOGTAG, "URISyntaxException getting default favicon URL", e);
   1.451 +            return null;
   1.452 +        }
   1.453 +    }
   1.454 +
   1.455 +    public static void removeLoadTask(int taskId) {
   1.456 +        synchronized(loadTasks) {
   1.457 +            loadTasks.delete(taskId);
   1.458 +        }
   1.459 +    }
   1.460 +
   1.461 +    /**
   1.462 +     * Method to wrap FaviconCache.isFailedFavicon for use by LoadFaviconTask.
   1.463 +     *
   1.464 +     * @param faviconURL Favicon URL to check for failure.
   1.465 +     */
   1.466 +    static boolean isFailedFavicon(String faviconURL) {
   1.467 +        return faviconsCache.isFailedFavicon(faviconURL);
   1.468 +    }
   1.469 +
   1.470 +    /**
   1.471 +     * Sidestep the cache and get, from either the database or the internet, a favicon
   1.472 +     * suitable for use as an app icon for the provided URL.
   1.473 +     *
   1.474 +     * Useful for creating homescreen shortcuts without being limited
   1.475 +     * by possibly low-resolution values in the cache.
   1.476 +     *
   1.477 +     * Deduces the favicon URL from the browser database, guessing if necessary.
   1.478 +     *
   1.479 +     * @param url page URL to get a large favicon image for.
   1.480 +     * @param onFaviconLoadedListener listener to call back with the result.
   1.481 +     */
   1.482 +    public static void getPreferredSizeFaviconForPage(String url, OnFaviconLoadedListener onFaviconLoadedListener) {
   1.483 +        int preferredSize = GeckoAppShell.getPreferredIconSize();
   1.484 +        loadUncachedFavicon(url, null, 0, preferredSize, onFaviconLoadedListener);
   1.485 +    }
   1.486 +}

mercurial