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 336330 0becc8c0fd30a2ac5bc1f34d80fea7b7afa4b97a
parent 336329 873e1bd0833abf954ac0e0fab6c43b1ce5380e1c
child 336331 c499af51f167880b1a51458a2d7d25e35e2f4b57
push id10033
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:50:26 +0000
treeherdermozilla-aurora@5dddbefdf759 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahunt
bugs1288106
milestone51.0a1
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>