// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.chrome.browser.widget; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.graphics.RectF; import android.text.TextPaint; import android.text.TextUtils; import android.util.Log; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.util.UrlUtilities; import java.net.URI; import java.util.Locale; import javax.annotation.Nullable; /** * Generator for transparent icons containing a rounded rectangle with a given background color, * having a centered character drawn on top of it. */ public class RoundedIconGenerator { private static final String TAG = RoundedIconGenerator.class.getSimpleName(); private final int mIconWidthPx; private final int mIconHeightPx; private final int mCornerRadiusPx; private final RectF mBackgroundRect; private final Paint mBackgroundPaint; private final TextPaint mTextPaint; private final float mTextHeight; private final float mTextYOffset; /** * Constructs the generator and initializes the common members based on the display density. * * @param context The context used for initialization. * @param iconWidthDp The width of the generated icon in dp. * @param iconHeightDp The height of the generated icon in dp. * @param cornerRadiusDp The radius of the corners in the icon in dp. * @param backgroundColor Color with which the rounded rectangle should be drawn. * @param textSizeDp Size at which the text should be drawn in dp. */ public RoundedIconGenerator(Context context, int iconWidthDp, int iconHeightDp, int cornerRadiusDp, int backgroundColor, int textSizeDp) { this((int) (context.getResources().getDisplayMetrics().density * iconWidthDp), (int) (context.getResources().getDisplayMetrics().density * iconHeightDp), (int) (context.getResources().getDisplayMetrics().density * cornerRadiusDp), backgroundColor, (int) (context.getResources().getDisplayMetrics().density * textSizeDp)); } /** * Constructs the generator and initializes the common members ignoring display density. * * @param iconWidthPx The width of the generated icon in pixels. * @param iconHeightPx The height of the generated icon in pixels. * @param cornerRadiusPx The radius of the corners in the icon in pixels. * @param backgroundColor Color at which the rounded rectangle should be drawn. * @param textSizePx Size at which the text should be drawn in pixels. */ public RoundedIconGenerator(int iconWidthPx, int iconHeightPx, int cornerRadiusPx, int backgroundColor, float textSizePx) { mIconWidthPx = iconWidthPx; mIconHeightPx = iconHeightPx; mCornerRadiusPx = cornerRadiusPx; mBackgroundRect = new RectF(0, 0, mIconWidthPx, mIconHeightPx); mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBackgroundPaint.setColor(backgroundColor); mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setColor(Color.WHITE); mTextPaint.setFakeBoldText(true); mTextPaint.setTextSize(textSizePx); FontMetrics textFontMetrics = mTextPaint.getFontMetrics(); mTextHeight = (float) Math.ceil(textFontMetrics.bottom - textFontMetrics.top); mTextYOffset = -textFontMetrics.top; } /** * Sets the background color to use when generating icons. */ public void setBackgroundColor(int color) { mBackgroundPaint.setColor(color); } /** * Generates an icon based on |text|. * * @param text The text to render the first character of on the icon. * @return The generated icon. */ public Bitmap generateIconForText(String text) { Bitmap icon = Bitmap.createBitmap(mIconWidthPx, mIconHeightPx, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(icon); canvas.drawRoundRect(mBackgroundRect, mCornerRadiusPx, mCornerRadiusPx, mBackgroundPaint); String displayText = text.substring(0, 1).toUpperCase(Locale.getDefault()); float textWidth = mTextPaint.measureText(displayText); canvas.drawText( displayText, (mIconWidthPx - textWidth) / 2f, Math.round((Math.max(mIconHeightPx, mTextHeight) - mTextHeight) / 2.0f + mTextYOffset), mTextPaint); return icon; } /** * Returns a Bitmap representing the icon to be used for |url|. * * @param url URL for which the icon should be generated. * @param includePrivateRegistries Should private registries be considered as TLDs? * @return The generated icon, or NULL if |url| is empty or the domain cannot be resolved. */ @Nullable public Bitmap generateIconForUrl(String url, boolean includePrivateRegistries) { if (TextUtils.isEmpty(url)) return null; String text = getIconTextForUrl(url, includePrivateRegistries); if (TextUtils.isEmpty(text)) return null; return generateIconForText(text); } /** * Returns a Bitmap representing the icon to be used for |url|. Private registries such * as "appspot.com" will not be considered as effective TLDs. * * @TODO(beverloo) Update all call-sites of rounded icons to be explicit about whether * private registries should be considered, matching the getDomainAndRegistry requirements. * See https://crbug.com/458104. * * @param url URL for which the icon should be generated. * @return The generated icon, or NULL if |url| is empty or the domain cannot be resolved. */ @Nullable public Bitmap generateIconForUrl(String url) { return generateIconForUrl(url, false); } /** * Returns the text which should be used for generating a rounded icon based on |url|. * * @param url URL to consider when getting the icon's text. * @param includePrivateRegistries Should private registries be considered as TLDs? * @return The text to use on the rounded icon, or NULL if |url| is empty or the domain cannot * be resolved. */ @Nullable @VisibleForTesting public static String getIconTextForUrl(String url, boolean includePrivateRegistries) { String domain = UrlUtilities.getDomainAndRegistry(url, includePrivateRegistries); if (!TextUtils.isEmpty(domain)) return domain; // Special-case chrome:// and chrome-native:// URLs. if (url.startsWith(UrlConstants.CHROME_SCHEME) || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME)) { return "chrome"; } // Use the host component of |url| when it can be parsed as a URI. try { URI uri = new URI(url); if (!TextUtils.isEmpty(uri.getHost())) { return uri.getHost(); } } catch (Exception e) { Log.w(TAG, "Unable to parse the URL for generating an icon: " + url); } return url; } }