2013-04-29 17:05:55 -07:00
|
|
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
|
|
|
/* 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.gecko.widget;
|
|
|
|
|
|
|
|
import org.mozilla.gecko.R;
|
2013-11-01 13:18:29 -07:00
|
|
|
import org.mozilla.gecko.favicons.Favicons;
|
2013-04-29 17:05:55 -07:00
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.graphics.Bitmap;
|
2013-11-01 13:18:29 -07:00
|
|
|
import android.graphics.Canvas;
|
|
|
|
import android.graphics.Paint;
|
|
|
|
import android.graphics.RectF;
|
2013-04-29 17:05:55 -07:00
|
|
|
import android.util.AttributeSet;
|
|
|
|
import android.widget.ImageView;
|
2013-09-12 07:50:20 -07:00
|
|
|
/**
|
2013-04-29 17:05:55 -07:00
|
|
|
* Special version of ImageView for favicons.
|
2013-09-12 07:50:20 -07:00
|
|
|
* Displays solid colour background around Favicon to fill space not occupied by the icon. Colour
|
|
|
|
* selected is the dominant colour of the provided Favicon.
|
2013-04-29 17:05:55 -07:00
|
|
|
*/
|
|
|
|
public class FaviconView extends ImageView {
|
2013-09-12 07:50:20 -07:00
|
|
|
private Bitmap mIconBitmap;
|
|
|
|
|
|
|
|
// Reference to the unscaled bitmap, if any, to prevent repeated assignments of the same bitmap
|
|
|
|
// to the view from causing repeated rescalings (Some of the callers do this)
|
|
|
|
private Bitmap mUnscaledBitmap;
|
|
|
|
|
|
|
|
// Key into the Favicon dominant colour cache. Should be the Favicon URL if the image displayed
|
|
|
|
// here is a Favicon managed by the caching system. If not, any appropriately unique-to-this-image
|
|
|
|
// string is acceptable.
|
|
|
|
private String mIconKey;
|
|
|
|
|
|
|
|
private int mActualWidth;
|
|
|
|
private int mActualHeight;
|
|
|
|
|
|
|
|
// Flag indicating if the most recently assigned image is considered likely to need scaling.
|
|
|
|
private boolean mScalingExpected;
|
2013-04-29 17:05:55 -07:00
|
|
|
|
2013-11-01 13:18:29 -07:00
|
|
|
// Dominant color of the favicon.
|
|
|
|
private int mDominantColor;
|
|
|
|
|
|
|
|
// Stroke width for the border.
|
|
|
|
private static float sStrokeWidth;
|
|
|
|
|
|
|
|
// Paint for drawing the stroke.
|
2014-10-10 16:17:01 -07:00
|
|
|
private static final Paint sStrokePaint;
|
2013-11-01 13:18:29 -07:00
|
|
|
|
|
|
|
// Paint for drawing the background.
|
2014-10-10 16:17:01 -07:00
|
|
|
private static final Paint sBackgroundPaint;
|
2013-11-01 13:18:29 -07:00
|
|
|
|
|
|
|
// Size of the stroke rectangle.
|
|
|
|
private final RectF mStrokeRect;
|
|
|
|
|
|
|
|
// Size of the background rectangle.
|
|
|
|
private final RectF mBackgroundRect;
|
|
|
|
|
|
|
|
// Initializing the static paints.
|
|
|
|
static {
|
|
|
|
sStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
|
|
sStrokePaint.setStyle(Paint.Style.STROKE);
|
|
|
|
|
|
|
|
sBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
|
|
sBackgroundPaint.setStyle(Paint.Style.FILL);
|
|
|
|
}
|
|
|
|
|
2013-04-29 17:05:55 -07:00
|
|
|
public FaviconView(Context context, AttributeSet attrs) {
|
|
|
|
super(context, attrs);
|
|
|
|
setScaleType(ImageView.ScaleType.CENTER);
|
2013-11-01 13:18:29 -07:00
|
|
|
|
|
|
|
mStrokeRect = new RectF();
|
|
|
|
mBackgroundRect = new RectF();
|
|
|
|
|
|
|
|
if (sStrokeWidth == 0) {
|
|
|
|
sStrokeWidth = getResources().getDisplayMetrics().density;
|
|
|
|
sStrokePaint.setStrokeWidth(sStrokeWidth);
|
|
|
|
}
|
|
|
|
|
|
|
|
mStrokeRect.left = mStrokeRect.top = sStrokeWidth;
|
|
|
|
mBackgroundRect.left = mBackgroundRect.top = sStrokeWidth * 2.0f;
|
2013-04-29 17:05:55 -07:00
|
|
|
}
|
|
|
|
|
2013-09-12 07:50:20 -07:00
|
|
|
@Override
|
2013-11-01 13:18:29 -07:00
|
|
|
protected void onSizeChanged(int w, int h, int oldw, int oldh){
|
|
|
|
super.onSizeChanged(w, h, oldw, oldh);
|
2013-09-12 07:50:20 -07:00
|
|
|
|
|
|
|
// No point rechecking the image if there hasn't really been any change.
|
2013-11-01 13:18:29 -07:00
|
|
|
if (w == mActualWidth && h == mActualHeight) {
|
2013-09-12 07:50:20 -07:00
|
|
|
return;
|
|
|
|
}
|
2013-11-01 13:18:29 -07:00
|
|
|
|
|
|
|
mActualWidth = w;
|
|
|
|
mActualHeight = h;
|
|
|
|
|
|
|
|
mStrokeRect.right = w - sStrokeWidth;
|
|
|
|
mStrokeRect.bottom = h - sStrokeWidth;
|
|
|
|
mBackgroundRect.right = mStrokeRect.right - sStrokeWidth;
|
|
|
|
mBackgroundRect.bottom = mStrokeRect.bottom - sStrokeWidth;
|
|
|
|
|
2013-09-12 07:50:20 -07:00
|
|
|
formatImage();
|
|
|
|
}
|
|
|
|
|
2013-11-01 13:18:29 -07:00
|
|
|
@Override
|
|
|
|
public void onDraw(Canvas canvas) {
|
|
|
|
super.onDraw(canvas);
|
|
|
|
|
|
|
|
// 27.5% transparent dominant color.
|
|
|
|
sBackgroundPaint.setColor(mDominantColor & 0x46FFFFFF);
|
|
|
|
canvas.drawRect(mStrokeRect, sBackgroundPaint);
|
|
|
|
|
|
|
|
sStrokePaint.setColor(mDominantColor);
|
|
|
|
canvas.drawRoundRect(mStrokeRect, sStrokeWidth, sStrokeWidth, sStrokePaint);
|
|
|
|
}
|
|
|
|
|
2013-09-12 07:50:20 -07:00
|
|
|
/**
|
|
|
|
* Formats the image for display, if the prerequisite data are available. Upscales tiny Favicons to
|
|
|
|
* normal sized ones, replaces null bitmaps with the default Favicon, and fills all remaining space
|
|
|
|
* in this view with the coloured background.
|
|
|
|
*/
|
|
|
|
private void formatImage() {
|
2013-09-27 04:12:22 -07:00
|
|
|
// If we're called before bitmap is set, or before size is set, show blank.
|
|
|
|
if (mIconBitmap == null || mActualWidth == 0 || mActualHeight == 0) {
|
2013-10-02 08:31:37 -07:00
|
|
|
showNoImage();
|
2013-09-12 07:50:20 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mScalingExpected && mActualWidth != mIconBitmap.getWidth()) {
|
|
|
|
scaleBitmap();
|
|
|
|
// Don't scale the image every time something changes.
|
|
|
|
mScalingExpected = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
setImageBitmap(mIconBitmap);
|
|
|
|
|
|
|
|
// After scaling, determine if we have empty space around the scaled image which we need to
|
|
|
|
// fill with the coloured background. If applicable, show it.
|
|
|
|
// We assume Favicons are still squares and only bother with the background if more than 3px
|
|
|
|
// of it would be displayed.
|
|
|
|
if (Math.abs(mIconBitmap.getWidth() - mActualWidth) > 3) {
|
2013-11-01 13:18:29 -07:00
|
|
|
mDominantColor = Favicons.getFaviconColor(mIconKey);
|
|
|
|
if (mDominantColor == -1) {
|
|
|
|
mDominantColor = 0;
|
|
|
|
}
|
2013-09-12 07:50:20 -07:00
|
|
|
} else {
|
2013-11-01 13:18:29 -07:00
|
|
|
mDominantColor = 0;
|
2013-09-12 07:50:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void scaleBitmap() {
|
|
|
|
// If the Favicon can be resized to fill the view exactly without an enlargment of more than
|
|
|
|
// a factor of two, do so.
|
|
|
|
int doubledSize = mIconBitmap.getWidth()*2;
|
|
|
|
if (mActualWidth > doubledSize) {
|
|
|
|
// If the view is more than twice the size of the image, just double the image size
|
|
|
|
// and do the rest with padding.
|
|
|
|
mIconBitmap = Bitmap.createScaledBitmap(mIconBitmap, doubledSize, doubledSize, true);
|
|
|
|
} else {
|
|
|
|
// Otherwise, scale the image to fill the view.
|
|
|
|
mIconBitmap = Bitmap.createScaledBitmap(mIconBitmap, mActualWidth, mActualWidth, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the icon displayed in this Favicon view to the bitmap provided. If the size of the view
|
|
|
|
* has been set, the display will be updated right away, otherwise the update will be deferred
|
|
|
|
* until then. The key provided is used to cache the result of the calculation of the dominant
|
|
|
|
* colour of the provided image - this value is used to draw the coloured background in this view
|
|
|
|
* if the icon is not large enough to fill it.
|
|
|
|
*
|
2013-05-06 13:13:59 -07:00
|
|
|
* @param bitmap favicon image
|
|
|
|
* @param key string used as a key to cache the dominant color of this image
|
2013-09-12 07:50:20 -07:00
|
|
|
* @param allowScaling If true, allows the provided bitmap to be scaled by this FaviconView.
|
|
|
|
* Typically, you should prefer using Favicons obtained via the caching system
|
|
|
|
* (Favicons class), so as to exploit caching.
|
2013-05-06 13:13:59 -07:00
|
|
|
*/
|
2013-09-12 07:50:20 -07:00
|
|
|
private void updateImageInternal(Bitmap bitmap, String key, boolean allowScaling) {
|
2013-09-27 04:12:22 -07:00
|
|
|
if (bitmap == null) {
|
|
|
|
showDefaultFavicon();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-12 07:50:20 -07:00
|
|
|
// Reassigning the same bitmap? Don't bother.
|
|
|
|
if (mUnscaledBitmap == bitmap) {
|
|
|
|
return;
|
2013-04-29 17:05:55 -07:00
|
|
|
}
|
2013-09-12 07:50:20 -07:00
|
|
|
mUnscaledBitmap = bitmap;
|
|
|
|
mIconBitmap = bitmap;
|
|
|
|
mIconKey = key;
|
|
|
|
mScalingExpected = allowScaling;
|
|
|
|
|
|
|
|
// Possibly update the display.
|
|
|
|
formatImage();
|
|
|
|
}
|
|
|
|
|
2013-10-11 16:42:39 -07:00
|
|
|
public void showDefaultFavicon() {
|
2013-09-27 04:12:22 -07:00
|
|
|
setImageResource(R.drawable.favicon);
|
2013-11-01 13:18:29 -07:00
|
|
|
mDominantColor = 0;
|
2013-09-27 04:12:22 -07:00
|
|
|
}
|
|
|
|
|
2013-10-02 08:31:37 -07:00
|
|
|
private void showNoImage() {
|
2013-11-01 15:56:41 -07:00
|
|
|
setImageDrawable(null);
|
2013-11-01 13:18:29 -07:00
|
|
|
mDominantColor = 0;
|
2013-10-02 08:31:37 -07:00
|
|
|
}
|
|
|
|
|
2013-09-18 09:58:58 -07:00
|
|
|
/**
|
|
|
|
* Clear image and background shown by this view.
|
|
|
|
*/
|
|
|
|
public void clearImage() {
|
2013-10-02 08:31:37 -07:00
|
|
|
showNoImage();
|
2013-09-27 04:12:22 -07:00
|
|
|
mUnscaledBitmap = null;
|
|
|
|
mIconBitmap = null;
|
|
|
|
mIconKey = null;
|
|
|
|
mScalingExpected = false;
|
2013-09-18 09:58:58 -07:00
|
|
|
}
|
|
|
|
|
2013-09-14 12:09:14 -07:00
|
|
|
/**
|
|
|
|
* Update the displayed image and apply the scaling logic.
|
|
|
|
* The scaling logic will attempt to resize the image to fit correctly inside the view in a way
|
|
|
|
* that avoids unreasonable levels of loss of quality.
|
|
|
|
* Scaling is necessary only when the icon being provided is not drawn from the Favicon cache
|
|
|
|
* introduced in Bug 914296.
|
|
|
|
*
|
|
|
|
* Due to Bug 913746, icons bundled for search engines are not available to the cache, so must
|
|
|
|
* always have the scaling logic applied here. At the time of writing, this is the only case in
|
|
|
|
* which the scaling logic here is applied.
|
|
|
|
*
|
|
|
|
* @param bitmap The bitmap to display in this favicon view.
|
|
|
|
* @param key The key to use into the dominant colours cache when selecting a background colour.
|
|
|
|
*/
|
2013-09-12 07:50:20 -07:00
|
|
|
public void updateAndScaleImage(Bitmap bitmap, String key) {
|
|
|
|
updateImageInternal(bitmap, key, true);
|
|
|
|
}
|
|
|
|
|
2013-09-14 12:09:14 -07:00
|
|
|
/**
|
|
|
|
* Update the image displayed in the Favicon view without scaling. Images larger than the view
|
|
|
|
* will be centrally cropped. Images smaller than the view will be placed centrally and the
|
|
|
|
* extra space filled with the dominant colour of the provided image.
|
|
|
|
*
|
|
|
|
* @param bitmap The bitmap to display in this favicon view.
|
|
|
|
* @param key The key to use into the dominant colours cache when selecting a background colour.
|
|
|
|
*/
|
2013-09-12 07:50:20 -07:00
|
|
|
public void updateImage(Bitmap bitmap, String key) {
|
|
|
|
updateImageInternal(bitmap, key, false);
|
2013-04-29 17:05:55 -07:00
|
|
|
}
|
2013-09-14 12:09:14 -07:00
|
|
|
|
|
|
|
public Bitmap getBitmap() {
|
|
|
|
return mIconBitmap;
|
|
|
|
}
|
2013-04-29 17:05:55 -07:00
|
|
|
}
|