diff --git a/briar-android/res/layout/activity_contact_list.xml b/briar-android/res/layout/activity_contact_list.xml
index 02faeff9bc82ed922eb43829e452a3ff175431eb..333dac2035f20f4006695966a4c3890c68c6373b 100644
--- a/briar-android/res/layout/activity_contact_list.xml
+++ b/briar-android/res/layout/activity_contact_list.xml
@@ -1,54 +1,28 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<android.support.design.widget.CoordinatorLayout
 	xmlns:android="http://schemas.android.com/apk/res/android"
 	xmlns:app="http://schemas.android.com/apk/res-auto"
-	xmlns:tools="http://schemas.android.com/tools"
+	android:id="@+id/coordinatorLayout"
 	android:layout_width="match_parent"
-	android:layout_height="match_parent"
-	android:orientation="vertical"
-	android:gravity="center">
+	android:layout_height="match_parent">
 
-	<android.support.design.widget.CoordinatorLayout
-		android:id="@+id/coordinatorLayout"
+	<org.briarproject.android.util.BriarRecyclerView
+		xmlns:android="http://schemas.android.com/apk/res/android"
+		android:id="@+id/contactList"
 		android:layout_width="match_parent"
-		android:layout_height="match_parent">
+		android:layout_height="match_parent"/>
 
-		<android.support.v7.widget.RecyclerView
-			android:id="@+id/contactList"
-			android:scrollbars="vertical"
-			android:layout_width="match_parent"
-			android:layout_height="match_parent"
-			tools:listitem="@layout/list_item_contact"/>
-
-		<android.support.design.widget.FloatingActionButton
-			android:id="@+id/addContactFAB"
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
-			android:layout_gravity="bottom|end"
-			android:layout_margin="@dimen/margin_activity_horizontal"
-			android:src="@drawable/ic_add_white"
-			app:fabSize="normal"
-			app:elevation="4dp"
-			app:layout_anchor="@id/contactList"
-			app:layout_anchorGravity="bottom|right|end"
-			app:layout_behavior="org.briarproject.android.util.HideFabOnScrollBehavior"/>
-
-	</android.support.design.widget.CoordinatorLayout>
-
-	<ProgressBar
-		android:id="@+id/progressBar"
-		style="?android:attr/progressBarStyleLarge"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:indeterminate="true"
-		android:visibility="gone"/>
-
-	<TextView
-		android:id="@+id/emptyView"
+	<android.support.design.widget.FloatingActionButton
+		android:id="@+id/addContactFAB"
 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
-		android:textSize="@dimen/text_size_large"
-		android:text="@string/no_contacts"
-		android:visibility="gone"/>
+		android:layout_gravity="bottom|end"
+		android:layout_margin="@dimen/margin_activity_horizontal"
+		android:src="@drawable/ic_add_white"
+		app:fabSize="normal"
+		app:elevation="4dp"
+		app:layout_anchor="@id/contactList"
+		app:layout_anchorGravity="bottom|right|end"
+		app:layout_behavior="org.briarproject.android.util.HideFabOnScrollBehavior"/>
 
-</LinearLayout>
\ No newline at end of file
+</android.support.design.widget.CoordinatorLayout>
diff --git a/briar-android/res/layout/briar_recycler_view.xml b/briar-android/res/layout/briar_recycler_view.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d84769f3e15ce47b19f380d2973da34039cf3ac0
--- /dev/null
+++ b/briar-android/res/layout/briar_recycler_view.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+	<android.support.v7.widget.RecyclerView
+		android:id="@+id/recyclerView"
+		android:layout_width="match_parent"
+		android:layout_height="match_parent"
+		android:scrollbars="vertical"
+		tools:listitem="@layout/list_item_contact"/>
+
+	<ProgressBar
+		android:id="@+id/progressBar"
+		style="?android:attr/progressBarStyleLarge"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_centerInParent="true"/>
+
+	<TextView
+		android:id="@+id/emptyView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_centerInParent="true"
+		android:textSize="@dimen/text_size_large"
+		android:text="@string/no_data"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index 0a7c7b1f0e5f97faa642e5295c5634c8dd28b43a..db7f2bfc4c1be306c8c2a197a441f760a8cdb141 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -128,4 +128,5 @@
     <string name="dialog_message_lost_password">Password recovery is not possible. Do you wish to delete your user, all contacts, and re-register ?</string>
     <string name="dialog_title_delete_contact">Confirm Contact Deletion</string>
     <string name="dialog_message_delete_contact">Are you sure that you want to remove this contact and all messages exchanged with this contact?</string>
+    <string name="no_data">No data</string>
 </resources>
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
index b3fec8962a6a67a2ea8821b88be40a3752e64c72..79188325459bfcc0ae347f82f233eb40e653cacd 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
@@ -4,15 +4,12 @@ import android.content.Intent;
 import android.os.Bundle;
 import android.support.design.widget.FloatingActionButton;
 import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
 import android.view.View;
-import android.view.View.OnCreateContextMenuListener;
-import android.widget.ProgressBar;
-import android.widget.TextView;
 
 import org.briarproject.R;
 import org.briarproject.android.BriarActivity;
 import org.briarproject.android.invitation.AddContactActivity;
+import org.briarproject.android.util.BriarRecyclerView;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager;
@@ -38,22 +35,18 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
-import static android.view.View.GONE;
-import static android.view.View.VISIBLE;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 
 public class ContactListActivity extends BriarActivity
-		implements OnCreateContextMenuListener, EventListener {
+		implements EventListener {
 
 	private static final Logger LOG =
 			Logger.getLogger(ContactListActivity.class.getName());
 
 	@Inject private ConnectionRegistry connectionRegistry;
-	private TextView empty = null;
 	private ContactListAdapter adapter = null;
-	private RecyclerView list = null;
-	private ProgressBar loading = null;
+	private BriarRecyclerView list = null;
 
 	// Fields that are accessed from background threads must be volatile
 	@Inject private volatile ContactManager contactManager;
@@ -67,18 +60,10 @@ public class ContactListActivity extends BriarActivity
 		setContentView(R.layout.activity_contact_list);
 
 		adapter = new ContactListAdapter(this);
-		list = (RecyclerView) findViewById(R.id.contactList);
+		list = (BriarRecyclerView) findViewById(R.id.contactList);
 		list.setLayoutManager(new LinearLayoutManager(this));
 		list.setAdapter(adapter);
-		list.setOnCreateContextMenuListener(this);
-		list.setVisibility(GONE);
-
-		// Show a notice when there are no contacts
-		empty = (TextView) findViewById(R.id.emptyView);
-
-		// Show a progress bar while the list is loading
-		loading = (ProgressBar) findViewById(R.id.progressBar);
-		loading.setVisibility(VISIBLE);
+		list.setEmptyText(getString(R.string.no_contacts));
 
 		// Show a floating action button
 		FloatingActionButton fab = (FloatingActionButton) findViewById(
@@ -104,18 +89,12 @@ public class ContactListActivity extends BriarActivity
 	public void onResume() {
 		super.onResume();
 		eventBus.addListener(this);
+
 		loadContacts();
 	}
 
 
 	private void loadContacts() {
-		runOnUiThread(new Runnable() {
-			public void run() {
-				empty.setVisibility(GONE);
-				list.setVisibility(GONE);
-				loading.setVisibility(VISIBLE);
-			}
-		});
 		runOnDbThread(new Runnable() {
 			public void run() {
 				try {
@@ -155,15 +134,11 @@ public class ContactListActivity extends BriarActivity
 		runOnUiThread(new Runnable() {
 			public void run() {
 				if (contacts.size() > 0) {
-					list.setVisibility(VISIBLE);
-					empty.setVisibility(GONE);
+					adapter.addAll(contacts);
 				} else {
-					list.setVisibility(GONE);
-					empty.setVisibility(VISIBLE);
+					// no contacts to display, make sure progress bar is hidden
+					list.showData();
 				}
-				loading.setVisibility(GONE);
-
-				adapter.addAll(contacts);
 			}
 		});
 	}
@@ -228,11 +203,6 @@ public class ContactListActivity extends BriarActivity
 				ContactListItem item = adapter.findItem(c);
 				if (item != null) {
 					adapter.remove(item);
-
-					if (adapter.isEmpty()) {
-						empty.setVisibility(VISIBLE);
-						list.setVisibility(GONE);
-					}
 				}
 			}
 		});
diff --git a/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java b/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e50c1d5d53a55ff8cca34f58cf66d02200ecab9
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java
@@ -0,0 +1,119 @@
+package org.briarproject.android.util;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import org.briarproject.R;
+
+public class BriarRecyclerView extends FrameLayout {
+
+	private RecyclerView recyclerView;
+	private TextView emptyView;
+	private ProgressBar progressBar;
+	private RecyclerView.AdapterDataObserver emptyObserver;
+
+	public BriarRecyclerView(Context context) {
+		super(context);
+
+		initViews();
+	}
+
+	public BriarRecyclerView(Context context, AttributeSet attrs) {
+		super(context, attrs);
+
+		initViews();
+	}
+
+	public BriarRecyclerView(Context context, AttributeSet attrs,
+			int defStyle) {
+		super(context, attrs, defStyle);
+
+		initViews();
+	}
+
+	private void initViews() {
+		if (isInEditMode()) {
+			return;
+		}
+
+		View v = LayoutInflater.from(getContext()).inflate(
+				R.layout.briar_recycler_view, this, true);
+
+		recyclerView = (RecyclerView) v.findViewById(R.id.recyclerView);
+		emptyView = (TextView) v.findViewById(R.id.emptyView);
+		progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
+
+		showProgressBar();
+
+		emptyObserver = new RecyclerView.AdapterDataObserver() {
+			@Override
+			public void onChanged() {
+				showData();
+			}
+
+			@Override
+			public void onItemRangeInserted(int positionStart, int itemCount) {
+				super.onItemRangeInserted(positionStart, itemCount);
+				onChanged();
+			}
+		};
+	}
+
+	public void setLayoutManager(RecyclerView.LayoutManager layout) {
+		recyclerView.setLayoutManager(layout);
+	}
+
+	public void setAdapter(RecyclerView.Adapter adapter) {
+		RecyclerView.Adapter oldAdapter = recyclerView.getAdapter();
+		if (oldAdapter != null) {
+			oldAdapter.unregisterAdapterDataObserver(emptyObserver);
+		}
+
+		recyclerView.setAdapter(adapter);
+
+		if (adapter != null) {
+			adapter.registerAdapterDataObserver(emptyObserver);
+
+			if (adapter.getItemCount() > 0) {
+				// only show data if adapter has data already
+				// otherwise progress bar is shown
+				emptyObserver.onChanged();
+			}
+		}
+	}
+
+	public void setEmptyText(String text) {
+		emptyView.setText(text);
+	}
+
+	public void showProgressBar() {
+		recyclerView.setVisibility(View.INVISIBLE);
+		emptyView.setVisibility(View.INVISIBLE);
+		progressBar.setVisibility(View.VISIBLE);
+	}
+
+	public void showData() {
+		RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();
+		if (adapter != null) {
+			if (adapter.getItemCount() == 0) {
+				emptyView.setVisibility(View.VISIBLE);
+				recyclerView.setVisibility(View.INVISIBLE);
+			} else {
+				emptyView.setVisibility(View.INVISIBLE);
+				recyclerView.setVisibility(View.VISIBLE);
+			}
+			progressBar.setVisibility(View.INVISIBLE);
+		}
+	}
+
+	public RecyclerView getRecyclerView() {
+		return recyclerView;
+	}
+
+}