diff --git a/briar-android/res/layout/activity_share_forum.xml b/briar-android/res/layout/activity_share_forum.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b91e96f00b0299104a5145334361b4b9d310df55
--- /dev/null
+++ b/briar-android/res/layout/activity_share_forum.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+	android:id="@+id/shareForumContainer"
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"/>
\ No newline at end of file
diff --git a/briar-android/res/layout/list_item_forum_invitation_in.xml b/briar-android/res/layout/list_item_forum_invitation_in.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c6881437b392b42a675d5d84c52b9b5f8e0c03e8
--- /dev/null
+++ b/briar-android/res/layout/list_item_forum_invitation_in.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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="wrap_content"
+	android:orientation="vertical">
+
+	<include
+		android:id="@+id/messageLayout"
+		layout="@layout/list_item_msg_in"/>
+
+	<RelativeLayout
+		android:id="@+id/introductionLayout"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_gravity="left|start"
+		android:background="@drawable/notice_in"
+		android:layout_marginLeft="@dimen/message_bubble_margin_tail"
+		android:layout_marginRight="@dimen/message_bubble_margin_non_tail">
+
+		<TextView
+			android:id="@+id/introductionText"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:minWidth="80dp"
+			android:textIsSelectable="true"
+			android:textSize="@dimen/text_size_medium"
+			android:textStyle="italic"
+			tools:text="@string/forum_invitation_received"/>
+
+		<TextView
+			android:id="@+id/introductionTime"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
+			android:layout_alignEnd="@+id/introductionText"
+			android:layout_alignRight="@+id/introductionText"
+			android:layout_below="@+id/showForumsButton"
+			android:textColor="@color/private_message_date"
+			android:textSize="@dimen/text_size_tiny"
+			tools:text="Dec 24, 13:37"/>
+
+		<Button
+			android:id="@+id/showForumsButton"
+			style="@style/BriarButtonFlat.Positive"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginBottom="-15dp"
+			android:layout_alignEnd="@+id/introductionText"
+			android:layout_alignRight="@+id/introductionText"
+			android:layout_below="@+id/introductionText"
+			android:text="@string/forum_show_available"/>
+
+	</RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/briar-android/res/layout/list_item_forum_invitation_out.xml b/briar-android/res/layout/list_item_forum_invitation_out.xml
new file mode 100644
index 0000000000000000000000000000000000000000..88070ea669ea57ab6402247f6c18cebae68eecb9
--- /dev/null
+++ b/briar-android/res/layout/list_item_forum_invitation_out.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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="wrap_content"
+	android:orientation="vertical">
+
+	<include
+		android:id="@+id/messageLayout"
+		layout="@layout/list_item_msg_out"/>
+
+	<RelativeLayout
+		android:id="@+id/introductionLayout"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_gravity="right|end"
+		android:background="@drawable/notice_out"
+		android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
+		android:layout_marginRight="@dimen/message_bubble_margin_tail">
+
+		<TextView
+			android:id="@+id/introductionText"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:textIsSelectable="true"
+			android:textSize="@dimen/text_size_medium"
+			android:textStyle="italic"
+			tools:text="@string/introduction_request_received"/>
+
+		<TextView
+			android:id="@+id/introductionTime"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
+			android:layout_alignParentLeft="true"
+			android:layout_alignParentStart="true"
+			android:layout_below="@+id/introductionText"
+			android:textColor="@color/private_message_date"
+			android:textSize="@dimen/text_size_tiny"
+			tools:text="Dec 24, 13:37"/>
+
+		<ImageView
+			android:id="@+id/introductionStatus"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_toEndOf="@+id/introductionTime"
+			android:layout_toRightOf="@+id/introductionTime"
+			android:layout_alignBottom="@+id/introductionTime"
+			android:layout_marginLeft="@dimen/margin_medium"
+			tools:ignore="ContentDescription"
+			tools:src="@drawable/message_delivered"/>
+
+	</RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/briar-android/res/layout/share_forum_message.xml b/briar-android/res/layout/share_forum_message.xml
new file mode 100644
index 0000000000000000000000000000000000000000..64a027914890b9d91afd500921f8e36b3cbc67fb
--- /dev/null
+++ b/briar-android/res/layout/share_forum_message.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.NestedScrollView
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	android:fillViewport="true">
+
+	<LinearLayout
+		android:layout_width="match_parent"
+		android:layout_height="match_parent"
+		android:padding="@dimen/margin_activity_horizontal"
+		android:orientation="vertical">
+
+		<TextView
+			android:id="@+id/introductionText"
+			android:layout_width="match_parent"
+			android:layout_height="0dp"
+			android:layout_marginTop="@dimen/margin_medium"
+			android:layout_weight="1"
+			android:gravity="top"
+			android:textSize="@dimen/text_size_medium"
+			android:text="@string/forum_share_message"/>
+
+		<EditText
+			android:id="@+id/invitationMessageView"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="@dimen/margin_medium"
+			android:gravity="bottom"
+			android:hint="@string/introduction_message_hint"
+			android:inputType="text|textMultiLine|textCapSentences"/>
+
+		<Button
+			android:id="@+id/shareForumButton"
+			style="@style/BriarButton"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:text="@string/forum_share_button"
+			/>
+
+	</LinearLayout>
+
+</android.support.v4.widget.NestedScrollView>
\ No newline at end of file
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index e5d21dec3eef771234840b9167a10436bda80f47..8cbd86d95626636532b5e34113291a482ee25a86 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -92,6 +92,11 @@
 	<string name="forum_created_toast">Forum created</string>
 	<string name="forum_share_action">Share this forum with chosen contacts</string>
 	<string name="forum_share_button">Share Forum</string>
+	<string name="forum_shared_snackbar">Forum shared with chosen contacts</string>
+	<string name="forum_share_message">You may compose an optional invitation message that will be sent to the selected contacts.</string>
+	<string name="forum_invitation_received">%1$s has shared the forum \"%2$s\" with you.</string>
+	<string name="forum_invitation_sent">You have shared the forum \"%1$s\" with %2$s.</string>
+	<string name="forum_show_available">Show Available Forums</string>
 	<string name="forum_compose_post">New Forum Post</string>
 	<string name="from">From:</string>
 	<string name="anonymous">Anonymous</string>
diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java
index 7c5777e5252ae226ce59b5a33603be3553d64661..8b2ac951ab65f8b7ed6e5565f850a054d06719bf 100644
--- a/briar-android/src/org/briarproject/android/AndroidComponent.java
+++ b/briar-android/src/org/briarproject/android/AndroidComponent.java
@@ -5,11 +5,13 @@ import org.briarproject.CoreModule;
 import org.briarproject.android.contact.ContactListFragment;
 import org.briarproject.android.contact.ConversationActivity;
 import org.briarproject.android.forum.AvailableForumsActivity;
+import org.briarproject.android.forum.ContactSelectorFragment;
 import org.briarproject.android.forum.CreateForumActivity;
 import org.briarproject.android.forum.ForumActivity;
 import org.briarproject.android.forum.ForumListFragment;
 import org.briarproject.android.forum.ReadForumPostActivity;
 import org.briarproject.android.forum.ShareForumActivity;
+import org.briarproject.android.forum.ShareForumMessageFragment;
 import org.briarproject.android.forum.WriteForumPostActivity;
 import org.briarproject.android.identity.CreateIdentityActivity;
 import org.briarproject.android.introduction.ContactChooserFragment;
@@ -70,6 +72,10 @@ public interface AndroidComponent extends CoreEagerSingletons {
 
 	void inject(ShareForumActivity activity);
 
+	void inject(ContactSelectorFragment fragment);
+
+	void inject(ShareForumMessageFragment fragment);
+
 	void inject(ReadForumPostActivity activity);
 
 	void inject(ForumActivity activity);
diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
index 0a7b1ea2bb569a685f65ab15402b3db7c711cbc9..623391cc3b356c755f20b8dd3c855a980abf619a 100644
--- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
+++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
@@ -20,6 +20,7 @@ import org.briarproject.api.db.DatabaseExecutor;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.ForumInvitationReceivedEvent;
 import org.briarproject.api.event.IntroductionRequestReceivedEvent;
 import org.briarproject.api.event.IntroductionResponseReceivedEvent;
 import org.briarproject.api.event.IntroductionSucceededEvent;
@@ -164,13 +165,16 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 			}
 		} else if (e instanceof IntroductionRequestReceivedEvent) {
 			ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId();
-			showIntroductionNotifications(c);
+			showNotificationForPrivateConversation(c);
 		} else if (e instanceof IntroductionResponseReceivedEvent) {
 			ContactId c = ((IntroductionResponseReceivedEvent) e).getContactId();
-			showIntroductionNotifications(c);
+			showNotificationForPrivateConversation(c);
 		} else if (e instanceof IntroductionSucceededEvent) {
 			Contact c = ((IntroductionSucceededEvent) e).getContact();
 			showIntroductionSucceededNotification(c);
+		} else if (e instanceof ForumInvitationReceivedEvent) {
+			ContactId c = ((ForumInvitationReceivedEvent) e).getContactId();
+			showNotificationForPrivateConversation(c);
 		}
 	}
 
@@ -359,7 +363,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		});
 	}
 
-	private void showIntroductionNotifications(final ContactId c) {
+	private void showNotificationForPrivateConversation(final ContactId c) {
 		androidExecutor.execute(new Runnable() {
 			public void run() {
 				try {
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index 9838eb06a830659af878b5ba9e4adad6232f9acf..82137d92ad531ebbcb49cca43c635d7d39abb961 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -29,6 +29,8 @@ import org.briarproject.api.event.ContactStatusChangedEvent;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.MessageValidatedEvent;
+import org.briarproject.api.forum.ForumInvitationMessage;
+import org.briarproject.api.forum.ForumSharingManager;
 import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.introduction.IntroductionManager;
@@ -86,6 +88,8 @@ public class ContactListFragment extends BaseEventFragment {
 	@Inject
 	protected volatile IntroductionManager introductionManager;
 	@Inject
+	protected volatile ForumSharingManager forumSharingManager;
+	@Inject
 	protected volatile EventBus eventBus;
 
 	@Override
@@ -226,7 +230,8 @@ public class ContactListFragment extends BaseEventFragment {
 			MessageValidatedEvent m = (MessageValidatedEvent) e;
 			ClientId c = m.getClientId();
 			if (m.isValid() && (c.equals(messagingManager.getClientId()) ||
-					c.equals(introductionManager.getClientId()))) {
+					c.equals(introductionManager.getClientId()) ||
+					c.equals(forumSharingManager.getClientId()))) {
 				LOG.info("Message added, reloading");
 				reloadConversation(m.getMessage().getGroupId());
 			}
@@ -317,6 +322,16 @@ public class ContactListFragment extends BaseEventFragment {
 		if (LOG.isLoggable(INFO))
 			LOG.info("Loading introduction messages took " + duration + " ms");
 
+		now = System.currentTimeMillis();
+		Collection<ForumInvitationMessage> invitations =
+				forumSharingManager.getForumInvitationMessages(id);
+		for (ForumInvitationMessage i : invitations) {
+			messages.add(ConversationItem.from(i));
+		}
+		duration = System.currentTimeMillis() - now;
+		if (LOG.isLoggable(INFO))
+			LOG.info("Loading forum invitations took " + duration + " ms");
+
 		return messages;
 	}
 }
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index 213e412b520aeea2edb35c6c4608bcd72d2ef148..5a62bef1f3c378449c2ce8e8ff1178d9aba76fa9 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -42,11 +42,14 @@ import org.briarproject.api.event.ContactRemovedEvent;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.ForumInvitationReceivedEvent;
 import org.briarproject.api.event.IntroductionRequestReceivedEvent;
 import org.briarproject.api.event.IntroductionResponseReceivedEvent;
 import org.briarproject.api.event.MessageValidatedEvent;
 import org.briarproject.api.event.MessagesAckedEvent;
 import org.briarproject.api.event.MessagesSentEvent;
+import org.briarproject.api.forum.ForumInvitationMessage;
+import org.briarproject.api.forum.ForumSharingManager;
 import org.briarproject.api.introduction.IntroductionManager;
 import org.briarproject.api.introduction.IntroductionMessage;
 import org.briarproject.api.introduction.IntroductionRequest;
@@ -109,6 +112,7 @@ public class ConversationActivity extends BriarActivity
 	@Inject protected volatile EventBus eventBus;
 	@Inject protected volatile PrivateMessageFactory privateMessageFactory;
 	@Inject protected volatile IntroductionManager introductionManager;
+	@Inject protected volatile ForumSharingManager forumSharingManager;
 	private volatile GroupId groupId = null;
 	private volatile ContactId contactId = null;
 	private volatile String contactName = null;
@@ -278,10 +282,13 @@ public class ConversationActivity extends BriarActivity
 					Collection<IntroductionMessage> introductions =
 							introductionManager
 									.getIntroductionMessages(contactId);
+					Collection<ForumInvitationMessage> invitations =
+							forumSharingManager
+									.getForumInvitationMessages(contactId);
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
 						LOG.info("Loading headers took " + duration + " ms");
-					displayMessages(headers, introductions);
+					displayMessages(headers, introductions, invitations);
 				} catch (NoSuchContactException e) {
 					finishOnUiThread();
 				} catch (DbException e) {
@@ -293,11 +300,13 @@ public class ConversationActivity extends BriarActivity
 	}
 
 	private void displayMessages(final Collection<PrivateMessageHeader> headers,
-			final Collection<IntroductionMessage> introductions) {
+			final Collection<IntroductionMessage> introductions,
+			final Collection<ForumInvitationMessage> invitations) {
 		runOnUiThread(new Runnable() {
 			public void run() {
 				sendButton.setEnabled(true);
-				if (headers.isEmpty() && introductions.isEmpty()) {
+				if (headers.isEmpty() && introductions.isEmpty() &&
+						invitations.isEmpty()) {
 					// we have no messages,
 					// so let the list know to hide progress bar
 					list.showData();
@@ -326,6 +335,10 @@ public class ConversationActivity extends BriarActivity
 						}
 						items.add(item);
 					}
+					for (ForumInvitationMessage i : invitations) {
+						ConversationItem item = ConversationItem.from(i);
+						items.add(item);
+					}
 					adapter.addAll(items);
 					// Scroll to the bottom
 					list.scrollToPosition(adapter.getItemCount() - 1);
@@ -476,6 +489,12 @@ public class ConversationActivity extends BriarActivity
 						ConversationItem.from(this, contactName, ir);
 				addIntroduction(item);
 			}
+		} else if (e instanceof ForumInvitationReceivedEvent) {
+			ForumInvitationReceivedEvent event =
+					(ForumInvitationReceivedEvent) e;
+			if (event.getContactId().equals(contactId)) {
+				loadMessages();
+			}
 		}
 	}
 
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java
index 91a006b9f53327f6534bfb30affd395f8599f4ef..cf1a4b738ea6610e31637d67378e50ea40da6af8 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java
@@ -1,6 +1,7 @@
 package org.briarproject.android.contact;
 
 import android.content.Context;
+import android.content.Intent;
 import android.support.v7.util.SortedList;
 import android.support.v7.widget.RecyclerView;
 import android.text.format.DateUtils;
@@ -13,8 +14,10 @@ import android.widget.ImageView;
 import android.widget.TextView;
 
 import org.briarproject.R;
-import org.briarproject.api.introduction.IntroductionRequest;
+import org.briarproject.android.forum.AvailableForumsActivity;
 import org.briarproject.api.clients.SessionId;
+import org.briarproject.api.forum.ForumInvitationMessage;
+import org.briarproject.api.introduction.IntroductionRequest;
 import org.briarproject.api.messaging.PrivateMessageHeader;
 import org.briarproject.util.StringUtils;
 
@@ -22,6 +25,8 @@ import java.util.List;
 
 import static android.support.v7.util.SortedList.INVALID_POSITION;
 import static android.support.v7.widget.RecyclerView.ViewHolder;
+import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_IN;
+import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_OUT;
 import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_IN;
 import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_OUT;
 import static org.briarproject.android.contact.ConversationItem.IncomingItem;
@@ -35,50 +40,7 @@ import static org.briarproject.android.contact.ConversationItem.OutgoingItem;
 class ConversationAdapter extends RecyclerView.Adapter {
 
 	private final SortedList<ConversationItem> items =
-			new SortedList<ConversationItem>(ConversationItem.class,
-					new SortedList.Callback<ConversationItem>() {
-						@Override
-						public void onInserted(int position, int count) {
-							notifyItemRangeInserted(position, count);
-						}
-
-						@Override
-						public void onChanged(int position, int count) {
-							notifyItemRangeChanged(position, count);
-						}
-
-						@Override
-						public void onMoved(int fromPosition, int toPosition) {
-							notifyItemMoved(fromPosition, toPosition);
-						}
-
-						@Override
-						public void onRemoved(int position, int count) {
-							notifyItemRangeRemoved(position, count);
-						}
-
-						@Override
-						public int compare(ConversationItem c1,
-								ConversationItem c2) {
-							long time1 = c1.getTime();
-							long time2 = c2.getTime();
-							if (time1 < time2) return -1;
-							if (time1 > time2) return 1;
-							return 0;
-						}
-
-						@Override
-						public boolean areItemsTheSame(ConversationItem c1,
-								ConversationItem c2) {
-							return c1.getId().equals(c2.getId());
-						}
-
-						@Override
-						public boolean areContentsTheSame(ConversationItem c1,
-								ConversationItem c2) {
-							return c1.equals(c2);
-						}
-					});
+			new SortedList<>(ConversationItem.class, new ListCallbacks());
 	private Context ctx;
 	private IntroductionHandler intro;
 	private String contactName;
@@ -129,6 +91,16 @@ class ConversationAdapter extends RecyclerView.Adapter {
 					.inflate(R.layout.list_item_notice_out, viewGroup, false);
 			return new NoticeHolder(v, type);
 		}
+		else if (type == FORUM_INVITATION_IN) {
+			v = LayoutInflater.from(viewGroup.getContext())
+					.inflate(R.layout.list_item_forum_invitation_in, viewGroup, false);
+			return new InvitationHolder(v, type);
+		}
+		else if (type == FORUM_INVITATION_OUT) {
+			v = LayoutInflater.from(viewGroup.getContext())
+					.inflate(R.layout.list_item_forum_invitation_out, viewGroup, false);
+			return new InvitationHolder(v, type);
+		}
 		// incoming message (non-local)
 		else {
 			v = LayoutInflater.from(viewGroup.getContext())
@@ -152,6 +124,12 @@ class ConversationAdapter extends RecyclerView.Adapter {
 			bindNotice((NoticeHolder) ui, (ConversationNoticeOutItem) item);
 		} else if (item instanceof ConversationNoticeInItem) {
 			bindNotice((NoticeHolder) ui, (ConversationNoticeInItem) item);
+		} else if (item instanceof ConversationForumInvitationOutItem) {
+			bindInvitation((InvitationHolder) ui,
+					(ConversationForumInvitationOutItem) item, position);
+		} else if (item instanceof ConversationForumInvitationInItem) {
+			bindInvitation((InvitationHolder) ui,
+					(ConversationForumInvitationInItem) item, position);
 		} else {
 			throw new IllegalArgumentException("Unhandled Conversation Item");
 		}
@@ -204,7 +182,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
 
 		final IntroductionRequest ir = item.getIntroductionRequest();
 
-		final String message = ir.getMessage();
+		String message = ir.getMessage();
 		if (StringUtils.isNullOrEmpty(message)) {
 			ui.messageLayout.setVisibility(View.GONE);
 		} else {
@@ -300,6 +278,63 @@ class ConversationAdapter extends RecyclerView.Adapter {
 		}
 	}
 
+	private void bindInvitation(InvitationHolder ui,
+			final ConversationForumInvitationItem item, final int position) {
+
+		ForumInvitationMessage fim = item.getForumInvitationMessage();
+
+		String message = fim.getMessage();
+		if (StringUtils.isNullOrEmpty(message)) {
+			ui.messageLayout.setVisibility(View.GONE);
+		} else {
+			ui.messageLayout.setVisibility(View.VISIBLE);
+			ui.message.body.setText(message);
+			ui.message.date.setText(
+					DateUtils.getRelativeTimeSpanString(ctx, item.getTime()));
+		}
+
+		// Outgoing Invitation
+		if (item instanceof ConversationForumInvitationOutItem) {
+			ui.text.setText(ctx.getString(R.string.forum_invitation_sent,
+					fim.getForumName(), contactName));
+			ConversationForumInvitationOutItem i =
+					(ConversationForumInvitationOutItem) item;
+			if (i.isSeen()) {
+				ui.status.setImageResource(R.drawable.message_delivered);
+				ui.message.status.setImageResource(R.drawable.message_delivered_white);
+			} else if (i.isSent()) {
+				ui.status.setImageResource(R.drawable.message_sent);
+				ui.message.status.setImageResource(R.drawable.message_sent_white);
+			} else {
+				ui.status.setImageResource(R.drawable.message_stored);
+				ui.message.status.setImageResource(R.drawable.message_stored_white);
+			}
+		}
+		// Incoming Invitation
+		else {
+			ui.text.setText(ctx.getString(R.string.forum_invitation_received,
+					contactName, fim.getForumName()));
+
+			if (fim.isAvailable()) {
+				ui.showForumsButton.setVisibility(View.VISIBLE);
+				ui.showForumsButton
+						.setOnClickListener(new View.OnClickListener() {
+							@Override
+							public void onClick(View v) {
+								Intent intent =
+										new Intent(ctx,
+												AvailableForumsActivity.class);
+								ctx.startActivity(intent);
+							}
+						});
+			} else {
+				ui.showForumsButton.setVisibility(View.GONE);
+			}
+		}
+		ui.date.setText(
+				DateUtils.getRelativeTimeSpanString(ctx, item.getTime()));
+	}
+
 	@Override
 	public int getItemCount() {
 		return items.size();
@@ -321,8 +356,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
 	}
 
 	public SparseArray<IncomingItem> getIncomingMessages() {
-		SparseArray<IncomingItem> messages =
-				new SparseArray<IncomingItem>();
+		SparseArray<IncomingItem> messages = new SparseArray<>();
 
 		for (int i = 0; i < items.size(); i++) {
 			ConversationItem item = items.get(i);
@@ -334,8 +368,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
 	}
 
 	public SparseArray<OutgoingItem> getOutgoingMessages() {
-		SparseArray<OutgoingItem> messages =
-				new SparseArray<OutgoingItem>();
+		SparseArray<OutgoingItem> messages = new SparseArray<>();
 
 		for (int i = 0; i < items.size(); i++) {
 			ConversationItem item = items.get(i);
@@ -347,8 +380,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
 	}
 
 	public SparseArray<ConversationMessageItem> getPrivateMessages() {
-		SparseArray<ConversationMessageItem> messages =
-				new SparseArray<ConversationMessageItem>();
+		SparseArray<ConversationMessageItem> messages =	new SparseArray<>();
 
 		for (int i = 0; i < items.size(); i++) {
 			ConversationItem item = items.get(i);
@@ -394,14 +426,14 @@ class ConversationAdapter extends RecyclerView.Adapter {
 
 	private static class IntroductionHolder extends RecyclerView.ViewHolder {
 
-		public ViewGroup layout;
-		public View messageLayout;
-		public MessageHolder message;
-		public TextView text;
-		public Button acceptButton;
-		public Button declineButton;
-		public TextView date;
-		public ImageView status;
+		final private ViewGroup layout;
+		final private View messageLayout;
+		final private MessageHolder message;
+		final private TextView text;
+		final private Button acceptButton;
+		final private Button declineButton;
+		final private TextView date;
+		final private ImageView status;
 
 		public IntroductionHolder(View v, int type) {
 			super(v);
@@ -417,16 +449,18 @@ class ConversationAdapter extends RecyclerView.Adapter {
 
 			if (type == INTRODUCTION_OUT) {
 				status = (ImageView) v.findViewById(R.id.introductionStatus);
+			} else {
+				status = null;
 			}
 		}
 	}
 
 	private static class NoticeHolder extends RecyclerView.ViewHolder {
 
-		public ViewGroup layout;
-		public TextView text;
-		public TextView date;
-		public ImageView status;
+		final private ViewGroup layout;
+		final private TextView text;
+		final private TextView date;
+		final private ImageView status;
 
 		public NoticeHolder(View v, int type) {
 			super(v);
@@ -437,10 +471,85 @@ class ConversationAdapter extends RecyclerView.Adapter {
 
 			if (type == NOTICE_OUT) {
 				status = (ImageView) v.findViewById(R.id.noticeStatus);
+			} else {
+				status = null;
 			}
 		}
 	}
 
+	private static class InvitationHolder extends RecyclerView.ViewHolder {
+
+		final private ViewGroup layout;
+		final private View messageLayout;
+		final private MessageHolder message;
+		final private TextView text;
+		final private Button showForumsButton;
+		final private TextView date;
+		final private ImageView status;
+
+		public InvitationHolder(View v, int type) {
+			super(v);
+
+			layout = (ViewGroup) v.findViewById(R.id.introductionLayout);
+			messageLayout = v.findViewById(R.id.messageLayout);
+			message = new MessageHolder(messageLayout,
+					type == FORUM_INVITATION_IN ? MSG_IN : MSG_OUT);
+			text = (TextView) v.findViewById(R.id.introductionText);
+			showForumsButton = (Button) v.findViewById(R.id.showForumsButton);
+			date = (TextView) v.findViewById(R.id.introductionTime);
+
+			if (type == FORUM_INVITATION_OUT) {
+				status = (ImageView) v.findViewById(R.id.introductionStatus);
+			} else {
+				status = null;
+			}
+		}
+	}
+
+	private class ListCallbacks extends SortedList.Callback<ConversationItem> {
+		@Override
+		public void onInserted(int position, int count) {
+			notifyItemRangeInserted(position, count);
+		}
+
+		@Override
+		public void onChanged(int position, int count) {
+			notifyItemRangeChanged(position, count);
+		}
+
+		@Override
+		public void onMoved(int fromPosition, int toPosition) {
+			notifyItemMoved(fromPosition, toPosition);
+		}
+
+		@Override
+		public void onRemoved(int position, int count) {
+			notifyItemRangeRemoved(position, count);
+		}
+
+		@Override
+		public int compare(ConversationItem c1,
+				ConversationItem c2) {
+			long time1 = c1.getTime();
+			long time2 = c2.getTime();
+			if (time1 < time2) return -1;
+			if (time1 > time2) return 1;
+			return 0;
+		}
+
+		@Override
+		public boolean areItemsTheSame(ConversationItem c1,
+				ConversationItem c2) {
+			return c1.getId().equals(c2.getId());
+		}
+
+		@Override
+		public boolean areContentsTheSame(ConversationItem c1,
+				ConversationItem c2) {
+			return c1.equals(c2);
+		}
+	}
+
 	public interface IntroductionHandler {
 		void respondToIntroduction(final SessionId sessionId,
 				final boolean accept);
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationInItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..06bf4a1adb5529fdfee54af829d84f2c9b41a69c
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationInItem.java
@@ -0,0 +1,33 @@
+package org.briarproject.android.contact;
+
+import org.briarproject.api.forum.ForumInvitationMessage;
+
+// This class is not thread-safe
+public class ConversationForumInvitationInItem
+		extends ConversationForumInvitationItem
+		implements ConversationItem.IncomingItem {
+
+	private boolean read;
+
+	public ConversationForumInvitationInItem(ForumInvitationMessage fim) {
+		super(fim);
+
+		this.read = fim.isRead();
+	}
+
+	@Override
+	int getType() {
+		return FORUM_INVITATION_IN;
+	}
+
+	@Override
+	public boolean isRead() {
+		return read;
+	}
+
+	@Override
+	public void setRead(boolean read) {
+		this.read = read;
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationItem.java b/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..0bf7d72abe85136c49fb2ce2e89df8e4d6621ba9
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationItem.java
@@ -0,0 +1,19 @@
+package org.briarproject.android.contact;
+
+import org.briarproject.api.forum.ForumInvitationMessage;
+
+abstract class ConversationForumInvitationItem extends ConversationItem {
+
+	private ForumInvitationMessage fim;
+
+	public ConversationForumInvitationItem(ForumInvitationMessage fim) {
+		super(fim.getId(), fim.getTimestamp());
+
+		this.fim = fim;
+	}
+
+	public ForumInvitationMessage getForumInvitationMessage() {
+		return fim;
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationOutItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d4699abf3e3df877463227d5c028e0bb9c0124c
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationOutItem.java
@@ -0,0 +1,49 @@
+package org.briarproject.android.contact;
+
+import org.briarproject.api.forum.ForumInvitationMessage;
+
+/**
+ * This class is needed and can not be replaced by an ConversationNoticeOutItem,
+ * because it carries the optional invitation message
+ * to be displayed as a regular private message.
+ *
+ * This class is not thread-safe
+ */
+public class ConversationForumInvitationOutItem
+		extends ConversationForumInvitationItem
+		implements ConversationItem.OutgoingItem {
+
+	private boolean sent, seen;
+
+	public ConversationForumInvitationOutItem(ForumInvitationMessage fim) {
+		super(fim);
+		this.sent = fim.isSent();
+		this.seen = fim.isSeen();
+	}
+
+	@Override
+	int getType() {
+		return FORUM_INVITATION_OUT;
+	}
+
+	@Override
+	public boolean isSent() {
+		return sent;
+	}
+
+	@Override
+	public void setSent(boolean sent) {
+		this.sent = sent;
+	}
+
+	@Override
+	public boolean isSeen() {
+		return seen;
+	}
+
+	@Override
+	public void setSeen(boolean seen) {
+		this.seen = seen;
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationIntroductionInItem.java
index fb891d0a1b3b5bb84bac329c397587bfa5cc44ea..9ccf21efdc57355a009f12b141d731b8a00955fd 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionInItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationIntroductionInItem.java
@@ -3,6 +3,7 @@ package org.briarproject.android.contact;
 import org.briarproject.api.introduction.IntroductionRequest;
 import org.briarproject.api.sync.MessageId;
 
+// This class is not thread-safe
 public class ConversationIntroductionInItem extends ConversationIntroductionItem
 		implements ConversationItem.IncomingItem {
 
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationIntroductionOutItem.java
index a2aba398f93dee2f6305b346b2d60ad206dff389..7584c87380b1aa0581675df02ffaca9b403aaf8b 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionOutItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationIntroductionOutItem.java
@@ -6,6 +6,8 @@ import org.briarproject.api.introduction.IntroductionRequest;
  * This class is needed and can not be replaced by an ConversationNoticeOutItem,
  * because it carries the optional introduction message
  * to be displayed as a regular private message.
+ *
+ *  This class is not thread-safe
  */
 public class ConversationIntroductionOutItem
 		extends ConversationIntroductionItem
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationItem.java b/briar-android/src/org/briarproject/android/contact/ConversationItem.java
index 2fc96b6ab7e54857c3bb53f212b45c50c18fda67..93e95e24f3a0490364db04867365b1054956a18b 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationItem.java
@@ -3,6 +3,7 @@ package org.briarproject.android.contact;
 import android.content.Context;
 
 import org.briarproject.R;
+import org.briarproject.api.forum.ForumInvitationMessage;
 import org.briarproject.api.introduction.IntroductionMessage;
 import org.briarproject.api.introduction.IntroductionRequest;
 import org.briarproject.api.introduction.IntroductionResponse;
@@ -20,6 +21,8 @@ public abstract class ConversationItem {
 	final static int INTRODUCTION_OUT = 4;
 	final static int NOTICE_IN = 5;
 	final static int NOTICE_OUT = 6;
+	final static int FORUM_INVITATION_IN = 7;
+	final static int FORUM_INVITATION_OUT = 8;
 
 	private MessageId id;
 	private long time;
@@ -92,6 +95,14 @@ public abstract class ConversationItem {
 		}
 	}
 
+	public static ConversationItem from(ForumInvitationMessage fim) {
+		if (fim.isLocal()) {
+			return new ConversationForumInvitationOutItem(fim);
+		} else {
+			return new ConversationForumInvitationInItem(fim);
+		}
+	}
+
 	/** This method should not be used to get user-facing objects,
 	 *  Its purpose is to provider data for the contact list.
 	 */
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationNoticeInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationNoticeInItem.java
index 610b703c17ada2defc6cd39233376929092d2658..3affb3d4836e73ed130a152a1b41505230a7e8e1 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationNoticeInItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationNoticeInItem.java
@@ -2,6 +2,7 @@ package org.briarproject.android.contact;
 
 import org.briarproject.api.sync.MessageId;
 
+// This class is not thread-safe
 public class ConversationNoticeInItem extends ConversationNoticeItem implements
 		ConversationItem.IncomingItem {
 
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutItem.java
index b398897013f82366dbb252d43e7b679032b5a811..ca4503cf671e23595c7cbcd6d459e6f3c2f92689 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutItem.java
@@ -2,6 +2,7 @@ package org.briarproject.android.contact;
 
 import org.briarproject.api.sync.MessageId;
 
+// This class is not thread-safe
 public class ConversationNoticeOutItem extends ConversationNoticeItem implements
 		ConversationItem.OutgoingItem {
 
diff --git a/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java b/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java
index 0e3e81181f37ec6a16b713e8772e09b8386b4077..2f1ce156b65c3ca75406156bf207d27339f27b32 100644
--- a/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java
+++ b/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java
@@ -1,6 +1,11 @@
 package org.briarproject.android.forum;
 
 import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.os.Build;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -47,10 +52,16 @@ public class ContactSelectorAdapter
 		} else {
 			ui.checkBox.setChecked(false);
 		}
+
+		if (item.isDisabled()) {
+			// we share this forum already with that contact
+			ui.layout.setEnabled(false);
+			grayOutItem(ui);
+		}
 	}
 
 	public Collection<ContactId> getSelectedContactIds() {
-		Collection<ContactId> selected = new ArrayList<ContactId>();
+		Collection<ContactId> selected = new ArrayList<>();
 
 		for (int i = 0; i < contacts.size(); i++) {
 			SelectableContactListItem item =
@@ -78,4 +89,19 @@ public class ContactSelectorAdapter
 		return compareByName(c1, c2);
 	}
 
+	private void grayOutItem(final SelectableContactHolder ui) {
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+			float alpha = 0.25f;
+			ui.avatar.setAlpha(alpha);
+			ui.name.setAlpha(alpha);
+			ui.checkBox.setAlpha(alpha);
+		} else {
+			ColorFilter colorFilter = new PorterDuffColorFilter(Color.GRAY,
+					PorterDuff.Mode.MULTIPLY);
+			ui.avatar.setColorFilter(colorFilter);
+			ui.name.setEnabled(false);
+			ui.checkBox.setEnabled(false);
+		}
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java b/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..73de55ec1817a8f7dff2dde0a87576a1a5b0afca
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java
@@ -0,0 +1,247 @@
+package org.briarproject.android.forum;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.transition.Fade;
+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 org.briarproject.R;
+import org.briarproject.android.AndroidComponent;
+import org.briarproject.android.contact.BaseContactListAdapter;
+import org.briarproject.android.contact.ContactListItem;
+import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.android.util.BriarRecyclerView;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.forum.ForumSharingManager;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+import static org.briarproject.android.forum.ShareForumActivity.CONTACTS;
+import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds;
+import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
+
+public class ContactSelectorFragment extends BaseFragment implements
+		BaseContactListAdapter.OnItemClickListener {
+
+	public final static String TAG = "ContactSelectorFragment";
+	private ShareForumActivity shareForumActivity;
+	private Menu menu;
+	private BriarRecyclerView list;
+	private ContactSelectorAdapter adapter;
+	private Collection<ContactId> selectedContacts;
+
+	private static final Logger LOG =
+			Logger.getLogger(ContactSelectorFragment.class.getName());
+
+	// Fields that are accessed from background threads must be volatile
+	protected volatile GroupId groupId;
+	@Inject
+	protected volatile ContactManager contactManager;
+	@Inject
+	protected volatile IdentityManager identityManager;
+	@Inject
+	protected volatile ForumSharingManager forumSharingManager;
+
+	public static ContactSelectorFragment newInstance(GroupId groupId) {
+		Bundle args = new Bundle();
+		args.putByteArray(GROUP_ID, groupId.getBytes());
+
+		ContactSelectorFragment fragment = new ContactSelectorFragment();
+		fragment.setArguments(args);
+		return fragment;
+	}
+
+	@Override
+	public void onAttach(Context context) {
+		super.onAttach(context);
+		try {
+			shareForumActivity = (ShareForumActivity) context;
+		} catch (ClassCastException e) {
+			throw new InstantiationError(
+					"This fragment is only meant to be attached to the ShareForumActivity");
+		}
+	}
+
+	@Override
+	public void injectActivity(AndroidComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+
+		setHasOptionsMenu(true);
+		groupId = new GroupId(getArguments().getByteArray(GROUP_ID));
+		if (groupId == null) throw new IllegalStateException("No GroupId");
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		View contentView =
+				inflater.inflate(R.layout.introduction_contact_chooser,
+						container, false);
+
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+			setExitTransition(new Fade());
+		}
+
+		adapter = new ContactSelectorAdapter(getActivity(), this);
+
+		list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
+		list.setLayoutManager(new LinearLayoutManager(getActivity()));
+		list.setAdapter(adapter);
+		list.setEmptyText(getString(R.string.no_contacts));
+
+		// restore selected contacts if available
+		if (savedInstanceState != null) {
+			ArrayList<Integer> intContacts =
+					savedInstanceState.getIntegerArrayList(CONTACTS);
+			selectedContacts = ShareForumActivity.getContactsFromIntegers(intContacts);
+		}
+
+		return contentView;
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+
+		if (selectedContacts != null) {
+			loadContacts(Collections.unmodifiableCollection(selectedContacts));
+		} else {
+			loadContacts(null);
+		}
+	}
+
+	@Override
+	public void onSaveInstanceState(Bundle outState) {
+		if (adapter != null) {
+			selectedContacts = adapter.getSelectedContactIds();
+			outState.putIntegerArrayList(CONTACTS,
+					getContactsFromIds(selectedContacts));
+		}
+	}
+
+	@Override
+	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+		inflater.inflate(R.menu.forum_share_actions, menu);
+		super.onCreateOptionsMenu(menu, inflater);
+		this.menu = menu;
+		// hide sharing action initially, if no contact is selected
+		updateMenuItem();
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		// Handle presses on the action bar items
+		switch (item.getItemId()) {
+			case android.R.id.home:
+				shareForumActivity.onBackPressed();
+				return true;
+			case R.id.action_share_forum:
+				selectedContacts = adapter.getSelectedContactIds();
+				shareForumActivity.showMessageScreen(groupId, selectedContacts);
+				return true;
+			default:
+				return super.onOptionsItemSelected(item);
+		}
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	@Override
+	public void onItemClick(View view, ContactListItem item) {
+		((SelectableContactListItem) item).toggleSelected();
+		adapter.notifyItemChanged(adapter.findItemPosition(item), item);
+
+		updateMenuItem();
+	}
+
+	private void loadContacts(final Collection<ContactId> selection) {
+		shareForumActivity.runOnDbThread(new Runnable() {
+			public void run() {
+				try {
+					long now = System.currentTimeMillis();
+					List<ContactListItem> contacts =
+							new ArrayList<>();
+
+					for (Contact c : contactManager.getActiveContacts()) {
+						LocalAuthor localAuthor = identityManager
+								.getLocalAuthor(c.getLocalAuthorId());
+						// was this contact already selected?
+						boolean selected = selection != null &&
+								selection.contains(c.getId());
+						// do we have already some sharing with that contact?
+						boolean disabled =
+								!forumSharingManager.canBeShared(groupId, c);
+						contacts.add(
+								new SelectableContactListItem(c, localAuthor,
+										groupId, selected, disabled));
+					}
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Load took " + duration + " ms");
+					displayContacts(Collections.unmodifiableList(contacts));
+				} catch (DbException e) {
+					displayContacts(Collections.<ContactListItem>emptyList());
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
+			}
+		});
+	}
+
+	private void displayContacts(final List<ContactListItem> contacts) {
+		shareForumActivity.runOnUiThread(new Runnable() {
+			public void run() {
+				if (!contacts.isEmpty()) {
+					adapter.addAll(contacts);
+				} else {
+					list.showData();
+				}
+				updateMenuItem();
+			}
+		});
+	}
+
+	private void updateMenuItem() {
+		if (menu == null) return;
+		MenuItem item = menu.findItem(R.id.action_share_forum);
+		if (item == null) return;
+
+		selectedContacts = adapter.getSelectedContactIds();
+		if (selectedContacts.size() > 0) {
+			item.setVisible(true);
+		} else {
+			item.setVisible(false);
+		}
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index c092791a57c5876fd1f632c46240d8d06cf38fd7..9a2ec711d9233f87d2a60ad3d643433511de4419 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -3,6 +3,7 @@ package org.briarproject.android.forum;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.ActivityCompat;
 import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v7.app.AlertDialog;
@@ -49,6 +50,7 @@ import javax.inject.Inject;
 
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
+import static android.support.design.widget.Snackbar.LENGTH_LONG;
 import static android.view.Gravity.CENTER;
 import static android.view.Gravity.CENTER_HORIZONTAL;
 import static android.view.View.GONE;
@@ -68,6 +70,7 @@ public class ForumActivity extends BriarActivity implements EventListener,
 	public static final String MIN_TIMESTAMP = "briar.MIN_TIMESTAMP";
 
 	private static final int REQUEST_READ = 2;
+	private static final int REQUEST_FORUM_SHARED = 3;
 	private static final Logger LOG =
 			Logger.getLogger(ForumActivity.class.getName());
 
@@ -165,7 +168,9 @@ public class ForumActivity extends BriarActivity implements EventListener,
 				ActivityOptionsCompat options = ActivityOptionsCompat
 						.makeCustomAnimation(this, android.R.anim.slide_in_left,
 								android.R.anim.slide_out_right);
-				ActivityCompat.startActivity(this, i2, options.toBundle());
+				ActivityCompat
+						.startActivityForResult(this, i2, REQUEST_FORUM_SHARED,
+								options.toBundle());
 				return true;
 			case R.id.action_forum_delete:
 				showUnsubscribeDialog();
@@ -297,6 +302,12 @@ public class ForumActivity extends BriarActivity implements EventListener,
 			if (position >= 0 && position < adapter.getCount())
 				displayPost(position);
 		}
+		else if (request == REQUEST_FORUM_SHARED && result == RESULT_OK) {
+			Snackbar s = Snackbar.make(list, R.string.forum_shared_snackbar,
+					LENGTH_LONG);
+			s.getView().setBackgroundResource(R.color.briar_primary);
+			s.show();
+		}
 	}
 
 	@Override
diff --git a/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java b/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java
index 93d266c6c03e699d4f436f54588a92519d6da07a..6b9116ca41a8b2feb935fe0b7e7c123a075376c3 100644
--- a/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java
+++ b/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java
@@ -11,14 +11,15 @@ import java.util.Collections;
 // This class is not thread-safe
 public class SelectableContactListItem extends ContactListItem {
 
-	private boolean selected;
+	private boolean selected, disabled;
 
 	public SelectableContactListItem(Contact contact, LocalAuthor localAuthor,
-			GroupId groupId, boolean selected) {
+			GroupId groupId, boolean selected, boolean disabled) {
 
 		super(contact, localAuthor, false, groupId, Collections.<ConversationItem>emptyList());
 
 		this.selected = selected;
+		this.disabled = disabled;
 	}
 
 	public void setSelected(boolean selected) {
@@ -33,4 +34,8 @@ public class SelectableContactListItem extends ContactListItem {
 		selected = !selected;
 	}
 
+	public boolean isDisabled() {
+		return disabled;
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java b/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
index ccb0b365d907f65b419929850c9b1d7ea369279e..820e25666fdf0a26d0644a2ffa25cb0031aeb0dc 100644
--- a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
@@ -2,147 +2,102 @@ package org.briarproject.android.forum;
 
 import android.content.Intent;
 import android.os.Bundle;
-import android.support.v7.widget.LinearLayoutManager;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
 import android.view.View;
 
 import org.briarproject.R;
 import org.briarproject.android.AndroidComponent;
 import org.briarproject.android.BriarActivity;
-import org.briarproject.android.contact.BaseContactListAdapter;
-import org.briarproject.android.contact.ContactListItem;
-import org.briarproject.android.util.BriarRecyclerView;
-import org.briarproject.api.contact.Contact;
+import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.contact.ContactManager;
-import org.briarproject.api.db.DbException;
-import org.briarproject.api.forum.ForumSharingManager;
-import org.briarproject.api.identity.IdentityManager;
-import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.sync.GroupId;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
-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 ShareForumActivity extends BriarActivity implements
-		BaseContactListAdapter.OnItemClickListener {
-
-	private static final Logger LOG =
-			Logger.getLogger(ShareForumActivity.class.getName());
+		BaseFragment.BaseFragmentListener {
 
-	private ContactSelectorAdapter adapter;
-
-	// Fields that are accessed from background threads must be volatile
-	@Inject protected volatile IdentityManager identityManager;
-	@Inject protected volatile ContactManager contactManager;
-	@Inject protected volatile ForumSharingManager forumSharingManager;
-	private volatile GroupId groupId;
+	public final static String CONTACTS = "contacts";
 
 	@Override
-	public void onCreate(Bundle state) {
-		super.onCreate(state);
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
 
-		setContentView(R.layout.introduction_contact_chooser);
+		setContentView(R.layout.activity_share_forum);
 
 		Intent i = getIntent();
 		byte[] b = i.getByteArrayExtra(GROUP_ID);
-		if (b == null) throw new IllegalStateException();
-		groupId = new GroupId(b);
-
-		adapter = new ContactSelectorAdapter(this, this);
-		BriarRecyclerView list =
-				(BriarRecyclerView) findViewById(R.id.contactList);
-		list.setLayoutManager(new LinearLayoutManager(this));
-		list.setAdapter(adapter);
-		list.setEmptyText(getString(R.string.no_contacts));
+		if (b == null) throw new IllegalStateException("No GroupId");
+		GroupId groupId = new GroupId(b);
+
+		if (savedInstanceState == null) {
+			ContactSelectorFragment contactSelectorFragment =
+					ContactSelectorFragment.newInstance(groupId);
+			getSupportFragmentManager().beginTransaction()
+					.add(R.id.shareForumContainer, contactSelectorFragment)
+					.commit();
+		}
 	}
 
 	@Override
-	public void onResume() {
-		super.onResume();
-
-		loadContactsAndVisibility();
+	public void injectActivity(AndroidComponent component) {
+		component.inject(this);
 	}
 
-	@Override
-	public boolean onCreateOptionsMenu(Menu menu) {
-		// Inflate the menu items for use in the action bar
-		MenuInflater inflater = getMenuInflater();
-		inflater.inflate(R.menu.forum_share_actions, menu);
-
-		return super.onCreateOptionsMenu(menu);
+	public void showMessageScreen(GroupId groupId,
+			Collection<ContactId> contacts) {
+
+		ShareForumMessageFragment messageFragment =
+				ShareForumMessageFragment.newInstance(groupId, contacts);
+
+		getSupportFragmentManager().beginTransaction()
+				.setCustomAnimations(android.R.anim.fade_in,
+						android.R.anim.fade_out,
+						android.R.anim.slide_in_left,
+						android.R.anim.slide_out_right)
+				.replace(R.id.shareForumContainer, messageFragment,
+						ContactSelectorFragment.TAG)
+				.addToBackStack(null)
+				.commit();
 	}
 
-	@Override
-	public boolean onOptionsItemSelected(final MenuItem item) {
-		// Handle presses on the action bar items
-		switch (item.getItemId()) {
-			case android.R.id.home:
-				onBackPressed();
-				return true;
-			case R.id.action_share_forum:
-				return true;
-			default:
-				return super.onOptionsItemSelected(item);
+	public static ArrayList<Integer> getContactsFromIds(
+			Collection<ContactId> contacts) {
+
+		// transform ContactIds to Integers so they can be added to a bundle
+		ArrayList<Integer> intContacts = new ArrayList<>(contacts.size());
+		for (ContactId contactId : contacts) {
+			intContacts.add(contactId.getInt());
 		}
+		return intContacts;
 	}
 
-	@Override
-	public void injectActivity(AndroidComponent component) {
-		component.inject(this);
+	public void sharingSuccessful(View v) {
+		setResult(RESULT_OK);
+		hideSoftKeyboard(v);
+		supportFinishAfterTransition();
 	}
 
-	private void loadContactsAndVisibility() {
-		runOnDbThread(new Runnable() {
-			public void run() {
-				try {
-					long now = System.currentTimeMillis();
-					List<ContactListItem> contacts = new ArrayList<>();
-					Collection<ContactId> selectedContacts = new HashSet<>(
-							forumSharingManager.getSharedWith(groupId));
-
-					for (Contact c : contactManager.getActiveContacts()) {
-						LocalAuthor localAuthor = identityManager
-								.getLocalAuthor(c.getLocalAuthorId());
-						boolean selected = selectedContacts.contains(c.getId());
-						contacts.add(
-								new SelectableContactListItem(c, localAuthor,
-										groupId, selected));
-					}
-					long duration = System.currentTimeMillis() - now;
-					if (LOG.isLoggable(INFO))
-						LOG.info("Load took " + duration + " ms");
-					displayContacts(contacts);
-				} catch (DbException e) {
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, e.toString(), e);
-				}
-			}
-		});
+	protected static Collection<ContactId> getContactsFromIntegers(
+			ArrayList<Integer> intContacts) {
+
+		// turn contact integers from a bundle back to ContactIds
+		List<ContactId> contacts = new ArrayList<>(intContacts.size());
+		for(Integer c : intContacts) {
+			contacts.add(new ContactId(c));
+		}
+		return contacts;
 	}
 
-	private void displayContacts(final List<ContactListItem> contact) {
-		runOnUiThread(new Runnable() {
-			public void run() {
-				adapter.addAll(contact);
-			}
-		});
+	@Override
+	public void showLoadingScreen(boolean isBlocking, int stringId) {
+		// this is handled by the recycler view in ContactSelectorFragment
 	}
 
 	@Override
-	public void onItemClick(View view, ContactListItem item) {
-		((SelectableContactListItem) item).toggleSelected();
-		adapter.notifyItemChanged(adapter.findItemPosition(item), item);
+	public void hideLoadingScreen() {
+		// this is handled by the recycler view in ContactSelectorFragment
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java b/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ed79d67cde563f0669adb6afde95095fbed7b51
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java
@@ -0,0 +1,177 @@
+package org.briarproject.android.forum;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.briarproject.R;
+import org.briarproject.android.AndroidComponent;
+import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.forum.ForumSharingManager;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.WARNING;
+import static org.briarproject.android.forum.ShareForumActivity.CONTACTS;
+import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds;
+import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
+
+public class ShareForumMessageFragment extends BaseFragment {
+
+	private static final Logger LOG =
+			Logger.getLogger(ShareForumMessageFragment.class.getName());
+
+	public final static String TAG = "IntroductionMessageFragment";
+	private ShareForumActivity shareForumActivity;
+	private ViewHolder ui;
+
+	// Fields that are accessed from background threads must be volatile
+	@Inject protected volatile ForumSharingManager forumSharingManager;
+	private volatile GroupId groupId;
+	private volatile Collection<ContactId> contacts;
+
+	public static ShareForumMessageFragment newInstance(GroupId groupId,
+			Collection<ContactId> contacts) {
+
+		ShareForumMessageFragment f = new ShareForumMessageFragment();
+
+		Bundle args = new Bundle();
+		args.putByteArray(GROUP_ID, groupId.getBytes());
+		args.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts));
+		f.setArguments(args);
+
+		return f;
+	}
+
+	@Override
+	public void onAttach(Context context) {
+		super.onAttach(context);
+		try {
+			shareForumActivity = (ShareForumActivity) context;
+		} catch (ClassCastException e) {
+			throw new InstantiationError(
+					"This fragment is only meant to be attached to the ShareForumActivity");
+		}
+	}
+
+	@Override
+	public void injectActivity(AndroidComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		// change toolbar text
+		ActionBar actionBar = shareForumActivity.getSupportActionBar();
+		if (actionBar != null) {
+			actionBar.setTitle(R.string.forum_share_button);
+		}
+
+		// allow for home button to act as back button
+		setHasOptionsMenu(true);
+
+		// inflate view
+		View v =
+				inflater.inflate(R.layout.share_forum_message, container,
+						false);
+		ui = new ViewHolder(v);
+		ui.button.setOnClickListener(new View.OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				onButtonClick();
+			}
+		});
+
+		// get groupID and contactIDs from fragment arguments
+		groupId = new GroupId(getArguments().getByteArray(GROUP_ID));
+		ArrayList<Integer> intContacts =
+				getArguments().getIntegerArrayList(CONTACTS);
+		if (intContacts == null) throw new IllegalArgumentException();
+		contacts = ShareForumActivity.getContactsFromIntegers(intContacts);
+
+		return v;
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		switch (item.getItemId()) {
+			case android.R.id.home:
+				shareForumActivity.onBackPressed();
+				return true;
+			default:
+				return super.onOptionsItemSelected(item);
+		}
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	public void onButtonClick() {
+		// disable button to prevent accidental double invitations
+		ui.button.setEnabled(false);
+
+		String msg = ui.message.getText().toString();
+		shareForum(msg);
+
+		// don't wait for the introduction to be made before finishing activity
+		shareForumActivity.sharingSuccessful(ui.message);
+	}
+
+	private void shareForum(final String msg) {
+		shareForumActivity.runOnDbThread(new Runnable() {
+			public void run() {
+				try {
+					for (ContactId c : contacts) {
+						forumSharingManager
+								.sendForumInvitation(groupId, c, msg);
+					}
+				} catch (DbException e) {
+					sharingError();
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
+			}
+		});
+	}
+
+	private void sharingError() {
+		shareForumActivity.runOnUiThread(new Runnable() {
+			public void run() {
+				Toast.makeText(shareForumActivity,
+						R.string.introduction_error, Toast.LENGTH_SHORT)
+						.show();
+			}
+		});
+	}
+
+	private static class ViewHolder {
+		final private TextView text;
+		final private EditText message;
+		final private Button button;
+
+		ViewHolder(View v) {
+			text = (TextView) v.findViewById(R.id.introductionText);
+			message = (EditText) v.findViewById(R.id.invitationMessageView);
+			button = (Button) v.findViewById(R.id.shareForumButton);
+		}
+	}
+}