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 java.lang.ref.WeakReference; michael@0: michael@0: import org.mozilla.gecko.R; michael@0: import org.mozilla.gecko.Tab; michael@0: import org.mozilla.gecko.Tabs; michael@0: import org.mozilla.gecko.db.BrowserContract.Combined; michael@0: import org.mozilla.gecko.db.BrowserDB.URLColumns; michael@0: import org.mozilla.gecko.favicons.Favicons; michael@0: import org.mozilla.gecko.favicons.OnFaviconLoadedListener; michael@0: import org.mozilla.gecko.util.ThreadUtils; michael@0: import org.mozilla.gecko.widget.FaviconView; michael@0: michael@0: import android.content.Context; michael@0: import android.database.Cursor; michael@0: import android.graphics.Bitmap; michael@0: import android.text.TextUtils; michael@0: import android.util.AttributeSet; michael@0: import android.view.Gravity; michael@0: import android.view.LayoutInflater; michael@0: import android.widget.LinearLayout; michael@0: import android.widget.TextView; michael@0: michael@0: public class TwoLinePageRow extends LinearLayout michael@0: implements Tabs.OnTabsChangedListener { michael@0: michael@0: protected static final int NO_ICON = 0; michael@0: michael@0: private final TextView mTitle; michael@0: private final TextView mUrl; michael@0: michael@0: private int mSwitchToTabIconId; michael@0: private int mPageTypeIconId; michael@0: michael@0: private final FaviconView mFavicon; michael@0: michael@0: private boolean mShowIcons; michael@0: private int mLoadFaviconJobId = Favicons.NOT_LOADING; michael@0: michael@0: // Only holds a reference to the FaviconView itself, so if the row gets michael@0: // discarded while a task is outstanding, we'll leak less memory. michael@0: private static class UpdateViewFaviconLoadedListener implements OnFaviconLoadedListener { michael@0: private final WeakReference view; michael@0: public UpdateViewFaviconLoadedListener(FaviconView view) { michael@0: this.view = new WeakReference(view); michael@0: } michael@0: michael@0: @Override michael@0: public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) { michael@0: FaviconView v = view.get(); michael@0: if (v == null) { michael@0: // Guess we stuck around after the TwoLinePageRow went away. michael@0: return; michael@0: } michael@0: michael@0: if (favicon == null) { michael@0: v.showDefaultFavicon(); michael@0: return; michael@0: } michael@0: michael@0: v.updateImage(favicon, faviconURL); michael@0: } michael@0: } michael@0: michael@0: // Listener for handling Favicon loads. michael@0: private final OnFaviconLoadedListener mFaviconListener; michael@0: michael@0: // The URL for the page corresponding to this view. michael@0: private String mPageUrl; michael@0: michael@0: public TwoLinePageRow(Context context) { michael@0: this(context, null); michael@0: } michael@0: michael@0: public TwoLinePageRow(Context context, AttributeSet attrs) { michael@0: super(context, attrs); michael@0: michael@0: setGravity(Gravity.CENTER_VERTICAL); michael@0: michael@0: LayoutInflater.from(context).inflate(R.layout.two_line_page_row, this); michael@0: mTitle = (TextView) findViewById(R.id.title); michael@0: mUrl = (TextView) findViewById(R.id.url); michael@0: michael@0: mSwitchToTabIconId = NO_ICON; michael@0: mPageTypeIconId = NO_ICON; michael@0: mShowIcons = true; michael@0: michael@0: mFavicon = (FaviconView) findViewById(R.id.icon); michael@0: mFaviconListener = new UpdateViewFaviconLoadedListener(mFavicon); michael@0: } michael@0: michael@0: @Override michael@0: protected void onAttachedToWindow() { michael@0: Tabs.registerOnTabsChangedListener(this); michael@0: } michael@0: michael@0: @Override michael@0: protected void onDetachedFromWindow() { michael@0: // Tabs' listener array is safe to modify during use: its michael@0: // iteration pattern is based on snapshots. michael@0: Tabs.unregisterOnTabsChangedListener(this); michael@0: } michael@0: michael@0: @Override michael@0: public void onTabChanged(final Tab tab, final Tabs.TabEvents msg, final Object data) { michael@0: switch(msg) { michael@0: case ADDED: michael@0: case CLOSED: michael@0: case LOCATION_CHANGE: michael@0: updateDisplayedUrl(); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: private void setTitle(String text) { michael@0: mTitle.setText(text); michael@0: } michael@0: michael@0: protected void setUrl(String text) { michael@0: mUrl.setText(text); michael@0: } michael@0: michael@0: protected void setUrl(int stringId) { michael@0: mUrl.setText(stringId); michael@0: } michael@0: michael@0: protected String getUrl() { michael@0: return mPageUrl; michael@0: } michael@0: michael@0: protected void setSwitchToTabIcon(int iconId) { michael@0: if (mSwitchToTabIconId == iconId) { michael@0: return; michael@0: } michael@0: michael@0: mSwitchToTabIconId = iconId; michael@0: mUrl.setCompoundDrawablesWithIntrinsicBounds(mSwitchToTabIconId, 0, mPageTypeIconId, 0); michael@0: } michael@0: michael@0: private void setPageTypeIcon(int iconId) { michael@0: if (mPageTypeIconId == iconId) { michael@0: return; michael@0: } michael@0: michael@0: mPageTypeIconId = iconId; michael@0: mUrl.setCompoundDrawablesWithIntrinsicBounds(mSwitchToTabIconId, 0, mPageTypeIconId, 0); michael@0: } michael@0: michael@0: /** michael@0: * Stores the page URL, so that we can use it to replace "Switch to tab" if the open michael@0: * tab changes or is closed. michael@0: */ michael@0: private void updateDisplayedUrl(String url) { michael@0: mPageUrl = url; michael@0: updateDisplayedUrl(); michael@0: } michael@0: michael@0: /** michael@0: * Replaces the page URL with "Switch to tab" if there is already a tab open with that URL. michael@0: * Only looks for tabs that are either private or non-private, depending on the current michael@0: * selected tab. michael@0: */ michael@0: protected void updateDisplayedUrl() { michael@0: boolean isPrivate = Tabs.getInstance().getSelectedTab().isPrivate(); michael@0: Tab tab = Tabs.getInstance().getFirstTabForUrl(mPageUrl, isPrivate); michael@0: if (!mShowIcons || tab == null) { michael@0: setUrl(mPageUrl); michael@0: setSwitchToTabIcon(NO_ICON); michael@0: } else { michael@0: setUrl(R.string.switch_to_tab); michael@0: setSwitchToTabIcon(R.drawable.ic_url_bar_tab); michael@0: } michael@0: } michael@0: michael@0: public void setShowIcons(boolean showIcons) { michael@0: mShowIcons = showIcons; michael@0: } michael@0: michael@0: public void updateFromCursor(Cursor cursor) { michael@0: if (cursor == null) { michael@0: return; michael@0: } michael@0: michael@0: int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE); michael@0: final String title = cursor.getString(titleIndex); michael@0: michael@0: int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL); michael@0: final String url = cursor.getString(urlIndex); michael@0: michael@0: if (mShowIcons) { michael@0: final int bookmarkIdIndex = cursor.getColumnIndex(Combined.BOOKMARK_ID); michael@0: if (bookmarkIdIndex != -1) { michael@0: final long bookmarkId = cursor.getLong(bookmarkIdIndex); michael@0: final int displayIndex = cursor.getColumnIndex(Combined.DISPLAY); michael@0: michael@0: final int display; michael@0: if (displayIndex != -1) { michael@0: display = cursor.getInt(displayIndex); michael@0: } else { michael@0: display = Combined.DISPLAY_NORMAL; michael@0: } michael@0: michael@0: // The bookmark id will be 0 (null in database) when the url michael@0: // is not a bookmark. michael@0: if (bookmarkId == 0) { michael@0: setPageTypeIcon(NO_ICON); michael@0: } else if (display == Combined.DISPLAY_READER) { michael@0: setPageTypeIcon(R.drawable.ic_url_bar_reader); michael@0: } else { michael@0: setPageTypeIcon(R.drawable.ic_url_bar_star); michael@0: } michael@0: } else { michael@0: setPageTypeIcon(NO_ICON); michael@0: } michael@0: } michael@0: michael@0: // Use the URL instead of an empty title for consistency with the normal URL michael@0: // bar view - this is the equivalent of getDisplayTitle() in Tab.java michael@0: setTitle(TextUtils.isEmpty(title) ? url : title); michael@0: michael@0: // No point updating the below things if URL has not changed. Prevents evil Favicon flicker. michael@0: if (url.equals(mPageUrl)) { michael@0: return; michael@0: } michael@0: michael@0: // Blank the Favicon, so we don't show the wrong Favicon if we scroll and miss DB. michael@0: mFavicon.clearImage(); michael@0: mLoadFaviconJobId = Favicons.getSizedFaviconForPageFromLocal(url, mFaviconListener); michael@0: michael@0: updateDisplayedUrl(url); michael@0: } michael@0: }