Bug 988909 - Context menus for dynamic panels. r=lucasr, a=sledru
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Thu, 01 May 2014 08:44:11 -0700
changeset 199139 21c5285cd1b4ff658985bc91975b378f0f37d6ba
parent 199138 7234179747ab4b63ae7bcfa645ae409b68906b77
child 199140 9a3bd204631108ba14929883c770ac289fd8e86c
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslucasr, sledru
bugs988909
milestone31.0a2
Bug 988909 - Context menus for dynamic panels. r=lucasr, a=sledru Bug 988909 - Move ContextMenuInfoFactory out of HomeListView and into HomeContextMenuInfo. r=lucasr * * * Bug 988909 - Support context menus in PanelViews. r=lucasr
mobile/android/base/home/BookmarksPanel.java
mobile/android/base/home/DynamicPanel.java
mobile/android/base/home/FramePanelLayout.java
mobile/android/base/home/HomeContextMenuInfo.java
mobile/android/base/home/HomeListView.java
mobile/android/base/home/LastTabsPanel.java
mobile/android/base/home/MostRecentPanel.java
mobile/android/base/home/PanelGridView.java
mobile/android/base/home/PanelLayout.java
mobile/android/base/home/ReadingListPanel.java
mobile/android/base/home/TopSitesPanel.java
--- a/mobile/android/base/home/BookmarksPanel.java
+++ b/mobile/android/base/home/BookmarksPanel.java
@@ -60,17 +60,17 @@ public class BookmarksPanel extends Home
     private CursorLoaderCallbacks mLoaderCallbacks;
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         final View view = inflater.inflate(R.layout.home_bookmarks_panel, container, false);
 
         mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list);
 
-        mList.setContextMenuInfoFactory(new HomeListView.ContextMenuInfoFactory() {
+        mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
                 final int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE));
                 if (type == Bookmarks.TYPE_FOLDER) {
                     // We don't show a context menu for folders
                     return null;
                 }
                 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
--- a/mobile/android/base/home/DynamicPanel.java
+++ b/mobile/android/base/home/DynamicPanel.java
@@ -10,16 +10,17 @@ import org.json.JSONObject;
 
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserContract.HomeItems;
 import org.mozilla.gecko.db.DBUtils;
 import org.mozilla.gecko.db.HomeProvider;
 import org.mozilla.gecko.home.HomeConfig.PanelConfig;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
+import org.mozilla.gecko.home.PanelLayout.ContextMenuRegistry;
 import org.mozilla.gecko.home.PanelLayout.DatasetHandler;
 import org.mozilla.gecko.home.PanelLayout.DatasetRequest;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UiAsyncTask;
 
 import android.app.Activity;
 import android.content.ContentResolver;
@@ -221,20 +222,28 @@ public class DynamicPanel extends HomeFr
     private boolean requiresAuth() {
         return mPanelConfig.getAuthConfig() != null;
     }
 
     /**
      * Lazily creates layout for panel data.
      */
     private void createPanelLayout() {
+        final ContextMenuRegistry contextMenuRegistry = new ContextMenuRegistry() {
+            @Override
+            public void register(View view) {
+                registerForContextMenu(view);
+            }
+        };
+
         switch(mPanelConfig.getLayoutType()) {
             case FRAME:
                 final PanelDatasetHandler datasetHandler = new PanelDatasetHandler();
-                mPanelLayout = new FramePanelLayout(getActivity(), mPanelConfig, datasetHandler, mUrlOpenListener);
+                mPanelLayout = new FramePanelLayout(getActivity(), mPanelConfig, datasetHandler,
+                        mUrlOpenListener, contextMenuRegistry);
                 break;
 
             default:
                 throw new IllegalStateException("Unrecognized layout type in DynamicPanel");
         }
 
         Log.d(LOGTAG, "Created layout of type: " + mPanelConfig.getLayoutType());
         mView.addView(mPanelLayout);
--- a/mobile/android/base/home/FramePanelLayout.java
+++ b/mobile/android/base/home/FramePanelLayout.java
@@ -14,18 +14,19 @@ import android.util.Log;
 import android.view.View;
 
 class FramePanelLayout extends PanelLayout {
     private static final String LOGTAG = "GeckoFramePanelLayout";
 
     private final View mChildView;
     private final ViewConfig mChildConfig;
 
-    public FramePanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler, OnUrlOpenListener urlOpenListener) {
-        super(context, panelConfig, datasetHandler, urlOpenListener);
+    public FramePanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler,
+        OnUrlOpenListener urlOpenListener, ContextMenuRegistry contextMenuRegistry) {
+        super(context, panelConfig, datasetHandler, urlOpenListener, contextMenuRegistry);
 
         // This layout can only hold one view so we simply
         // take the first defined view from PanelConfig.
         mChildConfig = panelConfig.getViewAt(0);
         if (mChildConfig == null) {
             throw new IllegalStateException("FramePanelLayout requires a view in PanelConfig");
         }
 
--- a/mobile/android/base/home/HomeContextMenuInfo.java
+++ b/mobile/android/base/home/HomeContextMenuInfo.java
@@ -3,21 +3,21 @@
  * 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 org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.util.StringUtils;
 
+import android.database.Cursor;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.AdapterView.AdapterContextMenuInfo;
 
-
 /**
  * A ContextMenuInfo for HomeListView
  */
 public class HomeContextMenuInfo extends AdapterContextMenuInfo {
 
     public String url;
     public String title;
     public boolean isFolder = false;
@@ -47,9 +47,16 @@ public class HomeContextMenuInfo extends
     }
 
     public String getDisplayTitle() {
         if (!TextUtils.isEmpty(title)) {
             return title;
         }
         return StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url, StringUtils.UrlFlags.STRIP_HTTPS));
     }
-}
\ No newline at end of file
+
+    /*
+     * Interface for creating ContextMenuInfo from cursors
+     */
+    public interface Factory {
+        public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor);
+    }
+}
--- a/mobile/android/base/home/HomeListView.java
+++ b/mobile/android/base/home/HomeListView.java
@@ -31,17 +31,17 @@ public class HomeListView extends ListVi
 
     // On URL open listener
     protected OnUrlOpenListener mUrlOpenListener;
 
     // Top divider
     private boolean mShowTopDivider;
 
     // ContextMenuInfo maker
-    private ContextMenuInfoFactory mContextMenuInfoFactory;
+    private HomeContextMenuInfo.Factory mContextMenuInfoFactory;
 
     public HomeListView(Context context) {
         this(context, null);
     }
 
     public HomeListView(Context context, AttributeSet attrs) {
         this(context, attrs, R.attr.homeListViewStyle);
     }
@@ -116,27 +116,20 @@ public class HomeListView extends ListVi
                     position--;
                 }
 
                 listener.onItemClick(parent, view, position, id);
             }
         });
     }
 
-    public void setContextMenuInfoFactory(final ContextMenuInfoFactory factory) {
+    public void setContextMenuInfoFactory(final HomeContextMenuInfo.Factory factory) {
         mContextMenuInfoFactory = factory;
     }
 
     public OnUrlOpenListener getOnUrlOpenListener() {
         return mUrlOpenListener;
     }
 
     public void setOnUrlOpenListener(OnUrlOpenListener listener) {
         mUrlOpenListener = listener;
     }
-
-    /*
-     * Interface for creating ContextMenuInfo from cursors
-     */
-    public interface ContextMenuInfoFactory {
-    	public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor);
-    }
 }
--- a/mobile/android/base/home/LastTabsPanel.java
+++ b/mobile/android/base/home/LastTabsPanel.java
@@ -114,17 +114,17 @@ public class LastTabsPanel extends HomeF
 
                 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
 
                 final String url = c.getString(c.getColumnIndexOrThrow(Combined.URL));
                 mNewTabsListener.onNewTabs(new String[] { url });
             }
         });
 
-        mList.setContextMenuInfoFactory(new HomeListView.ContextMenuInfoFactory() {
+        mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
                 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
                 info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL));
                 info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE));
                 return info;
             }
         });
--- a/mobile/android/base/home/MostRecentPanel.java
+++ b/mobile/android/base/home/MostRecentPanel.java
@@ -102,17 +102,17 @@ public class MostRecentPanel extends Hom
 
                 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
 
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
-        mList.setContextMenuInfoFactory(new HomeListView.ContextMenuInfoFactory() {
+        mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
                 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
                 info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL));
                 info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE));
                 info.display = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.DISPLAY));
                 info.historyId = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.HISTORY_ID));
                 final int bookmarkIdCol = cursor.getColumnIndexOrThrow(Combined.BOOKMARK_ID);
--- a/mobile/android/base/home/PanelGridView.java
+++ b/mobile/android/base/home/PanelGridView.java
@@ -27,27 +27,42 @@ import android.widget.GridView;
 public class PanelGridView extends GridView
                            implements DatasetBacked, PanelView {
     private static final String LOGTAG = "GeckoPanelGridView";
 
     private final ViewConfig viewConfig;
     private final PanelViewAdapter adapter;
     private PanelViewItemHandler itemHandler;
     private OnItemOpenListener itemOpenListener;
+    private HomeContextMenuInfo mContextMenuInfo;
+    private HomeContextMenuInfo.Factory mContextMenuInfoFactory;
 
     public PanelGridView(Context context, ViewConfig viewConfig) {
         super(context, null, R.attr.panelGridViewStyle);
 
         this.viewConfig = viewConfig;
         itemHandler = new PanelViewItemHandler(viewConfig);
 
         adapter = new PanelViewAdapter(context, viewConfig);
         setAdapter(adapter);
 
         setOnItemClickListener(new PanelGridItemClickListener());
+        setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+            @Override
+            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+                Cursor cursor = (Cursor) parent.getItemAtPosition(position);
+                if (cursor == null || mContextMenuInfoFactory == null) {
+                    mContextMenuInfo = null;
+                    return false;
+                }
+
+                mContextMenuInfo = mContextMenuInfoFactory.makeInfoForCursor(view, position, id, cursor);
+                return showContextMenuForChild(PanelGridView.this);
+            }
+        });
     }
 
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         itemHandler.setOnItemOpenListener(itemOpenListener);
     }
 
@@ -76,9 +91,19 @@ public class PanelGridView extends GridV
     }
 
     private class PanelGridItemClickListener implements AdapterView.OnItemClickListener {
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             itemHandler.openItemAtPosition(adapter.getCursor(), position);
         }
     }
+
+    @Override
+    public HomeContextMenuInfo getContextMenuInfo() {
+        return mContextMenuInfo;
+    }
+
+    @Override
+    public void setContextMenuInfoFactory(HomeContextMenuInfo.Factory factory) {
+        mContextMenuInfoFactory = factory;
+    }
 }
--- a/mobile/android/base/home/PanelLayout.java
+++ b/mobile/android/base/home/PanelLayout.java
@@ -1,16 +1,17 @@
 /* -*- 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 org.mozilla.gecko.R;
+import org.mozilla.gecko.db.BrowserContract.HomeItems;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.HomeConfig.EmptyViewConfig;
 import org.mozilla.gecko.home.HomeConfig.ItemHandler;
 import org.mozilla.gecko.home.HomeConfig.PanelConfig;
 import org.mozilla.gecko.home.HomeConfig.ViewConfig;
 import org.mozilla.gecko.util.StringUtils;
 
 import android.content.Context;
@@ -74,16 +75,17 @@ import com.squareup.picasso.Picasso;
  */
 abstract class PanelLayout extends FrameLayout {
     private static final String LOGTAG = "GeckoPanelLayout";
 
     protected final SparseArray<ViewState> mViewStates;
     private final PanelConfig mPanelConfig;
     private final DatasetHandler mDatasetHandler;
     private final OnUrlOpenListener mUrlOpenListener;
+    private final ContextMenuRegistry mContextMenuRegistry;
 
     /**
      * To be used by panel views to express that they are
      * backed by datasets.
      */
     public interface DatasetBacked {
         public void setDataset(Cursor cursor);
         public void setFilterManager(FilterManager manager);
@@ -214,30 +216,37 @@ abstract class PanelLayout extends Frame
          * before.
          */
         public void resetDataset(int viewIndex);
     }
 
     public interface PanelView {
         public void setOnItemOpenListener(OnItemOpenListener listener);
         public void setOnKeyListener(OnKeyListener listener);
+        public void setContextMenuInfoFactory(HomeContextMenuInfo.Factory factory);
     }
 
     public interface FilterManager {
         public FilterDetail getPreviousFilter();
         public boolean canGoBack();
         public void goBack();
     }
 
-    public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler, OnUrlOpenListener urlOpenListener) {
+    public interface ContextMenuRegistry {
+        public void register(View view);
+    }
+
+    public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler,
+            OnUrlOpenListener urlOpenListener, ContextMenuRegistry contextMenuRegistry) {
         super(context);
         mViewStates = new SparseArray<ViewState>();
         mPanelConfig = panelConfig;
         mDatasetHandler = datasetHandler;
         mUrlOpenListener = urlOpenListener;
+        mContextMenuRegistry = contextMenuRegistry;
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
         final int count = mViewStates.size();
         for (int i = 0; i < count; i++) {
@@ -364,16 +373,27 @@ abstract class PanelLayout extends Frame
 
                 default:
                     throw new IllegalStateException("Unrecognized view type in " + getClass().getSimpleName());
             }
 
             PanelView panelView = (PanelView) view;
             panelView.setOnItemOpenListener(new PanelOnItemOpenListener(viewState));
             panelView.setOnKeyListener(new PanelKeyListener(viewState));
+            panelView.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
+                @Override
+                public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
+                    final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
+                    info.url = cursor.getString(cursor.getColumnIndexOrThrow(HomeItems.URL));
+                    info.title = cursor.getString(cursor.getColumnIndexOrThrow(HomeItems.TITLE));
+                    return info;
+                }
+            });
+
+            mContextMenuRegistry.register(view);
 
             if (view instanceof DatasetBacked) {
                 DatasetBacked datasetBacked = (DatasetBacked) view;
                 datasetBacked.setFilterManager(new PanelFilterManager(viewState));
 
                 if (viewConfig.isRefreshEnabled()) {
                     view = new PanelRefreshLayout(getContext(), view,
                                                   mPanelConfig.getId(), viewConfig.getIndex());
--- a/mobile/android/base/home/ReadingListPanel.java
+++ b/mobile/android/base/home/ReadingListPanel.java
@@ -109,17 +109,17 @@ public class ReadingListPanel extends Ho
 
                 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
 
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
-        mList.setContextMenuInfoFactory(new HomeListView.ContextMenuInfoFactory() {
+        mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
                 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
                 info.url = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.URL));
                 info.title = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.TITLE));
                 info.readingListItemId = cursor.getInt(cursor.getColumnIndexOrThrow(ReadingListItems._ID));
                 return info;
             }
--- a/mobile/android/base/home/TopSitesPanel.java
+++ b/mobile/android/base/home/TopSitesPanel.java
@@ -183,17 +183,17 @@ public class TopSitesPanel extends HomeF
 
                 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM);
 
                 // This item is a TwoLinePageRow, so we allow switch-to-tab.
                 mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
-        mList.setContextMenuInfoFactory(new HomeListView.ContextMenuInfoFactory() {
+        mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
             @Override
             public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
                 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
                 info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL));
                 info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE));
                 info.historyId = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.HISTORY_ID));
                 final int bookmarkIdCol = cursor.getColumnIndexOrThrow(Combined.BOOKMARK_ID);
                 if (cursor.isNull(bookmarkIdCol)) {