Bug 1410221: Move TopSite openContextMenu to StreamRecyclerAdapter with listener. r=liuche draft
authorMichael Comella <michael.l.comella@gmail.com>
Mon, 23 Oct 2017 14:50:17 -0700
changeset 689875 207a2eda79398f880332391c4559763fff2d51ae
parent 689874 906036ec63861906b94f75697b7de3c2576d0057
child 689876 fb85df4bacd6a308692dcc64171f2e496394c254
push id87125
push usermichael.l.comella@gmail.com
push dateWed, 01 Nov 2017 02:42:22 +0000
reviewersliuche
bugs1410221
milestone58.0a1
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
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamRecyclerAdapter.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/TopPanelRow.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesCard.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesPageAdapter.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topsites/TopSitesPagerAdapter.java
--- 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);