Bug 1288106 - Explore implementing layout manager for multi row panel r=ahunt
MozReview-Commit-ID: J2DSx9UYNFN
--- 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
@@ -434,16 +434,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>