diff --git a/briar-android/res/layout/author_view.xml b/briar-android/res/layout/author_view.xml
index 48892daf258b52dd9e311e73a24787f743d1d928..6f389e1de237bb90a63e5ca5c5d3a4cfd779b92e 100644
--- a/briar-android/res/layout/author_view.xml
+++ b/briar-android/res/layout/author_view.xml
@@ -9,7 +9,7 @@
 		style="@style/BriarAvatar"
 		android:layout_width="@dimen/blogs_avatar_normal_size"
 		android:layout_height="@dimen/blogs_avatar_normal_size"
-		android:layout_centerVertical="true"
+		android:layout_alignTop="@+id/authorName"
 		android:layout_marginRight="@dimen/margin_medium"
 		tools:src="@drawable/ic_launcher"/>
 
@@ -30,7 +30,6 @@
 		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"
diff --git a/briar-android/res/layout/list_item_group_join_notice.xml b/briar-android/res/layout/list_item_group_join_notice.xml
new file mode 100644
index 0000000000000000000000000000000000000000..71d6ceec17f6c855a735bc59b39948a860e146f3
--- /dev/null
+++ b/briar-android/res/layout/list_item_group_join_notice.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+	android:id="@+id/layout"
+	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="wrap_content"
+	android:layout_marginLeft="@dimen/margin_medium"
+	android:baselineAligned="false"
+	android:orientation="vertical">
+
+	<View
+		android:id="@+id/top_divider"
+		style="@style/Divider.ForumList"
+		android:layout_alignParentTop="true"/>
+
+	<org.thoughtcrime.securesms.components.emoji.EmojiTextView
+		android:id="@+id/text"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:layout_below="@+id/top_divider"
+		android:layout_marginBottom="@dimen/margin_small"
+		android:layout_marginRight="@dimen/margin_medium"
+		android:layout_marginTop="@dimen/margin_medium"
+		android:textColor="@color/briar_text_secondary"
+		android:textSize="@dimen/text_size_medium"
+		android:textStyle="italic"
+		tools:text="@string/groups_member_joined"/>
+
+	<ImageView
+		android:id="@+id/icon"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignLeft="@+id/text"
+		android:layout_below="@+id/text"
+		android:layout_marginRight="@dimen/margin_small"
+		tools:ignore="ContentDescription"
+		tools:src="@drawable/ic_visibility"/>
+
+	<TextView
+		android:id="@+id/info"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:layout_alignEnd="@+id/text"
+		android:layout_alignRight="@+id/text"
+		android:layout_below="@+id/text"
+		android:layout_toRightOf="@+id/icon"
+		android:gravity="center_vertical"
+		android:minHeight="24dp"
+		android:textColor="@color/briar_text_secondary"
+		android:textIsSelectable="true"
+		android:textSize="@dimen/text_size_tiny"
+		android:textStyle="italic"
+		tools:text="@string/groups_reveal_visible_revealed_by_contact"/>
+
+	<org.briarproject.android.view.AuthorView
+		android:id="@+id/author"
+		android:layout_width="wrap_content"
+		android:layout_height="@dimen/button_size"
+		android:layout_alignLeft="@+id/text"
+		android:layout_below="@+id/info"
+		android:gravity="center"
+		app:persona="commenter"/>
+
+	<Button
+		android:id="@+id/optionsButton"
+		style="@style/BriarButtonFlat.Positive.Tiny"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignEnd="@+id/text"
+		android:layout_alignRight="@+id/text"
+		android:layout_alignTop="@+id/author"
+		android:layout_toRightOf="@+id/author"
+		android:gravity="right|center_vertical"
+		android:text="@string/options"/>
+
+</RelativeLayout>
diff --git a/briar-android/res/layout/list_item_thread.xml b/briar-android/res/layout/list_item_thread.xml
index f788c0ce8e6aff841c4d3f4dcae07e423576966c..3757cc7e7c9e3335e095ef62a2ad709d906b6487 100644
--- a/briar-android/res/layout/list_item_thread.xml
+++ b/briar-android/res/layout/list_item_thread.xml
@@ -6,8 +6,8 @@
 	xmlns:tools="http://schemas.android.com/tools"
 	android:layout_width="match_parent"
 	android:layout_height="wrap_content"
-	android:orientation="horizontal"
-	android:baselineAligned="false">
+	android:baselineAligned="false"
+	android:orientation="horizontal">
 
 	<RelativeLayout
 		android:layout_width="wrap_content"
@@ -76,60 +76,58 @@
 			android:id="@+id/text"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
-			android:layout_marginBottom="@dimen/margin_small"
-			android:layout_marginLeft="@dimen/margin_medium"
-			android:layout_marginRight="@dimen/margin_medium"
-			android:layout_marginTop="@dimen/margin_medium"
+			android:paddingRight="@dimen/margin_medium"
+			android:paddingTop="@dimen/margin_medium"
+			android:textColor="@color/briar_text_primary"
 			android:textIsSelectable="true"
 			android:textSize="@dimen/text_size_medium"
-			android:textColor="@color/briar_text_primary"
 			tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."/>
 
 		<org.briarproject.android.view.AuthorView
 			android:id="@+id/author"
 			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
+			android:layout_height="@dimen/button_size"
 			android:layout_alignLeft="@id/text"
 			android:layout_below="@id/text"
+			android:gravity="center"
 			app:persona="commenter"/>
 
-		<ImageView
-			android:id="@+id/chevron"
+		<TextView
+			android:id="@+id/replies"
 			android:layout_width="wrap_content"
 			android:layout_height="wrap_content"
-			android:layout_alignParentRight="true"
-			android:layout_below="@id/text"
-			android:layout_marginRight="@dimen/margin_medium"
-			android:layout_marginTop="@dimen/margin_small"
-			android:clickable="true"
-			android:src="@drawable/selector_chevron"/>
+			android:layout_alignBottom="@+id/author"
+			android:layout_alignTop="@+id/author"
+			android:layout_toLeftOf="@+id/btn_reply"
+			android:layout_toRightOf="@+id/author"
+			android:ellipsize="end"
+			android:gravity="right|end|center_vertical"
+			android:maxLines="1"
+			android:padding="@dimen/margin_medium"
+			android:textSize="@dimen/text_size_tiny"
+			tools:text="2 replies"/>
 
 		<TextView
 			android:id="@+id/btn_reply"
+			style="@style/BriarButtonFlat.Positive.Tiny"
 			android:layout_width="wrap_content"
 			android:layout_height="wrap_content"
-			android:layout_below="@id/text"
-			android:layout_marginRight="@dimen/margin_medium"
-			android:layout_toLeftOf="@id/chevron"
-			android:background="?attr/selectableItemBackground"
-			android:clickable="true"
-			android:padding="@dimen/margin_medium"
+			android:layout_alignBottom="@+id/author"
+			android:layout_toLeftOf="@+id/chevron"
 			android:text="@string/btn_reply"
-			android:textColor="@color/briar_button_positive"
 			android:textSize="@dimen/text_size_tiny"/>
 
-		<TextView
-			android:id="@+id/replies"
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
-			android:layout_alignBaseline="@id/btn_reply"
-			android:layout_toLeftOf="@id/btn_reply"
-			android:layout_toRightOf="@+id/author"
-			android:gravity="right|end"
-			android:maxLines="1"
+		<ImageView
+			android:id="@+id/chevron"
+			android:layout_width="@dimen/button_size"
+			android:layout_height="@dimen/button_size"
+			android:layout_alignBottom="@+id/author"
+			android:layout_alignParentRight="true"
+			android:background="?attr/selectableItemBackground"
+			android:clickable="true"
 			android:padding="@dimen/margin_medium"
-			android:textSize="@dimen/text_size_tiny"
-			tools:text="2 replies"/>
+			android:scaleType="center"
+			android:src="@drawable/selector_chevron"/>
 
 		<View
 			android:id="@+id/top_divider"
diff --git a/briar-android/res/layout/list_item_thread_notice.xml b/briar-android/res/layout/list_item_thread_notice.xml
deleted file mode 100644
index 2beecd1f2aabf4c2a2b209974e6d383268e3d8b1..0000000000000000000000000000000000000000
--- a/briar-android/res/layout/list_item_thread_notice.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-	android:id="@+id/layout"
-	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="wrap_content"
-	android:layout_marginLeft="@dimen/margin_medium"
-	android:baselineAligned="false"
-	android:orientation="vertical">
-
-	<View
-		android:id="@+id/top_divider"
-		style="@style/Divider.ForumList"
-		android:layout_width="match_parent"
-		android:layout_height="@dimen/margin_separator"/>
-
-	<LinearLayout
-		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:layout_marginBottom="@dimen/margin_small"
-		android:layout_marginLeft="@dimen/margin_medium"
-		android:layout_marginRight="@dimen/margin_medium"
-		android:layout_marginTop="@dimen/margin_medium"
-		android:orientation="horizontal">
-
-		<org.briarproject.android.view.AuthorView
-			android:id="@+id/author"
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
-			app:persona="commenter"/>
-
-		<org.thoughtcrime.securesms.components.emoji.EmojiTextView
-			android:id="@+id/text"
-			android:layout_width="match_parent"
-			android:layout_height="match_parent"
-			android:layout_marginLeft="@dimen/margin_medium"
-			android:gravity="center_vertical"
-			android:textColor="@color/briar_text_secondary"
-			android:textIsSelectable="true"
-			android:textSize="@dimen/text_size_medium"
-			android:textStyle="italic"
-			tools:text="@string/groups_member_joined"/>
-
-	</LinearLayout>
-
-</LinearLayout>
diff --git a/briar-android/res/values/dimens.xml b/briar-android/res/values/dimens.xml
index fc0531ab66293a47c0cc211e5d13da23d2589661..6662acfe4927fed5d6ce40d868c4d6ad15299ef3 100644
--- a/briar-android/res/values/dimens.xml
+++ b/briar-android/res/values/dimens.xml
@@ -31,6 +31,7 @@
 	<dimen name="avatar_forum_size">48dp</dimen>
 	<dimen name="avatar_border_width">2dp</dimen>
 	<dimen name="avatar_text_size">30sp</dimen>
+	<dimen name="button_size">48dp</dimen>
 
 	<dimen name="unread_bubble_text_size">12sp</dimen>
 	<dimen name="unread_bubble_padding_horizontal">6dp</dimen>
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index cc6f0f7d64c1be12b2ecfbb9dd4901e25f1c50ac..3dbbb17a0eb36f71837289387329d1a7b6734ccc 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -74,6 +74,7 @@
 	<string name="delete">Delete</string>
 	<string name="accept">Accept</string>
 	<string name="decline">Decline</string>
+	<string name="options">Options</string>
 	<string name="online">Online</string>
 	<string name="offline">Offline</string>
 	<string name="send">Send</string>
@@ -169,7 +170,10 @@
 	<string name="groups_message_received">Message received</string>
 	<string name="groups_member_list">Member List</string>
 	<string name="groups_invite_members">Invite Members</string>
-	<string name="groups_member_joined">joined the group.</string>
+	<string name="groups_member_created_you">You created the group</string>
+	<string name="groups_member_created">%s created the group</string>
+	<string name="groups_member_joined_you">You joined the group</string>
+	<string name="groups_member_joined">%s joined the group</string>
 	<string name="groups_leave">Leave Group</string>
 	<string name="groups_leave_dialog_title">Confirm Leaving Group</string>
 	<string name="groups_leave_dialog_message">Are you sure that you want to leave this group?</string>
@@ -220,7 +224,7 @@
 	<string name="forum_new_entry_received">New forum entry</string>
 	<string name="forum_new_message_hint">New Entry</string>
 	<string name="forum_message_reply_hint">New Reply</string>
-	<string name="btn_reply">REPLY</string>
+	<string name="btn_reply">Reply</string>
 	<plurals name="message_replies">
 		<item quantity="one">%1$d reply</item>
 		<item quantity="other">%1$d replies</item>
diff --git a/briar-android/res/values/styles.xml b/briar-android/res/values/styles.xml
index 71b6c910cecab6412a25ae372a47a5c8eb120c4f..0896e6e6b327e8f68ab6b30593b81ce03cad8a2c 100644
--- a/briar-android/res/values/styles.xml
+++ b/briar-android/res/values/styles.xml
@@ -45,6 +45,12 @@
 		<item name="android:padding">@dimen/margin_large</item>
 	</style>
 
+	<style name="BriarButtonFlat.Positive.Tiny" parent="BriarButtonFlat.Positive">
+		<item name="android:textSize">@dimen/text_size_tiny</item>
+		<item name="android:padding">@dimen/margin_medium</item>
+		<item name="android:minWidth">@dimen/button_size</item>
+	</style>
+
 	<style name="BriarTextTitle">
 		<item name="android:textSize">@dimen/text_size_medium</item>
 		<item name="android:textColor">@android:color/primary_text_light</item>
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index 22c713889bd2e88c9ff0a5bac7dc0beb2a58f3b5..0fe164367f3e1b8c26bd18f64e15ad88d59e8cf9 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -5,6 +5,7 @@ import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.v4.app.ActivityCompat;
 import android.support.v4.app.ActivityOptionsCompat;
@@ -26,6 +27,8 @@ import org.briarproject.android.threaded.ThreadListController;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.forum.Forum;
 import org.briarproject.api.forum.ForumPostHeader;
+import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
 
 import javax.inject.Inject;
 
@@ -35,8 +38,10 @@ import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
 import static android.widget.Toast.LENGTH_SHORT;
 import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
 
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
 public class ForumActivity extends
-		ThreadListActivity<Forum, ForumItem, ForumPostHeader> {
+		ThreadListActivity<Forum, ThreadItemAdapter<ForumItem>, ForumItem, ForumPostHeader> {
 
 	private static final int REQUEST_FORUM_SHARED = 3;
 
@@ -54,7 +59,7 @@ public class ForumActivity extends
 	}
 
 	@Override
-	public void onCreate(Bundle state) {
+	public void onCreate(@Nullable Bundle state) {
 		super.onCreate(state);
 
 		Intent i = getIntent();
diff --git a/briar-android/src/org/briarproject/android/privategroup/VisibilityStringProvider.java b/briar-android/src/org/briarproject/android/privategroup/VisibilityStringProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..0cd7e0dc7077855516a57ac9d6425d873669914e
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/VisibilityStringProvider.java
@@ -0,0 +1,26 @@
+package org.briarproject.android.privategroup;
+
+import android.support.annotation.StringRes;
+
+import org.briarproject.R;
+import org.briarproject.api.privategroup.Visibility;
+
+public class VisibilityStringProvider {
+
+	@StringRes
+	public static int getVisibilityStringId(Visibility v) {
+		switch (v) {
+			case VISIBLE:
+				return R.string.groups_reveal_visible;
+			case REVEALED_BY_US:
+				return R.string.groups_reveal_visible_revealed_by_us;
+			case REVEALED_BY_CONTACT:
+				return R.string.groups_reveal_visible_revealed_by_contact;
+			case INVISIBLE:
+				return R.string.groups_reveal_invisible;
+			default:
+				throw new IllegalArgumentException("Unknown visibility");
+		}
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
index 8a24aaba657bedc4176dc182ff4dfd60ec5e44ab..438c061120faaa8a4549eb8e31ed4fd3abe90318 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
@@ -5,6 +5,7 @@ import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.v4.app.ActivityCompat;
 import android.support.v4.app.ActivityOptionsCompat;
@@ -26,10 +27,13 @@ import org.briarproject.android.privategroup.reveal.RevealContactsActivity;
 import org.briarproject.android.threaded.ThreadListActivity;
 import org.briarproject.android.threaded.ThreadListController;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.identity.AuthorId;
+import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
 import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
 import org.briarproject.api.privategroup.GroupMessageHeader;
 import org.briarproject.api.privategroup.PrivateGroup;
+import org.briarproject.api.privategroup.Visibility;
 
 import javax.inject.Inject;
 
@@ -40,7 +44,7 @@ import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
 public class GroupActivity extends
-		ThreadListActivity<PrivateGroup, GroupMessageItem, GroupMessageHeader>
+		ThreadListActivity<PrivateGroup, GroupMessageAdapter, GroupMessageItem, GroupMessageHeader>
 		implements GroupListener, OnClickListener {
 
 	private final static int REQUEST_INVITE = 2;
@@ -63,7 +67,7 @@ public class GroupActivity extends
 	}
 
 	@Override
-	public void onCreate(Bundle state) {
+	public void onCreate(@Nullable Bundle state) {
 		super.onCreate(state);
 
 		Intent i = getIntent();
@@ -105,7 +109,7 @@ public class GroupActivity extends
 	}
 
 	@Override
-	protected void onNamedGroupLoaded(PrivateGroup group) {
+	protected void onNamedGroupLoaded(final PrivateGroup group) {
 		setTitle(group.getName());
 		// Created by
 		ActionBar actionBar = getSupportActionBar();
@@ -113,11 +117,12 @@ public class GroupActivity extends
 			actionBar.setSubtitle(getString(R.string.groups_created_by,
 					group.getCreator().getName()));
 		}
-		controller.isCreator(group,
-				new UiResultExceptionHandler<Boolean, DbException>(this) {
+		controller.loadLocalAuthor(
+				new UiResultExceptionHandler<LocalAuthor, DbException>(this) {
 					@Override
-					public void onResultUi(Boolean isCreator) {
-						GroupActivity.this.isCreator = isCreator;
+					public void onResultUi(LocalAuthor author) {
+						isCreator = group.getCreator().equals(author);
+						adapter.setPerspective(isCreator);
 						showMenuItems();
 					}
 
@@ -272,6 +277,11 @@ public class GroupActivity extends
 				});
 	}
 
+	@Override
+	public void onContactRelationshipRevealed(AuthorId memberId, Visibility v) {
+		adapter.updateVisibility(memberId, v);
+	}
+
 	@Override
 	public void onGroupDissolved() {
 		setGroupEnabled(false);
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
index 18915ab15df9bfb700b69f518d3099c9edae274f..523abc1b9b7b62c9e2d61df072fd57b54274edf5 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
@@ -5,20 +5,26 @@ import android.support.annotation.UiThread;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
 import org.briarproject.android.threaded.ThreadListController;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.identity.AuthorId;
+import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.privategroup.GroupMessageHeader;
 import org.briarproject.api.privategroup.PrivateGroup;
+import org.briarproject.api.privategroup.Visibility;
 
 public interface GroupController
 		extends
 		ThreadListController<PrivateGroup, GroupMessageItem, GroupMessageHeader> {
 
-	void isCreator(PrivateGroup group,
-			ResultExceptionHandler<Boolean, DbException> handler);
+	void loadLocalAuthor(
+			ResultExceptionHandler<LocalAuthor, DbException> handler);
 
 	void isDissolved(
 			ResultExceptionHandler<Boolean, DbException> handler);
 
 	interface GroupListener extends ThreadListListener<GroupMessageHeader> {
+		@UiThread
+		void onContactRelationshipRevealed(AuthorId memberId, Visibility v);
+
 		@UiThread
 		void onGroupDissolved();
 	}
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
index 1a11bd88ef5a23e35d0e424ecacd76e3dd75cd17..630da0e85b24e0824993ea0122d1441782b380b0 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
@@ -17,6 +17,7 @@ import org.briarproject.api.event.GroupMessageAddedEvent;
 import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.privategroup.ContactRelationshipRevealedEvent;
 import org.briarproject.api.privategroup.GroupMessage;
 import org.briarproject.api.privategroup.GroupMessageFactory;
 import org.briarproject.api.privategroup.GroupMessageHeader;
@@ -33,7 +34,6 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static java.lang.Math.max;
-
 import static java.util.logging.Level.WARNING;
 
 public class GroupControllerImpl extends
@@ -81,6 +81,18 @@ public class GroupControllerImpl extends
 					}
 				});
 			}
+		} else if (e instanceof ContactRelationshipRevealedEvent) {
+			final ContactRelationshipRevealedEvent c =
+					(ContactRelationshipRevealedEvent) e;
+			if (getGroupId().equals(c.getGroupId())) {
+				listener.runOnUiThreadUnlessDestroyed(new Runnable() {
+					@Override
+					public void run() {
+						listener.onContactRelationshipRevealed(c.getMemberId(),
+								c.getVisibility());
+					}
+				});
+			}
 		} else if (e instanceof GroupDissolvedEvent) {
 			GroupDissolvedEvent g = (GroupDissolvedEvent) e;
 			if (getGroupId().equals(g.getGroupId())) {
@@ -178,22 +190,20 @@ public class GroupControllerImpl extends
 	protected GroupMessageItem buildItem(GroupMessageHeader header,
 			String body) {
 		if (header instanceof JoinMessageHeader) {
-			return new JoinMessageItem(header, body);
+			return new JoinMessageItem((JoinMessageHeader) header, body);
 		}
 		return new GroupMessageItem(header, body);
 	}
 
 	@Override
-	public void isCreator(final PrivateGroup group,
-			final ResultExceptionHandler<Boolean, DbException> handler) {
+	public void loadLocalAuthor(
+			final ResultExceptionHandler<LocalAuthor, DbException> handler) {
 		runOnDbThread(new Runnable() {
 			@Override
 			public void run() {
 				try {
 					LocalAuthor author = identityManager.getLocalAuthor();
-					boolean isCreator =
-							author.getId().equals(group.getCreator().getId());
-					handler.onResult(isCreator);
+					handler.onResult(author);
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
index c042a182965f3dbe067a3a5a343898c1f13cb0f1..4c94438e0739060fa29bed3fc38e24e960836001 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
@@ -11,9 +11,17 @@ import org.briarproject.R;
 import org.briarproject.android.threaded.BaseThreadItemViewHolder;
 import org.briarproject.android.threaded.ThreadItemAdapter;
 import org.briarproject.android.threaded.ThreadPostViewHolder;
+import org.briarproject.api.identity.AuthorId;
+import org.briarproject.api.nullsafety.NotNullByDefault;
+import org.briarproject.api.privategroup.Visibility;
+
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
 
 @UiThread
-public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
+@NotNullByDefault
+class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
+
+	private boolean isCreator = false;
 
 	public GroupMessageAdapter(ThreadItemListener<GroupMessageItem> listener,
 			LinearLayoutManager layoutManager) {
@@ -33,10 +41,36 @@ public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
 			ViewGroup parent, int type) {
 		View v = LayoutInflater.from(parent.getContext())
 				.inflate(type, parent, false);
-		if (type == R.layout.list_item_thread_notice) {
-			return new JoinMessageItemViewHolder(v);
+		if (type == R.layout.list_item_group_join_notice) {
+			return new JoinMessageItemViewHolder(v, isCreator);
 		}
 		return new ThreadPostViewHolder<>(v);
 	}
 
+	void setPerspective(boolean isCreator) {
+		this.isCreator = isCreator;
+		notifyDataSetChanged();
+	}
+
+	void updateVisibility(AuthorId memberId, Visibility v) {
+		int position = findItemPosition(memberId);
+		if (position != NO_POSITION) {
+			GroupMessageItem item = items.get(position);
+			if (item instanceof JoinMessageItem) {
+				((JoinMessageItem) item).setVisibility(v);
+				notifyItemChanged(getVisiblePos(item), item);
+			}
+		}
+	}
+
+	private int findItemPosition(AuthorId a) {
+		int count = items.size();
+		for (int i = 0; i < count; i++) {
+			GroupMessageItem item = items.get(i);
+			if (item.getAuthor().getId().equals(a))
+				return i;
+		}
+		return NO_POSITION; // Not found
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageItem.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageItem.java
index 9deb0424e574fc582b56dc687246c3f74d3438fb..47104e88284b78e59bcdfa5f215327ef468034b3 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageItem.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageItem.java
@@ -8,6 +8,7 @@ import org.briarproject.android.threaded.ThreadItem;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.Author.Status;
 import org.briarproject.api.privategroup.GroupMessageHeader;
+import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 
 import javax.annotation.concurrent.NotThreadSafe;
@@ -16,15 +17,23 @@ import javax.annotation.concurrent.NotThreadSafe;
 @NotThreadSafe
 class GroupMessageItem extends ThreadItem {
 
-	private GroupMessageItem(MessageId messageId, MessageId parentId,
+	private final GroupId groupId;
+
+	private GroupMessageItem(MessageId messageId, GroupId groupId,
+			MessageId parentId,
 			String text, long timestamp, Author author, Status status,
 			boolean isRead) {
 		super(messageId, parentId, text, timestamp, author, status, isRead);
+		this.groupId = groupId;
 	}
 
 	GroupMessageItem(GroupMessageHeader h, String text) {
-		this(h.getId(), h.getParentId(), text, h.getTimestamp(), h.getAuthor(),
-				h.getAuthorStatus(), h.isRead());
+		this(h.getId(), h.getGroupId(), h.getParentId(), text, h.getTimestamp(),
+				h.getAuthor(), h.getAuthorStatus(), h.isRead());
+	}
+
+	public GroupId getGroupId() {
+		return groupId;
 	}
 
 	@LayoutRes
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItem.java b/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItem.java
index 44c732ffd48970cc59a4abd2a92e4bebac232219..c48f781a086605c1ce92e1ef5077d5bd4e3484a8 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItem.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItem.java
@@ -4,7 +4,8 @@ import android.support.annotation.LayoutRes;
 import android.support.annotation.UiThread;
 
 import org.briarproject.R;
-import org.briarproject.api.privategroup.GroupMessageHeader;
+import org.briarproject.api.privategroup.JoinMessageHeader;
+import org.briarproject.api.privategroup.Visibility;
 
 import javax.annotation.concurrent.NotThreadSafe;
 
@@ -12,9 +13,13 @@ import javax.annotation.concurrent.NotThreadSafe;
 @NotThreadSafe
 class JoinMessageItem extends GroupMessageItem {
 
-	JoinMessageItem(GroupMessageHeader h,
-			String text) {
+	private Visibility visibility;
+	private final boolean isInitial;
+
+	JoinMessageItem(JoinMessageHeader h, String text) {
 		super(h, text);
+		this.visibility = h.getVisibility();
+		this.isInitial = h.isInitial();
 	}
 
 	@Override
@@ -29,7 +34,19 @@ class JoinMessageItem extends GroupMessageItem {
 
 	@LayoutRes
 	public int getLayout() {
-		return R.layout.list_item_thread_notice;
+		return R.layout.list_item_group_join_notice;
+	}
+
+	public Visibility getVisibility() {
+		return visibility;
+	}
+
+	public void setVisibility(Visibility visibility) {
+		this.visibility = visibility;
+	}
+
+	public boolean isInitial() {
+		return isInitial;
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItemViewHolder.java b/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItemViewHolder.java
index 972a6149eaa0d3093156c1df28bbe2c43fa77954..d8705c8b949bfaedd584274499fe70b1dbca1dc0 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItemViewHolder.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/JoinMessageItemViewHolder.java
@@ -1,30 +1,108 @@
 package org.briarproject.android.privategroup.conversation;
 
+import android.content.Context;
+import android.content.Intent;
 import android.support.annotation.UiThread;
 import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import org.briarproject.R;
+import org.briarproject.android.privategroup.reveal.RevealContactsActivity;
 import org.briarproject.android.threaded.BaseThreadItemViewHolder;
 import org.briarproject.android.threaded.ThreadItemAdapter;
 import org.briarproject.android.threaded.ThreadItemAdapter.ThreadItemListener;
 import org.briarproject.api.nullsafety.NotNullByDefault;
 
+import static org.briarproject.android.BriarActivity.GROUP_ID;
+import static org.briarproject.android.privategroup.VisibilityStringProvider.getVisibilityStringId;
+import static org.briarproject.api.identity.Author.Status.OURSELVES;
+import static org.briarproject.api.identity.Author.Status.UNKNOWN;
+import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
+
 @UiThread
 @NotNullByDefault
-public class JoinMessageItemViewHolder
+class JoinMessageItemViewHolder
 		extends BaseThreadItemViewHolder<GroupMessageItem> {
 
-	public JoinMessageItemViewHolder(View v) {
+	private final boolean isCreator;
+	private final ImageView icon;
+	private final TextView info;
+	private final Button options;
+
+	public JoinMessageItemViewHolder(View v, boolean isCreator) {
 		super(v);
+		this.isCreator = isCreator;
+		icon = (ImageView) v.findViewById(R.id.icon);
+		info = (TextView) v.findViewById(R.id.info);
+		options = (Button) v.findViewById(R.id.optionsButton);
 	}
 
 	@Override
-	public void bind(final ThreadItemAdapter<GroupMessageItem> adapter,
-			final ThreadItemListener<GroupMessageItem> listener,
-			final GroupMessageItem item, int pos) {
+	public void bind(ThreadItemAdapter<GroupMessageItem> adapter,
+			ThreadItemListener<GroupMessageItem> listener,
+			GroupMessageItem item, int pos) {
 		super.bind(adapter, listener, item, pos);
 
-		textView.setText(getContext().getString(R.string.groups_member_joined));
+		if (isCreator) bindForCreator((JoinMessageItem) item);
+		else bind((JoinMessageItem) item);
+	}
+
+	private void bindForCreator(final JoinMessageItem item) {
+		if (item.isInitial()) {
+			textView.setText(R.string.groups_member_created_you);
+		} else {
+			textView.setText(
+					getContext().getString(R.string.groups_member_joined,
+							item.getAuthor().getName()));
+		}
+		icon.setVisibility(View.GONE);
+		info.setVisibility(View.GONE);
+		options.setVisibility(View.GONE);
+	}
+
+	private void bind(final JoinMessageItem item) {
+		final Context ctx = getContext();
+
+		if (item.isInitial()) {
+			textView.setText(ctx.getString(R.string.groups_member_created,
+					item.getAuthor().getName()));
+		} else {
+			if (item.getStatus() == OURSELVES) {
+				textView.setText(R.string.groups_member_joined_you);
+			} else {
+				textView.setText(ctx.getString(R.string.groups_member_joined,
+						item.getAuthor().getName()));
+			}
+		}
+
+		if (item.getStatus() == OURSELVES || item.getStatus() == UNKNOWN) {
+			icon.setVisibility(View.GONE);
+			info.setVisibility(View.GONE);
+			options.setVisibility(View.GONE);
+		} else {
+			icon.setVisibility(View.VISIBLE);
+			info.setVisibility(View.VISIBLE);
+			info.setText(getVisibilityStringId(item.getVisibility()));
+
+			if (item.getVisibility() == INVISIBLE) {
+				icon.setImageResource(R.drawable.ic_visibility_off);
+				options.setVisibility(View.VISIBLE);
+				options.setOnClickListener(new View.OnClickListener() {
+					@Override
+					public void onClick(View v) {
+						Intent i =
+								new Intent(ctx, RevealContactsActivity.class);
+						i.putExtra(GROUP_ID, item.getGroupId().getBytes());
+						ctx.startActivity(i);
+					}
+				});
+			} else {
+				icon.setImageResource(R.drawable.ic_visibility);
+				options.setVisibility(View.GONE);
+			}
+		}
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactViewHolder.java b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactViewHolder.java
index f2131482cb7b0d5c6fdcd9e12f129a83d17a5df4..5261a58e27009e18f02ca1c38cdf1981a82707a4 100644
--- a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactViewHolder.java
+++ b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactViewHolder.java
@@ -10,6 +10,7 @@ import org.briarproject.android.contactselection.BaseSelectableContactHolder;
 import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.jetbrains.annotations.Nullable;
 
+import static org.briarproject.android.privategroup.VisibilityStringProvider.getVisibilityStringId;
 import static org.briarproject.android.util.AndroidUtils.GREY_OUT;
 import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
 
@@ -31,21 +32,7 @@ public class RevealableContactViewHolder
 			OnContactClickListener<RevealableContactItem> listener) {
 		super.bind(item, listener);
 
-		switch (item.getVisibility()) {
-			case VISIBLE:
-				info.setText(R.string.groups_reveal_visible);
-				break;
-			case REVEALED_BY_US:
-				info.setText(R.string.groups_reveal_visible_revealed_by_us);
-				break;
-			case REVEALED_BY_CONTACT:
-				info.setText(
-						R.string.groups_reveal_visible_revealed_by_contact);
-				break;
-			case INVISIBLE:
-				info.setText(R.string.groups_reveal_invisible);
-				break;
-		}
+		info.setText(getVisibilityStringId(item.getVisibility()));
 
 		if (item.getVisibility() == INVISIBLE) {
 			icon.setImageResource(R.drawable.ic_visibility_off);
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java b/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java
index 7c3ee9079128f1d1424ab570e78f1da3c3b126e4..76b062508acdc56a81027b49fb24b7cbcec60771 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java
@@ -27,7 +27,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
 
 	static final int UNDEFINED = -1;
 
-	private final NestedTreeList<I> items = new NestedTreeList<>();
+	protected final NestedTreeList<I> items = new NestedTreeList<>();
 	private final Map<I, ValueAnimator> animatingItems = new HashMap<>();
 	private final ThreadItemListener<I> listener;
 	private final LinearLayoutManager layoutManager;
@@ -61,6 +61,11 @@ public class ThreadItemAdapter<I extends ThreadItem>
 		ui.bind(this, listener, item, position);
 	}
 
+	/**
+	 * Contrary to the super class adapter,
+	 * this returns the number of <b>visible</b> items,
+	 * not all items in the dataset.
+	 */
 	@Override
 	public int getItemCount() {
 		return getVisiblePos(null);
@@ -268,7 +273,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
 	 * items if 'item' is null. If 'item' is not visible, NO_POSITION is
 	 * returned.
 	 */
-	private int getVisiblePos(@Nullable I item) {
+	protected int getVisiblePos(@Nullable I item) {
 		int visibleCounter = 0;
 		int levelLimit = UNDEFINED;
 		for (I i : items) {
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
index e4bd39e2af758f4af1bddbb61462a9dc780dd74f..ed2ebdfa913c988a20df610348cffcebce41ff6b 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
@@ -24,6 +24,8 @@ import org.briarproject.android.view.TextInputView.TextInputListener;
 import org.briarproject.api.clients.NamedGroup;
 import org.briarproject.api.clients.PostHeader;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.util.StringUtils;
@@ -35,7 +37,9 @@ import static android.support.design.widget.Snackbar.make;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
-public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadItem, H extends PostHeader>
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadItemAdapter<I>, I extends ThreadItem, H extends PostHeader>
 		extends BriarActivity
 		implements ThreadListListener<H>, TextInputListener,
 		ThreadItemListener<I> {
@@ -46,7 +50,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
 	private static final Logger LOG =
 			Logger.getLogger(ThreadListActivity.class.getName());
 
-	protected ThreadItemAdapter<I> adapter;
+	protected A adapter;
 	protected BriarRecyclerView list;
 	protected TextInputView textInput;
 	protected GroupId groupId;
@@ -57,7 +61,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
 	@CallSuper
 	@Override
 	@SuppressWarnings("ConstantConditions")
-	public void onCreate(final Bundle state) {
+	public void onCreate(@Nullable Bundle state) {
 		super.onCreate(state);
 
 		setContentView(getLayout());
@@ -88,8 +92,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
 	@LayoutRes
 	protected abstract int getLayout();
 
-	protected abstract ThreadItemAdapter<I> createAdapter(
-			LinearLayoutManager layoutManager);
+	protected abstract A createAdapter(LinearLayoutManager layoutManager);
 
 	protected void loadNamedGroup() {
 		getController().loadNamedGroup(
diff --git a/briar-api/src/org/briarproject/api/privategroup/ContactRelationshipRevealedEvent.java b/briar-api/src/org/briarproject/api/privategroup/ContactRelationshipRevealedEvent.java
index 40de39d1eb2cd591054c38ecc913f4ccb224a355..2fd120315844edf27ab8a67818086c4e8815c75d 100644
--- a/briar-api/src/org/briarproject/api/privategroup/ContactRelationshipRevealedEvent.java
+++ b/briar-api/src/org/briarproject/api/privategroup/ContactRelationshipRevealedEvent.java
@@ -1,6 +1,7 @@
 package org.briarproject.api.privategroup;
 
 import org.briarproject.api.event.Event;
+import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.GroupId;
 
@@ -11,11 +12,13 @@ import javax.annotation.concurrent.Immutable;
 public class ContactRelationshipRevealedEvent extends Event {
 
 	private final GroupId groupId;
+	private final AuthorId memberId;
 	private final Visibility visibility;
 
-	public ContactRelationshipRevealedEvent(GroupId groupId,
+	public ContactRelationshipRevealedEvent(GroupId groupId, AuthorId memberId,
 			Visibility visibility) {
 		this.groupId = groupId;
+		this.memberId = memberId;
 		this.visibility = visibility;
 	}
 
@@ -23,6 +26,10 @@ public class ContactRelationshipRevealedEvent extends Event {
 		return groupId;
 	}
 
+	public AuthorId getMemberId() {
+		return memberId;
+	}
+
 	public Visibility getVisibility() {
 		return visibility;
 	}
diff --git a/briar-api/src/org/briarproject/api/privategroup/JoinMessageHeader.java b/briar-api/src/org/briarproject/api/privategroup/JoinMessageHeader.java
index 8b14717a6e622d9f3a52f745673bb90e46088d39..ae83be9327ae6555b9128487760296ff692ebb5a 100644
--- a/briar-api/src/org/briarproject/api/privategroup/JoinMessageHeader.java
+++ b/briar-api/src/org/briarproject/api/privategroup/JoinMessageHeader.java
@@ -9,15 +9,21 @@ import javax.annotation.concurrent.Immutable;
 public class JoinMessageHeader extends GroupMessageHeader {
 
 	private final Visibility visibility;
+	private final boolean isInitial;
 
-	public JoinMessageHeader(GroupMessageHeader h, Visibility visibility) {
+	public JoinMessageHeader(GroupMessageHeader h, Visibility visibility, boolean isInitial) {
 		super(h.getGroupId(), h.getId(), h.getParentId(), h.getTimestamp(),
 				h.getAuthor(), h.getAuthorStatus(), h.isRead());
 		this.visibility = visibility;
+		this.isInitial = isInitial;
 	}
 
 	public Visibility getVisibility() {
 		return visibility;
 	}
 
+	public boolean isInitial() {
+		return isInitial;
+	}
+
 }
diff --git a/briar-core/src/org/briarproject/privategroup/GroupConstants.java b/briar-core/src/org/briarproject/privategroup/GroupConstants.java
index 72f57748c89f72a54b812da31aca614e8acef5eb..b1e203aa7a1da3c23f3732948aa6f74f1e7a1aca 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupConstants.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupConstants.java
@@ -13,6 +13,7 @@ interface GroupConstants {
 	String KEY_MEMBER_ID = "memberId";
 	String KEY_MEMBER_NAME = "memberName";
 	String KEY_MEMBER_PUBLIC_KEY = "memberPublicKey";
+	String KEY_INITIAL_JOIN_MSG = "initialJoinMsg";
 
 	String GROUP_KEY_MEMBERS = "members";
 	String GROUP_KEY_OUR_GROUP = "ourGroup";
diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
index 54b6324fc5b2a3d52d8d87716e364f52905c3d8c..13ab704aaee6dc0e11516dcf63dc9f5edf984cd8 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
@@ -29,6 +29,7 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH
 import static org.briarproject.api.privategroup.MessageType.JOIN;
 import static org.briarproject.api.privategroup.MessageType.POST;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
+import static org.briarproject.privategroup.GroupConstants.KEY_INITIAL_JOIN_MSG;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_ID;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_NAME;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_PUBLIC_KEY;
@@ -99,10 +100,12 @@ class GroupMessageValidator extends BdfMessageValidator {
 
 		// invite is null if the member is the creator of the private group
 		Author creator = pg.getCreator();
+		boolean isCreator = false;
 		BdfList invite = body.getOptionalList(3);
 		if (invite == null) {
 			if (!member.equals(creator))
 				throw new InvalidMessageException();
+			isCreator = true;
 		} else {
 			if (member.equals(creator))
 				throw new InvalidMessageException();
@@ -149,6 +152,7 @@ class GroupMessageValidator extends BdfMessageValidator {
 
 		// Return the metadata and no dependencies
 		BdfDictionary meta = new BdfDictionary();
+		meta.put(KEY_INITIAL_JOIN_MSG, isCreator);
 		return new BdfMessageContext(meta);
 	}
 
diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
index 0a8c436ec0610b12d5a82d0cf48e2b0ffeef9205..462a8e11c18ffc7a923642e543a3986d1cb18705 100644
--- a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
@@ -60,6 +60,7 @@ import static org.briarproject.privategroup.GroupConstants.GROUP_KEY_DISSOLVED;
 import static org.briarproject.privategroup.GroupConstants.GROUP_KEY_MEMBERS;
 import static org.briarproject.privategroup.GroupConstants.GROUP_KEY_OUR_GROUP;
 import static org.briarproject.privategroup.GroupConstants.GROUP_KEY_VISIBILITY;
+import static org.briarproject.privategroup.GroupConstants.KEY_INITIAL_JOIN_MSG;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_ID;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_NAME;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_PUBLIC_KEY;
@@ -114,16 +115,17 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 					new BdfEntry(GROUP_KEY_DISSOLVED, false)
 			);
 			clientHelper.mergeGroupMetadata(txn, group.getId(), meta);
-			joinPrivateGroup(txn, joinMsg);
+			joinPrivateGroup(txn, joinMsg, creator);
 		} catch (FormatException e) {
 			throw new DbException(e);
 		}
 	}
 
-	private void joinPrivateGroup(Transaction txn, GroupMessage m)
-			throws DbException, FormatException {
+	private void joinPrivateGroup(Transaction txn, GroupMessage m,
+			boolean creator) throws DbException, FormatException {
 		BdfDictionary meta = new BdfDictionary();
 		meta.put(KEY_TYPE, JOIN.getInt());
+		meta.put(KEY_INITIAL_JOIN_MSG, creator);
 		addMessageMetadata(meta, m, true);
 		clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
 		trackOutgoingMessage(txn, m.getMessage());
@@ -377,7 +379,8 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 
 		GroupMessageHeader header =
 				getGroupMessageHeader(txn, g, id, meta, statuses);
-		return new JoinMessageHeader(header, v);
+		boolean creator = meta.getBoolean(KEY_INITIAL_JOIN_MSG);
+		return new JoinMessageHeader(header, v, creator);
 	}
 
 	@Override
@@ -451,7 +454,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 		if (!foundMember) throw new ProtocolStateException();
 		if (changed) {
 			clientHelper.mergeGroupMetadata(txn, g, meta);
-			txn.attach(new ContactRelationshipRevealedEvent(g, v));
+			txn.attach(new ContactRelationshipRevealedEvent(g, a, v));
 		}
 	}