diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 03664f0efa100ae0cba4cdeaeadc960454b77007..702e7e6976c6f524859ff2561788bfb2b2ef643d 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -150,6 +150,36 @@
 				/>
 		</activity>
 
+		<activity
+			android:name=".android.blogs.CreateBlogActivity"
+			android:label="@string/blogs_my_blogs_label"
+			android:parentActivityName=".android.NavDrawerActivity">
+			<meta-data
+				android:name="android.support.PARENT_ACTIVITY"
+				android:value=".android.NavDrawerActivity"
+				/>
+		</activity>
+
+		<activity
+			android:name=".android.blogs.BlogActivity"
+			android:parentActivityName=".android.NavDrawerActivity">
+			<meta-data
+				android:name="android.support.PARENT_ACTIVITY"
+				android:value=".android.NavDrawerActivity"
+				/>
+		</activity>
+
+		<activity
+			android:name=".android.blogs.WriteBlogPostActivity"
+			android:label="@string/blogs_write_blog_post"
+			android:parentActivityName=".android.blogs.BlogActivity"
+			android:windowSoftInputMode="stateVisible|adjustResize">
+			<meta-data
+				android:name="android.support.PARENT_ACTIVITY"
+				android:value=".android.blogs.BlogActivity"
+				/>
+		</activity>
+
 		<activity
 			android:name=".android.identity.CreateIdentityActivity"
 			android:label="@string/new_identity_title"
diff --git a/briar-android/res/drawable/ic_chat.xml b/briar-android/res/drawable/ic_chat.xml
new file mode 100644
index 0000000000000000000000000000000000000000..04f9fdcdfd7427decad183a5e0a8ae3434f2b4f8
--- /dev/null
+++ b/briar-android/res/drawable/ic_chat.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:alpha="0.54"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0">
+	<path
+		android:fillColor="#FF000000"
+		android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z"/>
+</vector>
diff --git a/briar-android/res/drawable/ic_repeat.xml b/briar-android/res/drawable/ic_repeat.xml
new file mode 100644
index 0000000000000000000000000000000000000000..115dbda813aa64bde9f6c4a91ba0e92ef4ddda1b
--- /dev/null
+++ b/briar-android/res/drawable/ic_repeat.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:alpha="0.54"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0">
+	<path
+		android:fillColor="#FF000000"
+		android:pathData="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
+</vector>
diff --git a/briar-android/res/drawable/trust_indicator_anonymous.xml b/briar-android/res/drawable/trust_indicator_anonymous.xml
index 99ccadd1bea9fae5483b327fcfc48f89d574638f..82214b8d4fccc90ac9c85f520467d160cebdf546 100644
--- a/briar-android/res/drawable/trust_indicator_anonymous.xml
+++ b/briar-android/res/drawable/trust_indicator_anonymous.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="15dp"
+        android:width="31dp"
+        android:height="12dp"
         android:viewportHeight="20"
         android:viewportWidth="49">
 	<path
diff --git a/briar-android/res/drawable/trust_indicator_unknown.xml b/briar-android/res/drawable/trust_indicator_unknown.xml
index ec65b5a5528a0f636c76c6f990c0e35b91afbfbb..63e6ab7afaedca0935498be5f0847429846b665f 100644
--- a/briar-android/res/drawable/trust_indicator_unknown.xml
+++ b/briar-android/res/drawable/trust_indicator_unknown.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="15dp"
+        android:width="31dp"
+        android:height="12dp"
         android:viewportHeight="20"
         android:viewportWidth="49">
 	<path
diff --git a/briar-android/res/drawable/trust_indicator_unverified.xml b/briar-android/res/drawable/trust_indicator_unverified.xml
index 3697685b0666be83b532c311431426749aa993e7..97af93df956bd198b07ba367c960741a7cf7becc 100644
--- a/briar-android/res/drawable/trust_indicator_unverified.xml
+++ b/briar-android/res/drawable/trust_indicator_unverified.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="15dp"
+        android:width="31dp"
+        android:height="12dp"
         android:viewportHeight="20"
         android:viewportWidth="49">
 	<path
diff --git a/briar-android/res/drawable/trust_indicator_verified.xml b/briar-android/res/drawable/trust_indicator_verified.xml
index 40a8ff035f262a9294255d6279bae1a0de241240..5b37c223ad2a2cba22690643cf43d0ea90eb81cc 100644
--- a/briar-android/res/drawable/trust_indicator_verified.xml
+++ b/briar-android/res/drawable/trust_indicator_verified.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="15dp"
+        android:width="31dp"
+        android:height="12dp"
         android:viewportHeight="20"
         android:viewportWidth="49">
 	<path
diff --git a/briar-android/res/layout/activity_blog.xml b/briar-android/res/layout/activity_blog.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3ff8d409e0e60ee2b749b2e51726b8af8e5cab7c
--- /dev/null
+++ b/briar-android/res/layout/activity_blog.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+	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.v4.view.ViewPager
+		android:id="@+id/pager"
+		android:layout_width="match_parent"
+		android:layout_height="match_parent"
+		tools:context=".android.blogs.BlogActivity"/>
+
+	<ProgressBar
+		android:id="@+id/progressBar"
+		style="?android:attr/progressBarStyleLarge"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_gravity="center"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/briar-android/res/layout/activity_create_blog.xml b/briar-android/res/layout/activity_create_blog.xml
new file mode 100644
index 0000000000000000000000000000000000000000..31a1c3df85e8983dc973c65a3529486432c3e86b
--- /dev/null
+++ b/briar-android/res/layout/activity_create_blog.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView
+	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:layout_width="match_parent"
+	android:layout_height="match_parent">
+
+	<LinearLayout
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:gravity="center_horizontal"
+		android:orientation="vertical"
+		android:padding="@dimen/margin_activity_horizontal"
+		tools:context=".android.blogs.CreateBlogActivity">
+
+		<android.support.design.widget.TextInputLayout
+			android:id="@+id/titleLayout"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			app:counterEnabled="true"
+			app:counterOverflowTextAppearance="@style/BriarTextCounter.Overflow">
+
+			<android.support.design.widget.TextInputEditText
+				android:id="@+id/titleInput"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:hint="@string/blogs_my_blogs_create_hint_title"/>
+
+		</android.support.design.widget.TextInputLayout>
+
+		<android.support.design.widget.TextInputLayout
+			android:id="@+id/descLayout"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			app:counterEnabled="true"
+			app:counterOverflowTextAppearance="@style/BriarTextCounter.Overflow">
+
+			<android.support.design.widget.TextInputEditText
+				android:id="@+id/descInput"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:hint="@string/blogs_my_blogs_create_hint_desc"/>
+
+		</android.support.design.widget.TextInputLayout>
+
+		<TextView
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:text="@string/blogs_my_blogs_create_hint_desc_explanation"/>
+
+		<Button
+			android:id="@+id/createBlogButton"
+			style="@style/BriarButton"
+			android:layout_marginTop="@dimen/margin_activity_vertical"
+			android:enabled="false"
+			android:text="@string/blogs_my_blogs_create"/>
+
+		<ProgressBar
+			android:id="@+id/createBlogProgressBar"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="@dimen/margin_activity_vertical"
+			android:indeterminate="true"
+			android:visibility="gone"/>
+
+	</LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/briar-android/res/layout/activity_create_forum.xml b/briar-android/res/layout/activity_create_forum.xml
index b48bce90fac407609c4b4ad898636fe057e43563..96afb47a99fdc2174122ccf0635077a185ef73e5 100644
--- a/briar-android/res/layout/activity_create_forum.xml
+++ b/briar-android/res/layout/activity_create_forum.xml
@@ -32,9 +32,6 @@
 	<Button
 		style="@style/BriarButton"
 		android:id="@+id/createForumButton"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_centerHorizontal="true"
 		android:text="@string/create_forum_button" />
 
 	<ProgressBar
@@ -42,8 +39,6 @@
 		android:layout_height="wrap_content"
 		android:layout_width="wrap_content"
 		android:indeterminate="true"
-		android:layout_centerHorizontal="true"
 		android:visibility="gone" />
 
-
 </LinearLayout>
diff --git a/briar-android/res/layout/activity_create_identity.xml b/briar-android/res/layout/activity_create_identity.xml
index 23309bfa83e7f539fcffad2c5340b0bc806707fe..5919662cfba59435b0dfb13028e77fa63c8a26cb 100644
--- a/briar-android/res/layout/activity_create_identity.xml
+++ b/briar-android/res/layout/activity_create_identity.xml
@@ -34,8 +34,6 @@
 	<Button
 		android:id="@+id/createIdentityButton"
 		style="@style/BriarButton"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
 		android:layout_gravity="center_horizontal"
 		android:enabled="false"
 		android:text="@string/create_identity_button"/>
diff --git a/briar-android/res/layout/activity_write_blog_post.xml b/briar-android/res/layout/activity_write_blog_post.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5c3bcf3804982874598a6f63f4f28f47e9ae3607
--- /dev/null
+++ b/briar-android/res/layout/activity_write_blog_post.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+	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:layout_width="match_parent"
+	android:layout_height="match_parent"
+	android:orientation="vertical"
+	android:padding="@dimen/margin_small"
+	tools:context=".android.blogs.WriteBlogPostActivity">
+
+	<android.support.design.widget.TextInputLayout
+		android:id="@+id/titleLayout"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:visibility="gone"
+		app:counterEnabled="true"
+		app:counterOverflowTextAppearance="@style/BriarTextCounter.Overflow">
+
+		<android.support.design.widget.TextInputEditText
+			android:id="@+id/titleInput"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:hint="@string/blogs_write_blog_post_title_hint"
+			android:inputType="textCapWords|textCapSentences|textAutoCorrect"/>
+
+	</android.support.design.widget.TextInputLayout>
+
+	<EditText
+		android:id="@+id/bodyInput"
+		android:layout_width="match_parent"
+		android:layout_height="0dp"
+		android:layout_weight="1"
+		android:gravity="bottom"
+		android:hint="@string/blogs_write_blog_post_body_hint"
+		android:inputType="textMultiLine|textLongMessage|textCapSentences|textAutoCorrect">
+
+		<requestFocus/>
+
+	</EditText>
+
+	<Button
+		android:id="@+id/publishButton"
+		style="@style/BriarButton"
+		android:enabled="false"
+		android:text="@string/blogs_publish_blog_post"/>
+
+	<ProgressBar
+		android:id="@+id/progressBar"
+		style="?android:attr/progressBarStyle"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_gravity="center"
+		android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/briar-android/res/layout/fragment_blog.xml b/briar-android/res/layout/fragment_blog.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c0ac19fbe0eac0b76817a2c7bc9bb8566f6aa840
--- /dev/null
+++ b/briar-android/res/layout/fragment_blog.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<org.briarproject.android.util.BriarRecyclerView
+	android:id="@+id/postList"
+	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:layout_width="match_parent"
+	android:layout_height="match_parent"
+	app:scrollToEnd="false"
+	tools:context=".android.blogs.BlogActivity"/>
diff --git a/briar-android/res/layout/fragment_blog_post.xml b/briar-android/res/layout/fragment_blog_post.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7e5e73ed1c049c5aa805b6027b212557ef0b4995
--- /dev/null
+++ b/briar-android/res/layout/fragment_blog_post.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:tools="http://schemas.android.com/tools"
+	android:layout_width="match_parent"
+	android:layout_height="wrap_content">
+
+	<RelativeLayout
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:padding="@dimen/margin_activity_horizontal">
+
+		<de.hdodenhof.circleimageview.CircleImageView
+			android:id="@+id/avatar"
+			style="@style/BriarAvatar"
+			android:layout_width="30dp"
+			android:layout_height="30dp"
+			android:layout_marginRight="@dimen/margin_medium"
+			tools:src="@drawable/ic_launcher"/>
+
+		<TextView
+			android:id="@+id/authorName"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_alignTop="@+id/avatar"
+			android:layout_toEndOf="@+id/avatar"
+			android:layout_toRightOf="@+id/avatar"
+			android:textSize="@dimen/text_size_tiny"
+			tools:text="Author Name"/>
+
+		<TextView
+			android:id="@+id/date"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_alignBottom="@id/avatar"
+			android:layout_below="@+id/authorName"
+			android:layout_toEndOf="@+id/avatar"
+			android:layout_toRightOf="@+id/avatar"
+			android:gravity="bottom"
+			android:textSize="@dimen/text_size_tiny"
+			tools:text="yesterday"/>
+
+		<org.briarproject.android.util.TrustIndicatorView
+			android:id="@+id/trustIndicator"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginLeft="@dimen/margin_small"
+			android:layout_toRightOf="@+id/authorName"
+			tools:src="@drawable/trust_indicator_verified"/>
+
+		<TextView
+			android:id="@+id/title"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_alignParentLeft="true"
+			android:layout_alignParentStart="true"
+			android:layout_below="@+id/avatar"
+			android:layout_marginTop="@dimen/margin_medium"
+			android:textSize="@dimen/text_size_medium"
+			android:textStyle="bold"
+			tools:text="This Is A Blog Post Title"/>
+
+		<TextView
+			android:id="@+id/body"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:layout_below="@+id/title"
+			android:layout_marginTop="@dimen/margin_medium"
+			tools:text="Body of Blog Post. This could be insanely large or just a short text as well."/>
+
+	</RelativeLayout>
+
+</ScrollView>
diff --git a/briar-android/res/layout/fragment_blogs_list.xml b/briar-android/res/layout/fragment_blogs_list.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a552dc0fce49e2a0f2c2ca14340ab4c38ae51fff
--- /dev/null
+++ b/briar-android/res/layout/fragment_blogs_list.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This is just a placeholder to be replaced by the real My Blogs list -->
+<LinearLayout
+	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:orientation="vertical">
+
+	<TextView
+		android:id="@+id/num"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:gravity="center_horizontal"
+		android:padding="@dimen/margin_activity_horizontal"
+		android:textSize="128sp"
+		tools:text="1"/>
+
+	<TextView
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:padding="@dimen/margin_activity_horizontal"
+		android:text="There is nothing for you to see here.\n\nMove along and come back later."
+		android:textSize="@dimen/text_size_large"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/briar-android/res/layout/fragment_blogs_my.xml b/briar-android/res/layout/fragment_blogs_my.xml
index a552dc0fce49e2a0f2c2ca14340ab4c38ae51fff..288adfaa3937ea3d0b242b6c7177007b3e994d67 100644
--- a/briar-android/res/layout/fragment_blogs_my.xml
+++ b/briar-android/res/layout/fragment_blogs_my.xml
@@ -1,26 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- This is just a placeholder to be replaced by the real My Blogs list -->
-<LinearLayout
+<org.briarproject.android.util.BriarRecyclerView
 	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:orientation="vertical">
-
-	<TextView
-		android:id="@+id/num"
-		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:gravity="center_horizontal"
-		android:padding="@dimen/margin_activity_horizontal"
-		android:textSize="128sp"
-		tools:text="1"/>
-
-	<TextView
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:padding="@dimen/margin_activity_horizontal"
-		android:text="There is nothing for you to see here.\n\nMove along and come back later."
-		android:textSize="@dimen/text_size_large"/>
-
-</LinearLayout>
\ No newline at end of file
+	tools:listitem="@layout/list_item_blog"/>
diff --git a/briar-android/res/layout/introduction_message.xml b/briar-android/res/layout/introduction_message.xml
index a557059f14e3e5bac83394f247c3b8218ab17712..cf7e5a27e4fb5ebfa7999dd93e76bea4cf036702 100644
--- a/briar-android/res/layout/introduction_message.xml
+++ b/briar-android/res/layout/introduction_message.xml
@@ -123,10 +123,7 @@
 		<Button
 			android:id="@+id/makeIntroductionButton"
 			style="@style/BriarButton"
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:text="@string/introduction_button"
-			/>
+			android:text="@string/introduction_button"/>
 
 	</LinearLayout>
 
diff --git a/briar-android/res/layout/list_item_blog.xml b/briar-android/res/layout/list_item_blog.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1fe1f22db024383b68baea541fbfea8e11eb88e3
--- /dev/null
+++ b/briar-android/res/layout/list_item_blog.xml
@@ -0,0 +1,79 @@
+<?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="wrap_content"
+	android:layout_marginLeft="@dimen/listitem_horizontal_margin"
+	android:layout_marginStart="@dimen/listitem_horizontal_margin"
+	android:background="?attr/selectableItemBackground">
+
+	<org.briarproject.android.util.TextAvatarView
+		android:id="@+id/avatarView"
+		android:layout_width="@dimen/listitem_picture_frame_size"
+		android:layout_height="@dimen/listitem_picture_frame_size"
+		android:layout_alignParentLeft="true"
+		android:layout_alignParentStart="true"
+		android:layout_marginEnd="@dimen/listitem_horizontal_margin"
+		android:layout_marginRight="@dimen/listitem_horizontal_margin"
+		android:layout_marginTop="@dimen/margin_medium"/>
+
+	<TextView
+		android:id="@+id/nameView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignParentTop="true"
+		android:layout_marginTop="@dimen/listitem_horizontal_margin"
+		android:layout_toEndOf="@+id/avatarView"
+		android:layout_toRightOf="@+id/avatarView"
+		android:maxLines="2"
+		android:textColor="@color/briar_text_primary"
+		android:textSize="@dimen/text_size_medium"
+		tools:text="This is a name of a blog"/>
+
+	<TextView
+		android:id="@+id/postCountView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_below="@+id/nameView"
+		android:layout_marginBottom="@dimen/margin_small"
+		android:layout_toEndOf="@+id/avatarView"
+		android:layout_toRightOf="@+id/avatarView"
+		android:paddingTop="@dimen/margin_small"
+		android:textColor="@color/briar_text_secondary"
+		android:textSize="@dimen/text_size_small"
+		tools:text="1337 posts"/>
+
+	<TextView
+		android:id="@+id/dateView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignParentEnd="true"
+		android:layout_alignParentRight="true"
+		android:layout_below="@+id/nameView"
+		android:layout_marginEnd="@dimen/listitem_horizontal_margin"
+		android:layout_marginRight="@dimen/listitem_horizontal_margin"
+		android:paddingTop="@dimen/margin_small"
+		android:textColor="@color/briar_text_secondary"
+		android:textSize="@dimen/text_size_small"
+		tools:text="Dec 24"/>
+
+	<TextView
+		android:id="@+id/statusView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_below="@+id/postCountView"
+		android:layout_toEndOf="@+id/avatarView"
+		android:layout_toRightOf="@+id/avatarView"
+		android:textColor="@color/briar_text_tertiary"
+		tools:text="@string/blogs_blog_is_empty"/>
+
+	<View
+		style="@style/Divider.ForumList"
+		android:layout_alignParentLeft="true"
+		android:layout_alignParentStart="true"
+		android:layout_below="@+id/statusView"
+		android:layout_marginTop="@dimen/listitem_horizontal_margin"/>
+
+</RelativeLayout>
+
diff --git a/briar-android/res/layout/list_item_blog_post.xml b/briar-android/res/layout/list_item_blog_post.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0f7e1d53294356d48b0c7b3fd96164fee6e64b82
--- /dev/null
+++ b/briar-android/res/layout/list_item_blog_post.xml
@@ -0,0 +1,118 @@
+<?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="wrap_content"
+	android:layout_marginLeft="@dimen/listitem_horizontal_margin"
+	android:layout_marginStart="@dimen/listitem_horizontal_margin"
+	android:layout_marginTop="@dimen/listitem_vertical_margin"
+	android:background="?attr/selectableItemBackground">
+
+	<de.hdodenhof.circleimageview.CircleImageView
+		android:id="@+id/avatar"
+		style="@style/BriarAvatar"
+		android:layout_width="30dp"
+		android:layout_height="30dp"
+		android:layout_marginBottom="@dimen/margin_medium"
+		android:layout_marginRight="@dimen/margin_medium"
+		tools:src="@drawable/ic_launcher"/>
+
+	<TextView
+		android:id="@+id/authorName"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignTop="@+id/avatar"
+		android:layout_toEndOf="@+id/avatar"
+		android:layout_toRightOf="@+id/avatar"
+		android:textColor="@color/briar_text_primary"
+		android:textSize="@dimen/text_size_tiny"
+		tools:text="Author Name"/>
+
+	<TextView
+		android:id="@+id/dateView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignBottom="@id/avatar"
+		android:layout_below="@+id/authorName"
+		android:layout_toEndOf="@+id/avatar"
+		android:layout_toRightOf="@+id/avatar"
+		android:gravity="bottom"
+		android:textColor="@color/briar_text_primary"
+		android:textSize="@dimen/text_size_tiny"
+		tools:text="yesterday"/>
+
+	<TextView
+		android:id="@+id/newView"
+		style="@style/BriarTag"
+		android:layout_alignBottom="@+id/dateView"
+		android:layout_marginLeft="@dimen/margin_small"
+		android:layout_toRightOf="@+id/dateView"
+		android:text="@string/tag_new"
+		android:visibility="gone"/>
+
+	<org.briarproject.android.util.TrustIndicatorView
+		android:id="@+id/trustIndicator"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignBottom="@+id/authorName"
+		android:layout_alignTop="@+id/authorName"
+		android:layout_marginLeft="@dimen/margin_small"
+		android:layout_toRightOf="@+id/authorName"
+		android:scaleType="center"
+		tools:src="@drawable/trust_indicator_verified"/>
+
+	<ImageView
+		android:id="@+id/chatView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_toLeftOf="@+id/commentView"
+		android:padding="@dimen/margin_small"
+		android:src="@drawable/ic_chat"
+		android:visibility="gone"/>
+
+	<ImageView
+		android:id="@+id/commentView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignParentRight="true"
+		android:layout_marginRight="@dimen/listitem_horizontal_margin"
+		android:padding="@dimen/margin_small"
+		android:src="@drawable/ic_repeat"
+		android:visibility="gone"/>
+
+	<TextView
+		android:id="@+id/titleView"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:layout_below="@+id/avatar"
+		android:layout_marginBottom="@dimen/margin_medium"
+		android:layout_marginEnd="@dimen/listitem_horizontal_margin"
+		android:layout_marginRight="@dimen/listitem_horizontal_margin"
+		android:ellipsize="end"
+		android:maxLines="3"
+		android:textColor="@color/briar_text_primary"
+		android:textSize="@dimen/text_size_large"
+		android:visibility="gone"
+		tools:text="This is a blog post title which can also be longer"/>
+
+	<TextView
+		android:id="@+id/bodyView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_below="@id/titleView"
+		android:layout_marginEnd="@dimen/margin_medium"
+		android:layout_marginRight="@dimen/listitem_horizontal_margin"
+		android:textColor="@color/briar_text_secondary"
+		android:textSize="@dimen/text_size_medium"
+		tools:text="This is a body text that shows the content of a blog post. This one is not short, but it is also not too long."/>
+
+	<View
+		style="@style/Divider.ForumList"
+		android:layout_alignParentLeft="true"
+		android:layout_alignParentStart="true"
+		android:layout_below="@+id/bodyView"
+		android:layout_marginTop="@dimen/listitem_vertical_margin"/>
+
+</RelativeLayout>
+
diff --git a/briar-android/res/layout/list_item_forum.xml b/briar-android/res/layout/list_item_forum.xml
index 171a40859c67555455afbe89f7e902882c39ac6b..0652bec6b01927253e4bbe35986de7a1ace3cb24 100644
--- a/briar-android/res/layout/list_item_forum.xml
+++ b/briar-android/res/layout/list_item_forum.xml
@@ -31,7 +31,7 @@
 		tools:text="This is a name of a forum"/>
 
 	<TextView
-		android:id="@+id/unreadView"
+		android:id="@+id/postCountView"
 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
 		android:layout_below="@+id/forumNameView"
@@ -62,7 +62,7 @@
 		style="@style/Divider.ForumList"
 		android:layout_alignParentLeft="true"
 		android:layout_alignParentStart="true"
-		android:layout_below="@+id/unreadView"/>
+		android:layout_below="@+id/postCountView"/>
 
 </RelativeLayout>
 
diff --git a/briar-android/res/layout/share_forum_message.xml b/briar-android/res/layout/share_forum_message.xml
index 522a0472d75bda5ea46a11b6e56ce7253d102e68..33863fc13d0a3c9d1f5f08aea1c9a8ac058a50b3 100644
--- a/briar-android/res/layout/share_forum_message.xml
+++ b/briar-android/res/layout/share_forum_message.xml
@@ -34,10 +34,7 @@
 		<Button
 			android:id="@+id/shareForumButton"
 			style="@style/BriarButton"
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:text="@string/forum_share_button"
-			/>
+			android:text="@string/forum_share_button"/>
 
 	</LinearLayout>
 
diff --git a/briar-android/res/menu/blogs_feed_actions.xml b/briar-android/res/menu/blogs_feed_actions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..38721c812dd0b37c75a0dd5770bf12e89a187d35
--- /dev/null
+++ b/briar-android/res/menu/blogs_feed_actions.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+
+	<item
+		android:id="@+id/action_write_blog_post"
+		android:icon="@drawable/forum_item_create_white"
+		android:title="@string/blogs_write_blog_post"
+		app:showAsAction="always"/>
+
+</menu>
\ No newline at end of file
diff --git a/briar-android/res/menu/blogs_my_actions.xml b/briar-android/res/menu/blogs_my_actions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9a3ce5b49a27da3c2484e66fe542ca375b669b2b
--- /dev/null
+++ b/briar-android/res/menu/blogs_my_actions.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+
+	<item
+		android:id="@+id/action_create_blog"
+		android:icon="@drawable/ic_add_white"
+		android:title="@string/blogs_my_blogs_create"
+		app:showAsAction="ifRoom"/>
+
+</menu>
\ No newline at end of file
diff --git a/briar-android/res/menu/blogs_my_blog_actions.xml b/briar-android/res/menu/blogs_my_blog_actions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..95cb880e767e1eb826d1df231d74cb0ec25800c0
--- /dev/null
+++ b/briar-android/res/menu/blogs_my_blog_actions.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+
+	<item
+		android:id="@+id/action_write_blog_post"
+		android:icon="@drawable/forum_item_create_white"
+		android:title="@string/blogs_write_blog_post"
+		app:showAsAction="ifRoom"/>
+
+	<item
+		android:id="@+id/action_delete_blog"
+		android:icon="@drawable/action_delete_white"
+		android:title="@string/blogs_delete_blog"
+		app:showAsAction="never"/>
+
+</menu>
\ No newline at end of file
diff --git a/briar-android/res/menu/blogs_write_blog_post_actions.xml b/briar-android/res/menu/blogs_write_blog_post_actions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..11befe03696ed3c17e2200a9c2928b62e8ba31ca
--- /dev/null
+++ b/briar-android/res/menu/blogs_write_blog_post_actions.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+
+	<item
+		android:id="@+id/action_publish_blog_post"
+		android:title="@string/blogs_publish_blog_post"
+		android:icon="@drawable/social_send_now_white"
+		app:showAsAction="always"/>
+
+</menu>
\ No newline at end of file
diff --git a/briar-android/res/values/dimens.xml b/briar-android/res/values/dimens.xml
index 0a0fc5ce53d27e573c35226632070410fca9b305..8aa43b6693613f2104a9bbdf90f539478d47897b 100644
--- a/briar-android/res/values/dimens.xml
+++ b/briar-android/res/values/dimens.xml
@@ -20,6 +20,7 @@
 	<dimen name="text_size_xlarge">34sp</dimen>
 
 	<dimen name="listitem_horizontal_margin">16dp</dimen>
+	<dimen name="listitem_vertical_margin">10dp</dimen>
 	<dimen name="listitem_text_left_margin">72dp</dimen>
 	<dimen name="listitem_height_one_line_avatar">56dp</dimen>
 	<dimen name="listitem_height_contact_selector">68dp</dimen>
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index d44bc389a6872b6a3c538fa3e9efc5ff7d251c4b..4bef054ae141d9afbebc79a88938f564ce6084d3 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -84,12 +84,12 @@
 	<string name="forum_leave">Leave Forum</string>
 	<string name="forum_left_toast">Left Forum</string>
 	<string name="forum_sharing_status">Sharing Status</string>
-	<string name="forum_no_posts">No posts</string>
+	<string name="no_posts">No posts</string>
 	<plurals name="unread_posts">
 		<item quantity="one">%d unread post</item>
 		<item quantity="other">%d unread posts</item>
 	</plurals>
-	<plurals name="forum_posts">
+	<plurals name="posts">
 		<item quantity="one">%d post</item>
 		<item quantity="other">%d posts</item>
 	</plurals>
@@ -251,9 +251,39 @@
 	<string name="progress_title_please_wait">Please wait..</string>
 
 	<!-- Blogs -->
-	<string name="blogs_button">Blogs</string>
+	<string name="blogs_button">Micro Blogs</string>
 	<string name="blogs_feed">Feed</string>
+
 	<string name="blogs_my_blogs">My Blogs</string>
+	<string name="blogs_my_blogs_create">Create Blog</string>
+	<string name="blogs_my_blogs_label">Add new Blog</string>
+	<string name="blogs_my_blogs_create_hint_title">Blog title (cannot be changed later)</string>
+	<string name="blogs_my_blogs_create_hint_desc">A short description of your new blog</string>
+	<string name="blogs_my_blogs_create_hint_desc_explanation">Potential readers may or may not subscribe to your blog based on the content of the description.</string>
+	<string name="blogs_my_blogs_empty_state">You don\'t have any blogs.\n\nWhy don\'t you create one now by clicking the plus in the top right screen corner?</string>
+	<string name="blogs_my_blogs_blog_empty_state">This is the place for content of your blog.\n\nIt seems like you haven\'t written anything yet.\n\nPlease tap the pen icon to compose a new blog post.\n\nDon\'t forget to go public and share your blog.</string>
+	<string name="blogs_my_blogs_created">Blog created</string>
+	<string name="blogs_blog_is_empty">This blog is empty</string>
+	<string name="blogs_other_blog_empty_state">This blog is currently empty.\n\nEither the author hasn\'t written anything yet, or the person who shared this blog with you needs to come online, so posts can be synchronized.</string>
+	<string name="tag_new">NEW</string>
+	<string name="blogs_post_more">more</string>
+	<string name="blogs_write_blog_post">Write Blog Post</string>
+	<string name="blogs_write_blog_post_title_hint">Add a title (optional)</string>
+	<string name="blogs_write_blog_post_body_hint">Type your blog post here</string>
+	<string name="blogs_publish_blog_post">Publish</string>
+	<string name="blogs_blog_post_created">Blog Post Created</string>
+	<string name="blogs_blog_post_received">New Blog Post Received</string>
+	<string name="blogs_blog_post_scroll_to">Scroll To</string>
+	<string name="blogs_blog_failed_to_load">Blog failed to load</string>
+	<string name="blogs_blog_post_failed_to_load">Blog Post failed to load</string>
+	<string name="blogs_feed_empty_state">This is the global blog feed.\n\nIt looks like nobody blogged anything, yet.\n\nBe the first and tap the pen icon to write a new blog post.</string>
+	<string name="blogs_delete_blog">Delete Blog</string>
+	<string name="blogs_delete_blog_dialog_message">Are you sure that you want to delete this Blog and all posts?\nNote that this will not delete the blog from other people\'s devices.</string>
+	<string name="blogs_delete_blog_ok">Delete Blog</string>
+	<string name="blogs_delete_blog_cancel">Keep</string>
+	<string name="blogs_blog_deleted">Blog Deleted</string>
+	<string name="blogs_remove_blog">Remove Blog</string>
+
 	<string name="blogs_blog_list">Blog List</string>
 	<string name="blogs_available_blogs">Available Blogs</string>
 	<string name="blogs_drafts">Drafts</string>
diff --git a/briar-android/res/values/styles.xml b/briar-android/res/values/styles.xml
index 9251475dad32cb3016d210ed13969b3174e934b1..729554b99cb0f4f152b95dfecb85be8ac2f69a36 100644
--- a/briar-android/res/values/styles.xml
+++ b/briar-android/res/values/styles.xml
@@ -23,6 +23,8 @@
 	</style>
 
 	<style name="BriarButton" parent="Widget.AppCompat.Button.Colored">
+		<item name="android:layout_width">match_parent</item>
+		<item name="android:layout_height">wrap_content</item>
 		<item name="android:textSize">@dimen/text_size_medium</item>
 		<item name="android:padding">@dimen/margin_large</item>
 	</style>
@@ -55,6 +57,17 @@
 		<item name="android:textColor">@android:color/primary_text_light</item>
 	</style>
 
+	<style name="BriarTag">
+		<item name="android:layout_width">wrap_content</item>
+		<item name="android:layout_height">wrap_content</item>
+		<item name="android:layout_marginRight">@dimen/margin_medium</item>
+		<item name="android:paddingLeft">3dp</item>
+		<item name="android:paddingRight">3dp</item>
+		<item name="android:background">@color/briar_primary</item>
+		<item name="android:textSize">@dimen/text_size_tiny</item>
+		<item name="android:textColor">@color/briar_text_primary_inverse</item>
+	</style>
+
 	<style name="Divider">
 		<item name="android:background">@color/divider</item>
 	</style>
@@ -109,4 +122,9 @@
 		<item name="tabTextColor">@color/briar_text_primary_inverse</item>
 	</style>
 
+	<!-- This fixes the missing TextAppearance.Design.Counter.Overflow style -->
+	<style name="BriarTextCounter.Overflow" parent="TextAppearance.Design.Counter">
+		<item name="android:textColor">@color/briar_button_negative</item>
+	</style>
+
 </resources>
\ No newline at end of file
diff --git a/briar-android/res/values/themes.xml b/briar-android/res/values/themes.xml
index e87077f031f9ec1e2db96c41eba58cd7f88ff95c..49c05904f621ef1462ff5b771b7b73cf5dddc11d 100644
--- a/briar-android/res/values/themes.xml
+++ b/briar-android/res/values/themes.xml
@@ -42,6 +42,8 @@
 		<item name="colorPrimary">@color/briar_primary</item>
 		<item name="colorPrimaryDark">@color/briar_primary_dark</item>
 		<item name="colorAccent">@color/briar_accent</item>
+		<item name="buttonBarPositiveButtonStyle">@style/BriarButtonFlat.Positive</item>
+		<item name="buttonBarNegativeButtonStyle">@style/BriarButtonFlat.Negative</item>
 		<item name="android:textColorPrimary">@color/briar_text_primary</item>
 		<item name="android:textColorPrimaryInverse">@color/briar_text_primary_inverse</item>
 		<item name="android:textColorSecondary">@color/briar_text_secondary</item>
@@ -49,6 +51,12 @@
 		<item name="android:textColorTertiary">@color/briar_text_tertiary</item>
 		<item name="android:textColorTertiaryInverse">@color/briar_text_tertiary_inverse</item>
 		<item name="android:textColorLink">@color/briar_text_link</item>
+		<item name="android:windowAnimationStyle">@style/DialogAnimation</item>
+	</style>
+
+	<style name="DialogAnimation" parent="@android:style/Animation.Dialog">
+		<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
+		<item name="android:windowExitAnimation">@android:anim/fade_out</item>
 	</style>
 
 	<!-- This fixes a UI bug in the support preference library -->
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index 3353f62b44e7fd29024f72b03a96502d773cc077..3f3368b575b7bdfcbbf35964685864515a2fe6d9 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -2,7 +2,15 @@ package org.briarproject.android;
 
 import android.app.Activity;
 
+import org.briarproject.android.blogs.BlogActivity;
+import org.briarproject.android.blogs.BlogFragment;
+import org.briarproject.android.blogs.BlogListFragment;
+import org.briarproject.android.blogs.BlogPostFragment;
+import org.briarproject.android.blogs.BlogsFragment;
+import org.briarproject.android.blogs.CreateBlogActivity;
+import org.briarproject.android.blogs.FeedFragment;
 import org.briarproject.android.blogs.MyBlogsFragment;
+import org.briarproject.android.blogs.WriteBlogPostActivity;
 import org.briarproject.android.contact.ContactListFragment;
 import org.briarproject.android.contact.ConversationActivity;
 import org.briarproject.android.forum.ForumInvitationsActivity;
@@ -13,7 +21,6 @@ import org.briarproject.android.forum.ForumListFragment;
 import org.briarproject.android.forum.ForumSharingStatusActivity;
 import org.briarproject.android.forum.ShareForumActivity;
 import org.briarproject.android.forum.ShareForumMessageFragment;
-import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.android.identity.CreateIdentityActivity;
 import org.briarproject.android.introduction.ContactChooserFragment;
 import org.briarproject.android.introduction.IntroductionActivity;
@@ -64,6 +71,16 @@ public interface ActivityComponent {
 
 	void inject(ForumActivity activity);
 
+	void inject(CreateBlogActivity activity);
+
+	void inject(BlogActivity activity);
+
+	void inject(WriteBlogPostActivity activity);
+
+	void inject(BlogFragment fragment);
+
+	void inject(BlogPostFragment fragment);
+
 	void inject(SettingsActivity activity);
 
 	void inject(ChangePasswordActivity activity);
@@ -73,7 +90,9 @@ public interface ActivityComponent {
 	// Fragments
 	void inject(ContactListFragment fragment);
 	void inject(ForumListFragment fragment);
-	void inject(BaseFragment fragment);
+	void inject(BlogsFragment fragment);
+	void inject(BlogListFragment fragment);
+	void inject(FeedFragment fragment);
 	void inject(MyBlogsFragment fragment);
 	void inject(ChooseIdentityFragment fragment);
 	void inject(ShowQrCodeFragment fragment);
diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index ea984bca0bab42760c652869ea561f87c85e69ee..5627651aecb15d7f0ea76befa8c04d3c5b63302d 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -4,6 +4,10 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.SharedPreferences;
 
+import org.briarproject.android.blogs.BlogController;
+import org.briarproject.android.blogs.BlogControllerImpl;
+import org.briarproject.android.blogs.FeedController;
+import org.briarproject.android.blogs.FeedControllerImpl;
 import org.briarproject.android.controller.BriarController;
 import org.briarproject.android.controller.BriarControllerImpl;
 import org.briarproject.android.controller.ConfigController;
@@ -107,6 +111,20 @@ public class ActivityModule {
 		return forumController;
 	}
 
+	@ActivityScope
+	@Provides
+	BlogController provideBlogController(BlogControllerImpl blogController) {
+		activity.addLifecycleController(blogController);
+		return blogController;
+	}
+
+	@ActivityScope
+	@Provides
+	protected FeedController provideFeedController(
+			FeedControllerImpl feedController) {
+		return feedController;
+	}
+
 	@ActivityScope
 	@Provides
 	protected NavDrawerController provideNavDrawerController(
diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java
index 5bc21c1cc27c7d79099c6e82b545b692e3d11f1a..3e59233be3dc1542bfa2b4867cb90197cc5c6e39 100644
--- a/briar-android/src/org/briarproject/android/AndroidComponent.java
+++ b/briar-android/src/org/briarproject/android/AndroidComponent.java
@@ -5,8 +5,11 @@ import org.briarproject.CoreModule;
 import org.briarproject.android.api.AndroidExecutor;
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.api.ReferenceManager;
+import org.briarproject.android.blogs.BlogPersistentData;
 import org.briarproject.android.forum.ForumPersistentData;
 import org.briarproject.android.report.BriarReportSender;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPostFactory;
 import org.briarproject.api.contact.ContactExchangeTask;
 import org.briarproject.api.contact.ContactManager;
 import org.briarproject.api.crypto.CryptoComponent;
@@ -96,6 +99,10 @@ public interface AndroidComponent extends CoreEagerSingletons {
 
 	ForumPostFactory forumPostFactory();
 
+	BlogManager blogManager();
+
+	BlogPostFactory blogPostFactory();
+
 	SettingsManager settingsManager();
 
 	ContactExchangeTask contactExchangeTask();
@@ -112,6 +119,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
 
 	ForumPersistentData forumPersistentData();
 
+	BlogPersistentData blogPersistentData();
+
 	@IoExecutor
 	Executor ioExecutor();
 
diff --git a/briar-android/src/org/briarproject/android/AppModule.java b/briar-android/src/org/briarproject/android/AppModule.java
index 36933a1184aec4c6df22703b391d15046b1c0750..825e639c6c69ab442b1d85d388b2769e68920733 100644
--- a/briar-android/src/org/briarproject/android/AppModule.java
+++ b/briar-android/src/org/briarproject/android/AppModule.java
@@ -4,6 +4,7 @@ import android.app.Application;
 
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.api.ReferenceManager;
+import org.briarproject.android.blogs.BlogPersistentData;
 import org.briarproject.android.forum.ForumPersistentData;
 import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.PublicKey;
@@ -143,4 +144,10 @@ public class AppModule {
 	ForumPersistentData provideForumPersistence() {
 		return new ForumPersistentData();
 	}
+
+	@Provides
+	@Singleton
+	BlogPersistentData provideBlogPersistence() {
+		return new BlogPersistentData();
+	}
 }
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..35321f390a96ee09c356de2b294552cc812d9696
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java
@@ -0,0 +1,261 @@
+package org.briarproject.android.blogs;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.BriarActivity;
+import org.briarproject.android.blogs.BlogController.BlogPostListener;
+import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
+import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static android.widget.Toast.LENGTH_SHORT;
+
+public class BlogActivity extends BriarActivity implements BlogPostListener,
+		OnBlogPostClickListener, BaseFragmentListener {
+
+	static final int REQUEST_WRITE_POST = 1;
+	static final String BLOG_NAME = "briar.BLOG_NAME";
+	static final String IS_MY_BLOG = "briar.IS_MY_BLOG";
+	static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG";
+
+	private static final String BLOG_PAGER_ADAPTER = "briar.BLOG_PAGER_ADAPTER";
+	private static final Logger LOG =
+			Logger.getLogger(BlogActivity.class.getName());
+
+	private ProgressBar progressBar;
+	private ViewPager pager;
+	private BlogPagerAdapter blogPagerAdapter;
+	private BlogPostPagerAdapter postPagerAdapter;
+	private String blogName;
+	private boolean myBlog, isNew;
+
+	// Fields that are accessed from background threads must be volatile
+	private volatile GroupId groupId = null;
+	@Inject
+	BlogController blogController;
+
+	@Override
+	public void onCreate(Bundle state) {
+		super.onCreate(state);
+
+		// GroupId from Intent
+		Intent i = getIntent();
+		byte[] b = i.getByteArrayExtra(GROUP_ID);
+		if (b == null) throw new IllegalStateException("No Group in intent.");
+		groupId = new GroupId(b);
+
+		// Name of the Blog from Intent
+		blogName = i.getStringExtra(BLOG_NAME);
+		if (blogName != null) setTitle(blogName);
+
+		// Is this our blog and was it just created?
+		myBlog = i.getBooleanExtra(IS_MY_BLOG, false);
+		isNew = i.getBooleanExtra(IS_NEW_BLOG, false);
+
+		setContentView(R.layout.activity_blog);
+
+		pager = (ViewPager) findViewById(R.id.pager);
+		progressBar = (ProgressBar) findViewById(R.id.progressBar);
+		hideLoadingScreen();
+
+		blogPagerAdapter = new BlogPagerAdapter(getSupportFragmentManager());
+		if (state == null || state.getBoolean(BLOG_PAGER_ADAPTER, true)) {
+			pager.setAdapter(blogPagerAdapter);
+		} else {
+			// this initializes and restores the postPagerAdapter
+			loadBlogPosts();
+		}
+	}
+
+	@Override
+	public void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+
+		// remember which adapter we had active
+		outState.putBoolean(BLOG_PAGER_ADAPTER,
+				pager.getAdapter() == blogPagerAdapter);
+	}
+
+	@Override
+	public void onBackPressed() {
+		if (pager.getAdapter() == postPagerAdapter) {
+			pager.setAdapter(blogPagerAdapter);
+		} else {
+			super.onBackPressed();
+		}
+	}
+
+	@Override
+	public void injectActivity(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public void showLoadingScreen(boolean isBlocking, int stringId) {
+		progressBar.setVisibility(VISIBLE);
+	}
+
+	private void showLoadingScreen() {
+		showLoadingScreen(false, 0);
+	}
+
+	@Override
+	public void hideLoadingScreen() {
+		progressBar.setVisibility(GONE);
+	}
+
+	@Override
+	public void onFragmentCreated(String tag) {
+
+	}
+
+	@Override
+	public void onBlogPostClick(final int position) {
+		loadBlogPosts(position, true);
+	}
+
+	private void loadBlogPosts() {
+		loadBlogPosts(0, false);
+	}
+
+	private void loadBlogPosts(final int position, final boolean setItem) {
+		showLoadingScreen();
+		blogController
+				.loadBlog(groupId, false, new UiResultHandler<Boolean>(this) {
+					@Override
+					public void onResultUi(Boolean result) {
+						if (result) {
+							Collection<BlogPostItem> posts =
+									blogController.getBlogPosts();
+
+							if (postPagerAdapter == null) {
+								postPagerAdapter = new BlogPostPagerAdapter(
+										getSupportFragmentManager(),
+										posts.size());
+							} else {
+								postPagerAdapter.setSize(posts.size());
+							}
+							pager.setAdapter(postPagerAdapter);
+							if (setItem) pager.setCurrentItem(position);
+						} else {
+							Toast.makeText(BlogActivity.this,
+									R.string.blogs_blog_post_failed_to_load,
+									LENGTH_SHORT).show();
+						}
+					}
+				});
+	}
+
+	@Override
+	public void onBlogPostAdded(final BlogPostItem post, final boolean local) {
+		runOnUiThread(new Runnable() {
+			@Override
+			public void run() {
+				if (blogPagerAdapter != null) {
+					BlogFragment f = blogPagerAdapter.getFragment();
+					if (f != null && f.isVisible()) {
+						f.onBlogPostAdded(post, local);
+					}
+				}
+
+				if (postPagerAdapter != null) {
+					postPagerAdapter.onBlogPostAdded();
+					postPagerAdapter.notifyDataSetChanged();
+				}
+			}
+		});
+	}
+
+	@Override
+	protected void onActivityResult(int requestCode, int resultCode,
+			Intent data) {
+
+		// The BlogPostAddedEvent arrives when the controller is not listening,
+		// so we need to manually reload the blog posts :(
+		if (requestCode == REQUEST_WRITE_POST && resultCode == RESULT_OK) {
+			BlogFragment f = blogPagerAdapter.getFragment();
+			if (f != null && f.isVisible()) {
+				f.reload();
+			}
+		}
+	}
+
+
+	private class BlogPagerAdapter extends FragmentStatePagerAdapter {
+		private BlogFragment fragment = null;
+
+		BlogPagerAdapter(FragmentManager fm) {
+			super(fm);
+		}
+
+		@Override
+		public int getCount() {
+			return 1;
+		}
+
+		@Override
+		public Fragment getItem(int position) {
+			return BlogFragment.newInstance(groupId, blogName, myBlog, isNew);
+		}
+
+		@Override
+		public Object instantiateItem(ViewGroup container, int position) {
+			// save a reference to the single fragment here for later
+			fragment =
+					(BlogFragment) super.instantiateItem(container, position);
+			return fragment;
+		}
+
+		BlogFragment getFragment() {
+			return fragment;
+		}
+	}
+
+	private class BlogPostPagerAdapter extends FragmentStatePagerAdapter {
+		private int size;
+
+		BlogPostPagerAdapter(FragmentManager fm, int size) {
+			super(fm);
+			this.size = size;
+		}
+
+		@Override
+		public int getCount() {
+			return size;
+		}
+
+		@Override
+		public Fragment getItem(int position) {
+			MessageId postIdOfPos = blogController.getBlogPostId(position);
+			return BlogPostFragment.newInstance(groupId, postIdOfPos);
+		}
+
+		void onBlogPostAdded() {
+			size++;
+		}
+
+		void setSize(int size) {
+			this.size = size;
+		}
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogController.java b/briar-android/src/org/briarproject/android/blogs/BlogController.java
new file mode 100644
index 0000000000000000000000000000000000000000..a805c2d344e52c6295d60b90e7a79b563604583e
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogController.java
@@ -0,0 +1,31 @@
+package org.briarproject.android.blogs;
+
+import android.support.annotation.Nullable;
+
+import org.briarproject.android.controller.ActivityLifecycleController;
+import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+
+import java.util.TreeSet;
+
+public interface BlogController extends ActivityLifecycleController {
+
+	void loadBlog(final GroupId groupId, final boolean reload,
+			final UiResultHandler<Boolean> resultHandler);
+
+	TreeSet<BlogPostItem> getBlogPosts();
+
+	@Nullable
+	BlogPostItem getBlogPost(MessageId postId);
+
+	@Nullable
+	MessageId getBlogPostId(int position);
+
+	void deleteBlog(final UiResultHandler<Boolean> resultHandler);
+
+	interface BlogPostListener {
+		void onBlogPostAdded(final BlogPostItem post, final boolean local);
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2898602c68dd664a4709cd761667b7d4e01a1dd
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
@@ -0,0 +1,194 @@
+package org.briarproject.android.blogs;
+
+import android.app.Activity;
+import android.support.annotation.Nullable;
+
+import org.briarproject.android.controller.DbControllerImpl;
+import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPostHeader;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.event.BlogPostAddedEvent;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.EventBus;
+import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.GroupRemovedEvent;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.TreeSet;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+
+public class BlogControllerImpl extends DbControllerImpl
+		implements BlogController, EventListener {
+
+	private static final Logger LOG =
+			Logger.getLogger(BlogControllerImpl.class.getName());
+
+	@Inject
+	protected Activity activity;
+	@Inject
+	protected volatile BlogManager blogManager;
+	@Inject
+	protected volatile EventBus eventBus;
+	@Inject
+	protected BlogPersistentData data;
+
+	private volatile BlogPostListener listener;
+
+	@Inject
+	BlogControllerImpl() {
+	}
+
+	@Override
+	public void onActivityCreate() {
+		if (activity instanceof BlogPostListener) {
+			listener = (BlogPostListener) activity;
+		} else {
+			throw new IllegalStateException(
+					"An activity that injects the BlogController must " +
+							"implement the BlogPostListener");
+		}
+	}
+
+	@Override
+	public void onActivityResume() {
+		eventBus.addListener(this);
+	}
+
+	@Override
+	public void onActivityPause() {
+		eventBus.removeListener(this);
+	}
+
+	@Override
+	public void onActivityDestroy() {
+		if (activity.isFinishing()) {
+			data.clearAll();
+		}
+	}
+
+	@Override
+	public void eventOccurred(Event e) {
+		if (e instanceof BlogPostAddedEvent) {
+			final BlogPostAddedEvent m = (BlogPostAddedEvent) e;
+			if (m.getGroupId().equals(data.getGroupId())) {
+				LOG.info("New blog post added");
+				final BlogPostHeader header = m.getHeader();
+				try {
+					final byte[] body = blogManager.getPostBody(header.getId());
+					final BlogPostItem post = new BlogPostItem(header, body);
+					data.addPost(post);
+					listener.onBlogPostAdded(post, m.isLocal());
+				} catch (DbException ex) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, ex.toString(), ex);
+				}
+			}
+		} else if (e instanceof GroupRemovedEvent) {
+			GroupRemovedEvent s = (GroupRemovedEvent) e;
+			if (s.getGroup().getId().equals(data.getGroupId())) {
+				LOG.info("Blog removed");
+				activity.runOnUiThread(new Runnable() {
+					@Override
+					public void run() {
+						activity.finish();
+					}
+				});
+			}
+		}
+	}
+
+	@Override
+	public void loadBlog(final GroupId groupId, final boolean reload,
+			final UiResultHandler<Boolean> resultHandler) {
+
+		LOG.info("Loading blog...");
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					if (reload || data.getGroupId() == null ||
+							!data.getGroupId().equals(groupId)) {
+						data.setGroupId(groupId);
+						// load blog posts
+						long now = System.currentTimeMillis();
+						Collection<BlogPostItem> posts = new ArrayList<>();
+						Collection<BlogPostHeader> header =
+								blogManager.getPostHeaders(groupId);
+						for (BlogPostHeader h : header) {
+							byte[] body = blogManager.getPostBody(h.getId());
+							posts.add(new BlogPostItem(h, body));
+						}
+						data.setPosts(posts);
+						long duration = System.currentTimeMillis() - now;
+						if (LOG.isLoggable(INFO))
+							LOG.info("Post header load took " + duration +
+									" ms");
+					}
+					resultHandler.onResult(true);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					resultHandler.onResult(false);
+				}
+			}
+		});
+	}
+
+	@Override
+	public TreeSet<BlogPostItem> getBlogPosts() {
+		return data.getBlogPosts();
+	}
+
+	@Override
+	@Nullable
+	public BlogPostItem getBlogPost(MessageId id) {
+		for (BlogPostItem item : getBlogPosts()) {
+			if (item.getId().equals(id)) return item;
+		}
+		return null;
+	}
+
+	@Override
+	@Nullable
+	public MessageId getBlogPostId(int position) {
+		int i = 0;
+		for (BlogPostItem post : getBlogPosts()) {
+			if (i == position) return post.getId();
+			i++;
+		}
+		return null;
+	}
+
+	@Override
+	public void deleteBlog(final UiResultHandler<Boolean> resultHandler) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				if (data.getGroupId() == null) {
+					resultHandler.onResult(false);
+					return;
+				}
+				try {
+					Blog b = blogManager.getBlog(data.getGroupId());
+					blogManager.removeBlog(b);
+					resultHandler.onResult(true);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					resultHandler.onResult(false);
+				}
+			}
+		});
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ee529ca3c8ae9a35e36b8145b9a6552f0b75e3f
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
@@ -0,0 +1,228 @@
+package org.briarproject.android.blogs;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.blogs.BlogController.BlogPostListener;
+import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
+import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.android.util.BriarRecyclerView;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+import static android.support.design.widget.Snackbar.LENGTH_LONG;
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static android.widget.Toast.LENGTH_SHORT;
+import static org.briarproject.android.BriarActivity.GROUP_ID;
+import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
+import static org.briarproject.android.blogs.BlogActivity.IS_MY_BLOG;
+import static org.briarproject.android.blogs.BlogActivity.IS_NEW_BLOG;
+import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST;
+
+public class BlogFragment extends BaseFragment implements BlogPostListener {
+
+	public final static String TAG = BlogFragment.class.getName();
+
+	@Inject
+	BlogController blogController;
+
+	private GroupId groupId;
+	private String blogName;
+	private boolean myBlog;
+	private BlogPostAdapter adapter;
+	private BriarRecyclerView list;
+
+	static BlogFragment newInstance(GroupId groupId, String name,
+			boolean myBlog, boolean isNew) {
+
+		BlogFragment f = new BlogFragment();
+
+		Bundle bundle = new Bundle();
+		bundle.putByteArray(GROUP_ID, groupId.getBytes());
+		bundle.putString(BLOG_NAME, name);
+		bundle.putBoolean(IS_MY_BLOG, myBlog);
+		bundle.putBoolean(IS_NEW_BLOG, isNew);
+
+		f.setArguments(bundle);
+		return f;
+	}
+
+	@Nullable
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		setHasOptionsMenu(true);
+
+		Bundle args = getArguments();
+		byte[] b = args.getByteArray(GROUP_ID);
+		if (b == null) throw new IllegalStateException("No Group found.");
+		groupId = new GroupId(b);
+		blogName = args.getString(BLOG_NAME);
+		myBlog = args.getBoolean(IS_MY_BLOG);
+		boolean isNew = args.getBoolean(IS_NEW_BLOG);
+
+		View v = inflater.inflate(R.layout.fragment_blog, container, false);
+
+		adapter = new BlogPostAdapter(getActivity(),
+				(OnBlogPostClickListener) getActivity());
+		list = (BriarRecyclerView) v.findViewById(R.id.postList);
+		list.setLayoutManager(new LinearLayoutManager(getActivity()));
+		list.setAdapter(adapter);
+		if (myBlog) {
+			list.setEmptyText(
+					getString(R.string.blogs_my_blogs_blog_empty_state));
+		} else {
+			list.setEmptyText(getString(R.string.blogs_other_blog_empty_state));
+		}
+
+		// show snackbar if this blog was just created
+		if (isNew) {
+			Snackbar s = Snackbar.make(list, R.string.blogs_my_blogs_created,
+					LENGTH_LONG);
+			s.getView().setBackgroundResource(R.color.briar_primary);
+			s.show();
+
+			// show only once
+			args.putBoolean(IS_NEW_BLOG, false);
+		}
+		return v;
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public void onStart() {
+		super.onStart();
+		loadData(false);
+	}
+
+	@Override
+	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+		if (myBlog) {
+			inflater.inflate(R.menu.blogs_my_blog_actions, menu);
+		}
+		super.onCreateOptionsMenu(menu, inflater);
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		switch (item.getItemId()) {
+			case android.R.id.home:
+				getActivity().onBackPressed();
+				return true;
+			case R.id.action_write_blog_post:
+				Intent i =
+						new Intent(getActivity(), WriteBlogPostActivity.class);
+				i.putExtra(GROUP_ID, groupId.getBytes());
+				i.putExtra(BLOG_NAME, blogName);
+				ActivityOptionsCompat options =
+						makeCustomAnimation(getActivity(),
+								android.R.anim.slide_in_left,
+								android.R.anim.slide_out_right);
+				ActivityCompat.startActivityForResult(getActivity(), i,
+						REQUEST_WRITE_POST, options.toBundle());
+				return true;
+			case R.id.action_delete_blog:
+				showDeleteDialog();
+				return true;
+			default:
+				return super.onOptionsItemSelected(item);
+		}
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	@Override
+	public void onBlogPostAdded(BlogPostItem post, boolean local) {
+		adapter.add(post);
+		if (local) list.scrollToPosition(0);
+	}
+
+	private void loadData(final boolean reload) {
+		blogController.loadBlog(groupId, reload,
+				new UiResultHandler<Boolean>(getActivity()) {
+					@Override
+					public void onResultUi(Boolean result) {
+						if (result) {
+							Collection<BlogPostItem> posts =
+									blogController.getBlogPosts();
+							if (posts.size() > 0) {
+								adapter.addAll(posts);
+								if (reload) list.scrollToPosition(0);
+							} else {
+								list.showData();
+							}
+						} else {
+							Toast.makeText(getActivity(),
+									R.string.blogs_blog_failed_to_load,
+									LENGTH_SHORT).show();
+							getActivity().supportFinishAfterTransition();
+						}
+					}
+				});
+	}
+
+	void reload() {
+		loadData(true);
+	}
+
+	private void showDeleteDialog() {
+		DialogInterface.OnClickListener okListener =
+				new DialogInterface.OnClickListener() {
+					@Override
+					public void onClick(DialogInterface dialog, int which) {
+						deleteBlog();
+					}
+				};
+		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
+				R.style.BriarDialogTheme);
+		builder.setTitle(getString(R.string.blogs_delete_blog));
+		builder.setMessage(
+				getString(R.string.blogs_delete_blog_dialog_message));
+		builder.setPositiveButton(R.string.blogs_delete_blog_cancel, null);
+		builder.setNegativeButton(R.string.blogs_delete_blog_ok, okListener);
+		builder.show();
+	}
+
+	private void deleteBlog() {
+		blogController.deleteBlog(
+				new UiResultHandler<Boolean>(getActivity()) {
+					@Override
+					public void onResultUi(Boolean result) {
+						if (!result) return;
+						Toast.makeText(getActivity(),
+								R.string.blogs_blog_deleted, LENGTH_SHORT)
+								.show();
+						getActivity().supportFinishAfterTransition();
+					}
+				});
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java b/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..92526387f18948365615c9aa3aacc1c1e273f589
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java
@@ -0,0 +1,210 @@
+package org.briarproject.android.blogs;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.util.SortedList;
+import android.support.v7.widget.RecyclerView;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.briarproject.R;
+import org.briarproject.android.util.TextAvatarView;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static org.briarproject.android.BriarActivity.GROUP_ID;
+import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
+import static org.briarproject.android.blogs.BlogActivity.IS_MY_BLOG;
+
+class BlogListAdapter extends
+		RecyclerView.Adapter<BlogListAdapter.BlogViewHolder> {
+
+	private SortedList<BlogListItem> blogs = new SortedList<>(
+			BlogListItem.class, new SortedList.Callback<BlogListItem>() {
+
+		@Override
+		public int compare(BlogListItem a, BlogListItem b) {
+			if (a == b) return 0;
+			// The blog with the newest message comes first
+			long aTime = a.getTimestamp(), bTime = b.getTimestamp();
+			if (aTime > bTime) return -1;
+			if (aTime < bTime) return 1;
+			// Break ties by blog name
+			String aName = a.getName();
+			String bName = b.getName();
+			return String.CASE_INSENSITIVE_ORDER.compare(aName, bName);
+		}
+
+		@Override
+		public void onInserted(int position, int count) {
+			notifyItemRangeInserted(position, count);
+		}
+
+		@Override
+		public void onRemoved(int position, int count) {
+			notifyItemRangeRemoved(position, count);
+		}
+
+		@Override
+		public void onMoved(int fromPosition, int toPosition) {
+			notifyItemMoved(fromPosition, toPosition);
+		}
+
+		@Override
+		public void onChanged(int position, int count) {
+			notifyItemRangeChanged(position, count);
+		}
+
+		@Override
+		public boolean areContentsTheSame(BlogListItem a, BlogListItem b) {
+			return a.getBlog().equals(b.getBlog()) &&
+					a.getTimestamp() == b.getTimestamp() &&
+					a.getUnreadCount() == b.getUnreadCount();
+		}
+
+		@Override
+		public boolean areItemsTheSame(BlogListItem a, BlogListItem b) {
+			return a.getBlog().equals(b.getBlog());
+		}
+	});
+
+	private final Activity ctx;
+
+	BlogListAdapter(Activity ctx) {
+		this.ctx = ctx;
+	}
+
+	@Override
+	public BlogViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+		View v = LayoutInflater.from(ctx).inflate(
+				R.layout.list_item_blog, parent, false);
+		return new BlogViewHolder(v);
+	}
+
+	@Override
+	public void onBindViewHolder(BlogViewHolder ui, int position) {
+		final BlogListItem item = getItem(position);
+
+		// Avatar
+		ui.avatar.setText(item.getName().substring(0, 1));
+		ui.avatar.setBackgroundBytes(item.getBlog().getId().getBytes());
+		ui.avatar.setUnreadCount(item.getUnreadCount());
+
+		// Blog Name
+		ui.name.setText(item.getName());
+
+		// Post Count
+		int postCount = item.getPostCount();
+		ui.postCount.setText(ctx.getResources()
+				.getQuantityString(R.plurals.posts, postCount, postCount));
+		ui.postCount.setTextColor(
+				ContextCompat.getColor(ctx, R.color.briar_text_secondary));
+
+		// Date and Status
+		if (item.isEmpty()) {
+			ui.date.setVisibility(GONE);
+			ui.avatar.setProblem(true);
+			ui.status.setText(ctx.getString(R.string.blogs_blog_is_empty));
+			ui.status.setVisibility(VISIBLE);
+		} else {
+			long timestamp = item.getTimestamp();
+			ui.date.setText(
+					DateUtils.getRelativeTimeSpanString(ctx, timestamp));
+			ui.date.setVisibility(VISIBLE);
+			ui.avatar.setProblem(false);
+			ui.status.setVisibility(GONE);
+		}
+
+		// Open Blog on Click
+		ui.layout.setOnClickListener(new View.OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				Intent i = new Intent(ctx, BlogActivity.class);
+				Blog b = item.getBlog();
+				i.putExtra(GROUP_ID, b.getId().getBytes());
+				i.putExtra(BLOG_NAME, b.getName());
+				i.putExtra(IS_MY_BLOG, item.isOurs());
+				ActivityOptionsCompat options = ActivityOptionsCompat
+						.makeCustomAnimation(ctx, android.R.anim.fade_in,
+								android.R.anim.fade_out);
+				ActivityCompat.startActivity(ctx, i, options.toBundle());
+			}
+		});
+	}
+
+	@Override
+	public int getItemCount() {
+		return blogs.size();
+	}
+
+	public BlogListItem getItem(int position) {
+		return blogs.get(position);
+	}
+
+	@Nullable
+	public BlogListItem getItem(GroupId g) {
+		for (int i = 0; i < blogs.size(); i++) {
+			BlogListItem item = blogs.get(i);
+			if (item.getBlog().getGroup().getId().equals(g)) {
+				return item;
+			}
+		}
+		return null;
+	}
+
+	public void addAll(Collection<BlogListItem> items) {
+		blogs.addAll(items);
+	}
+
+	void updateItem(BlogListItem item) {
+		BlogListItem oldItem = getItem(item.getBlog().getGroup().getId());
+		int position = blogs.indexOf(oldItem);
+		blogs.updateItemAt(position, item);
+	}
+
+	public void remove(BlogListItem item) {
+		blogs.remove(item);
+	}
+
+	public void clear() {
+		blogs.clear();
+	}
+
+	public boolean isEmpty() {
+		return blogs.size() == 0;
+	}
+
+	static class BlogViewHolder extends RecyclerView.ViewHolder {
+
+		private final ViewGroup layout;
+		private final TextAvatarView avatar;
+		private final TextView name;
+		private final TextView postCount;
+		private final TextView date;
+		private final TextView status;
+
+		BlogViewHolder(View v) {
+			super(v);
+
+			layout = (ViewGroup) v;
+			avatar = (TextAvatarView) v.findViewById(R.id.avatarView);
+			name = (TextView) v.findViewById(R.id.nameView);
+			postCount = (TextView) v.findViewById(R.id.postCountView);
+			date = (TextView) v.findViewById(R.id.dateView);
+			status = (TextView) v.findViewById(R.id.statusView);
+		}
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogListFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogListFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..61f3dff54a547415013d1dc473f3406b763f2021
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogListFragment.java
@@ -0,0 +1,53 @@
+package org.briarproject.android.blogs;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.fragment.BaseFragment;
+
+public class BlogListFragment extends BaseFragment {
+
+	public final static String TAG = BlogListFragment.class.getName();
+
+	static BlogListFragment newInstance(int num) {
+		BlogListFragment f = new BlogListFragment();
+
+		Bundle args = new Bundle();
+		args.putInt("num", num);
+		f.setArguments(args);
+
+		return f;
+	}
+
+	@Nullable
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		View v = inflater.inflate(R.layout.fragment_blogs_list, container,
+				false);
+
+		TextView numView = (TextView) v.findViewById(R.id.num);
+		String num = String.valueOf(getArguments().getInt("num"));
+		numView.setText(num);
+
+		return v;
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogListItem.java b/briar-android/src/org/briarproject/android/blogs/BlogListItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..35c9f532be2a239fecd798a629955afb7d548a95
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogListItem.java
@@ -0,0 +1,67 @@
+package org.briarproject.android.blogs;
+
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogPostHeader;
+
+import java.util.Collection;
+
+class BlogListItem {
+
+	private final Blog blog;
+	private final int postCount;
+	private final long timestamp;
+	private final int unread;
+	private final boolean ours;
+
+	BlogListItem(Blog blog, Collection<BlogPostHeader> headers, boolean ours) {
+		this.blog = blog;
+		if (headers.isEmpty()) {
+			postCount = 0;
+			timestamp = 0;
+			unread = 0;
+		} else {
+			BlogPostHeader newest = null;
+			long timestamp = -1;
+			int unread = 0;
+			for (BlogPostHeader h : headers) {
+				if (h.getTimestamp() > timestamp) {
+					timestamp = h.getTimestamp();
+					newest = h;
+				}
+				if (!h.isRead()) unread++;
+			}
+			this.postCount = headers.size();
+			this.timestamp = newest.getTimestamp();
+			this.unread = unread;
+		}
+		this.ours = ours;
+	}
+
+	Blog getBlog() {
+		return blog;
+	}
+
+	String getName() {
+		return blog.getName();
+	}
+
+	boolean isEmpty() {
+		return postCount == 0;
+	}
+
+	int getPostCount() {
+		return postCount;
+	}
+
+	long getTimestamp() {
+		return timestamp;
+	}
+
+	int getUnreadCount() {
+		return unread;
+	}
+
+	boolean isOurs() {
+		return ours;
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPersistentData.java b/briar-android/src/org/briarproject/android/blogs/BlogPersistentData.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2834c809cd5afc0643be76ccdb7d6d5b3de1025
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPersistentData.java
@@ -0,0 +1,49 @@
+package org.briarproject.android.blogs;
+
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+import java.util.TreeSet;
+
+import javax.inject.Inject;
+
+/**
+ * This class is a singleton that defines the data that should persist, i.e.
+ * still be present in memory after activity restarts. This class is not thread
+ * safe.
+ */
+public class BlogPersistentData {
+
+	private volatile GroupId groupId;
+	private volatile TreeSet<BlogPostItem> posts = new TreeSet<>();
+
+	public BlogPersistentData() {
+
+	}
+
+	public void setGroupId(GroupId groupId) {
+		this.groupId = groupId;
+	}
+
+	public GroupId getGroupId() {
+		return groupId;
+	}
+
+	public void setPosts(Collection<BlogPostItem> posts) {
+		this.posts.clear();
+		this.posts.addAll(posts);
+	}
+
+	void addPost(BlogPostItem post) {
+		posts.add(post);
+	}
+
+	TreeSet<BlogPostItem> getBlogPosts() {
+		return posts;
+	}
+
+	void clearAll() {
+		groupId = null;
+		posts.clear();
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java b/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbcce29651b0b7531cd841a10300f92c09bf7b62
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java
@@ -0,0 +1,166 @@
+package org.briarproject.android.blogs;
+
+import android.content.Context;
+import android.support.v7.util.SortedList;
+import android.support.v7.widget.RecyclerView;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.briarproject.R;
+import org.briarproject.android.util.TrustIndicatorView;
+import org.briarproject.api.identity.Author;
+import org.briarproject.util.StringUtils;
+
+import java.util.Collection;
+
+import de.hdodenhof.circleimageview.CircleImageView;
+import im.delight.android.identicons.IdenticonDrawable;
+
+class BlogPostAdapter extends
+		RecyclerView.Adapter<BlogPostAdapter.BlogPostHolder> {
+
+	private SortedList<BlogPostItem> posts = new SortedList<>(
+			BlogPostItem.class, new SortedList.Callback<BlogPostItem>() {
+
+		@Override
+		public int compare(BlogPostItem a, BlogPostItem b) {
+			return a.compareTo(b);
+		}
+
+		@Override
+		public void onInserted(int position, int count) {
+			notifyItemRangeInserted(position, count);
+		}
+
+		@Override
+		public void onRemoved(int position, int count) {
+			notifyItemRangeRemoved(position, count);
+		}
+
+		@Override
+		public void onMoved(int fromPosition, int toPosition) {
+			notifyItemMoved(fromPosition, toPosition);
+		}
+
+		@Override
+		public void onChanged(int position, int count) {
+			notifyItemRangeChanged(position, count);
+		}
+
+		@Override
+		public boolean areContentsTheSame(BlogPostItem a, BlogPostItem b) {
+			return a.isRead() == b.isRead();
+		}
+
+		@Override
+		public boolean areItemsTheSame(BlogPostItem a, BlogPostItem b) {
+			return a.getId().equals(b.getId());
+		}
+	});
+
+	private final Context ctx;
+	private final OnBlogPostClickListener listener;
+
+	BlogPostAdapter(Context ctx, OnBlogPostClickListener listener) {
+		this.ctx = ctx;
+		this.listener = listener;
+	}
+
+	@Override
+	public BlogPostHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+		View v = LayoutInflater.from(ctx).inflate(
+				R.layout.list_item_blog_post, parent, false);
+		return new BlogPostHolder(v);
+	}
+
+	@Override
+	public void onBindViewHolder(final BlogPostHolder ui, int position) {
+		final BlogPostItem post = getItem(position);
+
+		Author author = post.getAuthor();
+		IdenticonDrawable d = new IdenticonDrawable(author.getId().getBytes());
+		ui.avatar.setImageDrawable(d);
+		ui.author.setText(author.getName());
+		ui.trust.setTrustLevel(post.getAuthorStatus());
+
+		// date
+		ui.date.setText(
+				DateUtils.getRelativeTimeSpanString(ctx, post.getTimestamp()));
+
+		// post body
+		ui.body.setText(StringUtils.fromUtf8(post.getBody()));
+
+		ui.layout.setOnClickListener(new View.OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				listener.onBlogPostClick(ui.getAdapterPosition());
+			}
+		});
+	}
+
+	@Override
+	public int getItemCount() {
+		return posts.size();
+	}
+
+	public BlogPostItem getItem(int position) {
+		return posts.get(position);
+	}
+
+	public void add(BlogPostItem item) {
+		posts.add(item);
+	}
+
+	public void addAll(Collection<BlogPostItem> items) {
+		posts.addAll(items);
+	}
+
+	public void remove(BlogPostItem item) {
+		posts.remove(item);
+	}
+
+	public void clear() {
+		posts.clear();
+	}
+
+	public boolean isEmpty() {
+		return posts.size() == 0;
+	}
+
+	static class BlogPostHolder extends RecyclerView.ViewHolder {
+		private final ViewGroup layout;
+		private final CircleImageView avatar;
+		private final TextView author;
+		private final TrustIndicatorView trust;
+		private final TextView date;
+		private final TextView unread;
+		private final ImageView chat;
+		private final ImageView comment;
+		private final TextView title;
+		private final TextView body;
+
+		BlogPostHolder(View v) {
+			super(v);
+
+			layout = (ViewGroup) v;
+			avatar = (CircleImageView) v.findViewById(R.id.avatar);
+			author = (TextView) v.findViewById(R.id.authorName);
+			trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
+			date = (TextView) v.findViewById(R.id.dateView);
+			unread = (TextView) v.findViewById(R.id.newView);
+			chat = (ImageView) v.findViewById(R.id.chatView);
+			comment = (ImageView) v.findViewById(R.id.commentView);
+			title = (TextView) v.findViewById(R.id.titleView);
+			body = (TextView) v.findViewById(R.id.bodyView);
+		}
+	}
+
+	interface OnBlogPostClickListener {
+		void onBlogPostClick(int position);
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..54ac2cdda54898ec9f60815fb3cbb63b2e9ef85c
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java
@@ -0,0 +1,157 @@
+package org.briarproject.android.blogs;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.android.util.TrustIndicatorView;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+import org.briarproject.util.StringUtils;
+
+import javax.inject.Inject;
+
+import im.delight.android.identicons.IdenticonDrawable;
+
+import static android.view.View.GONE;
+import static android.widget.Toast.LENGTH_SHORT;
+import static org.briarproject.android.BriarActivity.GROUP_ID;
+
+public class BlogPostFragment extends BaseFragment {
+
+	public final static String TAG = BlogPostFragment.class.getName();
+
+	private final static String BLOG_POST_ID = "briar.BLOG_NAME";
+
+	private GroupId groupId;
+	private MessageId postId;
+	private BlogPostViewHolder ui;
+
+	@Inject
+	BlogController blogController;
+
+	static BlogPostFragment newInstance(GroupId groupId, MessageId postId) {
+		BlogPostFragment f = new BlogPostFragment();
+
+		Bundle bundle = new Bundle();
+		bundle.putByteArray(GROUP_ID, groupId.getBytes());
+		bundle.putByteArray(BLOG_POST_ID, postId.getBytes());
+
+		f.setArguments(bundle);
+		return f;
+	}
+
+	@Nullable
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		setHasOptionsMenu(true);
+
+		byte[] b = getArguments().getByteArray(GROUP_ID);
+		if (b == null) throw new IllegalStateException("No Group found.");
+		groupId = new GroupId(b);
+		byte[] p = getArguments().getByteArray(BLOG_POST_ID);
+		if (p == null) throw new IllegalStateException("No MessageId found.");
+		postId = new MessageId(p);
+
+		View v = inflater.inflate(R.layout.fragment_blog_post, container,
+				false);
+		ui = new BlogPostViewHolder(v);
+		return v;
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public void onStart() {
+		super.onStart();
+		blogController.loadBlog(groupId, false,
+				new UiResultHandler<Boolean>((Activity) listener) {
+					@Override
+					public void onResultUi(Boolean result) {
+						listener.hideLoadingScreen();
+						if (result) {
+							BlogPostItem post =
+									blogController.getBlogPost(postId);
+							if (post != null) {
+								bind(post);
+							}
+						} else {
+							Toast.makeText(getActivity(),
+									R.string.blogs_blog_post_failed_to_load,
+									LENGTH_SHORT).show();
+						}
+					}
+				});
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		switch (item.getItemId()) {
+			case android.R.id.home:
+				getActivity().onBackPressed();
+				return true;
+			default:
+				return super.onOptionsItemSelected(item);
+		}
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	private void bind(BlogPostItem post) {
+		Author author = post.getAuthor();
+		IdenticonDrawable d = new IdenticonDrawable(author.getId().getBytes());
+		ui.avatar.setImageDrawable(d);
+		ui.authorName.setText(author.getName());
+		ui.trust.setTrustLevel(post.getAuthorStatus());
+		ui.date.setText(
+				DateUtils.getRelativeTimeSpanString(post.getTimestamp()));
+
+		if (post.getTitle() != null) {
+			ui.title.setText(post.getTitle());
+		} else {
+			ui.title.setVisibility(GONE);
+		}
+
+		ui.body.setText(StringUtils.fromUtf8(post.getBody()));
+	}
+
+	private static class BlogPostViewHolder {
+		private ImageView avatar;
+		private TextView authorName;
+		private TrustIndicatorView trust;
+		private TextView date;
+		private TextView title;
+		private TextView body;
+
+		BlogPostViewHolder(View v) {
+			avatar = (ImageView) v.findViewById(R.id.avatar);
+			authorName = (TextView) v.findViewById(R.id.authorName);
+			trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
+			date = (TextView) v.findViewById(R.id.date);
+			title = (TextView) v.findViewById(R.id.title);
+			body = (TextView) v.findViewById(R.id.body);
+		}
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java b/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdb9c15ef7950a1437944d60ceada841efbe0432
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java
@@ -0,0 +1,73 @@
+package org.briarproject.android.blogs;
+
+import android.support.annotation.NonNull;
+
+import org.briarproject.api.blogs.BlogPostHeader;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.Author.Status;
+import org.briarproject.api.sync.MessageId;
+
+// This class is not thread-safe
+class BlogPostItem implements Comparable<BlogPostItem> {
+
+	private final BlogPostHeader header;
+	private final byte[] body;
+	private boolean read;
+
+	BlogPostItem(BlogPostHeader header, byte[] body) {
+		this.header = header;
+		this.body = body;
+		read = header.isRead();
+	}
+
+	public MessageId getId() {
+		return header.getId();
+	}
+
+	public String getTitle() {
+		return header.getTitle();
+	}
+
+	public byte[] getBody() {
+		return body;
+	}
+
+	public long getTimestamp() {
+		return header.getTimestamp();
+	}
+
+	public long getTimeReceived() {
+		return header.getTimeReceived();
+	}
+
+	public Author getAuthor() {
+		return header.getAuthor();
+	}
+
+	Status getAuthorStatus() {
+		return header.getAuthorStatus();
+	}
+
+	public void setRead(boolean read) {
+		this.read = read;
+	}
+
+	public boolean isRead() {
+		return read;
+	}
+
+	@Override
+	public int compareTo(@NonNull BlogPostItem other) {
+		if (this == other) return 0;
+		// The blog with the newest message comes first
+		long aTime = getTimeReceived(), bTime = other.getTimeReceived();
+		if (aTime > bTime) return -1;
+		if (aTime < bTime) return 1;
+		// Break ties by post title
+		if (getTitle() != null && other.getTitle() != null) {
+			return String.CASE_INSENSITIVE_ORDER
+					.compare(getTitle(), other.getTitle());
+		}
+		return 0;
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java
index a736537ff9257943e90197255ef9b08e42079765..80f234010360227c985d37cfe9df029d80ef5e68 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java
@@ -15,6 +15,8 @@ import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.fragment.BaseFragment;
 
+import static android.view.View.GONE;
+
 public class BlogsFragment extends BaseFragment {
 
 	public final static String TAG = BlogsFragment.class.getName();
@@ -54,6 +56,8 @@ public class BlogsFragment extends BaseFragment {
 		viewPager.setAdapter(tabAdapter);
 		tabLayout.setupWithViewPager(viewPager);
 
+		tabLayout.setVisibility(GONE);
+
 		if (savedInstanceState != null) {
 			int position = savedInstanceState.getInt(SELECTED_TAB, 0);
 			viewPager.setCurrentItem(position);
@@ -88,16 +92,21 @@ public class BlogsFragment extends BaseFragment {
 
 		@Override
 		public int getCount() {
-			return titles.length;
+			return 1;
+//			return titles.length;
 		}
 
 		@Override
 		public Fragment getItem(int position) {
-			switch (position) {
-				// TODO add your fragments here
-				default:
-					return MyBlogsFragment.newInstance(position);
-			}
+			return FeedFragment.newInstance();
+//			switch (position) {
+//				case 0:
+//					return FeedFragment.newInstance();
+//				case 1:
+//					return new MyBlogsFragment();
+//				default:
+//					return BlogListFragment.newInstance(position);
+//			}
 		}
 
 		@Override
diff --git a/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java b/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..980d67d1da8c96f84882d3b94b6e1dda6a82d34b
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java
@@ -0,0 +1,195 @@
+package org.briarproject.android.blogs;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.design.widget.TextInputEditText;
+import android.support.design.widget.TextInputLayout;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+import android.widget.Toast;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.BriarActivity;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.util.StringUtils;
+
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static android.widget.Toast.LENGTH_LONG;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
+import static org.briarproject.android.blogs.BlogActivity.IS_MY_BLOG;
+import static org.briarproject.android.blogs.BlogActivity.IS_NEW_BLOG;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_DESC_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_TITLE_LENGTH;
+
+public class CreateBlogActivity extends BriarActivity
+		implements OnEditorActionListener, OnClickListener {
+
+	private static final Logger LOG =
+			Logger.getLogger(CreateBlogActivity.class.getName());
+
+	private TextInputEditText titleInput, descInput;
+	private Button button;
+	private ProgressBar progress;
+
+	// Fields that are accessed from background threads must be volatile
+	@Inject
+	protected volatile IdentityManager identityManager;
+	@Inject
+	volatile BlogManager blogManager;
+
+	@Override
+	public void onCreate(Bundle state) {
+		super.onCreate(state);
+
+		setContentView(R.layout.activity_create_blog);
+
+		TextInputLayout titleLayout =
+				(TextInputLayout) findViewById(R.id.titleLayout);
+		if (titleLayout != null) {
+			titleLayout.setCounterMaxLength(MAX_BLOG_TITLE_LENGTH);
+		}
+		titleInput = (TextInputEditText) findViewById(R.id.titleInput);
+		TextWatcher nameEntryWatcher = new TextWatcher() {
+			@Override
+			public void afterTextChanged(Editable s) {
+			}
+			@Override
+			public void beforeTextChanged(CharSequence s, int start, int count,
+					int after) {
+			}
+			@Override
+			public void onTextChanged(CharSequence text, int start,
+					int lengthBefore, int lengthAfter) {
+				enableOrDisableCreateButton();
+			}
+		};
+		titleInput.setOnEditorActionListener(this);
+		titleInput.addTextChangedListener(nameEntryWatcher);
+
+		TextInputLayout descLayout =
+				(TextInputLayout) findViewById(R.id.descLayout);
+		if (descLayout != null) {
+			descLayout.setCounterMaxLength(MAX_BLOG_DESC_LENGTH);
+		}
+		descInput = (TextInputEditText) findViewById(R.id.descInput);
+		if (descInput != null) {
+			descInput.addTextChangedListener(nameEntryWatcher);
+		}
+
+		button = (Button) findViewById(R.id.createBlogButton);
+		if (button != null) {
+			button.setOnClickListener(this);
+		}
+
+		progress = (ProgressBar) findViewById(R.id.createBlogProgressBar);
+	}
+
+	@Override
+	public void injectActivity(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	private void enableOrDisableCreateButton() {
+		if (progress == null) return; // Not created yet
+		button.setEnabled(validateTitle() && validateDescription());
+	}
+
+	@Override
+	public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
+		descInput.requestFocus();
+		return true;
+	}
+
+	private boolean validateTitle() {
+		String name = titleInput.getText().toString();
+		int length = StringUtils.toUtf8(name).length;
+		return length <= MAX_BLOG_TITLE_LENGTH && length > 0;
+	}
+
+	private boolean validateDescription() {
+		String name = descInput.getText().toString();
+		int length = StringUtils.toUtf8(name).length;
+		return length <= MAX_BLOG_DESC_LENGTH && length > 0;
+	}
+
+	@Override
+	public void onClick(View view) {
+		if (view == button) {
+			hideSoftKeyboard(view);
+			if (!validateTitle()) return;
+			button.setVisibility(GONE);
+			progress.setVisibility(VISIBLE);
+			addBlog(titleInput.getText().toString(),
+					descInput.getText().toString());
+		}
+	}
+
+	private void addBlog(final String title, final String description) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					long now = System.currentTimeMillis();
+					Collection<LocalAuthor> authors =
+							identityManager.getLocalAuthors();
+					// take first identity, don't support more for now
+					LocalAuthor author = authors.iterator().next();
+					Blog f = blogManager.addBlog(author, title, description);
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Storing blog took " + duration + " ms");
+					displayBlog(f);
+				} catch (DbException e) {
+					// TODO show error, e.g. blog with same title exists
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					finishOnUiThread();
+				}
+			}
+		});
+	}
+
+	private void displayBlog(final Blog b) {
+		runOnUiThread(new Runnable() {
+			@Override
+			public void run() {
+				Intent i =
+						new Intent(CreateBlogActivity.this, BlogActivity.class);
+				i.putExtra(GROUP_ID, b.getId().getBytes());
+				i.putExtra(BLOG_NAME, b.getName());
+				i.putExtra(IS_MY_BLOG, true);
+				i.putExtra(IS_NEW_BLOG, true);
+				ActivityOptionsCompat options =
+						makeCustomAnimation(CreateBlogActivity.this,
+								android.R.anim.fade_in,
+								android.R.anim.fade_out);
+				ActivityCompat.startActivity(CreateBlogActivity.this, i,
+						options.toBundle());
+				supportFinishAfterTransition();
+			}
+		});
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedController.java b/briar-android/src/org/briarproject/android/blogs/FeedController.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad98adeefed6788e8a9854921156ff4a1ee1de20
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/FeedController.java
@@ -0,0 +1,25 @@
+package org.briarproject.android.blogs;
+
+import org.briarproject.android.controller.ActivityLifecycleController;
+import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.api.blogs.Blog;
+
+import java.util.Collection;
+
+public interface FeedController {
+
+	void onResume();
+	void onPause();
+
+	void loadPosts(
+			final UiResultHandler<Collection<BlogPostItem>> resultHandler);
+
+	void loadPersonalBlog(final UiResultHandler<Blog> resultHandler);
+
+	void setOnBlogPostAddedListener(OnBlogPostAddedListener listener);
+
+	interface OnBlogPostAddedListener {
+		void onBlogPostAdded(final BlogPostItem post);
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e22757f675d4843de4fdcd4c9196f0804d82b81
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java
@@ -0,0 +1,133 @@
+package org.briarproject.android.blogs;
+
+import org.briarproject.android.controller.DbControllerImpl;
+import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPostHeader;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.event.BlogPostAddedEvent;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.EventBus;
+import org.briarproject.api.event.EventListener;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.IdentityManager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+
+public class FeedControllerImpl extends DbControllerImpl
+		implements FeedController, EventListener {
+
+	private static final Logger LOG =
+			Logger.getLogger(FeedControllerImpl.class.getName());
+
+	@Inject
+	protected volatile BlogManager blogManager;
+	@Inject
+	protected volatile IdentityManager identityManager;
+	@Inject
+	protected volatile EventBus eventBus;
+
+	private volatile OnBlogPostAddedListener listener;
+
+	@Inject
+	FeedControllerImpl() {
+	}
+
+	public void onResume() {
+		eventBus.addListener(this);
+	}
+
+	public void onPause() {
+		eventBus.removeListener(this);
+	}
+
+	@Override
+	public void eventOccurred(Event e) {
+		if (!(e instanceof BlogPostAddedEvent)) return;
+
+		LOG.info("New blog post added");
+		if (listener != null) {
+			final BlogPostAddedEvent m = (BlogPostAddedEvent) e;
+			final BlogPostHeader header = m.getHeader();
+			try {
+				final byte[] body = blogManager.getPostBody(header.getId());
+				final BlogPostItem post = new BlogPostItem(header, body);
+				listener.onBlogPostAdded(post);
+			} catch (DbException ex) {
+				if (LOG.isLoggable(WARNING))
+					LOG.log(WARNING, ex.toString(), ex);
+			}
+		}
+	}
+
+	@Override
+	public void loadPosts(
+			final UiResultHandler<Collection<BlogPostItem>> resultHandler) {
+
+		LOG.info("Loading blog posts...");
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				Collection<BlogPostItem> posts = new ArrayList<>();
+				try {
+					// load blog posts
+					long now = System.currentTimeMillis();
+					for (Blog b : blogManager.getBlogs()) {
+						Collection<BlogPostHeader> header =
+								blogManager.getPostHeaders(b.getId());
+						for (BlogPostHeader h : header) {
+							byte[] body = blogManager.getPostBody(h.getId());
+							posts.add(new BlogPostItem(h, body));
+						}
+					}
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Loading posts took " + duration + " ms");
+					resultHandler.onResult(posts);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					resultHandler.onResult(null);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void loadPersonalBlog(final UiResultHandler<Blog> resultHandler) {
+		LOG.info("Loading personal blog...");
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					// load blog posts
+					long now = System.currentTimeMillis();
+					Author a =
+							identityManager.getLocalAuthors().iterator().next();
+					Blog b = blogManager.getPersonalBlog(a);
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Loading pers. blog took " + duration + " ms");
+					resultHandler.onResult(b);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					resultHandler.onResult(null);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void setOnBlogPostAddedListener(OnBlogPostAddedListener listener) {
+		this.listener = listener;
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc7a1e457664384eeb5c5c76562c3981f9432f19
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
@@ -0,0 +1,203 @@
+package org.briarproject.android.blogs;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
+import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.android.util.BriarRecyclerView;
+import org.briarproject.api.blogs.Blog;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+import static android.app.Activity.RESULT_OK;
+import static android.support.design.widget.Snackbar.LENGTH_LONG;
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static org.briarproject.android.BriarActivity.GROUP_ID;
+import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
+import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST;
+
+public class FeedFragment extends BaseFragment implements
+		OnBlogPostClickListener, FeedController.OnBlogPostAddedListener {
+
+	public final static String TAG = FeedFragment.class.getName();
+
+	@Inject
+	FeedController feedController;
+
+	private BlogPostAdapter adapter;
+	private LinearLayoutManager layoutManager;
+	private BriarRecyclerView list;
+	private Blog personalBlog = null;
+
+	static FeedFragment newInstance() {
+		FeedFragment f = new FeedFragment();
+
+		Bundle args = new Bundle();
+		f.setArguments(args);
+
+		return f;
+	}
+
+	@Nullable
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		setHasOptionsMenu(true);
+		View v = inflater.inflate(R.layout.fragment_blog, container, false);
+
+		adapter = new BlogPostAdapter(getActivity(), this);
+
+		layoutManager = new LinearLayoutManager(getActivity());
+		list = (BriarRecyclerView) v.findViewById(R.id.postList);
+		list.setLayoutManager(layoutManager);
+		list.setAdapter(adapter);
+		list.setEmptyText(R.string.blogs_feed_empty_state);
+
+		return v;
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+		feedController.setOnBlogPostAddedListener(this);
+	}
+
+	@Override
+	public void onActivityResult(int requestCode, int resultCode, Intent data) {
+		super.onActivityResult(requestCode, resultCode, data);
+
+		// The BlogPostAddedEvent arrives when the controller is not listening
+		if (requestCode == REQUEST_WRITE_POST && resultCode == RESULT_OK) {
+			showSnackBar(R.string.blogs_blog_post_created);
+		}
+	}
+
+	@Override
+	public void onStart() {
+		super.onStart();
+		feedController
+				.loadPersonalBlog(new UiResultHandler<Blog>(getActivity()) {
+					@Override
+					public void onResultUi(Blog b) {
+						personalBlog = b;
+					}
+				});
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		feedController.onResume();
+		feedController.loadPosts(
+				new UiResultHandler<Collection<BlogPostItem>>(getActivity()) {
+					@Override
+					public void onResultUi(Collection<BlogPostItem> posts) {
+						if (posts == null) {
+							// TODO show error?
+						} else if (posts.isEmpty()) {
+							list.showData();
+						} else {
+							adapter.addAll(posts);
+						}
+					}
+				});
+	}
+
+	@Override
+	public void onPause() {
+		super.onPause();
+		feedController.onPause();
+		// TODO save list position in database/preferences?
+	}
+
+	@Override
+	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+		inflater.inflate(R.menu.blogs_feed_actions, menu);
+		super.onCreateOptionsMenu(menu, inflater);
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		switch (item.getItemId()) {
+			case R.id.action_write_blog_post:
+				if (personalBlog == null) return false;
+				Intent i =
+						new Intent(getActivity(), WriteBlogPostActivity.class);
+				i.putExtra(GROUP_ID, personalBlog.getId().getBytes());
+				i.putExtra(BLOG_NAME, personalBlog.getName());
+				ActivityOptionsCompat options =
+						makeCustomAnimation(getActivity(),
+								android.R.anim.slide_in_left,
+								android.R.anim.slide_out_right);
+				startActivityForResult(i, REQUEST_WRITE_POST,
+						options.toBundle());
+				return true;
+			default:
+				return super.onOptionsItemSelected(item);
+		}
+	}
+
+	@Override
+	public void onBlogPostAdded(final BlogPostItem post) {
+		getActivity().runOnUiThread(new Runnable() {
+			@Override
+			public void run() {
+				adapter.add(post);
+				showSnackBar(R.string.blogs_blog_post_received);
+			}
+		});
+	}
+
+	@Override
+	public void onBlogPostClick(int position) {
+		// noop
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	private void showSnackBar(int stringRes) {
+		int firstVisible =
+				layoutManager.findFirstCompletelyVisibleItemPosition();
+		int lastVisible = layoutManager.findLastCompletelyVisibleItemPosition();
+		int count = adapter.getItemCount();
+		boolean scroll = count > (lastVisible - firstVisible + 1);
+
+		Snackbar s = Snackbar.make(list, stringRes, LENGTH_LONG);
+		s.getView().setBackgroundResource(R.color.briar_primary);
+		if (scroll) {
+			OnClickListener onClick = new OnClickListener() {
+				@Override
+				public void onClick(View v) {
+					list.smoothScrollToPosition(0);
+				}
+			};
+			s.setActionTextColor(ContextCompat
+					.getColor(getContext(),
+							R.color.briar_button_positive));
+			s.setAction(R.string.blogs_blog_post_scroll_to, onClick);
+		}
+		s.show();
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java b/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java
index 572bc516eb02192deee98f8b4b0d9a7965e120c5..1cc4ee8aceec054f5ca25ff4bbe934ceb9d021c1 100644
--- a/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java
@@ -1,49 +1,113 @@
 package org.briarproject.android.blogs;
 
+import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v7.widget.LinearLayoutManager;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.TextView;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.android.util.BriarRecyclerView;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPostHeader;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.db.NoSuchGroupException;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+
 public class MyBlogsFragment extends BaseFragment {
 
 	public final static String TAG = MyBlogsFragment.class.getName();
 
+	private static final Logger LOG = Logger.getLogger(TAG);
+	private BriarRecyclerView list;
+	private BlogListAdapter adapter;
+
+	// Fields that are accessed from background threads must be volatile
+	@Inject
+	protected volatile IdentityManager identityManager;
+	@Inject
+	volatile BlogManager blogManager;
+
 	@Inject
 	public MyBlogsFragment() {
 	}
 
-	static MyBlogsFragment newInstance(int num) {
-		MyBlogsFragment f = new MyBlogsFragment();
+	@Nullable
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		setHasOptionsMenu(true);
+
+		adapter = new BlogListAdapter(getActivity());
 
-		Bundle args = new Bundle();
-		args.putInt("num", num);
-		f.setArguments(args);
+		list = (BriarRecyclerView) inflater
+				.inflate(R.layout.fragment_blogs_my, container, false);
+		list.setLayoutManager(new LinearLayoutManager(getActivity()));
+		list.setAdapter(adapter);
+		list.setEmptyText(getString(R.string.blogs_my_blogs_empty_state));
 
-		return f;
+		return list;
 	}
 
-	@Nullable
 	@Override
-	public View onCreateView(LayoutInflater inflater, ViewGroup container,
-			Bundle savedInstanceState) {
+	public void onActivityCreated(Bundle savedInstanceState) {
+		super.onActivityCreated(savedInstanceState);
+		listener.getActivityComponent().inject(this);
+		// Starting from here, we can use injected objects
+	}
 
-		View v = inflater.inflate(R.layout.fragment_blogs_my, container,
-				false);
+	@Override
+	public void onResume() {
+		super.onResume();
+		adapter.clear();
+		loadBlogs();
+	}
 
-		TextView numView = (TextView) v.findViewById(R.id.num);
-		String num = String.valueOf(getArguments().getInt("num"));
-		numView.setText(num);
+	@Override
+	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+		inflater.inflate(R.menu.blogs_my_actions, menu);
+		super.onCreateOptionsMenu(menu, inflater);
+	}
 
-		return v;
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		// Handle presses on the action bar items
+		switch (item.getItemId()) {
+			case R.id.action_create_blog:
+				Intent intent =
+						new Intent(getContext(), CreateBlogActivity.class);
+				ActivityOptionsCompat options =
+						makeCustomAnimation(getActivity(),
+								android.R.anim.slide_in_left,
+								android.R.anim.slide_out_right);
+				ActivityCompat.startActivity(getActivity(), intent,
+						options.toBundle());
+				return true;
+			default:
+				return super.onOptionsItemSelected(item);
+		}
 	}
 
 	@Override
@@ -56,4 +120,49 @@ public class MyBlogsFragment extends BaseFragment {
 		component.inject(this);
 	}
 
+	private void loadBlogs() {
+		listener.runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					// load blogs
+					long now = System.currentTimeMillis();
+					Collection<BlogListItem> blogs = new ArrayList<>();
+					Collection<LocalAuthor> authors =
+							identityManager.getLocalAuthors();
+					LocalAuthor a = authors.iterator().next();
+					for (Blog b : blogManager.getBlogs(a)) {
+						try {
+							Collection<BlogPostHeader> headers =
+									blogManager.getPostHeaders(b.getId());
+							blogs.add(new BlogListItem(b, headers, true));
+						} catch (NoSuchGroupException e) {
+							// Continue
+						}
+					}
+					displayBlogs(blogs);
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Full blog load took " + duration + " ms");
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
+			}
+		});
+	}
+
+	private void displayBlogs(final Collection<BlogListItem> items) {
+		listener.runOnUiThread(new Runnable() {
+			@Override
+			public void run() {
+				if (items.size() == 0) {
+					list.showData();
+				} else {
+					adapter.addAll(items);
+				}
+			}
+		});
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/blogs/WriteBlogPostActivity.java b/briar-android/src/org/briarproject/android/blogs/WriteBlogPostActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d9cc61518b154b4e148e1f93c099b689920f9ff
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/WriteBlogPostActivity.java
@@ -0,0 +1,200 @@
+package org.briarproject.android.blogs;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.design.widget.TextInputEditText;
+import android.support.design.widget.TextInputLayout;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.BriarActivity;
+import org.briarproject.api.FormatException;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPost;
+import org.briarproject.api.blogs.BlogPostFactory;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.util.StringUtils;
+
+import java.security.GeneralSecurityException;
+import java.util.Collection;
+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.WARNING;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
+
+public class WriteBlogPostActivity extends BriarActivity
+		implements OnEditorActionListener {
+
+	private static final Logger LOG =
+			Logger.getLogger(WriteBlogPostActivity.class.getName());
+	private static final String contentType = "text/plain";
+
+	private TextInputEditText titleInput;
+	private EditText bodyInput;
+	private Button publishButton;
+	private ProgressBar progressBar;
+
+	// Fields that are accessed from background threads must be volatile
+	private volatile GroupId groupId;
+	@Inject
+	protected volatile IdentityManager identityManager;
+	@Inject
+	volatile BlogPostFactory blogPostFactory;
+	@Inject
+	volatile BlogManager blogManager;
+
+	@Override
+	public void onCreate(Bundle state) {
+		super.onCreate(state);
+
+		Intent i = getIntent();
+		byte[] b = i.getByteArrayExtra(GROUP_ID);
+		if (b == null) throw new IllegalStateException("No Group in intent.");
+		groupId = new GroupId(b);
+//		String blogName = i.getStringExtra(BLOG_NAME);
+//		if (blogName != null) setTitle(blogName);
+
+		setContentView(R.layout.activity_write_blog_post);
+//		String title =
+//				getTitle() + ": " + getString(R.string.blogs_write_blog_post);
+//		setTitle(title);
+
+		TextInputLayout titleLayout =
+				(TextInputLayout) findViewById(R.id.titleLayout);
+		if (titleLayout != null) {
+			titleLayout.setCounterMaxLength(MAX_BLOG_POST_TITLE_LENGTH);
+		}
+		titleInput = (TextInputEditText) findViewById(R.id.titleInput);
+		if (titleInput != null) {
+			titleInput.setOnEditorActionListener(this);
+		}
+
+		bodyInput = (EditText) findViewById(R.id.bodyInput);
+		bodyInput.addTextChangedListener(new TextWatcher() {
+			@Override
+			public void beforeTextChanged(CharSequence s, int start, int count,
+					int after) {
+			}
+
+			@Override
+			public void onTextChanged(CharSequence s, int start, int before,
+					int count) {
+			}
+
+			@Override
+			public void afterTextChanged(Editable s) {
+				enableOrDisablePublishButton();
+			}
+		});
+
+		publishButton = (Button) findViewById(R.id.publishButton);
+		publishButton.setOnClickListener(new View.OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				publish();
+			}
+		});
+
+		progressBar = (ProgressBar) findViewById(R.id.progressBar);
+	}
+
+	@Override
+	public void injectActivity(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
+		bodyInput.requestFocus();
+		return true;
+	}
+
+	private void enableOrDisablePublishButton() {
+		int bodyLength =
+				StringUtils.toUtf8(bodyInput.getText().toString()).length;
+		if (bodyLength > 0 && bodyLength <= MAX_BLOG_POST_BODY_LENGTH &&
+				titleInput.getText().length() <= MAX_BLOG_POST_TITLE_LENGTH)
+			publishButton.setEnabled(true);
+		else
+			publishButton.setEnabled(false);
+	}
+
+	private void publish() {
+		// title
+		String title = titleInput.getText().toString();
+		if (title.length() > MAX_BLOG_POST_TITLE_LENGTH) return;
+		if (title.length() == 0) title = null;
+
+		// body
+		byte[] body = StringUtils.toUtf8(bodyInput.getText().toString());
+
+		// hide publish button, show progress bar
+		publishButton.setVisibility(GONE);
+		progressBar.setVisibility(VISIBLE);
+
+		storePost(title, body);
+	}
+
+	private void storePost(final String title, final byte[] body) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				long now = System.currentTimeMillis();
+				try {
+					Collection<LocalAuthor> authors =
+							identityManager.getLocalAuthors();
+					LocalAuthor author = authors.iterator().next();
+					BlogPost p = blogPostFactory
+							.createBlogPost(groupId, title, now, null, author,
+									contentType, body);
+					blogManager.addLocalPost(p);
+					postPublished();
+				} catch (DbException | GeneralSecurityException | FormatException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					postFailedToPublish();
+				}
+			}
+		});
+	}
+
+	private void postPublished() {
+		runOnUiThread(new Runnable() {
+			@Override
+			public void run() {
+				setResult(RESULT_OK);
+				supportFinishAfterTransition();
+			}
+		});
+	}
+
+	private void postFailedToPublish() {
+		runOnUiThread(new Runnable() {
+			@Override
+			public void run() {
+				// hide progress bar, show publish button
+				progressBar.setVisibility(GONE);
+				publishButton.setVisibility(VISIBLE);
+				// TODO show error
+			}
+		});
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumListAdapter.java b/briar-android/src/org/briarproject/android/forum/ForumListAdapter.java
index 17fd01346b17fbedbcf58567a430987c9c77d209..88759804aefa31203ede1b5047da0ab2becd6e48 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumListAdapter.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumListAdapter.java
@@ -104,16 +104,17 @@ class ForumListAdapter extends
 		// Post Count
 		int postCount = item.getPostCount();
 		if (postCount > 0) {
-			ui.unread.setText(ctx.getResources()
-					.getQuantityString(R.plurals.forum_posts, postCount,
+			ui.avatar.setProblem(false);
+			ui.postCount.setText(ctx.getResources()
+					.getQuantityString(R.plurals.posts, postCount,
 							postCount));
-			ui.unread.setTextColor(
+			ui.postCount.setTextColor(
 					ContextCompat
 							.getColor(ctx, R.color.briar_text_secondary));
 		} else {
 			ui.avatar.setProblem(true);
-			ui.unread.setText(ctx.getString(R.string.forum_no_posts));
-			ui.unread.setTextColor(
+			ui.postCount.setText(ctx.getString(R.string.no_posts));
+			ui.postCount.setTextColor(
 					ContextCompat
 							.getColor(ctx, R.color.briar_text_tertiary));
 		}
@@ -187,7 +188,7 @@ class ForumListAdapter extends
 		private final ViewGroup layout;
 		private final TextAvatarView avatar;
 		private final TextView name;
-		private final TextView unread;
+		private final TextView postCount;
 		private final TextView date;
 
 		ForumViewHolder(View v) {
@@ -196,7 +197,7 @@ class ForumListAdapter extends
 			layout = (ViewGroup) v;
 			avatar = (TextAvatarView) v.findViewById(R.id.avatarView);
 			name = (TextView) v.findViewById(R.id.forumNameView);
-			unread = (TextView) v.findViewById(R.id.unreadView);
+			postCount = (TextView) v.findViewById(R.id.postCountView);
 			date = (TextView) v.findViewById(R.id.dateView);
 		}
 	}
diff --git a/briar-android/src/org/briarproject/android/util/AndroidUtils.java b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
index 252d955749a925f725aacaf44432ef6d3ac78fe4..077dc8aa58f95d366a678bb18fffbd3f403047d7 100644
--- a/briar-android/src/org/briarproject/android/util/AndroidUtils.java
+++ b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
@@ -57,6 +57,11 @@ public class AndroidUtils {
 			til.setError(null);
 	}
 
+	public static void setError(TextInputLayout til, int res,
+			boolean condition) {
+		setError(til, til.getContext().getString(res), condition);
+	}
+
 	public static String getBluetoothAddress(Context ctx,
 			BluetoothAdapter adapter) {
 		// Return the adapter's address if it's valid and not fake
diff --git a/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java b/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java
index 7563354b65a1e335961c3348f76e52d424705259..6cd36dd6b0e71c06c815d7f4f5d0cf44ebb7d2fd 100644
--- a/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java
+++ b/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java
@@ -130,6 +130,11 @@ public class BriarRecyclerView extends FrameLayout {
 		emptyView.setText(text);
 	}
 
+	public void setEmptyText(int res) {
+		if (recyclerView == null) initViews();
+		emptyView.setText(res);
+	}
+
 	public void showProgressBar() {
 		if (recyclerView == null) initViews();
 		recyclerView.setVisibility(INVISIBLE);
@@ -158,6 +163,11 @@ public class BriarRecyclerView extends FrameLayout {
 		recyclerView.scrollToPosition(position);
 	}
 
+	public void smoothScrollToPosition(int position) {
+		if (recyclerView == null) initViews();
+		recyclerView.smoothScrollToPosition(position);
+	}
+
 	public RecyclerView getRecyclerView() {
 		return this.recyclerView;
 	}
diff --git a/briar-android/src/org/briarproject/android/util/TextAvatarView.java b/briar-android/src/org/briarproject/android/util/TextAvatarView.java
index cc4ab91485f01f58350ce663ddcbd81e39a0c502..6033021eb2699f4410799252dff8bea808e6ee5f 100644
--- a/briar-android/src/org/briarproject/android/util/TextAvatarView.java
+++ b/briar-android/src/org/briarproject/android/util/TextAvatarView.java
@@ -38,7 +38,7 @@ public class TextAvatarView extends FrameLayout {
 	}
 
 	public void setText(String text) {
-		character.setText(text);
+		character.setText(text.toUpperCase());
 	}
 
 	public void setUnreadCount(int count) {
diff --git a/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java b/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java
index ce01bf301e34e1176d307e81e23ef80dd226fb62..a9ea3c8fb24653f07cbfc09252813550abad1253 100644
--- a/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java
+++ b/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java
@@ -8,6 +8,8 @@ import android.widget.ImageView;
 import org.briarproject.R;
 import org.briarproject.api.identity.Author.Status;
 
+import static org.briarproject.api.identity.Author.Status.OURSELVES;
+
 public class TrustIndicatorView extends ImageView {
 
 	public TrustIndicatorView(Context context) {
@@ -24,6 +26,11 @@ public class TrustIndicatorView extends ImageView {
 	}
 
 	public void setTrustLevel(Status status) {
+		if (status == OURSELVES) {
+			setVisibility(GONE);
+			return;
+		}
+
 		int res;
 		switch (status) {
 			case ANONYMOUS:
@@ -39,6 +46,7 @@ public class TrustIndicatorView extends ImageView {
 				res = R.drawable.trust_indicator_unknown;
 		}
 		setImageDrawable(ContextCompat.getDrawable(getContext(), res));
+		setVisibility(VISIBLE);
 	}
 
 }
diff --git a/briar-api/src/org/briarproject/api/identity/Author.java b/briar-api/src/org/briarproject/api/identity/Author.java
index 9aa543b3acd09654cac3f81da23814350496336a..de15869d3ebcbdf646580e177128cffea130eac7 100644
--- a/briar-api/src/org/briarproject/api/identity/Author.java
+++ b/briar-api/src/org/briarproject/api/identity/Author.java
@@ -5,7 +5,7 @@ import java.io.UnsupportedEncodingException;
 /** A pseudonym for a user. */
 public class Author {
 
-	public enum Status { ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED }
+	public enum Status { ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES }
 
 	private final AuthorId id;
 	private final String name;
diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
index 5b1434ad1e3644b6964bea20916d5508d4ee53dc..bd14c334076b222f0218b5783118e3a5cd915c11 100644
--- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
+++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
@@ -17,6 +17,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.inject.Inject;
 
+import static org.briarproject.api.identity.Author.Status.OURSELVES;
 import static org.briarproject.api.identity.Author.Status.UNKNOWN;
 import static org.briarproject.api.identity.Author.Status.VERIFIED;
 
@@ -110,7 +111,7 @@ class IdentityManagerImpl implements IdentityManager {
 			throws DbException {
 		// Compare to the IDs of the user's identities
 		for (LocalAuthor a : db.getLocalAuthors(txn))
-			if (a.getId().equals(authorId)) return VERIFIED;
+			if (a.getId().equals(authorId)) return OURSELVES;
 		// Compare to the IDs of contacts' identities
 		for (Contact c : db.getContacts(txn))
 			if (c.getAuthor().getId().equals(authorId)) return VERIFIED;