Bug 1278840 - FF for Android offers no way to remove arbitrary sites from history apart from top sites or recent history. r=sebastian draft
authordlim@mozilla.com <dlim@mozilla.com>
Wed, 10 Aug 2016 08:56:06 -0700
changeset 399195 a414133005b6c20b310126c2caaed8a6424d1066
parent 399191 531100c1d950c8857dda172573fbd23360f7619b
child 527859 1176981f73885a1403dae526aaaa3431866b7db6
push id25754
push userdlim@mozilla.com
push dateWed, 10 Aug 2016 15:56:52 +0000
reviewerssebastian
bugs1278840
milestone51.0a1
Bug 1278840 - FF for Android offers no way to remove arbitrary sites from history apart from top sites or recent history. r=sebastian MozReview-Commit-ID: 2QAnzQLBd9q
mobile/android/base/java/org/mozilla/gecko/home/BrowserSearch.java
mobile/android/base/java/org/mozilla/gecko/home/HomeContextMenuInfo.java
mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
mobile/android/base/resources/menu/browsersearch_contextmenu.xml
--- a/mobile/android/base/java/org/mozilla/gecko/home/BrowserSearch.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/BrowserSearch.java
@@ -15,20 +15,18 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
 
 import android.content.SharedPreferences;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
-import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SuggestClient;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
@@ -42,26 +40,30 @@ import org.mozilla.gecko.preferences.Gec
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.AsyncTaskLoader;
 import android.support.v4.content.Loader;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.ContextMenu;
 import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.WindowManager.LayoutParams;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.Animation;
@@ -360,16 +362,44 @@ public class BrowserSearch extends HomeF
                 return mList.onItemLongClick(parent, view, position, id);
             }
         });
 
         final ListSelectionListener listener = new ListSelectionListener();
         mList.setOnItemSelectedListener(listener);
         mList.setOnFocusChangeListener(listener);
 
+        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(BrowserContract.Combined.URL));
+                info.title = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Combined.TITLE));
+
+                int bookmarkId = cursor.getInt(cursor.getColumnIndexOrThrow(BrowserContract.Combined.BOOKMARK_ID));
+                info.bookmarkId = bookmarkId;
+
+                int historyId = cursor.getInt(cursor.getColumnIndexOrThrow(BrowserContract.Combined.HISTORY_ID));
+                info.historyId = historyId;
+
+                boolean isBookmark = bookmarkId != -1;
+                boolean isHistory = historyId != -1;
+
+                if (isBookmark && isHistory) {
+                    info.itemType = HomeContextMenuInfo.RemoveItemType.COMBINED;
+                } else if (isBookmark) {
+                    info.itemType = HomeContextMenuInfo.RemoveItemType.BOOKMARKS;
+                } else if (isHistory) {
+                    info.itemType = HomeContextMenuInfo.RemoveItemType.HISTORY;
+                }
+
+                return info;
+            }
+        });
+
         mList.setOnKeyListener(new View.OnKeyListener() {
             @Override
             public boolean onKey(View v, int keyCode, android.view.KeyEvent event) {
                 final View selected = mList.getSelectedView();
 
                 if (selected instanceof SearchEngineRow) {
                     return selected.onKeyDown(keyCode, event);
                 }
@@ -380,16 +410,53 @@ public class BrowserSearch extends HomeF
         registerForContextMenu(mList);
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
             "SearchEngines:Data");
 
         mSearchEngineBar.setOnSearchBarClickListener(this);
     }
 
     @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        if (!(menuInfo instanceof HomeContextMenuInfo)) {
+            return;
+        }
+
+        HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
+
+        MenuInflater inflater = new MenuInflater(view.getContext());
+        inflater.inflate(R.menu.browsersearch_contextmenu, menu);
+
+        menu.setHeaderTitle(info.getDisplayTitle());
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        ContextMenuInfo menuInfo = item.getMenuInfo();
+        if (!(menuInfo instanceof HomeContextMenuInfo)) {
+            return false;
+        }
+
+        final HomeContextMenuInfo info = (HomeContextMenuInfo) menuInfo;
+        final Context context = getActivity();
+
+        final int itemId = item.getItemId();
+
+        if (itemId == R.id.browsersearch_remove) {
+            // Position for Top Sites grid items, but will always be -1 since this is only for BrowserSearch result
+            final int position = -1;
+
+            new RemoveItemByUrlTask(context, info.url, info.itemType, position).execute();
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
         // Initialize the search adapter
         mAdapter = new SearchAdapter(getActivity());
         mList.setAdapter(mAdapter);
 
         // Only create an instance when we need it
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomeContextMenuInfo.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomeContextMenuInfo.java
@@ -24,17 +24,17 @@ public class HomeContextMenuInfo extends
     public String title;
     public boolean isFolder;
     public int historyId = -1;
     public int bookmarkId = -1;
     public RemoveItemType itemType = null;
 
     // Item type to be handled with "Remove" selection.
     public static enum RemoveItemType {
-        BOOKMARKS, HISTORY
+        BOOKMARKS, COMBINED, HISTORY
     }
 
     public HomeContextMenuInfo(View targetView, int position, long id) {
         super(targetView, position, id);
     }
 
     public boolean hasBookmarkId() {
         return bookmarkId > -1;
--- a/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/HomeFragment.java
@@ -405,53 +405,65 @@ public abstract class HomeFragment exten
                 mDB.unpinSite(cr, mPosition);
                 if (mDB.hideSuggestedSite(mUrl)) {
                     cr.notifyChange(SuggestedSites.CONTENT_URI, null);
                 }
             }
 
             switch (mType) {
                 case BOOKMARKS:
-                    SavedReaderViewHelper rch = SavedReaderViewHelper.getSavedReaderViewHelper(mContext);
-                    final boolean isReaderViewPage = rch.isURLCached(mUrl);
-
-                    final String extra;
-                    if (isReaderViewPage) {
-                        extra = "bookmark_reader";
-                    } else {
-                        extra = "bookmark";
-                    }
-
-                    Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.CONTEXT_MENU, extra);
-                    mDB.removeBookmarksWithURL(cr, mUrl);
-
-                    if (isReaderViewPage) {
-                        ReadingListHelper.removeCachedReaderItem(mUrl, mContext);
-                    }
-
+                    removeBookmark(cr);
                     break;
 
                 case HISTORY:
-                    mDB.removeHistoryEntry(cr, mUrl);
+                    removeHistory(cr);
+                    break;
+
+                case COMBINED:
+                    removeBookmark(cr);
+                    removeHistory(cr);
                     break;
 
                 default:
                     Log.e(LOGTAG, "Can't remove item type " + mType.toString());
                     break;
             }
             return null;
         }
 
         @Override
         public void onPostExecute(Void result) {
             SnackbarBuilder.builder((Activity) mContext)
                     .message(R.string.page_removed)
                     .duration(Snackbar.LENGTH_LONG)
                     .buildAndShow();
         }
+
+        private void removeBookmark(ContentResolver cr) {
+            SavedReaderViewHelper rch = SavedReaderViewHelper.getSavedReaderViewHelper(mContext);
+            final boolean isReaderViewPage = rch.isURLCached(mUrl);
+
+            final String extra;
+            if (isReaderViewPage) {
+                extra = "bookmark_reader";
+            } else {
+                extra = "bookmark";
+            }
+
+            Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.CONTEXT_MENU, extra);
+            mDB.removeBookmarksWithURL(cr, mUrl);
+
+            if (isReaderViewPage) {
+                ReadingListHelper.removeCachedReaderItem(mUrl, mContext);
+            }
+        }
+
+        private void removeHistory(ContentResolver cr) {
+            mDB.removeHistoryEntry(cr, mUrl);
+        }
     }
 
     private static class RemovePartnerBookmarkTask extends UIAsyncTask.WithoutParams<Void> {
         private Context context;
         private long bookmarkId;
 
         public RemovePartnerBookmarkTask(Context context, long bookmarkId) {
             super(ThreadUtils.getBackgroundHandler());
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/menu/browsersearch_contextmenu.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/browsersearch_remove"
+        android:title="@string/contextmenu_remove"/>
+
+</menu>