Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 4 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | package org.mozilla.gecko.widget; |
michael@0 | 7 | |
michael@0 | 8 | import org.mozilla.gecko.R; |
michael@0 | 9 | import org.mozilla.gecko.favicons.Favicons; |
michael@0 | 10 | |
michael@0 | 11 | import android.content.Context; |
michael@0 | 12 | import android.graphics.Bitmap; |
michael@0 | 13 | import android.graphics.Canvas; |
michael@0 | 14 | import android.graphics.Paint; |
michael@0 | 15 | import android.graphics.RectF; |
michael@0 | 16 | import android.util.AttributeSet; |
michael@0 | 17 | import android.widget.ImageView; |
michael@0 | 18 | /** |
michael@0 | 19 | * Special version of ImageView for favicons. |
michael@0 | 20 | * Displays solid colour background around Favicon to fill space not occupied by the icon. Colour |
michael@0 | 21 | * selected is the dominant colour of the provided Favicon. |
michael@0 | 22 | */ |
michael@0 | 23 | public class FaviconView extends ImageView { |
michael@0 | 24 | private Bitmap mIconBitmap; |
michael@0 | 25 | |
michael@0 | 26 | // Reference to the unscaled bitmap, if any, to prevent repeated assignments of the same bitmap |
michael@0 | 27 | // to the view from causing repeated rescalings (Some of the callers do this) |
michael@0 | 28 | private Bitmap mUnscaledBitmap; |
michael@0 | 29 | |
michael@0 | 30 | // Key into the Favicon dominant colour cache. Should be the Favicon URL if the image displayed |
michael@0 | 31 | // here is a Favicon managed by the caching system. If not, any appropriately unique-to-this-image |
michael@0 | 32 | // string is acceptable. |
michael@0 | 33 | private String mIconKey; |
michael@0 | 34 | |
michael@0 | 35 | private int mActualWidth; |
michael@0 | 36 | private int mActualHeight; |
michael@0 | 37 | |
michael@0 | 38 | // Flag indicating if the most recently assigned image is considered likely to need scaling. |
michael@0 | 39 | private boolean mScalingExpected; |
michael@0 | 40 | |
michael@0 | 41 | // Dominant color of the favicon. |
michael@0 | 42 | private int mDominantColor; |
michael@0 | 43 | |
michael@0 | 44 | // Stroke width for the border. |
michael@0 | 45 | private static float sStrokeWidth; |
michael@0 | 46 | |
michael@0 | 47 | // Paint for drawing the stroke. |
michael@0 | 48 | private static Paint sStrokePaint; |
michael@0 | 49 | |
michael@0 | 50 | // Paint for drawing the background. |
michael@0 | 51 | private static Paint sBackgroundPaint; |
michael@0 | 52 | |
michael@0 | 53 | // Size of the stroke rectangle. |
michael@0 | 54 | private final RectF mStrokeRect; |
michael@0 | 55 | |
michael@0 | 56 | // Size of the background rectangle. |
michael@0 | 57 | private final RectF mBackgroundRect; |
michael@0 | 58 | |
michael@0 | 59 | // Initializing the static paints. |
michael@0 | 60 | static { |
michael@0 | 61 | sStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); |
michael@0 | 62 | sStrokePaint.setStyle(Paint.Style.STROKE); |
michael@0 | 63 | |
michael@0 | 64 | sBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); |
michael@0 | 65 | sBackgroundPaint.setStyle(Paint.Style.FILL); |
michael@0 | 66 | } |
michael@0 | 67 | |
michael@0 | 68 | public FaviconView(Context context, AttributeSet attrs) { |
michael@0 | 69 | super(context, attrs); |
michael@0 | 70 | setScaleType(ImageView.ScaleType.CENTER); |
michael@0 | 71 | |
michael@0 | 72 | mStrokeRect = new RectF(); |
michael@0 | 73 | mBackgroundRect = new RectF(); |
michael@0 | 74 | |
michael@0 | 75 | if (sStrokeWidth == 0) { |
michael@0 | 76 | sStrokeWidth = getResources().getDisplayMetrics().density; |
michael@0 | 77 | sStrokePaint.setStrokeWidth(sStrokeWidth); |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | mStrokeRect.left = mStrokeRect.top = sStrokeWidth; |
michael@0 | 81 | mBackgroundRect.left = mBackgroundRect.top = sStrokeWidth * 2.0f; |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | @Override |
michael@0 | 85 | protected void onSizeChanged(int w, int h, int oldw, int oldh){ |
michael@0 | 86 | super.onSizeChanged(w, h, oldw, oldh); |
michael@0 | 87 | |
michael@0 | 88 | // No point rechecking the image if there hasn't really been any change. |
michael@0 | 89 | if (w == mActualWidth && h == mActualHeight) { |
michael@0 | 90 | return; |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | mActualWidth = w; |
michael@0 | 94 | mActualHeight = h; |
michael@0 | 95 | |
michael@0 | 96 | mStrokeRect.right = w - sStrokeWidth; |
michael@0 | 97 | mStrokeRect.bottom = h - sStrokeWidth; |
michael@0 | 98 | mBackgroundRect.right = mStrokeRect.right - sStrokeWidth; |
michael@0 | 99 | mBackgroundRect.bottom = mStrokeRect.bottom - sStrokeWidth; |
michael@0 | 100 | |
michael@0 | 101 | formatImage(); |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | @Override |
michael@0 | 105 | public void onDraw(Canvas canvas) { |
michael@0 | 106 | super.onDraw(canvas); |
michael@0 | 107 | |
michael@0 | 108 | // 27.5% transparent dominant color. |
michael@0 | 109 | sBackgroundPaint.setColor(mDominantColor & 0x46FFFFFF); |
michael@0 | 110 | canvas.drawRect(mStrokeRect, sBackgroundPaint); |
michael@0 | 111 | |
michael@0 | 112 | sStrokePaint.setColor(mDominantColor); |
michael@0 | 113 | canvas.drawRoundRect(mStrokeRect, sStrokeWidth, sStrokeWidth, sStrokePaint); |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | /** |
michael@0 | 117 | * Formats the image for display, if the prerequisite data are available. Upscales tiny Favicons to |
michael@0 | 118 | * normal sized ones, replaces null bitmaps with the default Favicon, and fills all remaining space |
michael@0 | 119 | * in this view with the coloured background. |
michael@0 | 120 | */ |
michael@0 | 121 | private void formatImage() { |
michael@0 | 122 | // If we're called before bitmap is set, or before size is set, show blank. |
michael@0 | 123 | if (mIconBitmap == null || mActualWidth == 0 || mActualHeight == 0) { |
michael@0 | 124 | showNoImage(); |
michael@0 | 125 | return; |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | if (mScalingExpected && mActualWidth != mIconBitmap.getWidth()) { |
michael@0 | 129 | scaleBitmap(); |
michael@0 | 130 | // Don't scale the image every time something changes. |
michael@0 | 131 | mScalingExpected = false; |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | setImageBitmap(mIconBitmap); |
michael@0 | 135 | |
michael@0 | 136 | // After scaling, determine if we have empty space around the scaled image which we need to |
michael@0 | 137 | // fill with the coloured background. If applicable, show it. |
michael@0 | 138 | // We assume Favicons are still squares and only bother with the background if more than 3px |
michael@0 | 139 | // of it would be displayed. |
michael@0 | 140 | if (Math.abs(mIconBitmap.getWidth() - mActualWidth) > 3) { |
michael@0 | 141 | mDominantColor = Favicons.getFaviconColor(mIconKey); |
michael@0 | 142 | if (mDominantColor == -1) { |
michael@0 | 143 | mDominantColor = 0; |
michael@0 | 144 | } |
michael@0 | 145 | } else { |
michael@0 | 146 | mDominantColor = 0; |
michael@0 | 147 | } |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | private void scaleBitmap() { |
michael@0 | 151 | // If the Favicon can be resized to fill the view exactly without an enlargment of more than |
michael@0 | 152 | // a factor of two, do so. |
michael@0 | 153 | int doubledSize = mIconBitmap.getWidth()*2; |
michael@0 | 154 | if (mActualWidth > doubledSize) { |
michael@0 | 155 | // If the view is more than twice the size of the image, just double the image size |
michael@0 | 156 | // and do the rest with padding. |
michael@0 | 157 | mIconBitmap = Bitmap.createScaledBitmap(mIconBitmap, doubledSize, doubledSize, true); |
michael@0 | 158 | } else { |
michael@0 | 159 | // Otherwise, scale the image to fill the view. |
michael@0 | 160 | mIconBitmap = Bitmap.createScaledBitmap(mIconBitmap, mActualWidth, mActualWidth, true); |
michael@0 | 161 | } |
michael@0 | 162 | } |
michael@0 | 163 | |
michael@0 | 164 | /** |
michael@0 | 165 | * Sets the icon displayed in this Favicon view to the bitmap provided. If the size of the view |
michael@0 | 166 | * has been set, the display will be updated right away, otherwise the update will be deferred |
michael@0 | 167 | * until then. The key provided is used to cache the result of the calculation of the dominant |
michael@0 | 168 | * colour of the provided image - this value is used to draw the coloured background in this view |
michael@0 | 169 | * if the icon is not large enough to fill it. |
michael@0 | 170 | * |
michael@0 | 171 | * @param bitmap favicon image |
michael@0 | 172 | * @param key string used as a key to cache the dominant color of this image |
michael@0 | 173 | * @param allowScaling If true, allows the provided bitmap to be scaled by this FaviconView. |
michael@0 | 174 | * Typically, you should prefer using Favicons obtained via the caching system |
michael@0 | 175 | * (Favicons class), so as to exploit caching. |
michael@0 | 176 | */ |
michael@0 | 177 | private void updateImageInternal(Bitmap bitmap, String key, boolean allowScaling) { |
michael@0 | 178 | if (bitmap == null) { |
michael@0 | 179 | showDefaultFavicon(); |
michael@0 | 180 | return; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | // Reassigning the same bitmap? Don't bother. |
michael@0 | 184 | if (mUnscaledBitmap == bitmap) { |
michael@0 | 185 | return; |
michael@0 | 186 | } |
michael@0 | 187 | mUnscaledBitmap = bitmap; |
michael@0 | 188 | mIconBitmap = bitmap; |
michael@0 | 189 | mIconKey = key; |
michael@0 | 190 | mScalingExpected = allowScaling; |
michael@0 | 191 | |
michael@0 | 192 | // Possibly update the display. |
michael@0 | 193 | formatImage(); |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | public void showDefaultFavicon() { |
michael@0 | 197 | setImageResource(R.drawable.favicon); |
michael@0 | 198 | mDominantColor = 0; |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | private void showNoImage() { |
michael@0 | 202 | setImageDrawable(null); |
michael@0 | 203 | mDominantColor = 0; |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | /** |
michael@0 | 207 | * Clear image and background shown by this view. |
michael@0 | 208 | */ |
michael@0 | 209 | public void clearImage() { |
michael@0 | 210 | showNoImage(); |
michael@0 | 211 | mUnscaledBitmap = null; |
michael@0 | 212 | mIconBitmap = null; |
michael@0 | 213 | mIconKey = null; |
michael@0 | 214 | mScalingExpected = false; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | /** |
michael@0 | 218 | * Update the displayed image and apply the scaling logic. |
michael@0 | 219 | * The scaling logic will attempt to resize the image to fit correctly inside the view in a way |
michael@0 | 220 | * that avoids unreasonable levels of loss of quality. |
michael@0 | 221 | * Scaling is necessary only when the icon being provided is not drawn from the Favicon cache |
michael@0 | 222 | * introduced in Bug 914296. |
michael@0 | 223 | * |
michael@0 | 224 | * Due to Bug 913746, icons bundled for search engines are not available to the cache, so must |
michael@0 | 225 | * always have the scaling logic applied here. At the time of writing, this is the only case in |
michael@0 | 226 | * which the scaling logic here is applied. |
michael@0 | 227 | * |
michael@0 | 228 | * @param bitmap The bitmap to display in this favicon view. |
michael@0 | 229 | * @param key The key to use into the dominant colours cache when selecting a background colour. |
michael@0 | 230 | */ |
michael@0 | 231 | public void updateAndScaleImage(Bitmap bitmap, String key) { |
michael@0 | 232 | updateImageInternal(bitmap, key, true); |
michael@0 | 233 | } |
michael@0 | 234 | |
michael@0 | 235 | /** |
michael@0 | 236 | * Update the image displayed in the Favicon view without scaling. Images larger than the view |
michael@0 | 237 | * will be centrally cropped. Images smaller than the view will be placed centrally and the |
michael@0 | 238 | * extra space filled with the dominant colour of the provided image. |
michael@0 | 239 | * |
michael@0 | 240 | * @param bitmap The bitmap to display in this favicon view. |
michael@0 | 241 | * @param key The key to use into the dominant colours cache when selecting a background colour. |
michael@0 | 242 | */ |
michael@0 | 243 | public void updateImage(Bitmap bitmap, String key) { |
michael@0 | 244 | updateImageInternal(bitmap, key, false); |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | public Bitmap getBitmap() { |
michael@0 | 248 | return mIconBitmap; |
michael@0 | 249 | } |
michael@0 | 250 | } |