michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.home; michael@0: michael@0: import org.mozilla.gecko.favicons.Favicons; michael@0: import org.mozilla.gecko.R; michael@0: michael@0: import android.content.Context; michael@0: import android.graphics.Bitmap; michael@0: import android.text.TextUtils; michael@0: import android.util.AttributeSet; michael@0: import android.view.LayoutInflater; michael@0: import android.widget.ImageView; michael@0: import android.widget.ImageView.ScaleType; michael@0: import android.widget.RelativeLayout; michael@0: import android.widget.TextView; michael@0: michael@0: /** michael@0: * A view that displays the thumbnail and the title/url for a top/pinned site. michael@0: * If the title/url is longer than the width of the view, they are faded out. michael@0: * If there is no valid url, a default string is shown at 50% opacity. michael@0: * This is denoted by the empty state. michael@0: */ michael@0: public class TopSitesGridItemView extends RelativeLayout { michael@0: private static final String LOGTAG = "GeckoTopSitesGridItemView"; michael@0: michael@0: // Empty state, to denote there is no valid url. michael@0: private static final int[] STATE_EMPTY = { android.R.attr.state_empty }; michael@0: michael@0: private static final ScaleType SCALE_TYPE_FAVICON = ScaleType.CENTER; michael@0: private static final ScaleType SCALE_TYPE_RESOURCE = ScaleType.CENTER; michael@0: private static final ScaleType SCALE_TYPE_THUMBNAIL = ScaleType.CENTER_CROP; michael@0: michael@0: // Child views. michael@0: private final TextView mTitleView; michael@0: private final ImageView mThumbnailView; michael@0: michael@0: // Data backing this view. michael@0: private String mTitle; michael@0: private String mUrl; michael@0: private String mFaviconURL; michael@0: michael@0: private boolean mThumbnailSet; michael@0: michael@0: // Pinned state. michael@0: private boolean mIsPinned = false; michael@0: michael@0: // Dirty state. michael@0: private boolean mIsDirty = false; michael@0: michael@0: // Empty state. michael@0: private boolean mIsEmpty = true; michael@0: private int mLoadId = Favicons.NOT_LOADING; michael@0: michael@0: public TopSitesGridItemView(Context context) { michael@0: this(context, null); michael@0: } michael@0: michael@0: public TopSitesGridItemView(Context context, AttributeSet attrs) { michael@0: this(context, attrs, R.attr.topSitesGridItemViewStyle); michael@0: } michael@0: michael@0: public TopSitesGridItemView(Context context, AttributeSet attrs, int defStyle) { michael@0: super(context, attrs, defStyle); michael@0: michael@0: LayoutInflater.from(context).inflate(R.layout.top_sites_grid_item_view, this); michael@0: michael@0: mTitleView = (TextView) findViewById(R.id.title); michael@0: mThumbnailView = (ImageView) findViewById(R.id.thumbnail); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: @Override michael@0: public int[] onCreateDrawableState(int extraSpace) { michael@0: final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); michael@0: michael@0: if (mIsEmpty) { michael@0: mergeDrawableStates(drawableState, STATE_EMPTY); michael@0: } michael@0: michael@0: return drawableState; michael@0: } michael@0: michael@0: /** michael@0: * @return The title shown by this view. michael@0: */ michael@0: public String getTitle() { michael@0: return (!TextUtils.isEmpty(mTitle) ? mTitle : mUrl); michael@0: } michael@0: michael@0: /** michael@0: * @return The url shown by this view. michael@0: */ michael@0: public String getUrl() { michael@0: return mUrl; michael@0: } michael@0: michael@0: /** michael@0: * @return true, if this view is pinned, false otherwise. michael@0: */ michael@0: public boolean isPinned() { michael@0: return mIsPinned; michael@0: } michael@0: michael@0: /** michael@0: * @return true, if this view has no content to show. michael@0: */ michael@0: public boolean isEmpty() { michael@0: return mIsEmpty; michael@0: } michael@0: michael@0: /** michael@0: * @param title The title for this view. michael@0: */ michael@0: public void setTitle(String title) { michael@0: if (mTitle != null && mTitle.equals(title)) { michael@0: return; michael@0: } michael@0: michael@0: mTitle = title; michael@0: updateTitleView(); michael@0: } michael@0: michael@0: /** michael@0: * @param url The url for this view. michael@0: */ michael@0: public void setUrl(String url) { michael@0: if (mUrl != null && mUrl.equals(url)) { michael@0: return; michael@0: } michael@0: michael@0: mUrl = url; michael@0: updateTitleView(); michael@0: } michael@0: michael@0: /** michael@0: * @param pinned The pinned state of this view. michael@0: */ michael@0: public void setPinned(boolean pinned) { michael@0: mIsPinned = pinned; michael@0: mTitleView.setCompoundDrawablesWithIntrinsicBounds(pinned ? R.drawable.pin : 0, 0, 0, 0); michael@0: } michael@0: michael@0: public void blankOut() { michael@0: mUrl = ""; michael@0: mTitle = ""; michael@0: mIsPinned = false; michael@0: updateTitleView(); michael@0: mTitleView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); michael@0: setLoadId(Favicons.NOT_LOADING); michael@0: displayThumbnail(R.drawable.top_site_add); michael@0: } michael@0: michael@0: public void markAsDirty() { michael@0: mIsDirty = true; michael@0: } michael@0: michael@0: /** michael@0: * Updates the title, URL, and pinned state of this view. michael@0: * michael@0: * Also resets our loadId to NOT_LOADING. michael@0: * michael@0: * Returns true if any fields changed. michael@0: */ michael@0: public boolean updateState(final String title, final String url, final boolean pinned, final Bitmap thumbnail) { michael@0: boolean changed = false; michael@0: if (mUrl == null || !mUrl.equals(url)) { michael@0: mUrl = url; michael@0: changed = true; michael@0: } michael@0: michael@0: if (mTitle == null || !mTitle.equals(title)) { michael@0: mTitle = title; michael@0: changed = true; michael@0: } michael@0: michael@0: if (thumbnail != null) { michael@0: displayThumbnail(thumbnail); michael@0: } else if (changed) { michael@0: // Because we'll have a new favicon or thumbnail arriving shortly, and michael@0: // we need to not reject it because we already had a thumbnail. michael@0: mThumbnailSet = false; michael@0: } michael@0: michael@0: if (changed) { michael@0: updateTitleView(); michael@0: setLoadId(Favicons.NOT_LOADING); michael@0: } michael@0: michael@0: if (mIsPinned != pinned) { michael@0: mIsPinned = pinned; michael@0: mTitleView.setCompoundDrawablesWithIntrinsicBounds(pinned ? R.drawable.pin : 0, 0, 0, 0); michael@0: changed = true; michael@0: } michael@0: michael@0: // The dirty state forces the state update to return true michael@0: // so that the adapter loads favicons once the thumbnails michael@0: // are loaded in TopSitesPanel/TopSitesGridAdapter. michael@0: changed = (changed || mIsDirty); michael@0: mIsDirty = false; michael@0: michael@0: return changed; michael@0: } michael@0: michael@0: /** michael@0: * Display the thumbnail from a resource. michael@0: * michael@0: * @param resId Resource ID of the drawable to show. michael@0: */ michael@0: public void displayThumbnail(int resId) { michael@0: mThumbnailView.setScaleType(SCALE_TYPE_RESOURCE); michael@0: mThumbnailView.setImageResource(resId); michael@0: mThumbnailView.setBackgroundColor(0x0); michael@0: mThumbnailSet = false; michael@0: } michael@0: michael@0: /** michael@0: * Display the thumbnail from a bitmap. michael@0: * michael@0: * @param thumbnail The bitmap to show as thumbnail. michael@0: */ michael@0: public void displayThumbnail(Bitmap thumbnail) { michael@0: if (thumbnail == null) { michael@0: // Show a favicon based view instead. michael@0: displayThumbnail(R.drawable.favicon); michael@0: return; michael@0: } michael@0: mThumbnailSet = true; michael@0: Favicons.cancelFaviconLoad(mLoadId); michael@0: michael@0: mThumbnailView.setScaleType(SCALE_TYPE_THUMBNAIL); michael@0: mThumbnailView.setImageBitmap(thumbnail); michael@0: mThumbnailView.setBackgroundDrawable(null); michael@0: } michael@0: michael@0: public void displayFavicon(Bitmap favicon, String faviconURL, int expectedLoadId) { michael@0: if (mLoadId != Favicons.NOT_LOADING && michael@0: mLoadId != expectedLoadId) { michael@0: // View recycled. michael@0: return; michael@0: } michael@0: michael@0: // Yes, there's a chance of a race here. michael@0: displayFavicon(favicon, faviconURL); michael@0: } michael@0: michael@0: /** michael@0: * Display the thumbnail from a favicon. michael@0: * michael@0: * @param favicon The favicon to show as thumbnail. michael@0: */ michael@0: public void displayFavicon(Bitmap favicon, String faviconURL) { michael@0: if (mThumbnailSet) { michael@0: // Already showing a thumbnail; do nothing. michael@0: return; michael@0: } michael@0: michael@0: if (favicon == null) { michael@0: // Should show default favicon. michael@0: displayThumbnail(R.drawable.favicon); michael@0: return; michael@0: } michael@0: michael@0: if (faviconURL != null) { michael@0: mFaviconURL = faviconURL; michael@0: } michael@0: michael@0: mThumbnailView.setScaleType(SCALE_TYPE_FAVICON); michael@0: mThumbnailView.setImageBitmap(favicon); michael@0: michael@0: if (mFaviconURL != null) { michael@0: mThumbnailView.setBackgroundColor(Favicons.getFaviconColor(mFaviconURL)); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Update the title shown by this view. If both title and url michael@0: * are empty, mark the state as STATE_EMPTY and show a default text. michael@0: */ michael@0: private void updateTitleView() { michael@0: String title = getTitle(); michael@0: if (!TextUtils.isEmpty(title)) { michael@0: mTitleView.setText(title); michael@0: mIsEmpty = false; michael@0: } else { michael@0: mTitleView.setText(R.string.home_top_sites_add); michael@0: mIsEmpty = true; michael@0: } michael@0: michael@0: // Refresh for state change. michael@0: refreshDrawableState(); michael@0: } michael@0: michael@0: public void setLoadId(int aLoadId) { michael@0: Favicons.cancelFaviconLoad(mLoadId); michael@0: mLoadId = aLoadId; michael@0: } michael@0: }