Backed out changeset 81b90ed9ff46 (bug 1220928)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 30 Mar 2016 14:37:51 +0200
changeset 290882 3cdaff81f4d58e0e2ba8af736438e63993867a4a
parent 290881 a30dd3829cd0a7bb8f3a98067d0fc77117fd6697
child 290883 9c2817f5c9009571e11f65c208401a58fe280799
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1220928
milestone48.0a1
backs out81b90ed9ff464c38667488a5d4892728a802bdb0
Backed out changeset 81b90ed9ff46 (bug 1220928)
mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryAdapter.java
mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryItem.java
mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryRecyclerView.java
mobile/android/base/java/org/mozilla/gecko/home/RemoteTabsBaseFragment.java
mobile/android/base/resources/drawable/action_bar_button.xml
mobile/android/base/resources/layout/home_remote_tabs_hidden_devices.xml
mobile/android/base/resources/menu/home_remote_tabs_client_contextmenu.xml
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryAdapter.java
@@ -10,33 +10,31 @@ import android.content.Context;
 import android.database.Cursor;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 import org.json.JSONArray;
-import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.RemoteClient;
 import org.mozilla.gecko.db.RemoteTab;
 import org.mozilla.gecko.home.CombinedHistoryPanel.SectionHeader;
 
 import java.util.Collections;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 
 public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistoryItem> {
     private static final String LOGTAG = "GeckoCombinedHistAdapt";
 
     public enum ItemType {
-        CLIENT, HIDDEN_DEVICES, SECTION_HEADER, HISTORY, NAVIGATION_BACK, CHILD;
+        CLIENT, SECTION_HEADER, HISTORY, NAVIGATION_BACK, CHILD;
 
         public static ItemType viewTypeToItemType(int viewType) {
             if (viewType >= ItemType.values().length) {
                 Log.e(LOGTAG, "No corresponding ItemType!");
             }
             return ItemType.values()[viewType];
         }
 
@@ -44,138 +42,38 @@ public class CombinedHistoryAdapter exte
             return itemType.ordinal();
         }
     }
 
     private List<RemoteClient> remoteClients = Collections.emptyList();
     private List<RemoteTab> clientChildren;
     private Cursor historyCursor;
 
-    // Maintain group collapsed and hidden state. Only accessed from the UI thread.
-    protected static RemoteTabsExpandableListState sState;
-
-    // List of hidden remote clients.
-    // Only accessed from the UI thread.
-    protected final List<RemoteClient> hiddenClients = new ArrayList<>();
-
     // We use a sparse array to store each section header's position in the panel [more cheaply than a HashMap].
     private final SparseArray<CombinedHistoryPanel.SectionHeader> sectionHeaders;
 
     private final Context context;
 
     private boolean inChildView = false;
 
     public CombinedHistoryAdapter(Context context) {
         super();
         this.context = context;
         sectionHeaders = new SparseArray<>();
-
-        // This races when multiple Fragments are created. That's okay: one
-        // will win, and thereafter, all will be okay. If we create and then
-        // drop an instance the shared SharedPreferences backing all the
-        // instances will maintain the state for us. Since everything happens on
-        // the UI thread, this doesn't even need to be volatile.
-        if (sState == null) {
-            sState = new RemoteTabsExpandableListState(GeckoSharedPrefs.forProfile(context));
-        }
     }
 
     public void setClients(List<RemoteClient> clients) {
-        hiddenClients.clear();
-        remoteClients.clear();
-
-        final Iterator<RemoteClient> it = clients.iterator();
-        while (it.hasNext()) {
-            final RemoteClient client = it.next();
-            if (sState.isClientHidden(client.guid)) {
-                hiddenClients.add(client);
-                it.remove();
-            }
-        }
-
         remoteClients = clients;
-
-        // Add item for unhiding clients.
-        if (!hiddenClients.isEmpty()) {
-            remoteClients.add(null);
-        }
-
-        notifyItemRangeChanged(0, remoteClients.size());
+        notifyDataSetChanged();
     }
 
     public void setHistory(Cursor history) {
         historyCursor = history;
         populateSectionHeaders(historyCursor, sectionHeaders);
-        final int historySize = historyCursor == null ? 0 : historyCursor.getCount();
-        notifyItemRangeChanged(remoteClients.size(), historySize + sectionHeaders.size());
-    }
-
-    public void removeItem(int position) {
-        final ItemType  itemType = getItemTypeForPosition(position);
-        switch (itemType) {
-            case CLIENT:
-                final boolean hadHiddenClients = !hiddenClients.isEmpty();
-                final RemoteClient client = remoteClients.remove(transformAdapterPositionForDataStructure(ItemType.CLIENT, position));
-                notifyItemRemoved(position);
-
-                sState.setClientHidden(client.guid, true);
-                hiddenClients.add(client);
-                if (!hadHiddenClients) {
-                    // Add item for unhiding clients;
-                    remoteClients.add(null);
-                } else {
-                    // Update "hidden clients" item because number of hidden clients changed.
-                    notifyItemChanged(getRemoteClientsHiddenItemsIndex());
-                }
-                break;
-        }
-    }
-
-    public void unhideClients(List<RemoteClient> selectedClients) {
-        if (selectedClients.size() == 0) {
-            return;
-        }
-
-        for (RemoteClient client : selectedClients) {
-            sState.setClientHidden(client.guid, false);
-            hiddenClients.remove(client);
-        }
-
-        final int insertIndex = getRemoteClientsHiddenItemsIndex();
-
-        remoteClients.addAll(insertIndex, selectedClients);
-        notifyItemRangeInserted(insertIndex, selectedClients.size());
-
-        if (hiddenClients.isEmpty()) {
-            // No more hidden clients, remove "unhide" item.
-            remoteClients.remove(getRemoteClientsHiddenItemsIndex());
-        } else {
-            // Update "hidden clients" item because number of hidden clients changed.
-            notifyItemChanged(getRemoteClientsHiddenItemsIndex());
-        }
-    }
-
-    /**
-     * Get the position of the "N devices hidden" item in the remoteClients List.
-     *
-     * This is the last item in the remoteClients list, if any items are hidden.
-     * <code>hiddenClients</code> must be in a consistent state with <code>remoteClients</code>
-     * (e.g. each client should be in exactly one of the two lists).
-     *
-     * @return index of the "N devices hidden" item, or -1 if it doesn't exist.
-     */
-    private int getRemoteClientsHiddenItemsIndex() {
-        if (hiddenClients.isEmpty()) {
-            return -1;
-        }
-        return remoteClients.size() - 1;
-    }
-
-    public List<RemoteClient> getHiddenClients() {
-        return hiddenClients;
+        notifyDataSetChanged();
     }
 
     public JSONArray getCurrentChildTabs() {
         if (clientChildren != null) {
             final JSONArray urls = new JSONArray();
             for (int i = 1; i < clientChildren.size(); i++) {
                 urls.put(clientChildren.get(i).url);
             }
@@ -185,95 +83,73 @@ public class CombinedHistoryAdapter exte
     }
 
     public void showChildView(int parentPosition) {
         if (clientChildren == null) {
             clientChildren = new ArrayList<>();
         }
         // Handle "back" view.
         clientChildren.add(null);
-        clientChildren.addAll(remoteClients.get(transformAdapterPositionForDataStructure(ItemType.CLIENT, parentPosition)).tabs);
+        clientChildren.addAll(remoteClients.get(transformPosition(ItemType.CLIENT, parentPosition)).tabs);
         inChildView = true;
         notifyDataSetChanged();
     }
 
     public void exitChildView() {
         inChildView = false;
         clientChildren.clear();
         notifyDataSetChanged();
     }
 
-    private ItemType getItemTypeForPosition(int position) {
-        return ItemType.viewTypeToItemType(getItemViewType(position));
-    }
-
-    /**
-     * Transform an adapter position to the position for the data structure backing the item type.
-     *
-     * The type is not strictly necessary and could be fetched from <code>getItemTypeForPosition</code>,
-     * but is used for explicitness.
-     *
-     * @param type ItemType of the item
-     * @param position position in the adapter
-     * @return position of the item in the data structure
-     */
-    private int transformAdapterPositionForDataStructure(ItemType type, int position) {
+    private int transformPosition(ItemType type, int position) {
         if (type == ItemType.CLIENT) {
             return position;
         } else if (type == ItemType.SECTION_HEADER) {
             return position - remoteClients.size();
         } else if (type == ItemType.HISTORY){
             return position - remoteClients.size() - getHeadersBefore(position);
         } else {
             return position;
         }
     }
 
     public HomeContextMenuInfo makeContextMenuInfoFromPosition(View view, int position) {
-        final ItemType itemType = getItemTypeForPosition(position);
+        final ItemType itemType = ItemType.viewTypeToItemType(getItemViewType(position));
         HomeContextMenuInfo info;
         switch (itemType) {
             case CHILD:
                 info = new HomeContextMenuInfo(view, position, -1);
                 return CombinedHistoryPanel.populateChildInfoFromTab(info, clientChildren.get(position));
             case HISTORY:
                 info = new HomeContextMenuInfo(view, position, -1);
-                historyCursor.moveToPosition(transformAdapterPositionForDataStructure(ItemType.HISTORY, position));
+                historyCursor.moveToPosition(transformPosition(ItemType.HISTORY, position));
                 return CombinedHistoryPanel.populateHistoryInfoFromCursor(info, historyCursor);
-            case CLIENT:
-                final int clientPosition = transformAdapterPositionForDataStructure(ItemType.CLIENT, position);
-                info = new CombinedHistoryPanel.RemoteTabsClientContextMenuInfo(view, position,-1, remoteClients.get(clientPosition));
-                return info;
         }
         return null;
     }
 
     @Override
     public CombinedHistoryItem onCreateViewHolder(ViewGroup viewGroup, int viewType) {
         final LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
         final View view;
 
         final ItemType itemType = ItemType.viewTypeToItemType(viewType);
 
         switch (itemType) {
             case CLIENT:
                 view = inflater.inflate(R.layout.home_remote_tabs_group, viewGroup, false);
                 return new CombinedHistoryItem.ClientItem(view);
 
-            case HIDDEN_DEVICES:
-                view = inflater.inflate(R.layout.home_remote_tabs_hidden_devices, viewGroup, false);
-                return new CombinedHistoryItem.BasicItem(view);
-
             case NAVIGATION_BACK:
                 view = inflater.inflate(R.layout.home_combined_back_item, viewGroup, false);
                 return new CombinedHistoryItem.HistoryItem(view);
 
             case SECTION_HEADER:
                 view = inflater.inflate(R.layout.home_header_row, viewGroup, false);
-                return new CombinedHistoryItem.BasicItem(view);
+                return new CombinedHistoryItem.SectionItem(view);
 
             case CHILD:
             case HISTORY:
                 view = inflater.inflate(R.layout.home_item_row, viewGroup, false);
                 return new CombinedHistoryItem.HistoryItem(view);
             default:
                 throw new IllegalArgumentException("Unexpected Home Panel item type");
         }
@@ -284,38 +160,36 @@ public class CombinedHistoryAdapter exte
         if (inChildView) {
             if (position == 0) {
                 return ItemType.itemTypeToViewType(ItemType.NAVIGATION_BACK);
             }
             return ItemType.itemTypeToViewType(ItemType.CHILD);
         } else {
             final int numClients = remoteClients.size();
             if (position < numClients) {
-                if (!hiddenClients.isEmpty() && position == numClients - 1) {
-                    return ItemType.itemTypeToViewType(ItemType.HIDDEN_DEVICES);
-                }
                 return ItemType.itemTypeToViewType(ItemType.CLIENT);
             }
 
-            final int sectionPosition = transformAdapterPositionForDataStructure(ItemType.SECTION_HEADER, position);
+            final int sectionPosition = transformPosition(ItemType.SECTION_HEADER, position);
             if (sectionHeaders.get(sectionPosition) != null) {
                 return ItemType.itemTypeToViewType(ItemType.SECTION_HEADER);
             }
 
             return ItemType.itemTypeToViewType(ItemType.HISTORY);
         }
     }
 
     @Override
     public int getItemCount() {
         if (inChildView) {
             return (clientChildren == null) ? 0 : clientChildren.size();
         } else {
+            final int remoteSize = remoteClients.size();
             final int historySize = historyCursor == null ? 0 : historyCursor.getCount();
-            return remoteClients.size() + historySize + sectionHeaders.size();
+            return remoteSize + historySize + sectionHeaders.size();
         }
     }
 
     /**
      * Add only the SectionHeaders that have history items within their range to a SparseArray, where the
      * array index is the position of the header in the history-only (no clients) ordering.
      * @param c data Cursor
      * @param sparseArray SparseArray to populate
@@ -350,41 +224,35 @@ public class CombinedHistoryAdapter exte
         if (historyCursor == null) {
             return false;
         }
         return (historyCursor.getCount() > 0);
     }
 
     @Override
     public void onBindViewHolder(CombinedHistoryItem viewHolder, int position) {
-        final ItemType itemType = getItemTypeForPosition(position);
-        final int localPosition = transformAdapterPositionForDataStructure(itemType, position);
+        final ItemType itemType = ItemType.viewTypeToItemType(getItemViewType(position));
+        final int localPosition = transformPosition(itemType, position);
 
         switch (itemType) {
             case CLIENT:
                 final CombinedHistoryItem.ClientItem clientItem = (CombinedHistoryItem.ClientItem) viewHolder;
                 final RemoteClient client = remoteClients.get(localPosition);
                 clientItem.bind(client, context);
                 break;
 
-            case HIDDEN_DEVICES:
-                final String hiddenDevicesLabel = context.getResources().getString(R.string.home_remote_tabs_many_hidden_devices, hiddenClients.size());
-                ((TextView) viewHolder.itemView).setText(hiddenDevicesLabel);
-                break;
-
             case CHILD:
                 RemoteTab remoteTab = clientChildren.get(position);
                 ((CombinedHistoryItem.HistoryItem) viewHolder).bind(remoteTab);
                 break;
 
             case SECTION_HEADER:
                 ((TextView) viewHolder.itemView).setText(CombinedHistoryPanel.getSectionHeaderTitle(sectionHeaders.get(localPosition)));
                 break;
 
-
             case HISTORY:
                 if (historyCursor == null || !historyCursor.moveToPosition(localPosition)) {
                     throw new IllegalStateException("Couldn't move cursor to position " + localPosition);
                 }
                 ((CombinedHistoryItem.HistoryItem) viewHolder).bind(historyCursor);
                 break;
         }
     }
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryItem.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryItem.java
@@ -16,18 +16,18 @@ import org.mozilla.gecko.RemoteTabsExpan
 import org.mozilla.gecko.db.RemoteClient;
 import org.mozilla.gecko.db.RemoteTab;
 
 public abstract class CombinedHistoryItem extends RecyclerView.ViewHolder {
     public CombinedHistoryItem(View view) {
         super(view);
     }
 
-    public static class BasicItem extends CombinedHistoryItem {
-        public BasicItem(View view) {
+    public static class SectionItem extends CombinedHistoryItem {
+        public SectionItem(View view) {
             super(view);
         }
     }
 
     public static class HistoryItem extends CombinedHistoryItem {
         public HistoryItem(View view) {
             super(view);
         }
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
@@ -6,59 +6,53 @@
 package org.mozilla.gecko.home;
 
 import android.app.AlertDialog;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
 import android.os.Bundle;
-import android.support.v4.app.LoaderManager;
 import android.support.v4.content.Loader;
 import android.support.v7.widget.DefaultItemAnimator;
 import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
 import android.text.method.LinkMovementMethod;
 import android.text.style.ClickableSpan;
 import android.text.style.UnderlineSpan;
 import android.util.Log;
-import android.view.ContextMenu;
 import android.view.LayoutInflater;
-import android.view.MenuInflater;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.json.JSONArray;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
-import org.mozilla.gecko.RemoteClientsDialogFragment;
 import org.mozilla.gecko.Restrictions;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.RemoteClient;
 import org.mozilla.gecko.db.RemoteTab;
 import org.mozilla.gecko.home.HistorySectionsHelper.SectionDateRange;
 import org.mozilla.gecko.restrictions.Restrictable;
 import org.mozilla.gecko.widget.DividerItemDecoration;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
-public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsDialogFragment.RemoteClientsListener {
+public class CombinedHistoryPanel extends HomeFragment {
     private static final String LOGTAG = "GeckoCombinedHistoryPnl";
     private final int LOADER_ID_HISTORY = 0;
     private final int LOADER_ID_REMOTE = 1;
 
     // Semantic names for the time covered by each section
     public enum SectionHeader {
         TODAY,
         YESTERDAY,
@@ -107,17 +101,16 @@ public class CombinedHistoryPanel extend
 
         mRecyclerView = (CombinedHistoryRecyclerView) view.findViewById(R.id.combined_recycler_view);
         mAdapter = new CombinedHistoryAdapter(getContext());
         mRecyclerView.setAdapter(mAdapter);
         mRecyclerView.setItemAnimator(new DefaultItemAnimator());
         mRecyclerView.addItemDecoration(new DividerItemDecoration(getContext()));
         mRecyclerView.setOnHistoryClickedListener(mUrlOpenListener);
         mRecyclerView.setOnPanelLevelChangeListener(new OnLevelChangeListener());
-        mRecyclerView.setHiddenClientsDialogBuilder(new HiddenClientsHelper());
         registerForContextMenu(mRecyclerView);
 
         mPanelFooterButton = (Button) view.findViewById(R.id.clear_history_button);
         mPanelFooterButton.setOnClickListener(new OnFooterButtonClickListener());
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
@@ -172,17 +165,17 @@ public class CombinedHistoryPanel extend
             if (time > sectionDateRangeArray[i].start) {
                 return SectionHeader.values()[i];
             }
         }
 
         return SectionHeader.OLDER_THAN_SIX_MONTHS;
     }
 
-    private class CursorLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
+    private class CursorLoaderCallbacks extends TransitionAwareCursorLoaderCallbacks {
         private BrowserDB mDB;    // Pseudo-final: set in onCreateLoader.
 
         @Override
         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
             if (mDB == null) {
                 mDB = GeckoProfile.get(getActivity()).getDB();
             }
 
@@ -192,41 +185,34 @@ public class CombinedHistoryPanel extend
                 case LOADER_ID_REMOTE:
                     return new RemoteTabsCursorLoader(getContext());
                 default:
                     Log.e(LOGTAG, "Unknown loader id!");
                     return null;
             }
         }
 
-        @Override
-        public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
+        protected void onLoadFinishedAfterTransitions(Loader<Cursor> loader, Cursor c) {
             final int loaderId = loader.getId();
             switch (loaderId) {
                 case LOADER_ID_HISTORY:
                     mAdapter.setHistory(c);
                     break;
 
                 case LOADER_ID_REMOTE:
                     final List<RemoteClient> clients = mDB.getTabsAccessor().getClientsFromCursor(c);
-
+                    // TODO: Handle hidden clients
                     mAdapter.setClients(clients);
                     break;
             }
 
             // Check and set empty state.
             updateButtonFromLevel(OnPanelLevelChangeListener.PanelLevel.PARENT);
             updateEmptyView(mAdapter.getItemCount() == 0);
         }
-
-        @Override
-        public void onLoaderReset(Loader<Cursor> loader) {
-            mAdapter.setClients(Collections.<RemoteClient>emptyList());
-            mAdapter.setHistory(null);
-        }
     }
 
     protected class OnLevelChangeListener implements OnPanelLevelChangeListener {
         @Override
         public void onPanelLevelChange(PanelLevel level) {
             updateButtonFromLevel(level);
         }
     }
@@ -271,32 +257,32 @@ public class CombinedHistoryPanel extend
                             // Send message to Java to clear history.
                             final JSONObject json = new JSONObject();
                             try {
                                 json.put("history", true);
                             } catch (JSONException e) {
                                 Log.e(LOGTAG, "JSON error", e);
                             }
 
-                            GeckoAppShell.notifyObservers("Sanitize:ClearData", json.toString());
+                            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Sanitize:ClearData", json.toString()));
                             Telemetry.sendUIEvent(TelemetryContract.Event.SANITIZE, TelemetryContract.Method.BUTTON, "history");
                         }
                     });
 
                     dialogBuilder.show();
                     break;
 
                 case CHILD:
                     final JSONArray tabUrls = ((CombinedHistoryAdapter) mRecyclerView.getAdapter()).getCurrentChildTabs();
                     if (tabUrls != null) {
                         final JSONObject message = new JSONObject();
                         try {
                             message.put("urls", tabUrls);
                             message.put("shouldNotifyTabsOpenedToJava", false);
-                            GeckoAppShell.notifyObservers("Tabs:OpenMultiple", message.toString());
+                            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tabs:OpenMultiple", message.toString()));
                         } catch (JSONException e) {
                             Log.e(LOGTAG, "Error making JSON message to open tabs");
                         }
                     }
                     break;
             }
         }
     }
@@ -387,91 +373,16 @@ public class CombinedHistoryPanel extend
         ssb.setSpan(new UnderlineSpan(), underlineStart, underlineEnd, 0);
 
         ssb.delete(underlineEnd, underlineEnd + FORMAT_S2.length());
         ssb.delete(underlineStart, underlineStart + FORMAT_S1.length());
 
         return ssb;
     }
 
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
-        if (!(menuInfo instanceof RemoteTabsClientContextMenuInfo)) {
-            // Long pressed item was not a RemoteTabsGroup item. Superclass
-            // can handle this.
-            super.onCreateContextMenu(menu, view, menuInfo);
-            return;
-        }
-
-        // Long pressed item was a remote client; provide the appropriate menu.
-        final MenuInflater inflater = new MenuInflater(view.getContext());
-        inflater.inflate(R.menu.home_remote_tabs_client_contextmenu, menu);
-
-        final RemoteTabsClientContextMenuInfo info = (RemoteTabsClientContextMenuInfo) menuInfo;
-        menu.setHeaderTitle(info.client.name);
-    }
-
-    @Override
-    public boolean onContextItemSelected(MenuItem item) {
-        if (super.onContextItemSelected(item)) {
-            // HomeFragment was able to handle to selected item.
-            return true;
-        }
-
-        final ContextMenu.ContextMenuInfo menuInfo = item.getMenuInfo();
-        if (!(menuInfo instanceof RemoteTabsClientContextMenuInfo)) {
-            return false;
-        }
-
-        final RemoteTabsClientContextMenuInfo info = (RemoteTabsClientContextMenuInfo) menuInfo;
-
-        final int itemId = item.getItemId();
-        if (itemId == R.id.home_remote_tabs_hide_client) {
-            ((CombinedHistoryAdapter) mRecyclerView.getAdapter()).removeItem(info.position);
-            return true;
-        }
-
-        return false;
-    }
-
-    interface DialogBuilder<E> {
-        void createAndShowDialog(List<E> items);
-    }
-
-    protected class HiddenClientsHelper implements DialogBuilder<RemoteClient> {
-        @Override
-        public void createAndShowDialog(List<RemoteClient> clientsList) {
-                        final RemoteClientsDialogFragment dialog = RemoteClientsDialogFragment.newInstance(
-                    getResources().getString(R.string.home_remote_tabs_hidden_devices_title),
-                    getResources().getString(R.string.home_remote_tabs_unhide_selected_devices),
-                    RemoteClientsDialogFragment.ChoiceMode.MULTIPLE, new ArrayList<>(clientsList));
-            dialog.setTargetFragment(CombinedHistoryPanel.this, 0);
-            dialog.show(getActivity().getSupportFragmentManager(), "show-clients");
-        }
-
-
-    }
-
-    @Override
-    public void onClients(List<RemoteClient> clients) {
-        ((CombinedHistoryAdapter) mRecyclerView.getAdapter()).unhideClients(clients);
-    }
-
-    /**
-     * Stores information regarding the creation of the context menu for a remote client.
-     */
-    protected static class RemoteTabsClientContextMenuInfo extends HomeContextMenuInfo {
-        protected final RemoteClient client;
-
-        public RemoteTabsClientContextMenuInfo(View targetView, int position, long id, RemoteClient client) {
-            super(targetView, position, id);
-            this.client = client;
-        }
-    }
-
     protected static HomeContextMenuInfo populateHistoryInfoFromCursor(HomeContextMenuInfo info, Cursor cursor) {
         info.url = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Combined.URL));
         info.title = cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.Combined.TITLE));
         info.historyId = cursor.getInt(cursor.getColumnIndexOrThrow(BrowserContract.Combined.HISTORY_ID));
         info.itemType = HomeContextMenuInfo.RemoveItemType.HISTORY;
         final int bookmarkIdCol = cursor.getColumnIndexOrThrow(BrowserContract.Combined.BOOKMARK_ID);
         if (cursor.isNull(bookmarkIdCol)) {
             // If this is a combined cursor, we may get a history item without a
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryRecyclerView.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryRecyclerView.java
@@ -5,31 +5,29 @@
 
 package org.mozilla.gecko.home;
 
 import android.content.Context;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.View;
-import org.mozilla.gecko.db.RemoteClient;
 import org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener;
 import org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.widget.RecyclerViewClickSupport;
 
 import java.util.EnumSet;
 
 public class CombinedHistoryRecyclerView extends RecyclerView
         implements RecyclerViewClickSupport.OnItemClickListener, RecyclerViewClickSupport.OnItemLongClickListener {
 
     protected HomePager.OnUrlOpenListener mOnUrlOpenListener;
     protected OnPanelLevelChangeListener mOnPanelLevelChangeListener;
-    protected CombinedHistoryPanel.DialogBuilder<RemoteClient> mDialogBuilder;
     protected HomeContextMenuInfo mContextMenuInfo;
 
     public CombinedHistoryRecyclerView(Context context) {
         super(context);
         init(context);
     }
 
     public CombinedHistoryRecyclerView(Context context, AttributeSet attributeSet) {
@@ -55,36 +53,26 @@ public class CombinedHistoryRecyclerView
     public void setOnHistoryClickedListener(HomePager.OnUrlOpenListener listener) {
         this.mOnUrlOpenListener = listener;
     }
 
     public void setOnPanelLevelChangeListener(OnPanelLevelChangeListener listener) {
         this.mOnPanelLevelChangeListener = listener;
     }
 
-    public void setHiddenClientsDialogBuilder(CombinedHistoryPanel.DialogBuilder<RemoteClient> builder) {
-        mDialogBuilder = builder;
-    }
-
     @Override
     public void onItemClicked(RecyclerView recyclerView, int position, View v) {
         final int viewType = getAdapter().getItemViewType(position);
         final CombinedHistoryAdapter.ItemType itemType = CombinedHistoryAdapter.ItemType.viewTypeToItemType(viewType);
 
         switch(itemType) {
             case CLIENT:
                 mOnPanelLevelChangeListener.onPanelLevelChange(PanelLevel.CHILD);
                 ((CombinedHistoryAdapter) getAdapter()).showChildView(position);
                 break;
-            case HIDDEN_DEVICES:
-                if (mDialogBuilder != null) {
-                    mDialogBuilder.createAndShowDialog(((CombinedHistoryAdapter) getAdapter()).getHiddenClients());
-                }
-                break;
-
             case NAVIGATION_BACK:
                 mOnPanelLevelChangeListener.onPanelLevelChange(PanelLevel.PARENT);
                 ((CombinedHistoryAdapter) getAdapter()).exitChildView();
                 break;
             case CHILD:
             case HISTORY:
                 if (mOnUrlOpenListener != null) {
                     final TwoLinePageRow historyItem = (TwoLinePageRow) v;
--- a/mobile/android/base/java/org/mozilla/gecko/home/RemoteTabsBaseFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/RemoteTabsBaseFragment.java
@@ -137,16 +137,23 @@ public abstract class RemoteTabsBaseFrag
         }
 
         // Long pressed item was a remote client; provide the appropriate menu.
         final MenuInflater inflater = new MenuInflater(view.getContext());
         inflater.inflate(R.menu.home_remote_tabs_client_contextmenu, menu);
 
         final RemoteTabsClientContextMenuInfo info = (RemoteTabsClientContextMenuInfo) menuInfo;
         menu.setHeaderTitle(info.client.name);
+
+        // Hide unused menu items.
+        final boolean isHidden = sState.isClientHidden(info.client.guid);
+        final MenuItem item = menu.findItem(isHidden
+                ? R.id.home_remote_tabs_hide_client
+                : R.id.home_remote_tabs_show_client);
+        item.setVisible(false);
     }
 
     @Override
     public boolean onContextItemSelected(MenuItem item) {
         if (super.onContextItemSelected(item)) {
             // HomeFragment was able to handle to selected item.
             return true;
         }
@@ -160,16 +167,22 @@ public abstract class RemoteTabsBaseFrag
 
         final int itemId = item.getItemId();
         if (itemId == R.id.home_remote_tabs_hide_client) {
             sState.setClientHidden(info.client.guid, true);
             getLoaderManager().restartLoader(LOADER_ID_REMOTE_TABS, null, mCursorLoaderCallbacks);
             return true;
         }
 
+        if (itemId == R.id.home_remote_tabs_show_client) {
+            sState.setClientHidden(info.client.guid, false);
+            getLoaderManager().restartLoader(LOADER_ID_REMOTE_TABS, null, mCursorLoaderCallbacks);
+            return true;
+        }
+
         return false;
     }
 
     @Override
     public void onClients(List<RemoteClient> clients) {
         // The clients listed were hidden and have been checked by the user. We
         // interpret that as "show these clients now".
         for (RemoteClient client : clients) {
--- a/mobile/android/base/resources/drawable/action_bar_button.xml
+++ b/mobile/android/base/resources/drawable/action_bar_button.xml
@@ -14,11 +14,11 @@
 
     <item android:state_focused="true"
           android:state_pressed="false">
         <shape>
             <solid android:color="@color/highlight_focused" />
         </shape>
     </item>
 
-    <item android:drawable="@color/about_page_header_grey"/>
+    <item android:drawable="@android:color/transparent"/>
 
 </selector>
deleted file mode 100644
--- a/mobile/android/base/resources/layout/home_remote_tabs_hidden_devices.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?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/.
--->
-
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/hidden_devices"
-    style="@style/Widget.Home.ActionItem"
-    android:background="@drawable/action_bar_button"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/home_remote_tabs_hidden_footer_height"
-    android:gravity="center"
-    android:maxLength="1024"
-    android:textColor="@color/tabs_tray_icon_grey" />
--- a/mobile/android/base/resources/menu/home_remote_tabs_client_contextmenu.xml
+++ b/mobile/android/base/resources/menu/home_remote_tabs_client_contextmenu.xml
@@ -3,9 +3,12 @@
    - 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/home_remote_tabs_hide_client"
           android:title="@string/pref_panels_hide"/>
 
+    <item android:id="@+id/home_remote_tabs_show_client"
+          android:title="@string/pref_panels_show"/>
+
 </menu>