summaryrefslogtreecommitdiffstats
path: root/mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java')
-rw-r--r--mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java324
1 files changed, 324 insertions, 0 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java b/mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java
new file mode 100644
index 000000000..68eb8daa5
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java
@@ -0,0 +1,324 @@
+/* -*- 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.home;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Future;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.mozilla.gecko.AboutPages;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.db.BrowserContract.Combined;
+import org.mozilla.gecko.db.BrowserContract.URLColumns;
+import org.mozilla.gecko.distribution.PartnerBookmarksProviderProxy;
+import org.mozilla.gecko.icons.IconDescriptor;
+import org.mozilla.gecko.icons.IconResponse;
+import org.mozilla.gecko.icons.Icons;
+import org.mozilla.gecko.reader.ReaderModeUtils;
+import org.mozilla.gecko.reader.SavedReaderViewHelper;
+import org.mozilla.gecko.widget.FaviconView;
+
+public class TwoLinePageRow extends LinearLayout
+ implements Tabs.OnTabsChangedListener {
+
+ protected static final int NO_ICON = 0;
+
+ private final TextView mTitle;
+ private final TextView mUrl;
+ private final ImageView mStatusIcon;
+
+ private int mSwitchToTabIconId;
+
+ private final FaviconView mFavicon;
+ private Future<IconResponse> mOngoingIconLoad;
+
+ private boolean mShowIcons;
+
+ // The URL for the page corresponding to this view.
+ private String mPageUrl;
+
+ private boolean mHasReaderCacheItem;
+
+ public TwoLinePageRow(Context context) {
+ this(context, null);
+ }
+
+ public TwoLinePageRow(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ setGravity(Gravity.CENTER_VERTICAL);
+
+ LayoutInflater.from(context).inflate(R.layout.two_line_page_row, this);
+ // Merge layouts lose their padding, so set it dynamically.
+ setPadding(0, 0, (int) getResources().getDimension(R.dimen.page_row_edge_padding), 0);
+
+ mTitle = (TextView) findViewById(R.id.title);
+ mUrl = (TextView) findViewById(R.id.url);
+ mStatusIcon = (ImageView) findViewById(R.id.status_icon_bookmark);
+
+ mSwitchToTabIconId = NO_ICON;
+ mShowIcons = true;
+
+ mFavicon = (FaviconView) findViewById(R.id.icon);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ Tabs.registerOnTabsChangedListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ // Tabs' listener array is safe to modify during use: its
+ // iteration pattern is based on snapshots.
+ Tabs.unregisterOnTabsChangedListener(this);
+ }
+
+ /**
+ * Update the row in response to a tab change event.
+ * <p>
+ * This method is always invoked on the UI thread.
+ */
+ @Override
+ public void onTabChanged(final Tab tab, final Tabs.TabEvents msg, final String data) {
+ // Carefully check if this tab event is relevant to this row.
+ final String pageUrl = mPageUrl;
+ if (pageUrl == null) {
+ return;
+ }
+ if (tab == null) {
+ return;
+ }
+
+ // Return early if the page URL doesn't match the current tab URL,
+ // or the old tab URL.
+ // data is an empty String for ADDED/CLOSED, and contains the previous/old URL during
+ // LOCATION_CHANGE (the new URL is retrieved using tab.getURL()).
+ // tabURL and data may be about:reader URLs if the current or old tab page was a reader view
+ // page, however pageUrl will always be a plain URL (i.e. we only add about:reader when opening
+ // a reader view bookmark, at all other times it's a normal bookmark with normal URL).
+ final String tabUrl = tab.getURL();
+ if (!pageUrl.equals(ReaderModeUtils.stripAboutReaderUrl(tabUrl)) &&
+ !pageUrl.equals(ReaderModeUtils.stripAboutReaderUrl(data))) {
+ return;
+ }
+
+ // Note: we *might* need to update the display status (i.e. switch-to-tab icon/label) if
+ // a matching tab has been opened/closed/switched to a different page. updateDisplayedUrl() will
+ // determine the changes (if any) that actually need to be made. A tab change with a matching URL
+ // does not imply that any changes are needed - e.g. if a given URL is already open in one tab, and
+ // is also opened in a second tab, the switch-to-tab status doesn't change, closing 1 of 2 tabs with a URL
+ // similarly doesn't change the switch-to-tab display, etc. (However closing the last tab for
+ // a given URL does require a status change, as does opening the first tab with that URL.)
+ switch (msg) {
+ case ADDED:
+ case CLOSED:
+ case LOCATION_CHANGE:
+ updateDisplayedUrl();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void setTitle(String text) {
+ mTitle.setText(text);
+ }
+
+ protected void setUrl(String text) {
+ mUrl.setText(text);
+ }
+
+ protected void setUrl(int stringId) {
+ mUrl.setText(stringId);
+ }
+
+ protected String getUrl() {
+ return mPageUrl;
+ }
+
+ protected void setSwitchToTabIcon(int iconId) {
+ if (mSwitchToTabIconId == iconId) {
+ return;
+ }
+
+ mSwitchToTabIconId = iconId;
+ mUrl.setCompoundDrawablesWithIntrinsicBounds(mSwitchToTabIconId, 0, 0, 0);
+ }
+
+ private void updateStatusIcon(boolean isBookmark, boolean isReaderItem) {
+ if (isReaderItem) {
+ mStatusIcon.setImageResource(R.drawable.status_icon_readercache);
+ } else if (isBookmark) {
+ mStatusIcon.setImageResource(R.drawable.star_blue);
+ }
+
+ if (mShowIcons && (isBookmark || isReaderItem)) {
+ mStatusIcon.setVisibility(View.VISIBLE);
+ } else if (mShowIcons) {
+ // We use INVISIBLE to have consistent padding for our items. This means text/URLs
+ // fade consistently in the same location, regardless of them being bookmarked.
+ mStatusIcon.setVisibility(View.INVISIBLE);
+ } else {
+ mStatusIcon.setVisibility(View.GONE);
+ }
+
+ }
+
+ /**
+ * Stores the page URL, so that we can use it to replace "Switch to tab" if the open
+ * tab changes or is closed.
+ */
+ private void updateDisplayedUrl(String url, boolean hasReaderCacheItem) {
+ mPageUrl = url;
+ mHasReaderCacheItem = hasReaderCacheItem;
+ updateDisplayedUrl();
+ }
+
+ /**
+ * Replaces the page URL with "Switch to tab" if there is already a tab open with that URL.
+ * Only looks for tabs that are either private or non-private, depending on the current
+ * selected tab.
+ */
+ protected void updateDisplayedUrl() {
+ final Tab selectedTab = Tabs.getInstance().getSelectedTab();
+ final boolean isPrivate = (selectedTab != null) && (selectedTab.isPrivate());
+
+ // We always want to display the underlying page url, however for readermode pages
+ // we navigate to the about:reader equivalent, hence we need to use that url when finding
+ // existing tabs
+ final String navigationUrl = mHasReaderCacheItem ? ReaderModeUtils.getAboutReaderForUrl(mPageUrl) : mPageUrl;
+ Tab tab = Tabs.getInstance().getFirstTabForUrl(navigationUrl, isPrivate);
+
+
+ if (!mShowIcons || tab == null) {
+ setUrl(mPageUrl);
+ setSwitchToTabIcon(NO_ICON);
+ } else {
+ setUrl(R.string.switch_to_tab);
+ setSwitchToTabIcon(R.drawable.ic_url_bar_tab);
+ }
+ }
+
+ public void setShowIcons(boolean showIcons) {
+ mShowIcons = showIcons;
+ }
+
+ /**
+ * Update the data displayed by this row.
+ * <p>
+ * This method must be invoked on the UI thread.
+ *
+ * @param title to display.
+ * @param url to display.
+ */
+ public void update(String title, String url) {
+ update(title, url, 0, false);
+ }
+
+ protected void update(String title, String url, long bookmarkId, boolean hasReaderCacheItem) {
+ if (mShowIcons) {
+ // The bookmark id will be 0 (null in database) when the url
+ // is not a bookmark and negative for 'fake' bookmarks.
+ final boolean isBookmark = bookmarkId > 0;
+
+ updateStatusIcon(isBookmark, hasReaderCacheItem);
+ } else {
+ updateStatusIcon(false, false);
+ }
+
+ // Use the URL instead of an empty title for consistency with the normal URL
+ // bar view - this is the equivalent of getDisplayTitle() in Tab.java
+ setTitle(TextUtils.isEmpty(title) ? url : title);
+
+ // No point updating the below things if URL has not changed. Prevents evil Favicon flicker.
+ if (url.equals(mPageUrl)) {
+ return;
+ }
+
+ // Blank the Favicon, so we don't show the wrong Favicon if we scroll and miss DB.
+ mFavicon.clearImage();
+
+ if (mOngoingIconLoad != null) {
+ mOngoingIconLoad.cancel(true);
+ }
+
+ // Displayed RecentTabsPanel URLs may refer to pages opened in reader mode, so we
+ // remove the about:reader prefix to ensure the Favicon loads properly.
+ final String pageURL = ReaderModeUtils.stripAboutReaderUrl(url);
+
+ if (TextUtils.isEmpty(pageURL)) {
+ // If url is empty, display the item as-is but do not load an icon if we do not have a page URL (bug 1310622)
+ } else if (bookmarkId < BrowserContract.Bookmarks.FAKE_PARTNER_BOOKMARKS_START) {
+ mOngoingIconLoad = Icons.with(getContext())
+ .pageUrl(pageURL)
+ .skipNetwork()
+ .privileged(true)
+ .icon(IconDescriptor.createGenericIcon(
+ PartnerBookmarksProviderProxy.getUriForIcon(getContext(), bookmarkId).toString()))
+ .build()
+ .execute(mFavicon.createIconCallback());
+ } else {
+ mOngoingIconLoad = Icons.with(getContext())
+ .pageUrl(pageURL)
+ .skipNetwork()
+ .build()
+ .execute(mFavicon.createIconCallback());
+
+ }
+
+ updateDisplayedUrl(url, hasReaderCacheItem);
+ }
+
+ /**
+ * Update the data displayed by this row.
+ * <p>
+ * This method must be invoked on the UI thread.
+ *
+ * @param cursor to extract data from.
+ */
+ public void updateFromCursor(Cursor cursor) {
+ if (cursor == null) {
+ return;
+ }
+
+ int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
+ final String title = cursor.getString(titleIndex);
+
+ int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
+ final String url = cursor.getString(urlIndex);
+
+ final long bookmarkId;
+ final int bookmarkIdIndex = cursor.getColumnIndex(Combined.BOOKMARK_ID);
+ if (bookmarkIdIndex != -1) {
+ bookmarkId = cursor.getLong(bookmarkIdIndex);
+ } else {
+ bookmarkId = 0;
+ }
+
+ SavedReaderViewHelper rch = SavedReaderViewHelper.getSavedReaderViewHelper(getContext());
+ final boolean hasReaderCacheItem = rch.isURLCached(url);
+
+ update(title, url, bookmarkId, hasReaderCacheItem);
+ }
+}