Bug 1380808 - Add Pocket to new tab with placeholders. r?mcomella draft
authorChenxia Liu <liuche@mozilla.com>
Thu, 17 Aug 2017 19:05:44 -0700
changeset 652855 3cf20d3d2b80e840c39c9e3bec62de007ba5551c
parent 652854 d8a6a25b480fed2ac641e7eb68015457fdf6e178
child 652856 4e360472c68813018ca1a6674a77f8963a50bbe9
push id76170
push usercliu@mozilla.com
push dateFri, 25 Aug 2017 08:33:06 +0000
reviewersmcomella
bugs1380808
milestone57.0a1
Bug 1380808 - Add Pocket to new tab with placeholders. r?mcomella MozReview-Commit-ID: 7yqmBF1qlLR
mobile/android/app/src/main/res/drawable/ic_as_trending.xml
mobile/android/app/src/main/res/layout/activity_stream_main_highlightstitle.xml
mobile/android/base/java/org/mozilla/gecko/activitystream/ActivityStreamTelemetry.java
mobile/android/base/java/org/mozilla/gecko/activitystream/Utils.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/HighlightsDividerItemDecoration.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamHighlightItemRowContextMenuListener.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamRecyclerAdapter.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/menu/ActivityStreamContextMenu.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/model/Highlight.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/model/TopStory.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/model/WebpageRowModel.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightItemRow.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightsTitleRow.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/StreamTitleRow.java
mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/WebpageItemRow.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/moz.build
mobile/android/base/strings.xml.in
mobile/android/branding/beta/locales/en-US/brand.dtd
mobile/android/branding/nightly-old-id/locales/en-US/brand.dtd
mobile/android/branding/nightly/locales/en-US/brand.dtd
mobile/android/branding/official/locales/en-US/brand.dtd
mobile/android/branding/unofficial/locales/en-US/brand.dtd
copy from mobile/android/app/src/main/res/drawable/ic_as_bookmarked.xml
copy to mobile/android/app/src/main/res/drawable/ic_as_trending.xml
--- a/mobile/android/app/src/main/res/drawable/ic_as_bookmarked.xml
+++ b/mobile/android/app/src/main/res/drawable/ic_as_trending.xml
@@ -1,9 +1,9 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="24dp"
         android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportWidth="12.0"
+        android:viewportHeight="12.0">
     <path
         android:fillColor="@color/activity_stream_icon"
-        android:pathData="M12.01,1a1.34,1.34 0,0 0,-1.085 0.89l-2.747,5.67 -5.877,0.99c-1.345,0.22 -1.679,1.19 -0.744,2.15l4.16,4.49 -0.969,6.31c-0.147,0.94 0.242,1.5 0.925,1.5a1.986,1.986 0,0 0,0.891 -0.25l5.457,-2.93 5.521,2.93a1.993,1.993 0,0 0,0.892 0.25c0.683,0 1.07,-0.56 0.926,-1.5l-0.966,-6.31 4.111,-4.49c0.936,-0.96 0.6,-1.93 -0.744,-2.15l-5.789,-0.99 -2.877,-5.67a1.339,1.339 0,0 0,-1.085 -0.89h0Z"/>
+        android:pathData="M4.97.151l-2.819,6.5A.25.25,0,0,0,2.381,7H4.029a.25.25,0,0,1,.225.359L2,12,9.4,5.437A.25.25,0,0,0,9.234,5H6.791a.25.25,0,0,1-.19-.412L10.15.412A.25.25,0,0,0,9.959,0H5.2A.25.25,0,0,0,4.97.151Z"/>
 </vector>
--- a/mobile/android/app/src/main/res/layout/activity_stream_main_highlightstitle.xml
+++ b/mobile/android/app/src/main/res/layout/activity_stream_main_highlightstitle.xml
@@ -5,14 +5,13 @@
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/title_highlights"
     android:layout_marginLeft="@dimen/activity_stream_base_margin"
     android:layout_marginStart="@dimen/activity_stream_base_margin"
     android:layout_marginTop="@dimen/activity_stream_base_margin"
     android:layout_marginBottom="@dimen/activity_stream_base_margin"
     android:layout_marginRight="@dimen/activity_stream_base_margin"
     android:layout_marginEnd="@dimen/activity_stream_base_margin"
-    android:text="@string/activity_stream_highlights"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:textStyle="bold"
     android:textSize="16sp"
     android:textColor="#FF858585" />
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/ActivityStreamTelemetry.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/ActivityStreamTelemetry.java
@@ -29,16 +29,17 @@ public class ActivityStreamTelemetry {
         public final static String ACTION_POSITION = "action_position";
         public final static String COUNT = "count";
         public final static String PAGE_NUMBER = "page_number";
         public final static String INTERACTION = "interaction";
 
         // Values
         public final static String TYPE_TOPSITES = "topsites";
         public final static String TYPE_HIGHLIGHTS = "highlights";
+        public final static String TYPE_POCKET = "pocket";
         public final static String SUBTYPE_PINNED = "pinned";
         public final static String SUBTYPE_SUGGESTED = "suggested";
         public final static String SUBTYPE_TOP = "top";
         public final static String SUBTYPE_VISITED = "visited";
         public final static String SUBTYPE_BOOKMARKED = "bookmarked";
         public final static String ITEM_SHARE = "share";
         public final static String ITEM_ADD_BOOKMARK = "add_bookmark";
         public final static String ITEM_REMOVE_BOOKMARK = "remove_bookmark";
@@ -138,16 +139,19 @@ public class ActivityStreamTelemetry {
             public Builder forHighlightSource(Utils.HighlightSource source) {
                 switch (source) {
                     case VISITED:
                         this.set(Contract.SOURCE_SUBTYPE, Contract.SUBTYPE_VISITED);
                         break;
                     case BOOKMARKED:
                         this.set(Contract.SOURCE_SUBTYPE, Contract.SUBTYPE_BOOKMARKED);
                         break;
+                    case POCKET:
+                        this.set(Contract.SOURCE_TYPE, Contract.TYPE_POCKET);
+                        break;
                     default:
                         throw new IllegalStateException("Unknown highlight source: " + source);
                 }
                 return this;
             }
 
             public Builder forTopSite(final TopSite topSite) {
                 this.set(
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/Utils.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/Utils.java
@@ -9,17 +9,18 @@ import android.database.Cursor;
 import org.mozilla.gecko.activitystream.ranking.HighlightCandidateCursorIndices;
 
 /**
  * Various util methods and constants that are shared by different parts of Activity Stream.
  */
 public class Utils {
     public enum HighlightSource {
         VISITED,
-        BOOKMARKED
+        BOOKMARKED,
+        POCKET
     }
 
     public static HighlightSource highlightSource(final Cursor cursor, final HighlightCandidateCursorIndices cursorIndices) {
         if (-1 != cursor.getLong(cursorIndices.bookmarkIDColumnIndex)) {
             return HighlightSource.BOOKMARKED;
         }
 
         if (-1 != cursor.getLong(cursorIndices.historyIDColumnIndex)) {
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/HighlightsDividerItemDecoration.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/HighlightsDividerItemDecoration.java
@@ -12,17 +12,17 @@ import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
 
 /**
  * ItemDecoration implementation that draws horizontal divider line between highlight items.
  */
 /* package */ class HighlightsDividerItemDecoration extends RecyclerView.ItemDecoration {
-    // We do not want to draw a divider for the first items: Top sites panel and highlights title.
+    // We do not want to draw a divider for the Top Sites panel and the Welcome panel.
     private static final int START_DRAWING_AT_POSITION = 2;
 
     private static final int[] ATTRS = new int[]{
             android.R.attr.listDivider
     };
     private Drawable divider;
 
     /* package */ HighlightsDividerItemDecoration(Context context) {
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamHighlightItemRowContextMenuListener.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamHighlightItemRowContextMenuListener.java
@@ -1,18 +1,18 @@
 /* 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.activitystream.homepanel;
 
 import android.support.annotation.NonNull;
-import org.mozilla.gecko.activitystream.homepanel.stream.HighlightItemRow;
+import org.mozilla.gecko.activitystream.homepanel.stream.WebpageItemRow;
 
 /**
  * Provides a method to open the context menu for a highlight item.
  *
  * I tried declaring this inside StreamRecyclerAdapter but I got cyclical inheritance warnings
  * (I don't understand why) so it's here instead.
  */
 public interface StreamHighlightItemRowContextMenuListener {
-    void openContextMenu(HighlightItemRow highlightItem, int position, @NonNull final String interactionExtra);
+    void openContextMenu(WebpageItemRow highlightItem, int position, @NonNull final String interactionExtra);
 }
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamRecyclerAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/StreamRecyclerAdapter.java
@@ -8,61 +8,68 @@ package org.mozilla.gecko.activitystream
 import android.database.Cursor;
 import android.support.annotation.NonNull;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.activitystream.ActivityStreamTelemetry;
 import org.mozilla.gecko.activitystream.homepanel.menu.ActivityStreamContextMenu;
 import org.mozilla.gecko.activitystream.homepanel.model.RowModel;
+import org.mozilla.gecko.activitystream.homepanel.model.WebpageRowModel;
 import org.mozilla.gecko.activitystream.homepanel.stream.TopPanelRow;
+import org.mozilla.gecko.activitystream.homepanel.model.TopStory;
 import org.mozilla.gecko.home.HomePager;
 import org.mozilla.gecko.activitystream.homepanel.model.Highlight;
-import org.mozilla.gecko.activitystream.homepanel.stream.HighlightItemRow;
-import org.mozilla.gecko.activitystream.homepanel.stream.HighlightsTitleRow;
+import org.mozilla.gecko.activitystream.homepanel.stream.WebpageItemRow;
+import org.mozilla.gecko.activitystream.homepanel.stream.StreamTitleRow;
 import org.mozilla.gecko.activitystream.homepanel.stream.StreamViewHolder;
 import org.mozilla.gecko.activitystream.homepanel.stream.WelcomePanelRow;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.widget.RecyclerViewClickSupport;
 
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.LinkedList;
 import java.util.List;
 
 /**
  * The adapter for the Activity Stream panel.
  *
  * Every item is in a single adapter: Top Sites, Welcome panel, Highlights.
  */
 public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder> implements RecyclerViewClickSupport.OnItemClickListener,
         RecyclerViewClickSupport.OnItemLongClickListener, StreamHighlightItemRowContextMenuListener {
 
     private static final String LOGTAG = StringUtils.safeSubstring("Gecko" + StreamRecyclerAdapter.class.getSimpleName(), 0, 23);
 
     private Cursor topSitesCursor;
     private List<RowModel> recyclerViewModel; // List of item types backing this RecyclerView.
+    private List<TopStory> topStoriesQueue;
 
-    private final RowItemType[] FIXED_ROWS = {RowItemType.TOP_PANEL, RowItemType.WELCOME, RowItemType.HIGHLIGHTS_TITLE};
-    private static final int HIGHLIGHTS_OFFSET = 3; // Topsites, Welcome, Highlights Title
+    private final RowItemType[] FIXED_ROWS = {RowItemType.TOP_PANEL, RowItemType.WELCOME, RowItemType.TOP_STORIES_TITLE, RowItemType.HIGHLIGHTS_TITLE};
+    private final int MAX_TOP_STORIES = 3;
 
     private HomePager.OnUrlOpenListener onUrlOpenListener;
     private HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener;
 
     private int tiles;
     private int tilesSize;
 
     public enum RowItemType {
         TOP_PANEL (-2), // RecyclerView.NO_ID is -1, so start hard-coded stableIds at -2.
         WELCOME (-3),
-        HIGHLIGHTS_TITLE (-4),
+        TOP_STORIES_TITLE(-4),
+        TOP_STORIES_ITEM(-1), // There can be multiple Top Stories items so caller should handle as a special case.
+        HIGHLIGHTS_TITLE (-5),
         HIGHLIGHT_ITEM (-1); // There can be multiple Highlight Items so caller should handle as a special case.
 
         public final int stableId;
 
         RowItemType(int stableId) {
             this.stableId = stableId;
         }
 
@@ -81,16 +88,18 @@ public class StreamRecyclerAdapter exten
     }
 
     public StreamRecyclerAdapter() {
         setHasStableIds(true);
         recyclerViewModel = new LinkedList<>();
         for (RowItemType type : FIXED_ROWS) {
             recyclerViewModel.add(makeRowModelFromType(type));
         }
+        topStoriesQueue = Collections.emptyList();
+        loadTopStories();
     }
 
     void setOnUrlOpenListeners(HomePager.OnUrlOpenListener onUrlOpenListener, HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
         this.onUrlOpenListener = onUrlOpenListener;
         this.onUrlOpenInBackgroundListener = onUrlOpenInBackgroundListener;
     }
 
     public void setTileSize(int tiles, int tilesSize) {
@@ -109,81 +118,125 @@ public class StreamRecyclerAdapter exten
     }
 
     @Override
     public StreamViewHolder onCreateViewHolder(ViewGroup parent, final int type) {
         final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
 
         if (type == RowItemType.TOP_PANEL.getViewType()) {
             return new TopPanelRow(inflater.inflate(TopPanelRow.LAYOUT_ID, parent, false), onUrlOpenListener, onUrlOpenInBackgroundListener);
+        } else if (type == RowItemType.TOP_STORIES_TITLE.getViewType()) {
+            return new StreamTitleRow(inflater.inflate(StreamTitleRow.LAYOUT_ID, parent, false), R.string.activity_stream_topstories);
+        } else if (type == RowItemType.TOP_STORIES_ITEM.getViewType()) {
+            return new WebpageItemRow(inflater.inflate(WebpageItemRow.LAYOUT_ID, parent, false), this);
         } else if (type == RowItemType.WELCOME.getViewType()) {
             return new WelcomePanelRow(inflater.inflate(WelcomePanelRow.LAYOUT_ID, parent, false), this);
         } else if (type == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
-            return new HighlightItemRow(inflater.inflate(HighlightItemRow.LAYOUT_ID, parent, false), this);
+            return new WebpageItemRow(inflater.inflate(WebpageItemRow.LAYOUT_ID, parent, false), this);
         } else if (type == RowItemType.HIGHLIGHTS_TITLE.getViewType()) {
-            return new HighlightsTitleRow(inflater.inflate(HighlightsTitleRow.LAYOUT_ID, parent, false));
+            return new StreamTitleRow(inflater.inflate(StreamTitleRow.LAYOUT_ID, parent, false), R.string.activity_stream_highlights);
         } else {
             throw new IllegalStateException("Missing inflation for ViewType " + type);
         }
     }
 
-    private int getHighlightsOffsetFromRVPosition(int position) {
-        return position - HIGHLIGHTS_OFFSET;
+    /**
+     * Returns the index of an item within highlights.
+     * @param position position in adapter
+     * @return index of item within highlights
+     */
+    private int getHighlightsIndexFromAdapterPosition(int position) {
+        if (getItemViewType(position) != RowItemType.HIGHLIGHT_ITEM.getViewType()) {
+            throw new IllegalArgumentException("Item is not a highlight!");
+        }
+        return position - indexOfType(RowItemType.HIGHLIGHT_ITEM, recyclerViewModel);
+    }
+
+    /**
+     * Returns the index of an item within top stories.
+     * @param position position in adapter
+     * @return index of item within top stories
+     */
+    private int getTopStoriesIndexFromAdapterPosition(int position) {
+        if (getItemViewType(position) != RowItemType.TOP_STORIES_ITEM.getViewType()) {
+            throw new IllegalArgumentException("Item is not a topstory!");
+        }
+        return position - indexOfType(RowItemType.TOP_STORIES_ITEM, recyclerViewModel);
     }
 
     @Override
     public void onBindViewHolder(StreamViewHolder holder, int position) {
         int type = getItemViewType(position);
         if (type == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
             final Highlight highlight = (Highlight) recyclerViewModel.get(position);
-            ((HighlightItemRow) holder).bind(highlight, position, tilesSize);
+            ((WebpageItemRow) holder).bind(highlight, position, tilesSize);
         } else if (type == RowItemType.TOP_PANEL.getViewType()) {
             ((TopPanelRow) holder).bind(topSitesCursor, tiles, tilesSize);
+        } else if (type == RowItemType.TOP_STORIES_ITEM.getViewType()) {
+            final TopStory story = (TopStory) recyclerViewModel.get(position);
+            ((WebpageItemRow) holder).bind(story, position, tilesSize);
         }
     }
 
     @Override
     public void onItemClicked(RecyclerView recyclerView, int position, View v) {
-        if (!onItemClickIsValidHighlightItem(position)) {
+        if (!onItemClickIsValidRowItem(position)) {
             return;
         }
 
-        final Highlight highlight = (Highlight) recyclerViewModel.get(position);
+        final WebpageRowModel model = (WebpageRowModel) recyclerViewModel.get(position);
+
+        final String sourceType;
+        final int actionPosition;
+        final int size;
+        final int viewType = getItemViewType(position);
+
+        if (viewType == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
+            sourceType = ActivityStreamTelemetry.Contract.TYPE_HIGHLIGHTS;
+            actionPosition = getHighlightsIndexFromAdapterPosition(position);
+            size = getNumOfTypeShown(RowItemType.HIGHLIGHT_ITEM);
+        } else {
+            sourceType = ActivityStreamTelemetry.Contract.TYPE_POCKET;
+            actionPosition = getTopStoriesIndexFromAdapterPosition(position);
+            size = getNumOfTypeShown(RowItemType.TOP_STORIES_ITEM);
+        }
 
         ActivityStreamTelemetry.Extras.Builder extras = ActivityStreamTelemetry.Extras.builder()
-                .forHighlightSource(highlight.getSource())
-                .set(ActivityStreamTelemetry.Contract.SOURCE_TYPE, ActivityStreamTelemetry.Contract.TYPE_HIGHLIGHTS)
-                .set(ActivityStreamTelemetry.Contract.ACTION_POSITION, getHighlightsOffsetFromRVPosition(position))
-                .set(ActivityStreamTelemetry.Contract.COUNT, recyclerViewModel.size() - FIXED_ROWS.length);
+                .forHighlightSource(model.getSource())
+                .set(ActivityStreamTelemetry.Contract.SOURCE_TYPE, sourceType)
+                .set(ActivityStreamTelemetry.Contract.ACTION_POSITION, actionPosition)
+                .set(ActivityStreamTelemetry.Contract.COUNT, size);
 
         Telemetry.sendUIEvent(
                 TelemetryContract.Event.LOAD_URL,
                 TelemetryContract.Method.LIST_ITEM,
                 extras.build()
         );
 
         // NB: This is hacky. We need to process telemetry data first, otherwise we run a risk of
         // not having a cursor to work with once url is opened and BrowserApp closes A-S home screen
         // and clears its resources (read: cursors). See Bug 1326018.
-        onUrlOpenListener.onUrlOpen(highlight.getUrl(), EnumSet.of(HomePager.OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
+        onUrlOpenListener.onUrlOpen(model.getUrl(), EnumSet.of(HomePager.OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
     }
 
     @Override
     public boolean onItemLongClicked(final RecyclerView recyclerView, final int position, final View v) {
-        if (!onItemClickIsValidHighlightItem(position)) {
+        if (!onItemClickIsValidRowItem(position)) {
             return false;
         }
 
-        final HighlightItemRow highlightItem = (HighlightItemRow) recyclerView.getChildViewHolder(v);
+        final WebpageItemRow highlightItem = (WebpageItemRow) recyclerView.getChildViewHolder(v);
         openContextMenu(highlightItem, position, ActivityStreamTelemetry.Contract.INTERACTION_LONG_CLICK);
         return true;
     }
 
-    private boolean onItemClickIsValidHighlightItem(final int position) {
-        if (getItemViewType(position) != RowItemType.HIGHLIGHT_ITEM.getViewType()) {
+    private boolean onItemClickIsValidRowItem(final int position) {
+        final int viewType = getItemViewType(position);
+        if (viewType != RowItemType.HIGHLIGHT_ITEM.getViewType()
+                && viewType != RowItemType.TOP_STORIES_ITEM.getViewType()) {
             // Headers (containing topsites and/or the highlights title) do their own click handling as needed
             return false;
         }
 
         // The position this method receives is from RecyclerView.ViewHolder.getAdapterPosition, whose docs state:
         // "Note that if you've called notifyDataSetChanged(), until the next layout pass, the return value of this
         // method will be NO_POSITION."
         //
@@ -197,62 +250,136 @@ public class StreamRecyclerAdapter exten
             Log.w(LOGTAG, "onItemClicked: received NO_POSITION. Returning");
             return false;
         }
 
         return true;
     }
 
     @Override
-    public void openContextMenu(final HighlightItemRow highlightItem, final int position, @NonNull final String interactionExtra) {
-        final Highlight highlight = (Highlight) recyclerViewModel.get(position);
+    public void openContextMenu(final WebpageItemRow webpageItemRow, final int position, @NonNull final String interactionExtra) {
+        final WebpageRowModel model = (WebpageRowModel) recyclerViewModel.get(position);
+
+        final String sourceType;
+        final int actionPosition;
+        final ActivityStreamContextMenu.MenuMode menuMode;
+
+        if (model.getRowItemType() == RowItemType.HIGHLIGHT_ITEM) {
+            sourceType = ActivityStreamTelemetry.Contract.TYPE_HIGHLIGHTS;
+            actionPosition = getHighlightsIndexFromAdapterPosition(position);
+            menuMode = ActivityStreamContextMenu.MenuMode.HIGHLIGHT;
+        } else {
+            sourceType = ActivityStreamTelemetry.Contract.TYPE_POCKET;
+            actionPosition = getTopStoriesIndexFromAdapterPosition(position);
+            menuMode = ActivityStreamContextMenu.MenuMode.TOPSTORY;
+        }
 
         ActivityStreamTelemetry.Extras.Builder extras = ActivityStreamTelemetry.Extras.builder()
-                .set(ActivityStreamTelemetry.Contract.SOURCE_TYPE, ActivityStreamTelemetry.Contract.TYPE_HIGHLIGHTS)
-                .set(ActivityStreamTelemetry.Contract.ACTION_POSITION, position - HIGHLIGHTS_OFFSET)
+                .set(ActivityStreamTelemetry.Contract.SOURCE_TYPE, sourceType)
+                .set(ActivityStreamTelemetry.Contract.ACTION_POSITION, actionPosition)
                 .set(ActivityStreamTelemetry.Contract.INTERACTION, interactionExtra)
-                .forHighlightSource(highlight.getSource());
+                .forHighlightSource(model.getSource());
 
-        ActivityStreamContextMenu.show(highlightItem.itemView.getContext(),
-                highlightItem.getContextMenuAnchor(),
+        ActivityStreamContextMenu.show(webpageItemRow.itemView.getContext(),
+                webpageItemRow.getContextMenuAnchor(),
                 extras,
-                ActivityStreamContextMenu.MenuMode.HIGHLIGHT,
-                highlight,
+                menuMode,
+                model,
                 /* shouldOverrideWithImageProvider */ true, // we use image providers in HighlightItem.pageIconLayout.
                 onUrlOpenListener, onUrlOpenInBackgroundListener,
-                highlightItem.getTileWidth(), highlightItem.getTileHeight());
+                webpageItemRow.getTileWidth(), webpageItemRow.getTileHeight());
 
         Telemetry.sendUIEvent(
                 TelemetryContract.Event.SHOW,
                 TelemetryContract.Method.CONTEXT_MENU,
                 extras.build()
         );
     }
 
     @Override
     public int getItemCount() {
         return recyclerViewModel.size();
     }
 
     public void swapHighlights(List<Highlight> highlights) {
-        recyclerViewModel = recyclerViewModel.subList(0, HIGHLIGHTS_OFFSET);
+        recyclerViewModel = recyclerViewModel.subList(0, FIXED_ROWS.length + getNumOfTypeShown(RowItemType.TOP_STORIES_ITEM));
         recyclerViewModel.addAll(highlights);
         notifyDataSetChanged();
     }
 
+    private void loadTopStories() {
+        List<TopStory> newStories = makePlaceholderStories();
+        topStoriesQueue = newStories;
+
+        final int insertionIndex = indexOfType(RowItemType.TOP_STORIES_TITLE, recyclerViewModel) + 1;
+        for (int i = 0; i < Math.min(MAX_TOP_STORIES, newStories.size()); i++) {
+            recyclerViewModel.add(insertionIndex + i, newStories.get(i));
+        }
+    }
+
+    /**
+     * Returns the index of the first item of the type found.
+     * @param type viewType of RowItemType
+     * @param rowModelList List to be indexed into
+     * @return index of first item of the type, or -1 if it none exist.
+     */
+    private static int indexOfType(RowItemType type, List<RowModel> rowModelList) {
+        for (int i = 0; i < rowModelList.size(); i++) {
+            if (rowModelList.get(i).getRowItemType() == type) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the number of consecutive items in the adapter of the item type specified.
+     *
+     * This is intended to be used for counting the items that have a dynamic count
+     * (such as Highlights or TopStory)
+     *
+     * @param type RowItemType to be counted
+     * @return The number of items shown.
+     */
+    private int getNumOfTypeShown(RowItemType type) {
+        final int startIndex = indexOfType(type, recyclerViewModel);
+        if (startIndex == -1) {
+            return 0;
+        }
+        int count = 0;
+        for (int i = startIndex; i < recyclerViewModel.size(); i++) {
+            if (getItemViewType(i) == type.getViewType()) {
+                count++;
+            } else {
+                break;
+            }
+        }
+        return count;
+    }
+
+    private List<TopStory> makePlaceholderStories() {
+        final List<TopStory> stories = new LinkedList<>();
+        final String[] TITLES = { "Placeholder 1", "Placeholder 2", "Placeholder 3"};
+        for (String title : TITLES) {
+            stories.add(new TopStory(title, "https://www.mozilla.org/"));
+        }
+        return stories;
+    }
+
     public void swapTopSitesCursor(Cursor cursor) {
         this.topSitesCursor = cursor;
         notifyItemChanged(0);
     }
 
     @Override
     public long getItemId(int position) {
         final int viewType = getItemViewType(position);
-        if (viewType == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
+        if (viewType == RowItemType.HIGHLIGHT_ITEM.getViewType()
+                || viewType == RowItemType.TOP_STORIES_ITEM.getViewType()) {
             // Highlights are always picked from recent history - So using the history id should
             // give us a unique (positive) id.
-            final Highlight highlight = (Highlight) recyclerViewModel.get(position);
-            return highlight.getHistoryId();
+            final WebpageRowModel model = (WebpageRowModel) recyclerViewModel.get(position);
+            return model.getUniqueId();
         } else {
             return recyclerViewModel.get(position).getRowItemType().stableId;
         }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/menu/ActivityStreamContextMenu.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/menu/ActivityStreamContextMenu.java
@@ -30,17 +30,18 @@ import org.mozilla.gecko.util.UIAsyncTas
 import java.util.EnumSet;
 
 @RobocopTarget
 public abstract class ActivityStreamContextMenu
         implements NavigationView.OnNavigationItemSelectedListener {
 
     public enum MenuMode {
         HIGHLIGHT,
-        TOPSITE
+        TOPSITE,
+        TOPSTORY
     }
 
     private final Context context;
     private final WebpageModel item;
 
     private final ActivityStreamTelemetry.Extras.Builder telemetryExtraBuilder;
 
     private final HomePager.OnUrlOpenListener onUrlOpenListener;
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/model/Highlight.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/model/Highlight.java
@@ -13,17 +13,17 @@ import android.text.TextUtils;
 import org.mozilla.gecko.activitystream.Utils;
 import org.mozilla.gecko.activitystream.homepanel.StreamRecyclerAdapter;
 import org.mozilla.gecko.activitystream.ranking.HighlightCandidateCursorIndices;
 import org.mozilla.gecko.activitystream.ranking.HighlightsRanking;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class Highlight implements WebpageModel, RowModel {
+public class Highlight implements WebpageRowModel {
 
     /**
      * A pattern matching a json object containing the key "image_url" and extracting the value. afaik, these urls
      * are not encoded so it's entirely possible that the url will contain a quote and we will not extract the whole
      * url. However, given these are coming from websites providing favicon-like images, it's not likely a quote will
      * appear and since these urls are only being used to compare against one another (as imageURLs in Highlight items),
      * a partial URL may actually have the same behavior: good enough for me!
      */
@@ -209,16 +209,18 @@ public class Highlight implements Webpag
         this.isBookmarked = bookmarked;
     }
 
     @Override
     public void updatePinned(boolean pinned) {
         this.isPinned = pinned;
     }
 
+    @Override
     public Utils.HighlightSource getSource() {
         return source;
     }
 
-    public long getHistoryId() {
+    @Override
+    public long getUniqueId() {
         return historyId;
     }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/model/TopStory.java
@@ -0,0 +1,68 @@
+/* -*- 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.activitystream.homepanel.model;
+
+import org.mozilla.gecko.activitystream.Utils;
+import org.mozilla.gecko.activitystream.homepanel.StreamRecyclerAdapter;
+
+public class TopStory implements WebpageRowModel {
+    private final String title;
+    private final String url;
+    private final String imageUrl;
+
+    public TopStory(String title, String url) {
+        this(title, url, null);
+    }
+
+    public TopStory(String title, String url, String imageUrl) {
+        this.title = title;
+        this.url = url;
+        this.imageUrl = imageUrl;
+    }
+
+    @Override
+    public String getTitle() {
+        return title;
+    }
+
+    @Override
+    public String getUrl() {
+        return url;
+    }
+
+    @Override
+    public String getImageUrl() {
+        return imageUrl;
+    }
+
+    @Override
+    public StreamRecyclerAdapter.RowItemType getRowItemType() {
+        return StreamRecyclerAdapter.RowItemType.TOP_STORIES_ITEM;
+    }
+
+    @Override
+    public Boolean isBookmarked() {
+        return false;
+    }
+
+    @Override
+    public Boolean isPinned() {
+        return false;
+    }
+
+    public void updateBookmarked(boolean bookmarked) {}
+    public void updatePinned(boolean pinned) {}
+
+    @Override
+    public Utils.HighlightSource getSource() {
+        return Utils.HighlightSource.POCKET;
+    }
+
+    @Override
+    public long getUniqueId() {
+        return getUrl().hashCode();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/model/WebpageRowModel.java
@@ -0,0 +1,16 @@
+/* -*- 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.activitystream.homepanel.model;
+
+import org.mozilla.gecko.activitystream.Utils;
+
+/**
+ * Model for a row in Activity Stream that represents a webpage item.
+ */
+public interface WebpageRowModel extends WebpageModel, RowModel {
+    Utils.HighlightSource getSource();
+    long getUniqueId();
+}
rename from mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightsTitleRow.java
rename to mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/StreamTitleRow.java
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightsTitleRow.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/StreamTitleRow.java
@@ -1,19 +1,24 @@
 /* -*- 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.activitystream.homepanel.stream;
 
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
 import android.view.View;
+import android.widget.TextView;
 
 import org.mozilla.gecko.R;
 
-public class HighlightsTitleRow extends StreamViewHolder {
+public class StreamTitleRow extends StreamViewHolder {
     public static final int LAYOUT_ID = R.layout.activity_stream_main_highlightstitle;
 
-    public HighlightsTitleRow(final View itemView) {
+    public StreamTitleRow(final View itemView, final @StringRes @NonNull int titleResId) {
         super(itemView);
+        final TextView titleView = (TextView) itemView.findViewById(R.id.title_highlights);
+        titleView.setText(titleResId);
     }
 }
 
rename from mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightItemRow.java
rename to mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/WebpageItemRow.java
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightItemRow.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/WebpageItemRow.java
@@ -12,108 +12,115 @@ import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.activitystream.ActivityStreamTelemetry;
 import org.mozilla.gecko.activitystream.Utils;
 import org.mozilla.gecko.activitystream.homepanel.StreamHighlightItemRowContextMenuListener;
-import org.mozilla.gecko.activitystream.homepanel.model.Highlight;
+import org.mozilla.gecko.activitystream.homepanel.model.WebpageRowModel;
 import org.mozilla.gecko.util.DrawableUtil;
 import org.mozilla.gecko.util.TouchTargetUtil;
 import org.mozilla.gecko.util.URIUtils;
 import org.mozilla.gecko.util.ViewUtil;
 
 import java.lang.ref.WeakReference;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.UUID;
 
-public class HighlightItemRow extends StreamViewHolder {
-    private static final String LOGTAG = "GeckoHighlightItem";
+public class WebpageItemRow extends StreamViewHolder {
+    private static final String LOGTAG = "GeckoWebpageItemRow";
 
     public static final int LAYOUT_ID = R.layout.activity_stream_card_history_item;
     private static final double SIZE_RATIO = 0.75;
 
+    private WebpageRowModel webpageModel;
     private int position;
 
     private final StreamOverridablePageIconLayout pageIconLayout;
+    private final TextView pageDomainView;
     private final TextView pageTitleView;
+    private final ImageView pageSourceIconView;
     private final TextView pageSourceView;
-    private final TextView pageDomainView;
-    private final ImageView pageSourceIconView;
     private final ImageView menuButton;
 
-    public HighlightItemRow(final View itemView, final StreamHighlightItemRowContextMenuListener contextMenuListener) {
+    public WebpageItemRow(final View itemView, final StreamHighlightItemRowContextMenuListener contextMenuListener) {
         super(itemView);
 
         pageTitleView = (TextView) itemView.findViewById(R.id.card_history_label);
         pageIconLayout = (StreamOverridablePageIconLayout) itemView.findViewById(R.id.icon);
         pageSourceView = (TextView) itemView.findViewById(R.id.card_history_source);
         pageDomainView = (TextView) itemView.findViewById(R.id.page);
         pageSourceIconView = (ImageView) itemView.findViewById(R.id.source_icon);
 
         menuButton = (ImageView) itemView.findViewById(R.id.menu);
         menuButton.setImageDrawable(
                 DrawableUtil.tintDrawable(menuButton.getContext(), R.drawable.menu, Color.LTGRAY));
         TouchTargetUtil.ensureTargetHitArea(menuButton, itemView);
         ViewUtil.enableTouchRipple(menuButton);
         menuButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                contextMenuListener.openContextMenu(HighlightItemRow.this, position,
+                contextMenuListener.openContextMenu(WebpageItemRow.this, position,
                         ActivityStreamTelemetry.Contract.INTERACTION_MENU_BUTTON);
             }
         });
     }
 
-    public void bind(Highlight highlight, int position, int tilesWidth) {
+    public void bind(WebpageRowModel model, int position, int tilesWidth) {
+        this.webpageModel = model;
         this.position = position;
 
-        final String backendHightlightTitle = highlight.getTitle();
-        final String uiHighlightTitle = !TextUtils.isEmpty(backendHightlightTitle) ? backendHightlightTitle : highlight.getUrl();
+        final String backendHighlightTitle = model.getTitle();
+        final String uiHighlightTitle = !TextUtils.isEmpty(backendHighlightTitle) ? backendHighlightTitle : model.getUrl();
         pageTitleView.setText(uiHighlightTitle);
 
         ViewGroup.LayoutParams layoutParams = pageIconLayout.getLayoutParams();
         layoutParams.width = tilesWidth;
         layoutParams.height = (int) Math.floor(tilesWidth * SIZE_RATIO);
         pageIconLayout.setLayoutParams(layoutParams);
 
-        updateUiForSource(highlight.getSource());
-        updatePageDomain(highlight);
-        pageIconLayout.updateIcon(highlight.getUrl(), highlight.getImageUrl());
+        updateUiForSource(model.getSource());
+        updatePageDomain();
+        pageIconLayout.updateIcon(model.getUrl(), model.getImageUrl());
     }
 
-    private void updateUiForSource(Utils.HighlightSource source) {
+    public void updateUiForSource(Utils.HighlightSource source) {
         switch (source) {
             case BOOKMARKED:
                 pageSourceView.setText(R.string.activity_stream_highlight_label_bookmarked);
                 pageSourceView.setVisibility(View.VISIBLE);
                 pageSourceIconView.setImageResource(R.drawable.ic_as_bookmarked);
                 break;
             case VISITED:
                 pageSourceView.setText(R.string.activity_stream_highlight_label_visited);
                 pageSourceView.setVisibility(View.VISIBLE);
                 pageSourceIconView.setImageResource(R.drawable.ic_as_visited);
                 break;
+            case POCKET:
+                pageSourceView.setText(R.string.activity_stream_highlight_label_trending);
+                pageSourceView.setVisibility(View.VISIBLE);
+                pageSourceIconView.setImageResource(R.drawable.ic_as_trending);
+                break;
             default:
                 pageSourceView.setVisibility(View.INVISIBLE);
                 pageSourceIconView.setImageResource(0);
                 break;
         }
     }
 
-    private void updatePageDomain(final Highlight highlight) {
+    private void updatePageDomain() {
         final URI highlightURI;
         try {
-            highlightURI = new URI(highlight.getUrl());
+            highlightURI = new URI(webpageModel.getUrl());
         } catch (final URISyntaxException e) {
             // If the URL is invalid, there's not much extra processing we can do on it.
-            pageDomainView.setText(highlight.getUrl());
+            pageDomainView.setText(webpageModel.getUrl());
             return;
         }
 
         final UpdatePageDomainAsyncTask updatePageDomainTask = new UpdatePageDomainAsyncTask(itemView.getContext(),
                 highlightURI, pageDomainView);
         updatePageDomainTask.execute();
     }
 
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -829,24 +829,28 @@ just addresses the organization to follo
 <!ENTITY helper_first_offline_bookmark_message "Find your Reader View items in Bookmarks, even offline.">
 <!ENTITY helper_first_offline_bookmark_button "Go to Bookmarks">
 
 <!ENTITY helper_triple_readerview_open_title "Available offline">
 <!ENTITY helper_triple_readerview_open_message "Bookmark Reader View items to read them offline.">
 <!ENTITY helper_triple_readerview_open_button "Add to Bookmarks">
 
 <!ENTITY activity_stream_topsites "Top Sites">
+<!-- LOCALIZATION NOTE (activity_stream_topstories): &brandPocket is the brand of the company, Pocket, that is being used to provide suggestions for articles. -->
+<!ENTITY activity_stream_topstories "Recommended by &brandPocket;">
 <!ENTITY activity_stream_highlights "Highlights">
 
 <!-- LOCALIZATION NOTE (activity_stream_highlight_label_bookmarked): This label is shown in the Activity
 Stream list for highlights sourced from th user's bookmarks. -->
 <!ENTITY activity_stream_highlight_label_bookmarked "Bookmarked">
 <!-- LOCALIZATION NOTE (activity_stream_highlight_label_visited): This label is shown in the Activity
 Stream list for highlights sourced from th user's bookmarks. -->
 <!ENTITY activity_stream_highlight_label_visited "Visited">
+<!-- LOCALIZATION NOTE (activity_stream_highlight_label_trending): This label is shown in the Activity Stream list for highlights sourced from a recommendations engine. -->
+<!ENTITY activity_stream_highlight_label_trending "Trending">
 
 <!-- LOCALIZATION NOTE (activity_stream_remove): This label is shown in the Activity Stream context menu,
 and allows hiding a URL/page from highlights or topsites. The page remains in history/bookmarks, but
 is simply hidden from the Activity Stream panel. -->
 <!ENTITY activity_stream_remove "Remove">
 <!ENTITY activity_stream_delete_history "Delete from History">
 
 <!ENTITY activity_stream_welcome_title "Welcome to your Highlights">
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -511,22 +511,24 @@ gbjar.sources += ['java/org/mozilla/geck
     'activitystream/homepanel/HighlightsLoader.java',
     'activitystream/homepanel/menu/ActivityStreamContextMenu.java',
     'activitystream/homepanel/menu/BottomSheetContextMenu.java',
     'activitystream/homepanel/menu/PopupContextMenu.java',
     'activitystream/homepanel/model/Highlight.java',
     'activitystream/homepanel/model/Metadata.java',
     'activitystream/homepanel/model/RowModel.java',
     'activitystream/homepanel/model/TopSite.java',
+    'activitystream/homepanel/model/TopStory.java',
     'activitystream/homepanel/model/WebpageModel.java',
-    'activitystream/homepanel/stream/HighlightItemRow.java',
-    'activitystream/homepanel/stream/HighlightsTitleRow.java',
+    'activitystream/homepanel/model/WebpageRowModel.java',
     'activitystream/homepanel/stream/StreamOverridablePageIconLayout.java',
+    'activitystream/homepanel/stream/StreamTitleRow.java',
     'activitystream/homepanel/stream/StreamViewHolder.java',
     'activitystream/homepanel/stream/TopPanelRow.java',
+    'activitystream/homepanel/stream/WebpageItemRow.java',
     'activitystream/homepanel/stream/WelcomePanelRow.java',
     'activitystream/homepanel/StreamHighlightItemRowContextMenuListener.java',
     'activitystream/homepanel/StreamItemAnimator.java',
     'activitystream/homepanel/StreamRecyclerAdapter.java',
     'activitystream/homepanel/topsites/TopSitesCard.java',
     'activitystream/homepanel/topsites/TopSitesPage.java',
     'activitystream/homepanel/topsites/TopSitesPageAdapter.java',
     'activitystream/homepanel/topsites/TopSitesPagerAdapter.java',
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -617,19 +617,21 @@
   <string name="helper_first_offline_bookmark_message">&helper_first_offline_bookmark_message;</string>
   <string name="helper_first_offline_bookmark_button">&helper_first_offline_bookmark_button;</string>
 
   <string name="helper_triple_readerview_open_title">&helper_triple_readerview_open_title;</string>
   <string name="helper_triple_readerview_open_message">&helper_triple_readerview_open_message;</string>
   <string name="helper_triple_readerview_open_button">&helper_triple_readerview_open_button;</string>
 
   <string name="activity_stream_topsites">&activity_stream_topsites;</string>
+  <string name="activity_stream_topstories">&activity_stream_topstories;</string>
   <string name="activity_stream_highlights">&activity_stream_highlights;</string>
   <string name="activity_stream_highlight_label_bookmarked">&activity_stream_highlight_label_bookmarked;</string>
   <string name="activity_stream_highlight_label_visited">&activity_stream_highlight_label_visited;</string>
+  <string name="activity_stream_highlight_label_trending">&activity_stream_highlight_label_trending;</string>
   <string name="activity_stream_remove">&activity_stream_remove;</string>
   <string name="activity_stream_delete_history">&activity_stream_delete_history;</string>
 
   <string name="activity_stream_welcome_title">&activity_stream_welcome_title;</string>
   <string name="activity_stream_welcome_content">&activity_stream_welcome_content1;</string>
   <string name="activity_stream_welcome_dismiss">&activity_stream_welcome_dismiss;</string>
 
   <string name="private_tab_panel_title">&private_tab_panel_title;</string>
--- a/mobile/android/branding/beta/locales/en-US/brand.dtd
+++ b/mobile/android/branding/beta/locales/en-US/brand.dtd
@@ -1,7 +1,9 @@
 <!-- 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/. -->
 
 <!ENTITY  brandShortName  "Firefox Beta">
 <!ENTITY  brandFullName   "Mozilla Firefox Beta">
-<!ENTITY  vendorShortName "Mozilla">
\ No newline at end of file
+<!ENTITY  vendorShortName "Mozilla">
+
+<!ENTITY  brandPocket     "Pocket">
--- a/mobile/android/branding/nightly-old-id/locales/en-US/brand.dtd
+++ b/mobile/android/branding/nightly-old-id/locales/en-US/brand.dtd
@@ -1,7 +1,9 @@
 <!-- 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/. -->
 
 <!ENTITY  brandShortName  "Nightly">
 <!ENTITY  brandFullName   "Mozilla Nightly">
-<!ENTITY  vendorShortName "Mozilla">
\ No newline at end of file
+<!ENTITY  vendorShortName "Mozilla">
+
+<!ENTITY  brandPocket     "Pocket">
--- a/mobile/android/branding/nightly/locales/en-US/brand.dtd
+++ b/mobile/android/branding/nightly/locales/en-US/brand.dtd
@@ -1,7 +1,9 @@
 <!-- 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/. -->
 
 <!ENTITY  brandShortName  "Nightly">
 <!ENTITY  brandFullName   "Mozilla Nightly">
-<!ENTITY  vendorShortName "Mozilla">
\ No newline at end of file
+<!ENTITY  vendorShortName "Mozilla">
+
+<!ENTITY  brandPocket     "Pocket">
--- a/mobile/android/branding/official/locales/en-US/brand.dtd
+++ b/mobile/android/branding/official/locales/en-US/brand.dtd
@@ -1,7 +1,9 @@
 <!-- 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/. -->
 
 <!ENTITY  brandShortName  "Firefox">
 <!ENTITY  brandFullName   "Mozilla Firefox">
-<!ENTITY  vendorShortName "Mozilla">
\ No newline at end of file
+<!ENTITY  vendorShortName "Mozilla">
+
+<!ENTITY  brandPocket     "Pocket">
--- a/mobile/android/branding/unofficial/locales/en-US/brand.dtd
+++ b/mobile/android/branding/unofficial/locales/en-US/brand.dtd
@@ -1,7 +1,9 @@
 <!-- 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/. -->
 
 <!ENTITY  brandShortName  "Fennec">
 <!ENTITY  brandFullName   "Mozilla Fennec">
-<!ENTITY  vendorShortName "Mozilla">
\ No newline at end of file
+<!ENTITY  vendorShortName "Mozilla">
+
+<!ENTITY  brandPocket     "Pocket">