/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.goanna.favicons.cache; import android.graphics.Bitmap; import android.util.Log; import org.mozilla.goanna.gfx.BitmapUtils; import java.util.ArrayList; import java.util.Collections; public class FaviconsForURL { private static final String LOGTAG = "FaviconForURL"; private volatile int dominantColor = -1; final long downloadTimestamp; final ArrayList favicons; public final boolean hasFailed; public FaviconsForURL(int size) { this(size, false); } public FaviconsForURL(int size, boolean failed) { hasFailed = failed; downloadTimestamp = System.currentTimeMillis(); favicons = new ArrayList(size); } public FaviconCacheElement addSecondary(Bitmap favicon, int imageSize) { return addInternal(favicon, false, imageSize); } public FaviconCacheElement addPrimary(Bitmap favicon) { return addInternal(favicon, true, favicon.getWidth()); } private FaviconCacheElement addInternal(Bitmap favicon, boolean isPrimary, int imageSize) { FaviconCacheElement c = new FaviconCacheElement(favicon, isPrimary, imageSize, this); int index = Collections.binarySearch(favicons, c); // We've already got an equivalent one. We don't care about this new one. This only occurs in certain obscure // case conditions. if (index >= 0) { return favicons.get(index); } // binarySearch returns -x - 1 where x is the insertion point of the element. Convert // this to the actual insertion point.. index++; index = -index; favicons.add(index, c); return c; } /** * Get the index of the smallest image in this collection larger than or equal to * the given target size. * * @param targetSize Minimum size for the desired result. * @return The index of the smallest image larger than the target size, or -1 if none exists. */ public int getNextHighestIndex(int targetSize) { // Create a dummy object to hold the target value for comparable. FaviconCacheElement dummy = new FaviconCacheElement(null, false, targetSize, null); int index = Collections.binarySearch(favicons, dummy); // The search routine returns the index of an element equal to dummy, if present. // Otherwise, it returns -x - 1, where x is the index in the ArrayList where dummy would be // inserted if the list were to remain sorted. if (index < 0) { index++; index = -index; } // index is now 'x', as described above. // The routine will return favicons.size() as the index iff dummy is larger than all elements // present (So the "index at which it should be inserted" is the index after the end. // In this case, we set the sentinel value -1 to indicate that we just requested something // larger than all primaries. if (index == favicons.size()) { index = -1; } return index; } /** * Get the next valid primary icon from this collection, starting at the given index. * If the appropriate icon is found, but is invalid, we return null - the proper response is to * reacquire the primary from the database. * If no icon is found, the search is repeated going backwards from the start index to find any * primary at all (The input index may be a secondary which is larger than the actual available * primary.) * * @param fromIndex The index into favicons from which to start the search. * @return The FaviconCacheElement of the next valid primary from the given index. If none exists, * then returns the previous valid primary. If none exists, returns null (Insanity.). */ public FaviconCacheElement getNextPrimary(final int fromIndex) { final int numIcons = favicons.size(); int searchIndex = fromIndex; while (searchIndex < numIcons) { FaviconCacheElement element = favicons.get(searchIndex); if (element.isPrimary) { if (element.invalidated) { // We return null here, despite the possible existence of other primaries, // because we know the most suitable primary for this request exists, but is // no longer in the cache. By returning null, we cause the caller to load the // missing primary from the database and call again. return null; } return element; } searchIndex++; } // No larger primary available. Let's look for smaller ones... searchIndex = fromIndex - 1; while (searchIndex >= 0) { FaviconCacheElement element = favicons.get(searchIndex); if (element.isPrimary) { if (element.invalidated) { return null; } return element; } searchIndex--; } Log.e(LOGTAG, "No primaries found in Favicon cache structure. This is madness!"); return null; } /** * Ensure the dominant colour field is populated for this favicon. */ public int ensureDominantColor() { if (dominantColor == -1) { // Find a payload, any payload, that is not invalidated. for (FaviconCacheElement element : favicons) { if (!element.invalidated) { dominantColor = BitmapUtils.getDominantColor(element.faviconPayload); return dominantColor; } } dominantColor = 0xFFFFFF; } return dominantColor; } }