Bug 1410221: Move TopSite openContextMenu to StreamRecyclerAdapter with listener. r=liuche
We do this so we can bind the listener to the `parent` View argument in
onCreateViewHolder.
This is the last commit required to fix this bug.
In practice, top sites should never cause a crash because they should never be
off-screen on rotation but this is just for correctness/safety.
MozReview-Commit-ID: 5P1HiR6woTH
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamRecyclerAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamRecyclerAdapter.java
@@ -17,16 +17,17 @@ import android.view.ViewGroup;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.activitystream.ActivityStreamTelemetry;
import org.mozilla.gecko.activitystream.homepanel.menu.ActivityStreamContextMenu;
import org.mozilla.gecko.activitystream.homepanel.model.RowModel;
+import org.mozilla.gecko.activitystream.homepanel.model.TopSite;
import org.mozilla.gecko.activitystream.homepanel.model.WebpageModel;
import org.mozilla.gecko.activitystream.homepanel.model.WebpageRowModel;
import org.mozilla.gecko.activitystream.homepanel.stream.HighlightsEmptyStateRow;
import org.mozilla.gecko.activitystream.homepanel.stream.LearnMoreRow;
import org.mozilla.gecko.activitystream.homepanel.stream.TopPanelRow;
import org.mozilla.gecko.activitystream.homepanel.model.TopStory;
import org.mozilla.gecko.activitystream.homepanel.topstories.PocketStoriesLoader;
import org.mozilla.gecko.home.HomePager;
@@ -130,17 +131,23 @@ public class StreamRecyclerAdapter exten
return recyclerViewModel.get(position).getRowItemType().getViewType();
}
@Override
public StreamViewHolder onCreateViewHolder(final ViewGroup parent, final int type) {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (type == RowItemType.TOP_PANEL.getViewType()) {
- return new TopPanelRow(inflater.inflate(TopPanelRow.LAYOUT_ID, parent, false), onUrlOpenListener, onUrlOpenInBackgroundListener);
+ return new TopPanelRow(inflater.inflate(TopPanelRow.LAYOUT_ID, parent, false), onUrlOpenListener, new TopPanelRow.OnCardLongClickListener() {
+ @Override
+ public boolean onClick(final TopSite topSite, final int absolutePosition, final int faviconWidth, final int faviconHeight) {
+ openContextMenu(topSite, absolutePosition, parent, faviconWidth, faviconHeight);
+ return true;
+ }
+ });
} else if (type == RowItemType.TOP_STORIES_TITLE.getViewType()) {
return new StreamTitleRow(inflater.inflate(StreamTitleRow.LAYOUT_ID, parent, false), R.string.activity_stream_topstories, R.string.activity_stream_link_more, LINK_MORE_POCKET, onUrlOpenListener);
} else if (type == RowItemType.TOP_STORIES_ITEM.getViewType() ||
type == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
return new WebpageItemRow(inflater.inflate(WebpageItemRow.LAYOUT_ID, parent, false), new WebpageItemRow.OnMenuButtonClickListener() {
@Override
public void onMenuButtonClicked(final WebpageItemRow row, final int position) {
openContextMenu(row, position, parent, ActivityStreamTelemetry.Contract.INTERACTION_MENU_BUTTON);
@@ -360,16 +367,42 @@ public class StreamRecyclerAdapter exten
Telemetry.sendUIEvent(
TelemetryContract.Event.SHOW,
TelemetryContract.Method.CONTEXT_MENU,
extras.build()
);
}
+ /**
+ * @param snackbarAnchor See {@link ActivityStreamContextMenu#show(Context, View, ActivityStreamTelemetry.Extras.Builder, ActivityStreamContextMenu.MenuMode, WebpageModel, boolean, HomePager.OnUrlOpenListener, HomePager.OnUrlOpenInBackgroundListener, int, int)}
+ * for additional details.
+ */
+ private void openContextMenu(final TopSite topSite, final int absolutePosition, final View snackbarAnchor,
+ final int faviconWidth, final int faviconHeight) {
+ ActivityStreamTelemetry.Extras.Builder extras = ActivityStreamTelemetry.Extras.builder()
+ .forTopSite(topSite)
+ .set(ActivityStreamTelemetry.Contract.ACTION_POSITION, absolutePosition);
+
+ ActivityStreamContextMenu.show(snackbarAnchor.getContext(),
+ snackbarAnchor,
+ extras,
+ ActivityStreamContextMenu.MenuMode.TOPSITE,
+ topSite,
+ /* shouldOverrideWithImageProvider */ false, // we only use favicons for top sites.
+ onUrlOpenListener, onUrlOpenInBackgroundListener,
+ faviconWidth, faviconHeight);
+
+ Telemetry.sendUIEvent(
+ TelemetryContract.Event.SHOW,
+ TelemetryContract.Method.CONTEXT_MENU,
+ extras.build()
+ );
+ }
+
@Override
public int getItemCount() {
return recyclerViewModel.size();
}
public void swapHighlights(List<Highlight> highlights) {
final int insertionIndex = indexOfType(RowItemType.HIGHLIGHTS_TITLE, recyclerViewModel) + 1;
if (getNumOfTypeShown(RowItemType.HIGHLIGHTS_EMPTY_STATE) > 0) {
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/TopPanelRow.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/TopPanelRow.java
@@ -9,16 +9,17 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.activitystream.homepanel.model.TopSite;
import org.mozilla.gecko.activitystream.homepanel.topsites.TopSitesPage;
import org.mozilla.gecko.activitystream.homepanel.topsites.TopSitesPagerAdapter;
import org.mozilla.gecko.home.HomePager;
public class TopPanelRow extends StreamViewHolder {
public static final int LAYOUT_ID = R.layout.activity_stream_main_toppanel;
private final ViewPager topSitesPager;
@@ -41,21 +42,25 @@ public class TopPanelRow extends StreamV
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.LIST, extra);
currentPosition = newPosition;
}
}
private final SwipeListener swipeListener = new SwipeListener();
- public TopPanelRow(View itemView, HomePager.OnUrlOpenListener onUrlOpenListener, HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
+ /**
+ * @param onCardLongClickListener A listener for when a card is long-clicked.
+ */
+ public TopPanelRow(final View itemView, final HomePager.OnUrlOpenListener onUrlOpenListener,
+ final OnCardLongClickListener onCardLongClickListener) {
super(itemView);
topSitesPager = (ViewPager) itemView.findViewById(R.id.topsites_pager);
- topSitesPager.setAdapter(new TopSitesPagerAdapter(itemView.getContext(), onUrlOpenListener, onUrlOpenInBackgroundListener));
+ topSitesPager.setAdapter(new TopSitesPagerAdapter(itemView.getContext(), onUrlOpenListener, onCardLongClickListener));
topSitesPager.addOnPageChangeListener(swipeListener);
}
public void bind(Cursor cursor, int tilesSize) {
final TopSitesPagerAdapter adapter = (TopSitesPagerAdapter) topSitesPager.getAdapter();
adapter.swapCursor(cursor, tilesSize);
final Resources resources = itemView.getResources();
@@ -80,9 +85,13 @@ public class TopPanelRow extends StreamV
layoutParams.height = rows > 0 ? (tilesSize * rows) + (tilesMargin * 2) : 0;
topSitesPager.setLayoutParams(layoutParams);
// Reset the page position: binding a new Cursor means that topsites reverts to the first page,
// no event is sent in that case, but we need to know the right page number to send correct
// page swipe events
swipeListener.currentPosition = 0;
}
+
+ public interface OnCardLongClickListener {
+ boolean onClick(TopSite topSite, int absolutePosition, int faviconWidth, int faviconHeight);
+ }
}
\ No newline at end of file
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesCard.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesCard.java
@@ -1,37 +1,29 @@
/* -*- 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.activitystream.homepanel.topsites;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
import android.support.annotation.UiThread;
-import android.support.v4.widget.TextViewCompat;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import org.mozilla.gecko.R;
-import org.mozilla.gecko.Telemetry;
-import org.mozilla.gecko.TelemetryContract;
-import org.mozilla.gecko.activitystream.ActivityStreamTelemetry;
-import org.mozilla.gecko.activitystream.homepanel.menu.ActivityStreamContextMenu;
import org.mozilla.gecko.activitystream.homepanel.model.TopSite;
+import org.mozilla.gecko.activitystream.homepanel.stream.TopPanelRow;
import org.mozilla.gecko.db.BrowserDB;
-import org.mozilla.gecko.home.HomePager;
import org.mozilla.gecko.icons.IconCallback;
import org.mozilla.gecko.icons.IconResponse;
import org.mozilla.gecko.icons.Icons;
-import org.mozilla.gecko.util.DrawableUtil;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.URIUtils;
import org.mozilla.gecko.util.ViewUtil;
import org.mozilla.gecko.widget.FaviconView;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.net.URISyntaxException;
@@ -44,52 +36,30 @@ import java.util.concurrent.Future;
private final TextView title;
private final ImageView pinIconView;
private Future<IconResponse> ongoingIconLoad;
private TopSite topSite;
private int absolutePosition;
- private final HomePager.OnUrlOpenListener onUrlOpenListener;
- private final HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener;
-
- /* package-local */ TopSitesCard(final FrameLayout card, final HomePager.OnUrlOpenListener onUrlOpenListener, final HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
+ /* package-local */ TopSitesCard(final FrameLayout card, final TopPanelRow.OnCardLongClickListener onCardLongClickListener) {
super(card);
faviconView = (FaviconView) card.findViewById(R.id.favicon);
title = (TextView) card.findViewById(R.id.title);
pinIconView = (ImageView) card.findViewById(R.id.pin_icon);
- this.onUrlOpenListener = onUrlOpenListener;
- this.onUrlOpenInBackgroundListener = onUrlOpenInBackgroundListener;
-
card.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- ActivityStreamTelemetry.Extras.Builder extras = ActivityStreamTelemetry.Extras.builder()
- .forTopSite(topSite)
- .set(ActivityStreamTelemetry.Contract.ACTION_POSITION, absolutePosition);
-
- ActivityStreamContextMenu.show(itemView.getContext(),
- card,
- extras,
- ActivityStreamContextMenu.MenuMode.TOPSITE,
- topSite,
- /* shouldOverrideWithImageProvider */ false, // we only use favicons for top sites.
- onUrlOpenListener, onUrlOpenInBackgroundListener,
- faviconView.getWidth(), faviconView.getHeight());
-
- Telemetry.sendUIEvent(
- TelemetryContract.Event.SHOW,
- TelemetryContract.Method.CONTEXT_MENU,
- extras.build()
- );
-
- return true;
+ if (onCardLongClickListener != null) {
+ return onCardLongClickListener.onClick(topSite, absolutePosition, faviconView.getWidth(), faviconView.getHeight());
+ }
+ return false;
}
});
ViewUtil.enableTouchRipple(card);
}
void bind(final TopSite topSite, final int absolutePosition) {
this.topSite = topSite;
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesPageAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesPageAdapter.java
@@ -13,41 +13,42 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.activitystream.ActivityStreamTelemetry;
import org.mozilla.gecko.activitystream.homepanel.model.TopSite;
+import org.mozilla.gecko.activitystream.homepanel.stream.TopPanelRow;
import org.mozilla.gecko.home.HomePager;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.widget.RecyclerViewClickSupport;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/* package-local */ class TopSitesPageAdapter extends RecyclerView.Adapter<TopSitesCard> implements RecyclerViewClickSupport.OnItemClickListener {
private List<TopSite> topSites;
private final int pageNumber;
private int tilesSize;
private final HomePager.OnUrlOpenListener onUrlOpenListener;
- private final HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener;
+ private final TopPanelRow.OnCardLongClickListener onCardLongClickListener;
- /* package-local */ TopSitesPageAdapter(Context context, int pageNumber,
- HomePager.OnUrlOpenListener onUrlOpenListener, HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
+ /* package-local */ TopSitesPageAdapter(final int pageNumber, final HomePager.OnUrlOpenListener onUrlOpenListener,
+ final TopPanelRow.OnCardLongClickListener onCardLongClickListener) {
setHasStableIds(true);
this.topSites = new ArrayList<>();
this.pageNumber = pageNumber;
this.onUrlOpenListener = onUrlOpenListener;
- this.onUrlOpenInBackgroundListener = onUrlOpenInBackgroundListener;
+ this.onCardLongClickListener = onCardLongClickListener;
}
/**
* @param startIndex The first item that this topsites group should show. This item, and the following
* 3 items will be displayed by this adapter.
*/
public void swapCursor(final Cursor cursor, final int startIndex, final int tilesSize) {
this.tilesSize = tilesSize;
@@ -95,17 +96,17 @@ import java.util.List;
layoutParams.height = tilesSize;
card.setLayoutParams(layoutParams);
}
@Override
public TopSitesCard onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final FrameLayout card = (FrameLayout) inflater.inflate(R.layout.activity_stream_topsites_card, parent, false);
- return new TopSitesCard(card, onUrlOpenListener, onUrlOpenInBackgroundListener);
+ return new TopSitesCard(card, onCardLongClickListener);
}
@Override
public int getItemCount() {
return topSites.size();
}
@Override
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesPagerAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesPagerAdapter.java
@@ -8,16 +8,17 @@ import android.content.Context;
import android.database.Cursor;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import org.mozilla.gecko.R;
+import org.mozilla.gecko.activitystream.homepanel.stream.TopPanelRow;
import org.mozilla.gecko.home.HomePager;
import org.mozilla.gecko.widget.RecyclerViewClickSupport;
import java.util.ArrayList;
import java.util.List;
import static org.mozilla.gecko.activitystream.homepanel.topsites.TopSitesPage.NUM_TILES;
@@ -28,28 +29,28 @@ import static org.mozilla.gecko.activity
public class TopSitesPagerAdapter extends PagerAdapter {
public static final int PAGES = 2;
public static final int SUGGESTED_SITES_MAX_PAGES = 2;
private final List<TopSitesPage> pages;
private final Context context;
private final HomePager.OnUrlOpenListener onUrlOpenListener;
- private final HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener;
+ private final TopPanelRow.OnCardLongClickListener onCardLongClickListener;
private int count = 0;
- public TopSitesPagerAdapter(Context context,
- HomePager.OnUrlOpenListener onUrlOpenListener,
- HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
+ public TopSitesPagerAdapter(final Context context,
+ final HomePager.OnUrlOpenListener onUrlOpenListener,
+ final TopPanelRow.OnCardLongClickListener onCardLongClickListener) {
pages = new ArrayList<>(PAGES);
this.context = context;
this.onUrlOpenListener = onUrlOpenListener;
- this.onUrlOpenInBackgroundListener = onUrlOpenInBackgroundListener;
+ this.onCardLongClickListener = onCardLongClickListener;
}
@Override
public int getCount() {
return Math.min(count, 4);
}
@Override
@@ -99,18 +100,17 @@ public class TopSitesPagerAdapter extend
// happens when e.g. only one topsite has moved or has been added.
final int pageDelta = count - pages.size();
if (pageDelta > 0) {
final LayoutInflater inflater = LayoutInflater.from(context);
for (int i = 0; i < pageDelta; i++) {
final TopSitesPage page = (TopSitesPage) inflater.inflate(R.layout.activity_stream_topsites_page, null, false);
- final TopSitesPageAdapter adapter = new TopSitesPageAdapter(
- context, i, onUrlOpenListener, onUrlOpenInBackgroundListener);
+ final TopSitesPageAdapter adapter = new TopSitesPageAdapter(i, onUrlOpenListener, onCardLongClickListener);
page.setAdapter(adapter);
RecyclerViewClickSupport.addTo(page).setOnItemClickListener(adapter);
pages.add(page);
}
} else if (pageDelta < 0) {
for (int i = 0; i > pageDelta; i--) {
final TopSitesPage page = pages.get(pages.size() - 1);