Bug 1251362 - Part 2 - Show recent tabs smart folder draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Fri, 13 May 2016 23:52:24 +0200
changeset 369296 36af4da38b2f05bcbf3f4ab5ca69243d8fb0f16f
parent 369295 54f7de4bbd1e19afdda3334fc1163789d1ba36dc
child 369297 02cbce575e979677a7c4c920e9312c6f710c4d7c
push id18831
push usermozilla@buttercookie.de
push dateSat, 21 May 2016 18:52:21 +0000
bugs1251362
milestone49.0a1
Bug 1251362 - Part 2 - Show recent tabs smart folder MozReview-Commit-ID: IAL0yDrc2Ld
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/RecentTabsAdapter.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/moz.build
mobile/android/base/resources/layout/home_combined_history_panel.xml
mobile/android/base/strings.xml.in
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryAdapter.java
@@ -12,17 +12,18 @@ import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract;
 
 public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistoryItem> implements CombinedHistoryRecyclerView.AdapterContextMenuBuilder {
-    private static final int SYNCED_DEVICES_SMARTFOLDER_INDEX = 0;
+    private static final int RECENT_TABS_SMARTFOLDER_INDEX = 0;
+    private static final int SYNCED_DEVICES_SMARTFOLDER_INDEX = 1;
 
     // Array for the time ranges in milliseconds covered by each section.
     static final HistorySectionsHelper.SectionDateRange[] sectionDateRangeArray = new HistorySectionsHelper.SectionDateRange[SectionHeader.values().length];
 
     // Semantic names for the time covered by each section
     public enum SectionHeader {
         TODAY,
         YESTERDAY,
@@ -34,16 +35,17 @@ public class CombinedHistoryAdapter exte
         FOUR_MONTHS_AGO,
         FIVE_MONTHS_AGO,
         OLDER_THAN_SIX_MONTHS
     }
 
     private Cursor historyCursor;
     private DevicesUpdateHandler devicesUpdateHandler;
     private int deviceCount = 0;
+    private int recentTabsCount = 0;
 
     // We use a sparse array to store each section header's position in the panel [more cheaply than a HashMap].
     private final SparseArray<SectionHeader> sectionHeaders;
 
     public CombinedHistoryAdapter(Resources resources) {
         super();
         sectionHeaders = new SparseArray<>();
         HistorySectionsHelper.updateRecentSectionOffset(resources, sectionDateRangeArray);
@@ -60,31 +62,32 @@ public class CombinedHistoryAdapter exte
     }
 
     public DevicesUpdateHandler getDeviceUpdateHandler() {
         if (devicesUpdateHandler == null) {
             devicesUpdateHandler = new DevicesUpdateHandler() {
                 @Override
                 public void onDeviceCountUpdated(int count) {
                     deviceCount = count;
-                    notifyItemChanged(0);
+                    notifyItemChanged(SYNCED_DEVICES_SMARTFOLDER_INDEX);
                 }
             };
         }
         return devicesUpdateHandler;
     }
 
     @Override
     public CombinedHistoryItem onCreateViewHolder(ViewGroup viewGroup, int viewType) {
         final LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
         final View view;
 
         final CombinedHistoryItem.ItemType itemType = CombinedHistoryItem.ItemType.viewTypeToItemType(viewType);
 
         switch (itemType) {
+            case RECENT_TABS:
             case SYNCED_DEVICES:
                 view = inflater.inflate(R.layout.home_smartfolder, viewGroup, false);
                 return new CombinedHistoryItem.SmartFolder(view);
 
             case SECTION_HEADER:
                 view = inflater.inflate(R.layout.home_header_row, viewGroup, false);
                 return new CombinedHistoryItem.BasicItem(view);
 
@@ -97,16 +100,20 @@ public class CombinedHistoryAdapter exte
     }
 
     @Override
     public void onBindViewHolder(CombinedHistoryItem viewHolder, int position) {
         final CombinedHistoryItem.ItemType itemType = getItemTypeForPosition(position);
         final int localPosition = transformAdapterPositionForDataStructure(itemType, position);
 
         switch (itemType) {
+            case RECENT_TABS:
+                ((CombinedHistoryItem.SmartFolder) viewHolder).bind(R.drawable.icon_most_recent_empty, R.string.home_closed_tabs_title, R.string.home_closed_tabs_one, R.string.home_closed_tabs_number, recentTabsCount);
+                break;
+
             case SYNCED_DEVICES:
                 ((CombinedHistoryItem.SmartFolder) viewHolder).bind(R.drawable.cloud, R.string.home_synced_devices_smartfolder, R.string.home_synced_devices_one, R.string.home_synced_devices_number, deviceCount);
                 break;
 
             case SECTION_HEADER:
                 ((TextView) viewHolder.itemView).setText(getSectionHeaderTitle(sectionHeaders.get(localPosition)));
                 break;
 
@@ -135,16 +142,19 @@ public class CombinedHistoryAdapter exte
         } else if (type == CombinedHistoryItem.ItemType.HISTORY) {
             return position - getHeadersBefore(position) - CombinedHistoryPanel.NUM_SMART_FOLDERS;
         } else {
             return position;
         }
     }
 
     private CombinedHistoryItem.ItemType getItemTypeForPosition(int position) {
+        if (position == RECENT_TABS_SMARTFOLDER_INDEX) {
+            return CombinedHistoryItem.ItemType.RECENT_TABS;
+        }
         if (position == SYNCED_DEVICES_SMARTFOLDER_INDEX) {
             return CombinedHistoryItem.ItemType.SYNCED_DEVICES;
         }
         final int sectionPosition = transformAdapterPositionForDataStructure(CombinedHistoryItem.ItemType.SECTION_HEADER, position);
         if (sectionHeaders.get(sectionPosition) != null) {
             return CombinedHistoryItem.ItemType.SECTION_HEADER;
         }
         return CombinedHistoryItem.ItemType.HISTORY;
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryItem.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryItem.java
@@ -19,17 +19,18 @@ import org.mozilla.gecko.db.RemoteTab;
 public abstract class CombinedHistoryItem extends RecyclerView.ViewHolder {
     private static final String LOGTAG = "CombinedHistoryItem";
 
     public CombinedHistoryItem(View view) {
         super(view);
     }
 
     public enum ItemType {
-        CLIENT, HIDDEN_DEVICES, SECTION_HEADER, HISTORY, NAVIGATION_BACK, CHILD, SYNCED_DEVICES;
+        CLIENT, HIDDEN_DEVICES, SECTION_HEADER, HISTORY, NAVIGATION_BACK, CHILD, SYNCED_DEVICES,
+        RECENT_TABS, CLOSED_TAB;
 
         public static ItemType viewTypeToItemType(int viewType) {
             if (viewType >= ItemType.values().length) {
                 Log.e(LOGTAG, "No corresponding ItemType!");
             }
             return ItemType.values()[viewType];
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
@@ -63,55 +63,58 @@ public class CombinedHistoryPanel extend
     private final int LOADER_ID_HISTORY = 0;
     private final int LOADER_ID_REMOTE = 1;
 
     // String placeholders to mark formatting.
     private final static String FORMAT_S1 = "%1$s";
     private final static String FORMAT_S2 = "%2$s";
 
     // Number of smart folders for determining practical empty state.
-    public static final int NUM_SMART_FOLDERS = 1;
+    public static final int NUM_SMART_FOLDERS = 2;
 
     private CombinedHistoryRecyclerView mRecyclerView;
     private CombinedHistoryAdapter mHistoryAdapter;
     private ClientsAdapter mClientsAdapter;
+    private RecentTabsAdapter mRecentTabsAdapter;
     private CursorLoaderCallbacks mCursorLoaderCallbacks;
 
     private OnPanelLevelChangeListener.PanelLevel mPanelLevel;
     private Button mPanelFooterButton;
 
     // Child refresh layout view.
     protected SwipeRefreshLayout mRefreshLayout;
 
     // Sync listener that stops refreshing when a sync is completed.
     protected RemoteTabsSyncListener mSyncStatusListener;
 
     // Reference to the View to display when there are no results.
     private View mHistoryEmptyView;
     private View mClientsEmptyView;
+    private View mRecentTabsEmptyView;
 
     public interface OnPanelLevelChangeListener {
         enum PanelLevel {
-        PARENT, CHILD
+        PARENT, CHILD_SYNC, CHILD_RECENT_TABS
     }
 
         /**
          * Propagates level changes.
          * @param level
          * @return true if level changed, false otherwise.
          */
         boolean changeLevel(PanelLevel level);
     }
 
     @Override
     public void onCreate(Bundle savedInstance) {
         super.onCreate(savedInstance);
 
         mHistoryAdapter = new CombinedHistoryAdapter(getResources());
         mClientsAdapter = new ClientsAdapter(getContext());
+        mRecentTabsAdapter = new RecentTabsAdapter();
 
         mSyncStatusListener = new RemoteTabsSyncListener();
         FirefoxAccounts.addSyncStatusListener(mSyncStatusListener);
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         return inflater.inflate(R.layout.home_combined_history_panel, container, false);
@@ -124,28 +127,30 @@ public class CombinedHistoryPanel extend
         mRecyclerView = (CombinedHistoryRecyclerView) view.findViewById(R.id.combined_recycler_view);
         setUpRecyclerView();
 
         mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh_layout);
         setUpRefreshLayout();
 
         mClientsEmptyView = view.findViewById(R.id.home_clients_empty_view);
         mHistoryEmptyView = view.findViewById(R.id.home_history_empty_view);
+        mRecentTabsEmptyView = view.findViewById(R.id.home_recent_tabs_empty_view);
         setUpEmptyViews();
 
         mPanelFooterButton = (Button) view.findViewById(R.id.clear_history_button);
         mPanelFooterButton.setOnClickListener(new OnFooterButtonClickListener());
     }
 
     private void setUpRecyclerView() {
         if (mPanelLevel == null) {
             mPanelLevel = OnPanelLevelChangeListener.PanelLevel.PARENT;
         }
 
-        mRecyclerView.setAdapter(mPanelLevel == OnPanelLevelChangeListener.PanelLevel.PARENT ? mHistoryAdapter : mClientsAdapter);
+        mRecyclerView.setAdapter(mPanelLevel == OnPanelLevelChangeListener.PanelLevel.PARENT ? mHistoryAdapter :
+                mPanelLevel == OnPanelLevelChangeListener.PanelLevel.CHILD_SYNC ? mClientsAdapter : mRecentTabsAdapter);
 
         final RecyclerView.ItemAnimator animator = new DefaultItemAnimator();
         animator.setAddDuration(100);
         animator.setChangeDuration(100);
         animator.setMoveDuration(100);
         animator.setRemoveDuration(100);
         mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
         mRecyclerView.setItemAnimator(animator);
@@ -169,47 +174,54 @@ public class CombinedHistoryPanel extend
 
     private void setUpRefreshLayout() {
         mRefreshLayout.setColorSchemeResources(R.color.fennec_ui_orange, R.color.action_orange);
         mRefreshLayout.setOnRefreshListener(new RemoteTabsRefreshListener());
     }
 
     private void setUpEmptyViews() {
         // Set up history empty view.
-        final ImageView emptyIcon = (ImageView) mHistoryEmptyView.findViewById(R.id.home_empty_image);
-        emptyIcon.setVisibility(View.GONE);
+        final ImageView historyIcon = (ImageView) mHistoryEmptyView.findViewById(R.id.home_empty_image);
+        historyIcon.setVisibility(View.GONE);
 
-        final TextView emptyText = (TextView) mHistoryEmptyView.findViewById(R.id.home_empty_text);
-        emptyText.setText(R.string.home_most_recent_empty);
+        final TextView historyText = (TextView) mHistoryEmptyView.findViewById(R.id.home_empty_text);
+        historyText.setText(R.string.home_most_recent_empty);
 
-        final TextView emptyHint = (TextView) mHistoryEmptyView.findViewById(R.id.home_empty_hint);
+        final TextView historyHint = (TextView) mHistoryEmptyView.findViewById(R.id.home_empty_hint);
 
         if (!Restrictions.isAllowed(getActivity(), Restrictable.PRIVATE_BROWSING)) {
-            emptyHint.setVisibility(View.GONE);
+            historyHint.setVisibility(View.GONE);
         } else {
             final String hintText = getResources().getString(R.string.home_most_recent_emptyhint);
             final SpannableStringBuilder hintBuilder = formatHintText(hintText);
             if (hintBuilder != null) {
-                emptyHint.setText(hintBuilder);
-                emptyHint.setMovementMethod(LinkMovementMethod.getInstance());
-                emptyHint.setVisibility(View.VISIBLE);
+                historyHint.setText(hintBuilder);
+                historyHint.setMovementMethod(LinkMovementMethod.getInstance());
+                historyHint.setVisibility(View.VISIBLE);
             }
         }
 
         // Set up Clients empty view.
         final Button syncSetupButton = (Button) mClientsEmptyView.findViewById(R.id.sync_setup_button);
         syncSetupButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "history_syncsetup");
                 // This Activity will redirect to the correct Activity as needed.
                 final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
                 startActivity(intent);
             }
         });
+
+        // Set up Recent Tabs empty view.
+        final ImageView recentTabsIcon = (ImageView) mRecentTabsEmptyView.findViewById(R.id.home_empty_image);
+        recentTabsIcon.setImageResource(R.drawable.icon_remote_tabs_empty);
+
+        final TextView recentTabsText = (TextView) mRecentTabsEmptyView.findViewById(R.id.home_empty_text);
+        recentTabsText.setText(R.string.home_last_tabs_empty);
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
         mCursorLoaderCallbacks = new CursorLoaderCallbacks();
     }
 
@@ -304,19 +316,22 @@ public class CombinedHistoryPanel extend
                 return false;
             }
 
             mPanelLevel = level;
             switch (level) {
                 case PARENT:
                     mRecyclerView.swapAdapter(mHistoryAdapter, true);
                     break;
-                case CHILD:
+                case CHILD_SYNC:
                     mRecyclerView.swapAdapter(mClientsAdapter, true);
                     break;
+                case CHILD_RECENT_TABS:
+                    mRecyclerView.swapAdapter(mRecentTabsAdapter, true);
+                    break;
             }
 
             updateEmptyView();
             updateButtonFromLevel();
             return true;
         }
     }
 
@@ -325,17 +340,18 @@ public class CombinedHistoryPanel extend
             case PARENT:
                 final boolean historyRestricted = !Restrictions.isAllowed(getActivity(), Restrictable.CLEAR_HISTORY);
                 if (historyRestricted || mHistoryAdapter.getItemCount() == NUM_SMART_FOLDERS) {
                     mPanelFooterButton.setVisibility(View.GONE);
                 } else {
                     mPanelFooterButton.setVisibility(View.VISIBLE);
                 }
                 break;
-            case CHILD:
+            case CHILD_SYNC:
+            case CHILD_RECENT_TABS:
                 mPanelFooterButton.setVisibility(View.GONE);
                 break;
         }
     }
 
     private class OnFooterButtonClickListener implements View.OnClickListener {
         @Override
         public void onClick(View view) {
@@ -369,31 +385,37 @@ public class CombinedHistoryPanel extend
 
             dialogBuilder.show();
         }
     }
 
     private void updateEmptyView() {
         boolean showEmptyHistoryView = false;
         boolean showEmptyClientsView = false;
+        boolean showEmptyRecentTabsView = false;
         switch (mPanelLevel) {
             case PARENT:
                 showEmptyHistoryView = mHistoryAdapter.getItemCount() == NUM_SMART_FOLDERS;
                 break;
 
-            case CHILD:
+            case CHILD_SYNC:
                 showEmptyClientsView = mClientsAdapter.getItemCount() == 1;
                 break;
+
+            case CHILD_RECENT_TABS:
+                showEmptyRecentTabsView = mRecentTabsAdapter.getItemCount() == 1;
+                break;
         }
 
-        final boolean showEmptyView = showEmptyClientsView || showEmptyHistoryView;
+        final boolean showEmptyView = showEmptyClientsView || showEmptyHistoryView || showEmptyRecentTabsView;
         mRecyclerView.setOverScrollMode(showEmptyView ? View.OVER_SCROLL_NEVER : View.OVER_SCROLL_IF_CONTENT_SCROLLS);
 
         mClientsEmptyView.setVisibility(showEmptyClientsView ? View.VISIBLE : View.GONE);
         mHistoryEmptyView.setVisibility(showEmptyHistoryView ? View.VISIBLE : View.GONE);
+        mRecentTabsEmptyView.setVisibility(showEmptyRecentTabsView ? View.VISIBLE : View.GONE);
     }
 
     /**
      * Make Span that is clickable, and underlined
      * between the string markers <code>FORMAT_S1</code> and
      * <code>FORMAT_S2</code>.
      *
      * @param text String to format
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryRecyclerView.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryRecyclerView.java
@@ -14,17 +14,18 @@ import android.view.View;
 import org.mozilla.gecko.db.RemoteClient;
 import org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.widget.RecyclerViewClickSupport;
 
 import java.util.EnumSet;
 
-import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.CHILD;
+import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.CHILD_RECENT_TABS;
+import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.CHILD_SYNC;
 import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.PARENT;
 
 public class CombinedHistoryRecyclerView extends RecyclerView
         implements RecyclerViewClickSupport.OnItemClickListener, RecyclerViewClickSupport.OnItemLongClickListener {
     public static String LOGTAG = "CombinedHistoryRecycView";
 
     protected interface AdapterContextMenuBuilder {
         HomeContextMenuInfo makeContextMenuInfoFromPosition(View view, int position);
@@ -86,18 +87,22 @@ public class CombinedHistoryRecyclerView
     }
 
     @Override
     public void onItemClicked(RecyclerView recyclerView, int position, View v) {
         final int viewType = getAdapter().getItemViewType(position);
         final CombinedHistoryItem.ItemType itemType = CombinedHistoryItem.ItemType.viewTypeToItemType(viewType);
 
         switch (itemType) {
+            case RECENT_TABS:
+                mOnPanelLevelChangeListener.changeLevel(CHILD_RECENT_TABS);
+                break;
+
             case SYNCED_DEVICES:
-                mOnPanelLevelChangeListener.changeLevel(CHILD);
+                mOnPanelLevelChangeListener.changeLevel(CHILD_SYNC);
                 break;
 
             case CLIENT:
                 ((ClientsAdapter) getAdapter()).toggleClient(position);
                 break;
 
             case HIDDEN_DEVICES:
                 if (mDialogBuilder != null) {
@@ -112,16 +117,19 @@ public class CombinedHistoryRecyclerView
             case CHILD:
             case HISTORY:
                 if (mOnUrlOpenListener != null) {
                     final TwoLinePageRow historyItem = (TwoLinePageRow) v;
                     Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM, "history");
                     mOnUrlOpenListener.onUrlOpen(historyItem.getUrl(), EnumSet.of(HomePager.OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
                 }
                 break;
+
+            case CLOSED_TAB:
+                break;
         }
     }
 
     @Override
     public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
         mContextMenuInfo = ((AdapterContextMenuBuilder) getAdapter()).makeContextMenuInfoFromPosition(v, position);
         return showContextMenuForChild(this);
     }
new file mode 100755
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/home/RecentTabsAdapter.java
@@ -0,0 +1,73 @@
+/* -*- 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 android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.mozilla.gecko.R;
+
+import static org.mozilla.gecko.home.CombinedHistoryItem.ItemType.*;
+
+public class RecentTabsAdapter extends RecyclerView.Adapter<CombinedHistoryItem> implements CombinedHistoryRecyclerView.AdapterContextMenuBuilder {
+    private static final String LOGTAG = "GeckoRecentTabsAdapter";
+
+    @Override
+    public CombinedHistoryItem onCreateViewHolder(ViewGroup parent, int viewType) {
+        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+        final View view;
+
+        final CombinedHistoryItem.ItemType itemType = CombinedHistoryItem.ItemType.viewTypeToItemType(viewType);
+
+        switch (itemType) {
+            case NAVIGATION_BACK:
+                view = inflater.inflate(R.layout.home_combined_back_item, parent, false);
+                return new CombinedHistoryItem.HistoryItem(view);
+
+            case SECTION_HEADER:
+                view = inflater.inflate(R.layout.home_header_row, parent, false);
+                return new CombinedHistoryItem.BasicItem(view);
+
+            case CLOSED_TAB:
+                view = inflater.inflate(R.layout.home_item_row, parent, false);
+                return new CombinedHistoryItem.HistoryItem(view);
+        }
+        return null;
+    }
+
+    @Override
+    public void onBindViewHolder(CombinedHistoryItem holder, final int position) {
+        final CombinedHistoryItem.ItemType itemType = getItemTypeForPosition(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return 1;
+    }
+
+    private CombinedHistoryItem.ItemType getItemTypeForPosition(int position) {
+        if (position == 0) {
+            return NAVIGATION_BACK;
+        }
+
+        return CLOSED_TAB;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return CombinedHistoryItem.ItemType.itemTypeToViewType(getItemTypeForPosition(position));
+    }
+
+    @Override
+    public HomeContextMenuInfo makeContextMenuInfoFromPosition(View view, int position) {
+        final CombinedHistoryItem.ItemType itemType = getItemTypeForPosition(position);
+        HomeContextMenuInfo info;
+
+        return null;
+    }
+}
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -567,16 +567,19 @@ size. -->
 <!ENTITY home_history_back_to2 "Back to full History">
 <!ENTITY home_clear_history_button "Clear browsing history">
 <!ENTITY home_clear_history_confirm "Are you sure you want to clear your history?">
 <!ENTITY home_bookmarks_empty "Bookmarks you save show up here.">
 <!ENTITY home_closed_tabs_title "Recently closed tabs">
 <!ENTITY home_last_tabs_title "Tabs from last time">
 <!ENTITY home_last_tabs_empty "Your recent tabs show up here.">
 <!ENTITY home_open_all "Open all">
+<!ENTITY home_closed_tabs_number "&formatD; tabs">
+<!-- Localization note (home_closed_tabs_one): This is the singular version of home_closed_tabs_number, referring to the number of recently closed tabs available. -->
+<!ENTITY home_closed_tabs_one "1 tab">
 <!ENTITY home_most_recent_empty "Websites you visited most recently show up here.">
 <!-- Localization note (home_most_recent_emptyhint2): "Psst" is a sound that might be used to attract someone's attention unobtrusively, and intended to hint at Private Browsing to the user.
      The placeholders &formatS1; and &formatS2; are used to mark the location of text underlining. -->
 <!ENTITY home_most_recent_emptyhint2 "Psst: using a &formatS1;New Private Tab&formatS2; won\'t save your history.">
 
 <!-- Localization note (home_default_empty): This string is used as the default text when there
      is no data to show in an about:home panel that was created by an add-on. -->
 <!ENTITY home_default_empty "No content could be found for this panel.">
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -425,16 +425,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'home/PanelLayout.java',
     'home/PanelListView.java',
     'home/PanelRecyclerView.java',
     'home/PanelRecyclerViewAdapter.java',
     'home/PanelRefreshLayout.java',
     'home/PanelViewAdapter.java',
     'home/PanelViewItemHandler.java',
     'home/PinSiteDialog.java',
+    'home/RecentTabsAdapter.java',
     'home/RecentTabsPanel.java',
     'home/RemoteTabsExpandableListState.java',
     'home/SearchEngine.java',
     'home/SearchEngineAdapter.java',
     'home/SearchEngineBar.java',
     'home/SearchEngineRow.java',
     'home/SearchLoader.java',
     'home/SimpleCursorLoader.java',
--- a/mobile/android/base/resources/layout/home_combined_history_panel.xml
+++ b/mobile/android/base/resources/layout/home_combined_history_panel.xml
@@ -31,16 +31,23 @@
 
     <include android:id="@+id/home_clients_empty_view"
               layout="@layout/history_sync_setup"
               android:layout_width="match_parent"
               android:layout_height="0dp"
               android:layout_weight="3"
               android:visibility="gone"/>
 
+    <include android:id="@+id/home_recent_tabs_empty_view"
+              layout="@layout/home_empty_panel"
+              android:layout_width="match_parent"
+              android:layout_height="0dp"
+              android:layout_weight="3"
+              android:visibility="gone"/>
+
     <Button android:id="@+id/clear_history_button"
             style="@style/Widget.Home.ActionButton"
             android:text="@string/home_clear_history_button"
             android:layout_width="match_parent"
             android:layout_height="48dp"
             android:visibility="gone" />
 
 </LinearLayout>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -455,16 +455,18 @@
   <string name="home_history_back_to">&home_history_back_to2;</string>
   <string name="home_clear_history_button">&home_clear_history_button;</string>
   <string name="home_clear_history_confirm">&home_clear_history_confirm;</string>
   <string name="home_bookmarks_empty">&home_bookmarks_empty;</string>
   <string name="home_closed_tabs_title">&home_closed_tabs_title;</string>
   <string name="home_last_tabs_title">&home_last_tabs_title;</string>
   <string name="home_last_tabs_empty">&home_last_tabs_empty;</string>
   <string name="home_open_all">&home_open_all;</string>
+  <string name="home_closed_tabs_number">&home_closed_tabs_number;</string>
+  <string name="home_closed_tabs_one">&home_closed_tabs_one;</string>
   <string name="home_most_recent_empty">&home_most_recent_empty;</string>
   <string name="home_most_recent_emptyhint">&home_most_recent_emptyhint2;</string>
   <string name="home_default_empty">&home_default_empty;</string>
   <string name="home_move_back_to_filter">&home_move_back_to_filter;</string>
   <string name="home_remote_tabs_many_hidden_devices">&home_remote_tabs_many_hidden_devices;</string>
   <string name="home_remote_tabs_hidden_devices_title">&home_remote_tabs_hidden_devices_title;</string>
   <string name="home_remote_tabs_unhide_selected_devices">&home_remote_tabs_unhide_selected_devices;</string>
   <string name="pin_site_dialog_hint">&pin_site_dialog_hint;</string>