Bug 1251362 - Part 4 - Add a Recent Tabs folder to the Combined History panel. r=liuche draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Fri, 13 May 2016 23:52:24 +0200
changeset 376072 a1806a5a1e6714707dddddeae87eca0ecd28993c
parent 376071 5ce5eae4d5b7f7118edd53473c9313de18140e03
child 376073 c4d5afcaa45f2d851a4c1d9c462b204c687b0221
push id20500
push usermozilla@buttercookie.de
push dateTue, 07 Jun 2016 11:41:20 +0000
reviewersliuche
bugs1251362
milestone50.0a1
Bug 1251362 - Part 4 - Add a Recent Tabs folder to the Combined History panel. r=liuche This folder can be opened and closed to get back to the history view, however it doesn't contain any actual content (closed tabs) yet. Its empty view is based on the original empty view of the Recent Tabs panel. For displaying the recently closed tabs count within the smart folder similarly to how we display the number of synced devices, two new strings need to be added. 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_recent, R.string.home_closed_tabs_title2, 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
@@ -64,55 +64,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 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);
@@ -125,28 +128,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 = PanelLevel.PARENT;
         }
 
-        mRecyclerView.setAdapter(mPanelLevel == PanelLevel.PARENT ? mHistoryAdapter : mClientsAdapter);
+        mRecyclerView.setAdapter(mPanelLevel == PanelLevel.PARENT ? mHistoryAdapter :
+                mPanelLevel == 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);
@@ -170,47 +175,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();
     }
 
@@ -305,19 +317,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;
         }
     }
 
@@ -326,17 +341,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) {
@@ -370,31 +386,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 ItemType.NAVIGATION_BACK;
+        }
+
+        return ItemType.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);
+        final HomeContextMenuInfo info;
+
+        return null;
+    }
+}
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -566,19 +566,23 @@ size. -->
 <!ENTITY home_synced_devices_number "&formatD; devices">
 <!-- Localization note (home_synced_devices_one_device): This is the singular version of home_synced_devices_number, referring to the number of devices a user has synced. -->
 <!ENTITY home_synced_devices_one "1 device">
 <!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_closed_tabs_title2 "Recently closed">
 <!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
@@ -426,16 +426,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
@@ -448,19 +448,22 @@
   <string name="home_synced_devices_smartfolder">&home_synced_devices_smartfolder;</string>
   <string name="home_synced_devices_number">&home_synced_devices_number;</string>
   <string name="home_synced_devices_one">&home_synced_devices_one;</string>
   <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_closed_tabs_title2">&home_closed_tabs_title2;</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>