Bug 1288106 - Explore implementing layout manager for multi row panel r=ahunt
authorJonathan Almeida (:jonalmeida) <jonalmeida942@gmail.com>
Thu, 11 Aug 2016 17:10:23 -0700
changeset 351866 0becc8c0fd30a2ac5bc1f34d80fea7b7afa4b97a
parent 351865 873e1bd0833abf954ac0e0fab6c43b1ce5380e1c
child 351867 c499af51f167880b1a51458a2d7d25e35e2f4b57
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahunt
bugs1288106
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1288106 - Explore implementing layout manager for multi row panel r=ahunt MozReview-Commit-ID: J2DSx9UYNFN
mobile/android/base/java/org/mozilla/gecko/home/activitystream/ActivityStream.java
mobile/android/base/java/org/mozilla/gecko/home/activitystream/HighlightRecyclerAdapter.java
mobile/android/base/java/org/mozilla/gecko/home/activitystream/HistoryRecyclerAdapter.java
mobile/android/base/java/org/mozilla/gecko/home/activitystream/MainRecyclerAdapter.java
mobile/android/base/java/org/mozilla/gecko/home/activitystream/MainRecyclerLayout.java
mobile/android/base/java/org/mozilla/gecko/home/activitystream/TopSitesRecyclerAdapter.java
mobile/android/base/moz.build
mobile/android/base/resources/layout/activity_stream.xml
mobile/android/base/resources/layout/activity_stream_card_highlights_item.xml
mobile/android/base/resources/layout/activity_stream_card_history_item.xml
mobile/android/base/resources/layout/activity_stream_card_top_sites_item.xml
mobile/android/base/resources/layout/activity_stream_category.xml
--- a/mobile/android/base/java/org/mozilla/gecko/home/activitystream/ActivityStream.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/activitystream/ActivityStream.java
@@ -3,20 +3,22 @@
  * 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.activitystream;
 
 import android.content.Context;
 import android.os.Bundle;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.LoaderManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 
+import org.mozilla.gecko.R;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.home.HomeBanner;
 import org.mozilla.gecko.home.HomeFragment;
 import org.mozilla.gecko.home.HomeScreen;
 
 public class ActivityStream extends FrameLayout implements HomeScreen {
 
     public ActivityStream(Context context, AttributeSet attrs) {
@@ -57,15 +59,19 @@ public class ActivityStream extends Fram
     public void setBanner(HomeBanner banner) {
         // TODO: we should probably implement this to show snippets.
     }
 
     @Override
     public void load(LoaderManager lm, FragmentManager fm, String panelId, Bundle restoreData,
                      PropertyAnimator animator) {
         // Signal to load data from storage as needed, compare with HomePager
+        RecyclerView rv = (RecyclerView) findViewById(R.id.activity_stream_main_recyclerview);
+        rv.setAdapter(new MainRecyclerAdapter(getContext()));
+        rv.setLayoutManager(new LinearLayoutManager(getContext()));
+        rv.setHasFixedSize(true);
     }
 
     @Override
     public void unload() {
         // Signal to clear data that has been loaded, compare with HomePager
     }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/home/activitystream/HighlightRecyclerAdapter.java
@@ -0,0 +1,69 @@
+package org.mozilla.gecko.home.activitystream;
+
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.mozilla.gecko.R;
+
+class HighlightRecyclerAdapter extends RecyclerView.Adapter<HighlightRecyclerAdapter.ViewHolder> {
+
+    private final Context context;
+    private final String[] items = {
+            "What Do The Reviews Have To Say About the New Ghostbusters",
+            "The Dark Secrets Of This Now-Empty Island in Maine",
+            "How to Prototype a Game in Under 7 Days (2005)",
+            "Fuchsia, a new operating system"
+    };
+    private final String[] items_time = {
+            "3h",
+            "3h",
+            "3h",
+            "3h"
+    };
+
+    HighlightRecyclerAdapter(Context context) {
+        this.context = context;
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View v = LayoutInflater
+                .from(context)
+                .inflate(R.layout.activity_stream_card_highlights_item, parent, false);
+        return new ViewHolder(v);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        holder.vLabel.setText(items[position]);
+        holder.vTimeSince.setText(items_time[position]);
+        int res = context.getResources()
+                .getIdentifier("thumb_" + (position + 1), "drawable", context.getPackageName());
+        holder.vThumbnail.setImageResource(res);
+    }
+
+    @Override
+    public int getItemCount() {
+        return items.length;
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+        TextView vLabel;
+        TextView vTimeSince;
+        ImageView vThumbnail;
+
+        public ViewHolder(View itemView) {
+            super(itemView);
+            vLabel = (TextView) itemView.findViewById(R.id.card_highlights_label);
+            vTimeSince = (TextView) itemView.findViewById(R.id.card_highlights_time_since);
+            vThumbnail = (ImageView) itemView.findViewById(R.id.card_highlights_thumbnail);
+        }
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/home/activitystream/HistoryRecyclerAdapter.java
@@ -0,0 +1,56 @@
+package org.mozilla.gecko.home.activitystream;
+
+import android.content.Context;
+import android.support.v7.widget.CardView;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.mozilla.gecko.R;
+
+class HistoryRecyclerAdapter extends RecyclerView.Adapter<HistoryRecyclerAdapter.ViewHolder> {
+
+    private final Context context;
+    private final String[] items = {
+            "What Do The Reviews Have To Say About the New Ghostbusters",
+            "New “Leaf” Is More Efficient Than Natural Photosynthesis",
+            "Zero-cost futures in Rust",
+            "Indie Hackers: Learn how developers are making money",
+            "The fight to cheat death is heating up"
+    };
+
+    HistoryRecyclerAdapter(Context context) {
+        this.context = context;
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View v = LayoutInflater
+                .from(context)
+                .inflate(R.layout.activity_stream_card_history_item, parent, false);
+        return new ViewHolder(v);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        holder.vLabel.setText(items[position]);
+    }
+
+    @Override
+    public int getItemCount() {
+        return items.length;
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+        TextView vLabel;
+        ViewHolder(View itemView) {
+            super(itemView);
+            vLabel = (TextView) itemView.findViewById(R.id.card_history_label);
+        }
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/home/activitystream/MainRecyclerAdapter.java
@@ -0,0 +1,140 @@
+package org.mozilla.gecko.home.activitystream;
+
+
+import android.content.Context;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.mozilla.gecko.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+class MainRecyclerAdapter extends RecyclerView.Adapter<MainRecyclerAdapter.ViewHolder> {
+
+    private final Context context;
+
+    private static final int VIEW_TYPE_TOP_SITES = 0;
+    private static final int VIEW_TYPE_HIGHLIGHTS = 1;
+    private static final int VIEW_TYPE_HISTORY = 2;
+
+    private static final int VIEW_SIZE_TOP_SITES = 115;
+    private static final int VIEW_SIZE_HIGHLIGHTS = 220;
+    private static final int VIEW_SIZE_HISTORY = 80;
+
+    private final List<Item> categories = Arrays.asList(
+            new Item("Top Sites"),
+            new Item("Highlights"),
+            new Item("History")
+    );
+
+    MainRecyclerAdapter(Context context) {
+        this.context = context;
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View itemView = LayoutInflater
+                .from(context)
+                .inflate(R.layout.activity_stream_category, parent, false);
+        return new ViewHolder(itemView);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        LinearLayoutManager llm = new LinearLayoutManager(context);
+        if (position == VIEW_TYPE_TOP_SITES) {
+            holder.vCategoryRv.setAdapter(new TopSitesRecyclerAdapter(context));
+            setCorrectedLayoutOrientation(llm, LinearLayoutManager.HORIZONTAL);
+            setCorrectedLayoutHeight(context, VIEW_SIZE_TOP_SITES, holder, llm);
+        } else if (position == VIEW_TYPE_HIGHLIGHTS) {
+            holder.vCategoryRv.setAdapter(new HighlightRecyclerAdapter(context));
+            setCorrectedLayoutOrientation(llm, LinearLayoutManager.HORIZONTAL);
+            setCorrectedLayoutHeight(context, VIEW_SIZE_HIGHLIGHTS, holder, llm);
+        } else if (position == VIEW_TYPE_HISTORY) {
+            holder.vCategoryRv.setAdapter(new HistoryRecyclerAdapter(context));
+            setCorrectedLayoutOrientation(llm, LinearLayoutManager.VERTICAL);
+            setCorrectedLayoutHeight(context, VIEW_SIZE_HISTORY, holder, llm);
+        }
+
+        holder.vCategoryRv.setLayoutManager(llm);
+
+        if (position != VIEW_TYPE_HISTORY) {
+            holder.vTitle.setText(categories.get(position).getLabel());
+            holder.vMore.setVisibility(View.VISIBLE);
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        return categories.size();
+    }
+
+    private void setCorrectedLayoutHeight(final Context context,
+                                          final int height,
+                                          final ViewHolder holder,
+                                          final LinearLayoutManager llm) {
+        int correctedHeight = height;
+        if (isHistoryView(llm)) {
+            int holderItemCount;
+            holderItemCount = holder.vCategoryRv.getAdapter().getItemCount();
+            correctedHeight = height * holderItemCount;
+        }
+        setHolderRecyclerViewHeight(holder,
+                getActualPixelValue(context, correctedHeight));
+    }
+
+    private static int getActualPixelValue(Context context, int height) {
+        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height,
+                context.getResources().getDisplayMetrics());
+    }
+
+    private static void setHolderRecyclerViewHeight(ViewHolder holder, int height) {
+        holder.vCategoryRv.setLayoutParams(new LinearLayout.LayoutParams(
+                holder.vCategoryRv.getLayoutParams().width, height));
+    }
+
+    private static boolean isHistoryView(LinearLayoutManager llm) {
+        return llm.getOrientation() == LinearLayoutManager.VERTICAL;
+    }
+
+    private void setCorrectedLayoutOrientation(final LinearLayoutManager lm,
+                                               final int orientation) {
+        lm.setOrientation(orientation);
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+        TextView vTitle;
+        TextView vMore;
+        RecyclerView vCategoryRv;
+        ViewHolder(View itemView) {
+            super(itemView);
+            vTitle = (TextView) itemView.findViewById(R.id.category_title);
+            vMore = (TextView) itemView.findViewById(R.id.category_more_link);
+            vCategoryRv = (RecyclerView) itemView.findViewById(R.id.recycler_category);
+        }
+    }
+
+    static class Item {
+        String label;
+
+        Item(String label) {
+            setLabel(label);
+        }
+
+        public void setLabel(String label) {
+            this.label = label;
+        }
+
+        public String getLabel() {
+            return label;
+        }
+    }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/home/activitystream/MainRecyclerLayout.java
@@ -0,0 +1,27 @@
+package org.mozilla.gecko.home.activitystream;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import org.mozilla.gecko.R;
+
+public class MainRecyclerLayout extends LinearLayout {
+    public MainRecyclerLayout(Context context) {
+        super(context);
+        RecyclerView rv = (RecyclerView) findViewById(R.id.activity_stream_main_recyclerview);
+        rv.setAdapter(new MainRecyclerAdapter(context));
+        rv.setLayoutManager(new LinearLayoutManager(context));
+    }
+
+    public MainRecyclerLayout(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public MainRecyclerLayout(Context context, @Nullable AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/home/activitystream/TopSitesRecyclerAdapter.java
@@ -0,0 +1,55 @@
+package org.mozilla.gecko.home.activitystream;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.mozilla.gecko.R;
+
+class TopSitesRecyclerAdapter extends RecyclerView.Adapter<TopSitesRecyclerAdapter.ViewHolder> {
+
+    private final Context context;
+    private final String[] items = {
+            "FastMail",
+            "Firefox",
+            "Mozilla",
+            "Hacker News",
+            "Github",
+            "YouTube",
+            "Google Maps"
+    };
+
+    TopSitesRecyclerAdapter(Context context) {
+        this.context = context;
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View v = LayoutInflater
+                .from(context)
+                .inflate(R.layout.activity_stream_card_top_sites_item, parent, false);
+        return new ViewHolder(v);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        holder.vLabel.setText(items[position]);
+    }
+
+    @Override
+    public int getItemCount() {
+        return items.length;
+    }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+        TextView vLabel;
+        ViewHolder(View itemView) {
+            super(itemView);
+            vLabel = (TextView) itemView.findViewById(R.id.card_row_label);
+        }
+    }
+}
+
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -425,16 +425,21 @@ gbjar.sources += ['java/org/mozilla/geck
     'GeckoProfilesProvider.java',
     'GeckoUpdateReceiver.java',
     'GlobalHistory.java',
     'GuestSession.java',
     'health/HealthRecorder.java',
     'health/SessionInformation.java',
     'health/StubbedHealthRecorder.java',
     'home/activitystream/ActivityStream.java',
+    'home/activitystream/HighlightRecyclerAdapter.java',
+    'home/activitystream/HistoryRecyclerAdapter.java',
+    'home/activitystream/MainRecyclerAdapter.java',
+    'home/activitystream/MainRecyclerLayout.java',
+    'home/activitystream/TopSitesRecyclerAdapter.java',
     'home/BookmarkFolderView.java',
     'home/BookmarkScreenshotRow.java',
     'home/BookmarksListAdapter.java',
     'home/BookmarksListView.java',
     'home/BookmarksPanel.java',
     'home/BrowserSearch.java',
     'home/ClientsAdapter.java',
     'home/CombinedHistoryAdapter.java',
--- a/mobile/android/base/resources/layout/activity_stream.xml
+++ b/mobile/android/base/resources/layout/activity_stream.xml
@@ -1,16 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <org.mozilla.gecko.home.activitystream.ActivityStream xmlns:android="http://schemas.android.com/apk/res/android"
-                                                      xmlns:tools="http://schemas.android.com/tools"
                                                       android:orientation="vertical"
                                                       android:layout_width="match_parent"
                                                       android:layout_height="match_parent"
-                                                      android:background="@android:color/white">
+                                                      android:background="#FAFAFA">
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/activity_stream_main_recyclerview"
+        android:layout_marginTop="12dp"
+        android:layout_marginLeft="12dp"
+        android:layout_marginStart="12dp"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
 
-    <TextView
-        tools:ignore="HardcodedText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="Activity Stream \\o/"
-        android:id="@+id/textView"
-        android:layout_gravity="center_horizontal"/>
 </org.mozilla.gecko.home.activitystream.ActivityStream>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/activity_stream_card_highlights_item.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_marginRight="5dp"
+    android:layout_marginEnd="5dp"
+    android:layout_marginTop="10dp"
+    android:layout_marginBottom="10dp"
+    android:layout_width="180dp"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:baselineAligned="false"
+        android:orientation="vertical">
+
+        <FrameLayout
+            android:background="@color/disabled_grey"
+            android:layout_width="match_parent"
+            android:layout_height="140dp">
+
+            <ImageView
+                android:visibility="visible"
+                android:id="@+id/card_highlights_thumbnail"
+                android:src="@drawable/favicon_globe"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+        </FrameLayout>
+        <RelativeLayout
+            android:padding="3dp"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextView
+                android:id="@+id/card_highlights_label"
+                android:textSize="10sp"
+                android:maxLines="3"
+                android:ellipsize="end"
+                android:textColor="@android:color/black"
+                android:layout_marginLeft="4dp"
+                android:layout_marginStart="4dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"
+                android:layout_toStartOf="@+id/card_highlights_time_since"
+                android:layout_toLeftOf="@+id/card_highlights_time_since"
+                android:layout_alignParentTop="true"
+                android:layout_above="@+id/linearLayout3"/>
+
+            <TextView
+                android:id="@+id/card_highlights_time_since"
+                android:text="2h"
+                android:textSize="10sp"
+                android:layout_marginEnd="4dp"
+                android:layout_marginRight="4dp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:layout_alignParentRight="true"
+                android:layout_alignParentEnd="true"/>
+
+            <LinearLayout
+                android:layout_marginTop="5dp"
+                android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:layout_alignParentStart="true"
+                android:layout_alignParentLeft="true"
+                android:id="@+id/linearLayout3">
+
+                <ImageView
+                    android:src="@drawable/search_icon_active"
+                    android:alpha="0.5"
+                    android:layout_width="18dp"
+                    android:layout_height="18dp"/>
+
+                <TextView
+                    android:text="Bookmarked"
+                    android:textSize="10sp"
+                    android:gravity="center_vertical"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"/>
+            </LinearLayout>
+        </RelativeLayout>
+    </LinearLayout>
+</android.support.v7.widget.CardView>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/activity_stream_card_history_item.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_marginRight="5dp"
+    android:layout_marginEnd="5dp"
+    android:layout_marginTop="10dp"
+    android:layout_marginBottom="0dp"
+    android:layout_width="match_parent"
+    android:layout_height="60dp">
+
+    <RelativeLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="match_parent">
+
+        <FrameLayout
+            android:id="@+id/frameLayout1"
+            android:background="@android:color/holo_blue_bright"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
+            android:layout_width="60dp"
+            android:layout_height="match_parent">
+
+            <ImageView
+                android:src="@drawable/favicon_globe"
+                android:scaleType="fitCenter"
+                android:layout_gravity="center"
+                android:layout_width="30dp"
+                android:layout_height="30dp"/>
+        </FrameLayout>
+
+        <FrameLayout
+            android:id="@+id/frameLayout2"
+            android:layout_toRightOf="@id/frameLayout1"
+            android:layout_toEndOf="@id/frameLayout1"
+            android:layout_width="50dp"
+            android:layout_height="match_parent">
+
+            <ImageView
+                android:src="@drawable/tab_new"
+                android:alpha="0.5"
+                android:scaleType="fitCenter"
+                android:layout_gravity="center"
+                android:layout_width="15dp"
+                android:layout_height="15dp"/>
+        </FrameLayout>
+
+        <FrameLayout
+            android:id="@+id/frameLayout3"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_toRightOf="@id/frameLayout2"
+            android:layout_toEndOf="@id/frameLayout2">
+
+            <TextView
+                android:id="@+id/card_history_label"
+                android:gravity="center_vertical"
+                android:maxLines="2"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+
+        </FrameLayout>
+
+        <LinearLayout
+            android:id="@+id/linearLayout5"
+            android:orientation="vertical"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentEnd="true"
+            android:layout_width="40dp"
+            android:layout_height="match_parent">
+
+            <TextView
+                android:id="@+id/card_history_time_since"
+                android:text="20m"
+                android:textSize="12sp"
+                android:gravity="bottom|right"
+                android:paddingRight="4dp"
+                android:paddingEnd="4dp"
+                android:layout_margin="4dp"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+
+        </LinearLayout>
+    </RelativeLayout>
+</android.support.v7.widget.CardView>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/activity_stream_card_top_sites_item.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_marginRight="5dp"
+    android:layout_marginEnd="5dp"
+    android:layout_marginTop="10dp"
+    android:layout_marginBottom="10dp"
+    android:orientation="vertical"
+    android:layout_width="90dp"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:baselineAligned="false">
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:background="@color/disabled_grey"
+            android:layout_height="70dp">
+
+            <ImageView
+                android:src="@drawable/favicon_globe"
+                android:scaleType="fitCenter"
+                android:layout_gravity="center"
+                android:layout_width="40dp"
+                android:layout_height="40dp"/>
+        </FrameLayout>
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextView
+                android:id="@+id/card_row_label"
+                android:text="Firefox"
+                android:textSize="10sp"
+                android:textStyle="bold"
+                android:layout_gravity="center"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+        </FrameLayout>
+    </LinearLayout>
+</android.support.v7.widget.CardView>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/activity_stream_category.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="wrap_content"
+              android:layout_height="match_parent">
+
+        <RelativeLayout
+            android:orientation="horizontal"
+            android:layout_marginRight="12dp"
+            android:layout_marginEnd="12dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:id="@+id/category_title"
+                android:textStyle="bold"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_alignParentStart="true"/>
+
+            <TextView
+                android:id="@+id/category_more_link"
+                android:text="More"
+                android:visibility="invisible"
+                android:textAllCaps="true"
+                android:textSize="14sp"
+                android:textColor="@android:color/holo_orange_dark"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_alignParentEnd="true"/>
+
+        </RelativeLayout>
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/recycler_category"
+            android:layout_marginTop="5dp"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+</LinearLayout>