Updated Tile view for folder and favicon
- New layouts for folder icons in Bookmarks view.
- Reworked favicon tile views.
- Display of SSL certification information on top of favicon
- Modified logic behind showing counter on top of favicon
Change-Id: Iaacc13be37e7cc65e859b46e0d91d5d5d6b62a9e
diff --git a/src/com/android/browser/BrowserBookmarksAdapter.java b/src/com/android/browser/BrowserBookmarksAdapter.java
index fc75050..a65753c 100644
--- a/src/com/android/browser/BrowserBookmarksAdapter.java
+++ b/src/com/android/browser/BrowserBookmarksAdapter.java
@@ -17,6 +17,7 @@
package com.android.browser;
import android.content.Context;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -24,8 +25,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
import com.android.browser.mdm.EditBookmarksRestriction;
import com.android.browser.mdm.ManagedBookmarksRestriction;
@@ -62,75 +61,46 @@
return mInflater.inflate(R.layout.bookmark_thumbnail, parent, false);
}
- @Override
- public void bindView(View view, BrowserBookmarksAdapterItem object) {
- BookmarkContainer container = (BookmarkContainer) view;
- container.setIgnoreRequestLayout(true);
- bindGridView(view, mContext, object);
- container.setIgnoreRequestLayout(false);
- }
-
CharSequence getTitle(Cursor cursor) {
int type = cursor.getInt(BookmarksLoader.COLUMN_INDEX_TYPE);
switch (type) {
- case Bookmarks.BOOKMARK_TYPE_OTHER_FOLDER:
- return mContext.getText(R.string.other_bookmarks);
+ case Bookmarks.BOOKMARK_TYPE_OTHER_FOLDER:
+ return mContext.getText(R.string.other_bookmarks);
}
return cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE);
}
- void bindGridView(View view, Context context, BrowserBookmarksAdapterItem item) {
- // We need to set this to handle rotation and other configuration change
- // events. If the padding didn't change, this is a no op.
- int padding = context.getResources()
+ @Override
+ public void bindView(View view, BrowserBookmarksAdapterItem item) {
+ BookmarkContainer c = (BookmarkContainer) view;
+
+ // we need to set this to handle rotation and other configuration change events.
+ // (if the padding didn't change, this is a no op)
+ int padding = mContext.getResources()
.getDimensionPixelSize(R.dimen.combo_horizontalSpacing);
- view.setPadding(padding, view.getPaddingTop(),
- padding, view.getPaddingBottom());
- SiteTileView thumb = (SiteTileView) view.findViewById(R.id.thumb_image);
- TextView tv = (TextView) view.findViewById(R.id.label);
- tv.setText(item.title);
+ c.setPadding(padding, c.getPaddingTop(), padding, c.getPaddingBottom());
- Bitmap b;
-
- thumb.setFloating(false);
-
+ // configure the main content of the bookmark icon
if (item.is_folder) {
- b = BitmapFactory.decodeResource(mContext.getResources(),
- R.drawable.ic_deco_folder_normal);
- thumb.setFloating(true);
- }
- else if (item.thumbnail == null || !item.has_thumbnail) {
- b = BitmapFactory.decodeResource(mContext.getResources(),
- R.drawable.browser_thumbnail);
- }
- else {
- b = item.thumbnail.getBitmap();
+ c.reConfigureAsFolder(item.title.toString(), "");
+ } else {
+ final Bitmap favicon = (item.thumbnail == null || !item.has_thumbnail) ?
+ null : item.thumbnail.getBitmap();
+ c.reConfigureAsSite(favicon);
}
- // If the item is managed by mdm or edit bookmark restriction enabled
+ // configure the label under the bookmark
+ if (item.title != null) {
+ c.setBottomLabelText(item.title.toString());
+ }
+
+ // if the item is managed by mdm or edit bookmark restriction, show a badge
if (item.title != null &&
(item.is_mdm_managed || EditBookmarksRestriction.getInstance().isEnabled())) {
- int containerWidth = view.getResources().getDimensionPixelSize(R.dimen.bookmarkThumbnailWidth);
- int containerHeight = view.getResources().getDimensionPixelSize(R.dimen.bookmarkThumbnailHeight);
- Bitmap bm;
-
- if (item.is_mdm_managed) {
- bm = BrowserBookmarksPage.overlayBookmarkBitmap(mContext, b,
- R.drawable.img_deco_mdm_badge_bright,
- containerWidth, containerHeight, 0.6f, 185, 20);
- }
- else {
- bm = BrowserBookmarksPage.overlayBookmarkBitmap(mContext, b,
- R.drawable.ic_deco_secure,
- containerWidth, containerHeight, 1.7f, 110, 0);
- }
-
- thumb.replaceFavicon(bm);
- }
- else {
- thumb.replaceFavicon(b);
- }
- thumb.setLongClickable(true);
+ c.setOverlayBadge(item.is_mdm_managed ? R.drawable.img_deco_mdm_badge_bright :
+ R.drawable.ic_deco_secure);
+ } else
+ c.setOverlayBadge(0);
}
@Override
diff --git a/src/com/android/browser/FolderTileView.java b/src/com/android/browser/FolderTileView.java
new file mode 100644
index 0000000..2d23ebf
--- /dev/null
+++ b/src/com/android/browser/FolderTileView.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+package com.android.browser;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class FolderTileView extends ViewGroup {
+
+ // created in the constructor
+ private View mInnerLayout;
+ private TextView mTextView;
+ private TextView mLabelView;
+ private int mPaddingLeft = 0;
+ private int mPaddingTop = 0;
+ private int mPaddingRight = 0;
+ private int mPaddingBottom = 0;
+
+ // runtime params set on Layout
+ private int mCurrentWidth;
+ private int mCurrentHeight;
+
+ // static objects, to be recycled amongst instances (this is an optimization)
+ private static Paint sBackgroundPaint;
+ private String mText;
+ private String mLabel;
+
+
+ /* XML constructors */
+
+ public FolderTileView(Context context) {
+ super(context);
+ xmlInit(null, 0);
+ }
+
+ public FolderTileView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ xmlInit(attrs, 0);
+ }
+
+ public FolderTileView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ xmlInit(attrs, defStyle);
+ }
+
+
+ /* Programmatic Constructors */
+
+ public FolderTileView(Context context, String text, String label) {
+ super(context);
+ mText = text;
+ mLabel = label;
+ init();
+ }
+
+
+ /**
+ * Replaces the main text of the bookmark tile
+ */
+ public void setText(String text) {
+ mText = text;
+ if (mTextView != null)
+ mTextView.setText(mText);
+ }
+
+ /**
+ * Replaces the subtitle, for example "32 items"
+ */
+ public void setLabel(String label) {
+ mLabel = label;
+ if (mLabelView != null)
+ mLabelView.setText(mLabel);
+ }
+
+
+ /* private stuff ahead */
+
+ private void xmlInit(AttributeSet attrs, int defStyle) {
+ // load attributes
+ final TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.FolderTileView, defStyle, 0);
+
+ // saves the text for later
+ setText(a.getString(R.styleable.FolderTileView_android_text));
+
+ // saves the label for later
+ setLabel(a.getString(R.styleable.FolderTileView_android_label));
+
+ // delete attribute resolution
+ a.recycle();
+
+ // proceed with real initialization
+ init();
+ }
+
+ private void init() {
+ // create new Views for us from the XML (and automatically add them)
+ inflate(getContext(), R.layout.folder_tile_view, this);
+
+ // we make the assumption that the XML file will always have 1 and only 1 child
+ mInnerLayout = getChildAt(0);
+ mInnerLayout.setVisibility(View.VISIBLE);
+
+ // reference objects
+ mTextView = (TextView) mInnerLayout.findViewById(android.R.id.text1);
+ if (mText != null && !mText.isEmpty())
+ mTextView.setText(mText);
+ mLabelView = (TextView) mInnerLayout.findViewById(android.R.id.text2);
+ if (mLabel != null && !mLabel.isEmpty())
+ mLabelView.setText(mLabel);
+
+ // load the common statics, also for the SiteTileView if needed
+ final Resources resources = getResources();
+ SiteTileView.ensureCommonLoaded(resources);
+ ensureCommonLoaded(resources);
+
+ // get the padding rect from the Tile View (to stay synced in size)
+ final Rect padding = SiteTileView.getBackgroundDrawablePadding();
+ mPaddingLeft = padding.left;
+ mPaddingTop = padding.top;
+ mPaddingRight = padding.right;
+ mPaddingBottom = padding.bottom;
+
+ // we'll draw our background (usually ViewGroups don't)
+ setWillNotDraw(false);
+ }
+
+ private static void ensureCommonLoaded(Resources r) {
+ if (sBackgroundPaint != null)
+ return;
+
+ // shared tiles background paint
+ sBackgroundPaint = new Paint();
+ sBackgroundPaint.setColor(r.getColor(R.color.FolderTileBackground));
+ sBackgroundPaint.setAntiAlias(true);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // layout the xml inflated contents
+ if (mInnerLayout != null) {
+ final int desiredWidth = MeasureSpec.getSize(widthMeasureSpec);
+ final int desiredHeight = MeasureSpec.getSize(heightMeasureSpec);
+ if (desiredHeight > 0 || desiredHeight > 0)
+ mInnerLayout.measure(
+ MeasureSpec.EXACTLY | desiredWidth - mPaddingLeft - mPaddingRight,
+ MeasureSpec.EXACTLY | desiredHeight - mPaddingTop - mPaddingBottom);
+ else
+ mInnerLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ // update current params
+ mCurrentWidth = right - left;
+ mCurrentHeight = bottom - top;
+
+ // layout the inflated XML layout using the same Padding as the SiteTileView
+ if (mInnerLayout != null)
+ mInnerLayout.layout(mPaddingLeft, mPaddingTop,
+ mCurrentWidth - mPaddingRight, mCurrentHeight - mPaddingBottom);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ final int left = mPaddingLeft;
+ final int top = mPaddingTop;
+ final int right = mCurrentWidth - mPaddingRight;
+ final int bottom = mCurrentHeight - mPaddingBottom;
+
+ // draw the background rectangle
+ float roundedRadius = SiteTileView.sRoundedRadius;
+ if (roundedRadius >= 1.) {
+ SiteTileView.sRectF.set(left, top, right, bottom);
+ canvas.drawRoundRect(SiteTileView.sRectF, roundedRadius, roundedRadius, sBackgroundPaint);
+ } else
+ canvas.drawRect(left, top, right, bottom, sBackgroundPaint);
+ }
+
+}
diff --git a/src/com/android/browser/NavigationBarBase.java b/src/com/android/browser/NavigationBarBase.java
index c032872..c1ad5c9 100644
--- a/src/com/android/browser/NavigationBarBase.java
+++ b/src/com/android/browser/NavigationBarBase.java
@@ -147,7 +147,7 @@
if (wv != null && WebRefiner.isInitialized()) {
int count = WebRefiner.getInstance().getBlockedURLCount(wv);
if (count > 0) {
- mFaviconTile.setExtraBlockedObjectsCount(count);
+ mFaviconTile.setBadgeBlockedObjectsCount(count);
}
}
mHandler.sendEmptyMessageDelayed(WEBREFINER_COUNTER_MSG,
@@ -170,12 +170,15 @@
switch (mSecurityState) {
case SECURITY_STATE_SECURE:
mFaviconTile.setTrustLevel(SiteTileView.TRUST_TRUSTED);
+ mFaviconTile.setBadgeHasCertIssues(false);
break;
case SECURITY_STATE_MIXED:
mFaviconTile.setTrustLevel(SiteTileView.TRUST_UNTRUSTED);
+ mFaviconTile.setBadgeHasCertIssues(true);
break;
case SECURITY_STATE_BAD_CERTIFICATE:
mFaviconTile.setTrustLevel(SiteTileView.TRUST_AVOID);
+ mFaviconTile.setBadgeHasCertIssues(true);
break;
case SECURITY_STATE_NOT_SECURE:
default:
@@ -719,8 +722,9 @@
}
public void onProgressStarted() {
- mFaviconTile.setExtraBlockedObjectsCount(0);
+ mFaviconTile.setBadgeBlockedObjectsCount(0);
mFaviconTile.setTrustLevel(SiteTileView.TRUST_UNKNOWN);
+ mFaviconTile.setBadgeHasCertIssues(false);
mFaviconTile.replaceFavicon(mDefaultFavicon);
mSecurityState = Tab.SecurityState.SECURITY_STATE_NOT_SECURE;
mHandler.removeMessages(WEBREFINER_COUNTER_MSG);
diff --git a/src/com/android/browser/SiteTileView.java b/src/com/android/browser/SiteTileView.java
index e22404f..e2951ae 100644
--- a/src/com/android/browser/SiteTileView.java
+++ b/src/com/android/browser/SiteTileView.java
@@ -29,20 +29,26 @@
package com.android.browser;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
+import java.util.Map;
+
/**
* This represents a WebSite Tile that is created from a Drawable and will scale across any
* area this is externally layouted to. There are 3 possible looks:
@@ -70,14 +76,10 @@
private static final int THRESHOLD_LARGE_DP = 64;
private static final int LARGE_FAVICON_SIZE_DP = 48;
private static final int BACKGROUND_DRAWABLE_RES = R.drawable.img_tile_background;
+ private static final int DEFAULT_SITE_FAVICON = 0;
private static final float FILLER_RADIUS_DP = 2f; // sync with the bg image radius
private static final int FILLER_FALLBACK_COLOR = Color.WHITE; // in case there is no favicon
- private static final int OVERLINE_WIDTH_RES = R.dimen.SiteTileOverlineWidth;
- private static final int OVERLINE_COLOR_DEFAULT_RES = R.color.SiteTileOverlineUnknown;
- private static final int OVERLINE_COLOR_TRUSTED_RES = R.color.SiteTileOverlineTrusted;
- private static final int OVERLINE_COLOR_UNTRUSTED_RES = R.color.SiteTileOverlineUntrusted;
- private static final int OVERLINE_COLOR_AVOID_RES = R.color.SiteTileOverlineAvoid;
- private static final boolean SHOW_EXTRAS_BLOCKED_COUNT = true;
+ private static final boolean BADGE_SHOW_BLOCKED_COUNT = false;
// internal enums
private static final int TYPE_SMALL = 1;
@@ -92,12 +94,11 @@
private Paint mFundamentalPaint = null;
private int mFaviconWidth = 0;
private int mFaviconHeight = 0;
- private int mForcedType = TYPE_AUTO;
private int mForcedFundamentalColor = COLOR_AUTO;
- private boolean mFloating = false;
+ private boolean mBackgroundDisabled = false;
private int mTrustLevel = TRUST_UNKNOWN;
- private int mExtraBlockedObjectsCount = 0;
- private boolean mExtrasShown = false;
+ private int mBadgeBlockedObjectsCount = 0;
+ private boolean mBadgeHasCertIssues = false;
// runtime params set on Layout
private int mCurrentWidth = 0;
@@ -107,21 +108,28 @@
private int mPaddingTop = 0;
private int mPaddingRight = 0;
private int mPaddingBottom = 0;
- private boolean mCurrentBackgroundDrawn = false;
+ private boolean mCurrentShadowDrawn = false;
// static objects, to be recycled amongst instances (this is an optimization)
+ // NOTE: package-visible statics are for optimized usage inside FolderTileView as well
private static int sMediumPxThreshold = -1;
private static int sLargePxThreshold = -1;
private static int sLargeFaviconPx = -1;
- private static float sRoundedRadius = -1;
+ /* package */ static float sRoundedRadius = -1;
private static Paint sBitmapPaint = null;
- private static Paint sExtrasPaint = null;
+ private static Paint sBadgeTextPaint = null;
private static Rect sSrcRect = new Rect();
private static Rect sDstRect = new Rect();
- private static RectF sRectF = new RectF();
- private static Paint sOverlineOutlinePaint = null;
+ /* package */ static RectF sRectF = new RectF();
private static Drawable sBackgroundDrawable = null;
- private static Rect sBackgroundDrawablePadding = new Rect();
+ private static class BadgeAssets {
+ Drawable back;
+ Drawable accent;
+ int textColor;
+ }
+ private static Map<Integer, BadgeAssets> sBadges;
+ private static Bitmap sDefaultSiteBitmap = null;
+ /* package */ static Rect sBackgroundDrawablePadding = new Rect();
/* XML constructors */
@@ -175,24 +183,34 @@
/**
* Disables the automatic background and filling. Useful for things that are not really
* "Website Tiles", like folders.
- * @param floating true to disable the background (defaults to false)
+ * @param disabled true to disable the background (defaults to false)
*/
- public void setFloating(boolean floating) {
- if (mFloating != floating) {
- mFloating = floating;
+ public void setBackgroundDisabled(boolean disabled) {
+ if (mBackgroundDisabled != disabled) {
+ mBackgroundDisabled = disabled;
invalidate();
}
}
/**
- * For 'Medium' tiles updates the border and corner badges
+ * This results in the Badge being updated
* @param trustLevel one of the TRUST_ constants
*/
public void setTrustLevel(int trustLevel) {
if (mTrustLevel != trustLevel) {
mTrustLevel = trustLevel;
- if (requiresOverline())
- invalidate();
+ invalidate();
+ }
+ }
+
+ /**
+ * Tells that there will be some message about issues inside
+ * @param certIssues true if there are issues.
+ */
+ public void setBadgeHasCertIssues(boolean certIssues) {
+ if (certIssues != mBadgeHasCertIssues) {
+ mBadgeHasCertIssues = certIssues;
+ invalidate();
}
}
@@ -201,24 +219,16 @@
* may or may not have the number indication.
* @param sessionCounter Counter of blocked objects. Use 0 to not display anything.
*/
- public void setExtraBlockedObjectsCount(int sessionCounter) {
- if (sessionCounter != mExtraBlockedObjectsCount) {
- mExtraBlockedObjectsCount = sessionCounter;
- updateExtrasShown();
- if (SHOW_EXTRAS_BLOCKED_COUNT)
+ public void setBadgeBlockedObjectsCount(int sessionCounter) {
+ if (sessionCounter != mBadgeBlockedObjectsCount) {
+ // repaint if going from or to 0, or if showing the ads count
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ if (mBadgeBlockedObjectsCount == 0 || sessionCounter == 0 || BADGE_SHOW_BLOCKED_COUNT)
invalidate();
+ mBadgeBlockedObjectsCount = sessionCounter;
}
}
- private void updateExtrasShown() {
- boolean shouldBeShown = mExtraBlockedObjectsCount > 0;
- if (shouldBeShown != mExtrasShown) {
- mExtrasShown = shouldBeShown;
- invalidate();
- }
- }
-
-
/**
* @return The fundamental color representing the site.
@@ -234,6 +244,30 @@
/*** private stuff ahead ***/
+ private boolean requiresBadge() {
+ return !mBackgroundDisabled && (mTrustLevel != TRUST_UNKNOWN || mBadgeHasCertIssues
+ || mBadgeBlockedObjectsCount > 0);
+ }
+
+ private int computeBadgeMessages() {
+ // special case, for TRUST_AVOID, always show the common accent
+ if (mTrustLevel == TRUST_AVOID)
+ return 0;
+
+ // recompute number of 'messages' inside the badge
+ int count = 0;
+ if (mBadgeHasCertIssues)
+ count++;
+ if (mBadgeBlockedObjectsCount > 0)
+ count++;
+
+ // add the number of blocked objects (-1, for having already counted the message) if needed
+ if (BADGE_SHOW_BLOCKED_COUNT)
+ count += mBadgeBlockedObjectsCount - 1;
+
+ return count;
+ }
+
private void xmlInit(AttributeSet attrs, int defStyle) {
// load attributes
final TypedArray a = getContext().obtainStyledAttributes(attrs,
@@ -244,19 +278,15 @@
final Bitmap favicon = drawable instanceof BitmapDrawable ?
((BitmapDrawable) drawable).getBitmap() : null;
- // check if we disable shading (plain favicon)
- if (a.getBoolean(R.styleable.SiteTileView_flat, false))
- mForcedType = TYPE_SMALL;
-
- // check if we want it floating (disable shadow and filler)
- setFloating(a.getBoolean(R.styleable.SiteTileView_floating, false));
+ // check if we want it background-less (disable shadow and filler)
+ setBackgroundDisabled(a.getBoolean(R.styleable.SiteTileView_disableBackground, false));
// read the trust level (unknown, aka 'default', if not present)
setTrustLevel(a.getInteger(R.styleable.SiteTileView_trustLevel, TRUST_UNKNOWN)
& TRUST_MASK);
// read the amount of blocked objects (or 0 if not present)
- setExtraBlockedObjectsCount(a.getInteger(R.styleable.SiteTileView_blockedObjects, 0));
+ setBadgeBlockedObjectsCount(a.getInteger(R.styleable.SiteTileView_blockedObjects, 0));
// delete attribute resolution
a.recycle();
@@ -267,6 +297,16 @@
private void init(Bitmap favicon, int fundamentalColor) {
mFaviconBitmap = favicon;
+
+ // show a default favicon if nothing is set (consider removing this, it's ugly)
+ if (mFaviconBitmap == null && DEFAULT_SITE_FAVICON != 0) {
+ if (sDefaultSiteBitmap == null)
+ sDefaultSiteBitmap = BitmapFactory.decodeResource(getResources(),
+ DEFAULT_SITE_FAVICON);
+ mFaviconBitmap = sDefaultSiteBitmap;
+ fundamentalColor = 0xFF262626;
+ }
+
if (mFaviconBitmap != null) {
mFaviconWidth = mFaviconBitmap.getWidth();
mFaviconHeight = mFaviconBitmap.getHeight();
@@ -277,47 +317,70 @@
mForcedFundamentalColor = fundamentalColor;
// shared (static) resources initialization; except for background, inited on-demand
- if (sMediumPxThreshold < 0) {
- final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
-
- // heuristics thresholds
- sMediumPxThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- THRESHOLD_MEDIUM_DP, displayMetrics);
- sLargePxThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- THRESHOLD_LARGE_DP, displayMetrics);
- sLargeFaviconPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- LARGE_FAVICON_SIZE_DP, displayMetrics);
-
- // rounded radius
- sRoundedRadius = FILLER_RADIUS_DP > 0 ? TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, FILLER_RADIUS_DP, displayMetrics) : 0;
-
- // bitmap paint (copy, smooth scale)
- sBitmapPaint = new Paint();
- sBitmapPaint.setColor(Color.BLACK);
- sBitmapPaint.setFilterBitmap(true);
-
- // overline configuration (null if we don't need it)
- float ovlWidthPx = getResources().getDimension(OVERLINE_WIDTH_RES);
- if (ovlWidthPx > 0.5) {
- sOverlineOutlinePaint = new Paint();
- sOverlineOutlinePaint.setStrokeWidth(ovlWidthPx);
- sOverlineOutlinePaint.setStyle(Paint.Style.STROKE);
- }
-
- // extras paint (anti-aliased)
- sExtrasPaint = new Paint();
- sExtrasPaint.setAntiAlias(true);
- sExtrasPaint.setColor(getResources().getColor(OVERLINE_COLOR_TRUSTED_RES));
- sExtrasPaint.setStrokeWidth(Math.max(ovlWidthPx, 1));
- }
+ ensureCommonLoaded(getResources());
// change when clicked
setClickable(true);
- // disable by default the long click
- setLongClickable(false);
}
+ static void ensureCommonLoaded(Resources r) {
+ // check if already initialized
+ if (sMediumPxThreshold != -1)
+ return;
+
+ // heuristics thresholds
+ final DisplayMetrics displayMetrics = r.getDisplayMetrics();
+ sMediumPxThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ THRESHOLD_MEDIUM_DP, displayMetrics);
+ sLargePxThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ THRESHOLD_LARGE_DP, displayMetrics);
+ sLargeFaviconPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ LARGE_FAVICON_SIZE_DP, displayMetrics);
+
+ // rounded radius
+ sRoundedRadius = FILLER_RADIUS_DP > 0 ? TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, FILLER_RADIUS_DP, displayMetrics) : 0;
+
+ // bitmap paint (copy, smooth scale)
+ sBitmapPaint = new Paint();
+ sBitmapPaint.setColor(Color.BLACK);
+ sBitmapPaint.setFilterBitmap(true);
+
+ // badge text paint (anti-aliased)
+ sBadgeTextPaint = new Paint();
+ sBadgeTextPaint.setAntiAlias(true);
+ Typeface badgeTypeface = Typeface.create("sans-serif-medium", Typeface.NORMAL);
+ if (badgeTypeface != null)
+ sBadgeTextPaint.setTypeface(badgeTypeface);
+
+ // load the background (could be loaded on demand, but in the end it's always needed)
+ sBackgroundDrawable = r.getDrawable(BACKGROUND_DRAWABLE_RES);
+ if (sBackgroundDrawable != null)
+ sBackgroundDrawable.getPadding(sBackgroundDrawablePadding);
+
+ // load all the badge drawables
+ sBadges = new ArrayMap<>();
+ loadBadgeResources(r, TRUST_AVOID, R.drawable.img_deco_tile_avoid,
+ R.drawable.img_deco_tile_avoid_accent, R.color.TileBadgeTextAvoid);
+ loadBadgeResources(r, TRUST_UNTRUSTED, R.drawable.img_deco_tile_untrusted,
+ R.drawable.img_deco_tile_untrusted_accent, R.color.TileBadgeTextUntrusted);
+ loadBadgeResources(r, TRUST_UNKNOWN, R.drawable.img_deco_tile_unknown,
+ R.drawable.img_deco_tile_unknown_accent, R.color.TileBadgeTextUnknown);
+ loadBadgeResources(r, TRUST_TRUSTED, R.drawable.img_deco_tile_verified,
+ R.drawable.img_deco_tile_verified_accent, R.color.TileBadgeTextVerified);
+ }
+
+ private static void loadBadgeResources(Resources r, int t, int back, int accent, int color) {
+ BadgeAssets ba = new BadgeAssets();
+ ba.back = back == 0 ? null : r.getDrawable(back);
+ ba.accent = accent == 0 ? null : r.getDrawable(accent);
+ ba.textColor = color == 0 ? Color.TRANSPARENT : r.getColor(color);
+ sBadges.put(t, ba);
+ }
+
+ static Rect getBackgroundDrawablePadding() {
+ return sBackgroundDrawablePadding != null ? sBackgroundDrawablePadding : new Rect();
+ }
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
@@ -325,39 +388,27 @@
mCurrentHeight = bottom - top;
// auto-determine the "TYPE_" from the physical size of the layout
- if (mForcedType == TYPE_AUTO) {
- if (mCurrentWidth < sMediumPxThreshold && mCurrentHeight < sMediumPxThreshold)
- mCurrentType = TYPE_SMALL;
- else if (mCurrentWidth < sLargePxThreshold && mCurrentHeight < sLargePxThreshold)
- mCurrentType = TYPE_MEDIUM;
- else
- mCurrentType = TYPE_LARGE;
- } else {
- // or use the forced one, if defined
- mCurrentType = mForcedType;
- }
+ if (mCurrentWidth < sMediumPxThreshold && mCurrentHeight < sMediumPxThreshold)
+ mCurrentType = TYPE_SMALL;
+ else if (mCurrentWidth < sLargePxThreshold && mCurrentHeight < sLargePxThreshold)
+ mCurrentType = TYPE_MEDIUM;
+ else
+ mCurrentType = TYPE_LARGE;
// set or remove the background (if the need changed!)
- boolean requiresBackground = mCurrentType >= TYPE_MEDIUM;
- if (requiresBackground && !mCurrentBackgroundDrawn) {
+ boolean requiresBackgroundDrawable = mCurrentType >= TYPE_MEDIUM;
+ if (requiresBackgroundDrawable && !mCurrentShadowDrawn) {
// draw the background
- mCurrentBackgroundDrawn = true;
-
- // load the background just the first time, on demand (it may fail too)
- if (sBackgroundDrawable == null) {
- sBackgroundDrawable = getResources().getDrawable(BACKGROUND_DRAWABLE_RES);
- if (sBackgroundDrawable != null)
- sBackgroundDrawable.getPadding(sBackgroundDrawablePadding);
- }
+ mCurrentShadowDrawn = mCurrentType >= TYPE_LARGE;
// background -> padding
mPaddingLeft = sBackgroundDrawablePadding.left;
mPaddingTop = sBackgroundDrawablePadding.top;
mPaddingRight = sBackgroundDrawablePadding.right;
mPaddingBottom = sBackgroundDrawablePadding.bottom;
- } else if (!requiresBackground && mCurrentBackgroundDrawn) {
+ } else if (!requiresBackgroundDrawable && mCurrentShadowDrawn) {
// turn off background drawing
- mCurrentBackgroundDrawn = false;
+ mCurrentShadowDrawn = false;
// no background -> no padding
mPaddingLeft = 0;
@@ -410,15 +461,15 @@
final int contentHeight = bottom - top;
// A. the background drawable (if set)
- boolean requiresBackground = mCurrentBackgroundDrawn && sBackgroundDrawable != null
- && !isPressed() && !mFloating;
+ boolean requiresBackground = mCurrentShadowDrawn && sBackgroundDrawable != null
+ && !isPressed() && !mBackgroundDisabled;
if (requiresBackground) {
sBackgroundDrawable.setBounds(0, 0, mCurrentWidth, mCurrentHeight);
sBackgroundDrawable.draw(canvas);
}
// B. (when needed) draw the background rectangle; sharp our rounded
- boolean requiresFundamentalFiller = mCurrentType >= TYPE_LARGE && !mFloating;
+ boolean requiresFundamentalFiller = mCurrentType >= TYPE_LARGE && !mBackgroundDisabled;
if (requiresFundamentalFiller) {
// create the filler paint on demand (not all icons need it)
if (mFundamentalPaint == null)
@@ -481,73 +532,52 @@
canvas.drawBitmap(mFaviconBitmap, sSrcRect, sDstRect, sBitmapPaint);
}
- // D. (when needed) draw the thin over-line
- if (requiresOverline()) {
- int colorRes;
- switch (mTrustLevel) {
- case TRUST_TRUSTED:
- colorRes = OVERLINE_COLOR_TRUSTED_RES;
- break;
- case TRUST_UNTRUSTED:
- colorRes = OVERLINE_COLOR_UNTRUSTED_RES;
- break;
- case TRUST_AVOID:
- colorRes = OVERLINE_COLOR_AVOID_RES;
- break;
- default:
- colorRes = OVERLINE_COLOR_DEFAULT_RES;
- break;
- }
- int lineColor = getResources().getColor(colorRes);
- if (lineColor != Color.TRANSPARENT) {
- // draw the white inline first
- boolean needSeparation = mTrustLevel != TRUST_UNKNOWN;
- if (needSeparation) {
- sOverlineOutlinePaint.setColor(Color.WHITE);
- float d = sOverlineOutlinePaint.getStrokeWidth();
- canvas.drawRect(left + d, top + d, right - d, bottom - d, sOverlineOutlinePaint);
+ // D. show badge, if requested
+ if (requiresBadge()) {
+ // retrieve the badge resources
+ final BadgeAssets ba = sBadges.get(mTrustLevel);
+ if (ba != null) {
+
+ // paint back
+ final Drawable back = ba.back;
+ int badgeL = 0, badgeT = 0, badgeW = 0, badgeH = 0;
+ if (back != null) {
+ badgeW = back.getIntrinsicWidth();
+ badgeH = back.getIntrinsicHeight();
+ badgeL = mCurrentWidth - mPaddingRight / 3 - badgeW;
+ badgeT = mCurrentHeight - mPaddingBottom / 3 - badgeH;
+ back.setBounds(badgeL, badgeT, badgeL + badgeW, badgeT + badgeH);
+ back.draw(canvas);
}
+ int messagesCount = computeBadgeMessages();
- // then draw the outline
- sOverlineOutlinePaint.setColor(lineColor);
- canvas.drawRect(left, top, right, bottom, sOverlineOutlinePaint);
+ // paint accent, if 0 messages
+ if (messagesCount < 1) {
+ final Drawable accent = ba.accent;
+ if (accent != null && badgeW > 0 && badgeH > 0) {
+ int accentW = accent.getIntrinsicWidth();
+ int accentH = accent.getIntrinsicHeight();
+ int accentL = badgeL + (badgeW - accentW) / 2;
+ int accentT = badgeT + (badgeH - accentH) / 2;
+ accent.setBounds(accentL, accentT, accentL + accentW, accentT + accentH);
+ accent.draw(canvas);
+ }
+ }
+ // at least 1 message, draw text
+ else if (Color.alpha(ba.textColor) > 0) {
+ float textSize = Math.min(2 * contentWidth / 5, sMediumPxThreshold / 4) * 1.1f;
+ sBadgeTextPaint.setTextSize(textSize);
+ sBadgeTextPaint.setColor(ba.textColor);
+ final String text = String.valueOf(messagesCount);
+ int textWidth = Math.round(sBadgeTextPaint.measureText(text) / 2);
+ int textCx = badgeL + badgeW / 2;
+ int textCy = badgeT + badgeH / 2;
+ canvas.drawText(text, textCx - textWidth, textCy + textSize / 3 + 1,
+ sBadgeTextPaint);
+ }
}
}
- // E. show extra, if requested
- if (mExtrasShown) {
- // as default, we show a bubble
- int eRad = Math.min(2 * contentWidth / 5, sMediumPxThreshold / 4);
- int eCX = Math.min(right - eRad / 2, mCurrentWidth - eRad); //left + (4 * contentWidth / 5) - eRad;
- int eCY = Math.min(bottom - eRad / 4, mCurrentHeight - eRad);
-
- // circle back
- //canvas.drawCircle(eCX, eCY, eRad, sExtrasPaint);
-
- // round rect back
- sRectF.set(eCX - eRad, eCY - eRad, eCX + eRad, eCY + eRad);
- sExtrasPaint.setStyle(Paint.Style.FILL);
- sExtrasPaint.setColor(0xff666666);
- canvas.drawRoundRect(sRectF, eRad / 2, eRad / 2, sExtrasPaint);
-
- // DEBUG! -- draw blocked count
- if (SHOW_EXTRAS_BLOCKED_COUNT && mExtraBlockedObjectsCount > 0) {
- final Paint paint = new Paint();
- float textSize = eRad * 1.2f;
- paint.setColor(Color.WHITE);
- paint.setAntiAlias(true);
- paint.setTextSize(textSize);
- String text = String.valueOf(mExtraBlockedObjectsCount);
- int textWidth = Math.round(paint.measureText(text) / 2);
- canvas.drawText(text, eCX - textWidth - 1, eCY + textSize / 3 + 1, paint);
- }
-
- // round rect stroke
- sExtrasPaint.setStyle(Paint.Style.STROKE);
- sExtrasPaint.setColor(0xFFeeeeee);
- canvas.drawRoundRect(sRectF, eRad / 2, eRad / 2, sExtrasPaint);
- }
-
/*if (true) { // DEBUG TYPE
Paint paint = new Paint();
paint.setColor(Color.BLACK);
@@ -556,10 +586,6 @@
}*/
}
- private boolean requiresOverline() {
- return mCurrentType == TYPE_MEDIUM && sOverlineOutlinePaint != null && !mFloating;
- }
-
/**
* Creates a fill Paint from the favicon, or using the forced color (if not COLOR_AUTO)
@@ -651,4 +677,4 @@
return color;
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/browser/view/BookmarkContainer.java b/src/com/android/browser/view/BookmarkContainer.java
index 730f127..b36dde2 100644
--- a/src/com/android/browser/view/BookmarkContainer.java
+++ b/src/com/android/browser/view/BookmarkContainer.java
@@ -17,15 +17,22 @@
package com.android.browser.view;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.browser.FolderTileView;
import com.android.browser.R;
import com.android.browser.SiteTileView;
@@ -34,6 +41,11 @@
private OnClickListener mClickListener;
private boolean mIgnoreRequestLayout = false;
+ private FrameLayout mTileContainer;
+ private FolderTileView mFolderTile;
+ private SiteTileView mSiteTile;
+ private ImageView mOverlayBadge;
+
public BookmarkContainer(Context context) {
super(context);
init();
@@ -44,33 +56,107 @@
init();
}
- public BookmarkContainer(
- Context context, AttributeSet attrs, int defStyle) {
+ public BookmarkContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
void init() {
setFocusable(true);
+
+ if (mSiteTile == null) {
+ mSiteTile = new SiteTileView(getContext(), (Bitmap)null);
+ }
+
+ if (mFolderTile == null) {
+ mFolderTile = new FolderTileView(getContext(), null, null);
+ mFolderTile.setClickable(true);
+ }
super.setOnClickListener(this);
}
+ public void reConfigureAsFolder(String title, String numItems) {
+ // hide elements that may have been already created
+ mSiteTile.setVisibility(View.GONE);
+ if (mOverlayBadge != null)
+ mOverlayBadge.setVisibility(View.GONE);
+
+ // reconfigure the existing Folder
+ mFolderTile.setVisibility(View.VISIBLE);
+ mFolderTile.setText(title);
+ mFolderTile.setLabel(numItems);
+ addTileToContainer(mFolderTile);
+ }
+
+ public void reConfigureAsSite(Bitmap favicon) {
+ // hide elements that may have been already created
+ mFolderTile.setVisibility(View.GONE);
+ if (mOverlayBadge != null)
+ mOverlayBadge.setVisibility(View.GONE);
+
+ // reconfigure the existing Site
+ mSiteTile.setVisibility(View.VISIBLE);
+ mSiteTile.replaceFavicon(favicon);
+ addTileToContainer(mSiteTile);
+ }
+
+ public void setBottomLabelText(String bottomLabel) {
+ ((TextView) findViewById(R.id.label)).setText(bottomLabel);
+ }
+
+ public void setOverlayBadge(int imgResId) {
+ // remove the badge if already existing
+ if (imgResId == 0) {
+ if (mOverlayBadge != null) {
+ mOverlayBadge.setVisibility(View.GONE);
+ mOverlayBadge.setImageDrawable(null);
+ }
+ return;
+ }
+
+ // create the badge if needed and not present
+ if (mOverlayBadge == null) {
+ mOverlayBadge = new ImageView(getContext());
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ lp.gravity = Gravity.BOTTOM | Gravity.END;
+ FrameLayout frameLayout = (FrameLayout) findViewById(R.id.container);
+ frameLayout.addView(mOverlayBadge, lp);
+ }
+ mOverlayBadge.setVisibility(View.VISIBLE);
+ mOverlayBadge.bringToFront();
+ mOverlayBadge.setImageResource(imgResId);
+ }
+
+
+ private void addTileToContainer(View view) {
+ if (view.getParent() != null) {
+ return;
+ }
+
+ // insert the view in the container, filling it
+ FrameLayout frameLayout = (FrameLayout) findViewById(R.id.container);
+ frameLayout.addView(view, 0, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+ // common customizations for folders or sites
+ view.setLongClickable(true);
+ }
+
+
@Override
public void setOnClickListener(OnClickListener l) {
mClickListener = l;
- View thumb = findViewById(R.id.thumb_image);
- if (thumb != null) {
- thumb.setOnClickListener(l);
- }
+ mSiteTile.setOnClickListener(l);
+ mFolderTile.setOnClickListener(l);
}
@Override
public void setTag(int key, final Object tag) {
super.setTag(key, tag);
- View thumb = findViewById(R.id.thumb_image);
- if (thumb != null) {
- thumb.setTag(key, tag);
- }
+ mSiteTile.setTag(key, tag);
+ mFolderTile.setTag(key, tag);
}
@Override
@@ -80,12 +166,12 @@
}
void updateTransitionDrawable(boolean pressed) {
- final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
Drawable selector = getBackground();
if (selector != null && selector instanceof StateListDrawable) {
Drawable d = ((StateListDrawable)selector).getCurrent();
if (d != null && d instanceof TransitionDrawable) {
if (pressed && isLongClickable()) {
+ final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
((TransitionDrawable) d).startTransition(longPressTimeout);
} else {
((TransitionDrawable) d).resetTransition();
@@ -108,8 +194,8 @@
@Override
public void requestLayout() {
- if (!mIgnoreRequestLayout) {
+ if (!mIgnoreRequestLayout)
super.requestLayout();
- }
}
+
}