diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 1bb12bfff447a50c72d83e6b30d5323d9a464d08..aad00c6d9e747d81de25845bbd0493c0f2060861 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -100,6 +100,17 @@
 				/>
 		</activity>
 
+		<activity
+			android:name=".android.privategroup.creation.CreateGroupActivity"
+			android:label="@string/groups_create_group_title"
+			android:parentActivityName=".android.NavDrawerActivity"
+			android:windowSoftInputMode="adjustResize">
+			<meta-data
+				android:name="android.support.PARENT_ACTIVITY"
+				android:value=".android.NavDrawerActivity"
+				/>
+		</activity>
+
 		<activity
 			android:name=".android.privategroup.conversation.GroupActivity"
 			android:label="@string/app_name"
diff --git a/briar-android/res/layout/activity_share.xml b/briar-android/res/layout/activity_share.xml
deleted file mode 100644
index 80f19387f68dfda0613074f4fdc19ee32ab4b8cc..0000000000000000000000000000000000000000
--- a/briar-android/res/layout/activity_share.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
-	android:id="@+id/shareContainer"
-	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/fragment_create_group.xml b/briar-android/res/layout/fragment_create_group.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a9601e2abbc02e615672d72bda762bc08c90a4fe
--- /dev/null
+++ b/briar-android/res/layout/fragment_create_group.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	android:orientation="vertical"
+	android:padding="@dimen/margin_medium">
+
+	<EditText
+		android:id="@+id/name"
+		android:layout_width="match_parent"
+		android:layout_height="0dp"
+		android:layout_weight="1"
+		android:gravity="bottom"
+		android:hint="@string/groups_create_group_hint"/>
+
+	<Button
+		android:id="@+id/button"
+		style="@style/BriarButton"
+		android:enabled="false"
+		android:text="@string/groups_create_group_button"/>
+
+</LinearLayout>
diff --git a/briar-android/res/layout/fragment_message.xml b/briar-android/res/layout/fragment_message.xml
new file mode 100644
index 0000000000000000000000000000000000000000..168ff34a4521410e4d828751dca43f489ff1cd3b
--- /dev/null
+++ b/briar-android/res/layout/fragment_message.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<org.briarproject.android.view.LargeTextInputView
+	android:id="@+id/messageView"
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	app:buttonText="@string/forum_share_button"
+	app:fillHeight="true"
+	app:hint="@string/forum_share_message"/>
\ No newline at end of file
diff --git a/briar-android/res/layout/fragment_share_message.xml b/briar-android/res/layout/fragment_share_message.xml
deleted file mode 100644
index 45b33c0bac6aceb5c534f4847f2ac8afadcc6c30..0000000000000000000000000000000000000000
--- a/briar-android/res/layout/fragment_share_message.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-	xmlns:android="http://schemas.android.com/apk/res/android"
-	xmlns:app="http://schemas.android.com/apk/res-auto"
-	android:layout_width="match_parent"
-	android:layout_height="match_parent"
-	android:orientation="vertical">
-
-	<TextView
-		android:id="@+id/introductionText"
-		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:padding="@dimen/margin_activity_horizontal"
-		android:text="@string/forum_share_message"
-		android:textColor="@color/briar_text_primary"
-		android:textSize="@dimen/text_size_medium"/>
-
-	<org.briarproject.android.view.LargeTextInputView
-		android:id="@+id/invitationMessageView"
-		android:layout_width="match_parent"
-		android:layout_height="0dp"
-		android:layout_weight="1"
-		android:gravity="bottom"
-		app:buttonText="@string/forum_share_button"
-		app:fillHeight="true"
-		app:hint="@string/introduction_message_hint"/>
-
-</LinearLayout>
diff --git a/briar-android/res/menu/forum_share_actions.xml b/briar-android/res/menu/contact_selection_actions.xml
similarity index 73%
rename from briar-android/res/menu/forum_share_actions.xml
rename to briar-android/res/menu/contact_selection_actions.xml
index a3d6c154774c13dfe9d370c6eec048fc38dcb7b0..fbef25059b583515e0a331941059f8a5893c9f3d 100644
--- a/briar-android/res/menu/forum_share_actions.xml
+++ b/briar-android/res/menu/contact_selection_actions.xml
@@ -4,9 +4,9 @@
 	xmlns:app="http://schemas.android.com/apk/res-auto">
 
 	<item
-		android:id="@+id/action_share_forum"
+		android:id="@+id/action_contacts_selected"
 		android:icon="@drawable/ic_check_white"
-		android:title="@string/forum_share_action"
+		android:title="@string/contacts_selected"
 		app:showAsAction="always"/>
 
 </menu>
\ No newline at end of file
diff --git a/briar-android/res/menu/groups_list_actions.xml b/briar-android/res/menu/groups_list_actions.xml
index 511224ff1650225e103d86b24531ea559da54bcf..16eeba4625ce8a78907e35818f693ef191560154 100644
--- a/briar-android/res/menu/groups_list_actions.xml
+++ b/briar-android/res/menu/groups_list_actions.xml
@@ -6,7 +6,7 @@
 	<item
 		android:id="@+id/action_add_group"
 		android:icon="@drawable/ic_add_white"
-		android:title="@string/groups_add_group_title"
+		android:title="@string/groups_create_group_title"
 		app:showAsAction="ifRoom"/>
 
 </menu>
\ No newline at end of file
diff --git a/briar-android/res/values-de/strings.xml b/briar-android/res/values-de/strings.xml
index 2967f16b1993290d636d1f4ed594cba6fd448105..e3f45375514005cfc8cebc11090704b1bdbed01c 100644
--- a/briar-android/res/values-de/strings.xml
+++ b/briar-android/res/values-de/strings.xml
@@ -171,7 +171,7 @@
   <string name="forum_left_toast">Forum wurde verlassen</string>
   <!--Forum Sharing-->
   <string name="forum_share_button">Forum teilen</string>
-  <string name="forum_share_action">Teile dieses Forum mit den gewählten Kontakten</string>
+  <string name="contacts_selected">Teile dieses Forum mit den gewählten Kontakten</string>
   <string name="activity_share_toolbar_header">Kontakte auswählen</string>
   <string name="no_contacts_selector">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nBitte komm zurück, wenn du deinen ersten Kontakt hinzugefügt hast.</string>
   <string name="forum_shared_snackbar">Forum mit gewählten Kontakten geteilt</string>
diff --git a/briar-android/res/values-es/strings.xml b/briar-android/res/values-es/strings.xml
index 521fbd9f866aa42481062518bcb255069fdb86c9..63135b3bf91dcf4f93c2ddeb1e442975b9e70d39 100644
--- a/briar-android/res/values-es/strings.xml
+++ b/briar-android/res/values-es/strings.xml
@@ -159,7 +159,7 @@
   <string name="forum_left_toast">Foro abandonado</string>
   <!--Forum Sharing-->
   <string name="forum_share_button">Compartir foro</string>
-  <string name="forum_share_action">Compartir este foro con los contactos seleccionados</string>
+  <string name="contacts_selected">Compartir este foro con los contactos seleccionados</string>
   <string name="activity_share_toolbar_header">Elige contactos</string>
   <string name="no_contacts_selector">Parece que eres nuevo aquí y no tienes contactos aún.\n\nPor favor, vuelve cuando hayas añadido tu primer contacto.</string>
   <string name="forum_shared_snackbar">Foro compartido con los contactos seleccionados</string>
diff --git a/briar-android/res/values-it/strings.xml b/briar-android/res/values-it/strings.xml
index 28a764499d57e3310eac59bef28c39c07e69d34a..e7b7a678f8b5c689dc1e47e8110c85a28bbcabfd 100644
--- a/briar-android/res/values-it/strings.xml
+++ b/briar-android/res/values-it/strings.xml
@@ -136,7 +136,7 @@
   <string name="forum_left_toast">Forum lasciato</string>
   <!--Forum Sharing-->
   <string name="forum_share_button">Condividi Forum</string>
-  <string name="forum_share_action">Condividi questo forum con i contatti scelti</string>
+  <string name="contacts_selected">Condividi questo forum con i contatti scelti</string>
   <string name="activity_share_toolbar_header">Scegli Contatti</string>
   <string name="forum_shared_snackbar">Forum condiviso con i contatti scelti</string>
   <string name="forum_share_error">C\'è stato un errore nella condivisione di questo forum.</string>
diff --git a/briar-android/res/values-pt-rBR/strings.xml b/briar-android/res/values-pt-rBR/strings.xml
index 4fd4eb8c7bad47344326c2ef7932251405d9a96d..75d4cc14b4edd7d34a3c5f67e2d9e9140b999c18 100644
--- a/briar-android/res/values-pt-rBR/strings.xml
+++ b/briar-android/res/values-pt-rBR/strings.xml
@@ -171,7 +171,7 @@ Se sentido sozinho aqui? Compartilhe esse fórum com seus contatos!</string>
   <string name="forum_left_toast">Saiu do fórum</string>
   <!--Forum Sharing-->
   <string name="forum_share_button">Compartilhar fórum</string>
-  <string name="forum_share_action">Compartilhar este fórum com os contatos escolhidos</string>
+  <string name="contacts_selected">Compartilhar este fórum com os contatos escolhidos</string>
   <string name="activity_share_toolbar_header">Escolher contatos</string>
   <string name="no_contacts_selector">Parece que você é novo aqui e não tem nenhum contato ainda.
 Por favor volte aqui depois de adicionar um contato.</string>
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index b1652f5fb7c1ea51c54367e5d0137ac4bdc99d83..872800c9f05297d0fa40ae400567dfe9d37069da 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -155,8 +155,11 @@
 	<string name="groups_group_is_empty">This group is empty</string>
 	<string name="groups_group_is_dissolved">This group is dissolved</string>
 	<string name="groups_remove">Remove</string>
-	<string name="groups_add_group_title">Add Private Group</string>
+	<string name="groups_create_group_title">Create Private Group</string>
 	<string name="groups_no_messages">This group is empty.\n\nYou can use the pen icon at the top to compose the first message.</string>
+	<string name="groups_create_group_button">Create Group</string>
+	<string name="groups_create_group_invitation_button">Send Invitation</string>
+	<string name="groups_create_group_hint">Add a name for your private group</string>
 	<string name="groups_compose_message">Compose Message</string>
 	<string name="groups_message_sent">Message sent</string>
 	<string name="groups_message_received">Message received</string>
@@ -195,7 +198,7 @@
 
 	<!-- Forum Sharing -->
 	<string name="forum_share_button">Share Forum</string>
-	<string name="forum_share_action">Share this forum with chosen contacts</string>
+	<string name="contacts_selected">Contacts selected</string>
 	<string name="activity_share_toolbar_header">Choose Contacts</string>
 	<string name="no_contacts_selector">It seems that you are new here and have no contacts yet.\n\nPlease come back here after you added your first contact.</string>
 	<string name="forum_shared_snackbar">Forum shared with chosen contacts</string>
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index 4e66c87aba6e1fbb891dc9e669d55348a8df974a..1179905b6932ced492c512debc964cc53d0ff76a 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -28,7 +28,10 @@ import org.briarproject.android.keyagreement.KeyAgreementActivity;
 import org.briarproject.android.keyagreement.ShowQrCodeFragment;
 import org.briarproject.android.panic.PanicPreferencesActivity;
 import org.briarproject.android.panic.PanicResponderActivity;
+import org.briarproject.android.privategroup.creation.CreateGroupActivity;
+import org.briarproject.android.privategroup.creation.CreateGroupFragment;
 import org.briarproject.android.privategroup.conversation.GroupActivity;
+import org.briarproject.android.privategroup.creation.CreateGroupMessageFragment;
 import org.briarproject.android.privategroup.list.GroupListFragment;
 import org.briarproject.android.sharing.ContactSelectorFragment;
 import org.briarproject.android.sharing.InvitationsBlogActivity;
@@ -73,6 +76,8 @@ public interface ActivityComponent {
 
 	void inject(InvitationsBlogActivity activity);
 
+	void inject(CreateGroupActivity activity);
+
 	void inject(GroupActivity activity);
 
 	void inject(CreateForumActivity activity);
@@ -118,6 +123,8 @@ public interface ActivityComponent {
 
 	// Fragments
 	void inject(ContactListFragment fragment);
+	void inject(CreateGroupFragment fragment);
+	void inject(CreateGroupMessageFragment fragment);
 	void inject(GroupListFragment fragment);
 	void inject(ForumListFragment fragment);
 	void inject(FeedFragment fragment);
diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index 07cace566205890ee8a101bd04e8a9f651baa94b..69505d32f3ea0d2f7bbef96cd5e21e97470dc57b 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -23,6 +23,8 @@ import org.briarproject.android.forum.ForumController;
 import org.briarproject.android.forum.ForumControllerImpl;
 import org.briarproject.android.privategroup.conversation.GroupController;
 import org.briarproject.android.privategroup.conversation.GroupControllerImpl;
+import org.briarproject.android.privategroup.creation.CreateGroupController;
+import org.briarproject.android.privategroup.creation.CreateGroupControllerImpl;
 import org.briarproject.android.privategroup.list.GroupListController;
 import org.briarproject.android.privategroup.list.GroupListControllerImpl;
 
@@ -101,6 +103,13 @@ public class ActivityModule {
 		return groupListController;
 	}
 
+	@ActivityScope
+	@Provides
+	protected CreateGroupController provideCreateGroupController(
+			CreateGroupControllerImpl createGroupController) {
+		return createGroupController;
+	}
+
 	@ActivityScope
 	@Provides
 	protected GroupController provideGroupController(
diff --git a/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java b/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java
index 04ee0a747db6aac0901c0e381ff2d823f15a797c..9e2db81dc85c5cf54e9831f5902e1ee5fc2b1f15 100644
--- a/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java
@@ -5,7 +5,6 @@ import android.support.annotation.CallSuper;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
 import android.view.LayoutInflater;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ProgressBar;
@@ -36,8 +35,6 @@ abstract class BasePostFragment extends BaseFragment {
 	@Override
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
-		setHasOptionsMenu(true);
-
 		view = inflater.inflate(R.layout.fragment_blog_post, container,
 				false);
 		progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
@@ -60,17 +57,6 @@ abstract class BasePostFragment extends BaseFragment {
 		stopPeriodicUpdate();
 	}
 
-	@Override
-	public boolean onOptionsItemSelected(final MenuItem item) {
-		switch (item.getItemId()) {
-			case android.R.id.home:
-				getActivity().onBackPressed();
-				return true;
-			default:
-				return super.onOptionsItemSelected(item);
-		}
-	}
-
 	@UiThread
 	protected void onBlogPostLoaded(BlogPostItem post) {
 		progressBar.setVisibility(INVISIBLE);
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
index eb655209108c6a290394c31c3fc3241d4f711c12..8809864d55e7103f684d1262bfcf017752b4a342 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
@@ -80,9 +80,6 @@ public class BlogFragment extends BaseFragment implements
 	@Override
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
-
-		setHasOptionsMenu(true);
-
 		Bundle args = getArguments();
 		byte[] b = args.getByteArray(GROUP_ID);
 		if (b == null) throw new IllegalStateException("No group ID in args");
@@ -151,9 +148,6 @@ public class BlogFragment extends BaseFragment implements
 						android.R.anim.slide_in_left,
 						android.R.anim.slide_out_right);
 		switch (item.getItemId()) {
-			case android.R.id.home:
-				getActivity().onBackPressed();
-				return true;
 			case R.id.action_write_blog_post:
 				Intent i = new Intent(getActivity(),
 						WriteBlogPostActivity.class);
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
index 9545f34ff4c8f7fe58e7d431bcb501269b01ab0d..4be0bc5413403741fed04e20a4dbf21f5be73129 100644
--- a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
@@ -66,7 +66,6 @@ public class FeedFragment extends BaseFragment implements
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
 
-		setHasOptionsMenu(true);
 		View v = inflater.inflate(R.layout.fragment_blog, container, false);
 
 		adapter = new BlogPostAdapter(getActivity(), this);
diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
index e3d50dfeefa8d288cc2e67477f04937784d6732a..479fecaadaff9cfaeea1b394f8faa64983ed07b2 100644
--- a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
@@ -72,8 +72,6 @@ public class ReblogFragment extends BaseFragment implements TextInputListener {
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
 
-		setHasOptionsMenu(true);
-
 		Bundle args = getArguments();
 		blogId = new GroupId(args.getByteArray(GROUP_ID));
 		postId = new MessageId(args.getByteArray(POST_ID));
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index 474b2cc2b7b7b7f854541eb1128776378b266679..fe1e62a4da2244079eeb01d04c9fb1d8604ecacc 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -106,8 +106,6 @@ public class ContactListFragment extends BaseFragment implements EventListener {
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
 
-		setHasOptionsMenu(true);
-
 		View contentView =
 				inflater.inflate(R.layout.list, container,
 						false);
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index e52bc4a89d0ec2b9b542b71e39906b153dfe7748..a5496d9c709b464113bfc49999552d9427ab8b2f 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -623,6 +623,7 @@ public class ConversationActivity extends BriarActivity
 		long timestamp = System.currentTimeMillis();
 		timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
 		createMessage(StringUtils.toUtf8(text), timestamp);
+		textInputView.setText("");
 	}
 
 	private long getMinTimestampForNewMessage() {
diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
index 6bbf1965776b3ea3c90c7eccce1b2d661d89282c..f1faecd46d260908e5c7b7e89428df6b2aed2b8b 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
@@ -78,8 +78,6 @@ public class ForumListFragment extends BaseEventFragment implements
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
 
-		setHasOptionsMenu(true);
-
 		View contentView =
 				inflater.inflate(R.layout.fragment_forum_list, container,
 						false);
diff --git a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
index 2cb6b13e6152d89b1e118e8b2cd8494559e78b26..223c01367e28fb25a7a49b9e204941cca5c8f61e 100644
--- a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
+++ b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
@@ -5,6 +5,7 @@ import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
 import android.support.v4.app.Fragment;
+import android.view.MenuItem;
 
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.DestroyableContext;
@@ -27,6 +28,9 @@ public abstract class BaseFragment extends Fragment
 	@Override
 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
+
+		// allow for "up" button to act as back button
+		setHasOptionsMenu(true);
 	}
 
 
@@ -37,6 +41,17 @@ public abstract class BaseFragment extends Fragment
 		listener.onFragmentCreated(getUniqueTag());
 	}
 
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		switch (item.getItemId()) {
+			case android.R.id.home:
+				listener.onBackPressed();
+				return true;
+			default:
+				return super.onOptionsItemSelected(item);
+		}
+	}
+
 	@UiThread
 	protected void finish() {
 		getActivity().supportFinishAfterTransition();
@@ -47,6 +62,9 @@ public abstract class BaseFragment extends Fragment
 		@Deprecated
 		void runOnDbThread(Runnable runnable);
 
+		@UiThread
+		void onBackPressed();
+
 		@UiThread
 		ActivityComponent getActivityComponent();
 
diff --git a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
index 019aff689978c3354668d9af1e38cfebc26d8768..f5c9c32a635bead8e019289a12515f62f1964471 100644
--- a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
+++ b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
@@ -14,6 +14,7 @@ import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.android.view.TextInputView;
+import org.briarproject.android.view.TextInputView.TextInputListener;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
@@ -37,7 +38,7 @@ import static java.util.logging.Level.WARNING;
 import static org.briarproject.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
 
 public class IntroductionMessageFragment extends BaseFragment
-		implements TextInputView.TextInputListener {
+		implements TextInputListener {
 
 	public static final String TAG =
 			IntroductionMessageFragment.class.getName();
@@ -56,7 +57,8 @@ public class IntroductionMessageFragment extends BaseFragment
 	@Inject
 	protected volatile IntroductionManager introductionManager;
 
-	public static IntroductionMessageFragment newInstance(int contactId1, int contactId2) {
+	public static IntroductionMessageFragment newInstance(int contactId1,
+			int contactId2) {
 		Bundle args = new Bundle();
 		args.putInt(CONTACT_ID_1, contactId1);
 		args.putInt(CONTACT_ID_2, contactId2);
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..355de7c274ddfd0f1d978dd5eb8ad29533bae1da
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
@@ -0,0 +1,165 @@
+package org.briarproject.android.privategroup.creation;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.widget.Toast;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.controller.handler.UiResultExceptionHandler;
+import org.briarproject.android.privategroup.conversation.GroupActivity;
+import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener;
+import org.briarproject.android.sharing.ContactSelectorActivity;
+import org.briarproject.android.sharing.ContactSelectorFragment;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.sync.GroupId;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static android.widget.Toast.LENGTH_SHORT;
+import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
+
+public class CreateGroupActivity extends ContactSelectorActivity implements
+		CreateGroupListener, MessageFragmentListener {
+
+	@Inject
+	CreateGroupController controller;
+
+	@Override
+	public void injectActivity(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public void onCreate(Bundle bundle) {
+		super.onCreate(bundle);
+
+		setContentView(R.layout.activity_fragment_container);
+
+		if (bundle == null) {
+			CreateGroupFragment fragment = new CreateGroupFragment();
+			getSupportFragmentManager().beginTransaction()
+					.add(R.id.fragmentContainer, fragment)
+					.commit();
+		} else {
+			byte[] groupBytes = bundle.getByteArray(GROUP_ID);
+			if (groupBytes != null) groupId = new GroupId(groupBytes);
+		}
+	}
+
+	@Override
+	public void onBackPressed() {
+		if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
+			// At this point, the group had been created already,
+			// so don't allow to create it again.
+			openNewGroup();
+		} else {
+			super.onBackPressed();
+		}
+	}
+
+	@Override
+	public void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+		if (groupId != null) {
+			outState.putByteArray(GROUP_ID, groupId.getBytes());
+		}
+	}
+
+	@Override
+	public void onGroupNameChosen(String name) {
+		controller.createGroup(name,
+				new UiResultExceptionHandler<GroupId, DbException>(this) {
+					@Override
+					public void onResultUi(GroupId g) {
+						switchToContactSelectorFragment(g);
+					}
+
+					@Override
+					public void onExceptionUi(DbException exception) {
+						// TODO proper error handling
+						finish();
+					}
+				});
+	}
+
+	private void switchToContactSelectorFragment(GroupId g) {
+		ContactSelectorFragment fragment =
+				ContactSelectorFragment.newInstance(g);
+		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.fragmentContainer, fragment)
+				.addToBackStack(fragment.getUniqueTag())
+				.commit();
+	}
+
+	@Override
+	public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
+		return false;
+	}
+
+	@Override
+	public void contactsSelected(GroupId groupId,
+			Collection<ContactId> contacts) {
+		super.contactsSelected(groupId, contacts);
+
+		CreateGroupMessageFragment fragment = new CreateGroupMessageFragment();
+		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.fragmentContainer, fragment)
+				.addToBackStack(fragment.getUniqueTag())
+				.commit();
+	}
+
+	@Override
+	public boolean onButtonClick(@NotNull String message) {
+		controller.sendInvitation(groupId, contacts, message,
+				new UiResultExceptionHandler<Void, DbException>(this) {
+					@Override
+					public void onResultUi(Void result) {
+						Toast.makeText(CreateGroupActivity.this,
+								"Inviting members is not yet implemented",
+								LENGTH_SHORT).show();
+						openNewGroup();
+					}
+
+					@Override
+					public void onExceptionUi(DbException exception) {
+						// TODO proper error handling
+						finish();
+					}
+				});
+		return true;
+	}
+
+	@Override
+	public int getMaximumMessageLength() {
+		return MAX_GROUP_INVITATION_MSG_LENGTH;
+	}
+
+	private void openNewGroup() {
+		Intent i = new Intent(this, GroupActivity.class);
+		i.putExtra(GROUP_ID, groupId.getBytes());
+		ActivityOptionsCompat options =
+				makeCustomAnimation(this, android.R.anim.fade_in,
+						android.R.anim.fade_out);
+		ActivityCompat.startActivity(this, i, options.toBundle());
+		// finish this activity, so we can't come back to it
+		finish();
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java
new file mode 100644
index 0000000000000000000000000000000000000000..da1a00a0d1275559ebdb13272a91d626ef27fffb
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java
@@ -0,0 +1,19 @@
+package org.briarproject.android.privategroup.creation;
+
+import org.briarproject.android.controller.DbController;
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+
+public interface CreateGroupController extends DbController {
+
+	void createGroup(String name,
+			ResultExceptionHandler<GroupId, DbException> result);
+
+	void sendInvitation(GroupId groupId, Collection<ContactId> contacts,
+			String message, ResultExceptionHandler<Void, DbException> result);
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..a35c2ac20435a7486ecbc1407989c3216d1b42e1
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java
@@ -0,0 +1,68 @@
+package org.briarproject.android.privategroup.creation;
+
+import org.briarproject.android.controller.DbControllerImpl;
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DatabaseExecutor;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.WARNING;
+
+public class CreateGroupControllerImpl extends DbControllerImpl
+		implements CreateGroupController {
+
+	private static final Logger LOG =
+			Logger.getLogger(CreateGroupControllerImpl.class.getName());
+
+	private final PrivateGroupManager groupManager;
+
+	@Inject
+	CreateGroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
+			LifecycleManager lifecycleManager,
+			PrivateGroupManager groupManager) {
+		super(dbExecutor, lifecycleManager);
+		this.groupManager = groupManager;
+	}
+
+	@Override
+	public void createGroup(final String name,
+			final ResultExceptionHandler<GroupId, DbException> handler) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				LOG.info("Adding group to database...");
+				try {
+					handler.onResult(groupManager.addPrivateGroup(name));
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					handler.onException(e);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void sendInvitation(final GroupId groupId,
+			final Collection<ContactId> contacts, final String message,
+			final ResultExceptionHandler<Void, DbException> result) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				// TODO actually send invitation
+				//noinspection ConstantConditions
+				result.onResult(null);
+			}
+		});
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupFragment.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f21508e38d1fc0f27b66ce6f08c3bed42780447
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupFragment.java
@@ -0,0 +1,93 @@
+package org.briarproject.android.privategroup.creation;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.fragment.BaseFragment;
+
+import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
+
+public class CreateGroupFragment extends BaseFragment {
+
+	public final static String TAG = CreateGroupFragment.class.getName();
+
+	private CreateGroupListener listener;
+	private EditText name;
+	private Button button;
+
+	@Override
+	public void onAttach(Context context) {
+		super.onAttach(context);
+		listener = (CreateGroupListener) context;
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		// inflate view
+		View v = inflater.inflate(R.layout.fragment_create_group, container,
+				false);
+		name = (EditText) v.findViewById(R.id.name);
+		name.addTextChangedListener(new TextWatcher() {
+			@Override
+			public void beforeTextChanged(CharSequence s, int start, int count,
+					int after) {
+			}
+
+			@Override
+			public void onTextChanged(CharSequence s, int start, int before,
+					int count) {
+				validateName();
+			}
+
+			@Override
+			public void afterTextChanged(Editable s) {
+			}
+		});
+		button = (Button) v.findViewById(R.id.button);
+		button.setOnClickListener(new View.OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				listener.hideSoftKeyboard(name);
+				listener.onGroupNameChosen(name.getText().toString());
+			}
+		});
+
+		return v;
+	}
+
+	@Override
+	public void onStart() {
+		super.onStart();
+		listener.showSoftKeyboard(name);
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	private void validateName() {
+		String name = this.name.getText().toString();
+		if (name.length() < 1 || name.length() > MAX_GROUP_NAME_LENGTH)
+			button.setEnabled(false);
+		else if(!button.isEnabled())
+			button.setEnabled(true);
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupListener.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..3347be1586748e6d63e9098deb407f481302d2dd
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupListener.java
@@ -0,0 +1,15 @@
+package org.briarproject.android.privategroup.creation;
+
+import android.view.View;
+
+import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
+
+interface CreateGroupListener extends BaseFragmentListener {
+
+	void onGroupNameChosen(String name);
+
+	void showSoftKeyboard(View view);
+
+	void hideSoftKeyboard(View view);
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupMessageFragment.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupMessageFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..a261ce0167313adb3b10585127d2fcfc2856aa0e
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupMessageFragment.java
@@ -0,0 +1,36 @@
+package org.briarproject.android.privategroup.creation;
+
+import android.support.annotation.StringRes;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.sharing.BaseMessageFragment;
+
+public class CreateGroupMessageFragment extends BaseMessageFragment {
+
+	private final static String TAG =
+			CreateGroupMessageFragment.class.getName();
+
+	@Override
+	@StringRes
+	protected int getButtonText() {
+		return R.string.groups_create_group_invitation_button;
+	}
+
+	@Override
+	@StringRes
+	protected int getHintText() {
+		return R.string.forum_share_message;
+	}
+
+	@Override
+	public String getUniqueTag() {
+		return TAG;
+	}
+
+	@Override
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
index 4489dd6bf003aa9829dc0d2fcd6d4e8ef5f438d2..9e77003acedb1f79f17c76cac2df0309848ed2ad 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
@@ -1,8 +1,10 @@
 package org.briarproject.android.privategroup.list;
 
+import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
+import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v7.widget.LinearLayoutManager;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -15,6 +17,7 @@ import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.controller.handler.UiResultExceptionHandler;
 import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.android.privategroup.creation.CreateGroupActivity;
 import org.briarproject.android.privategroup.list.GroupListController.GroupListListener;
 import org.briarproject.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
 import org.briarproject.android.view.BriarRecyclerView;
@@ -27,6 +30,8 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+
 public class GroupListFragment extends BaseFragment implements
 		GroupListListener, OnGroupRemoveClickListener {
 
@@ -48,8 +53,6 @@ public class GroupListFragment extends BaseFragment implements
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
 
-		setHasOptionsMenu(true);
-
 		View v = inflater.inflate(R.layout.list, container, false);
 
 		adapter = new GroupListAdapter(getContext(), this);
@@ -94,7 +97,12 @@ public class GroupListFragment extends BaseFragment implements
 	public boolean onOptionsItemSelected(final MenuItem item) {
 		switch (item.getItemId()) {
 			case R.id.action_add_group:
-				// TODO
+				Intent i = new Intent(getContext(), CreateGroupActivity.class);
+				ActivityOptionsCompat options =
+						makeCustomAnimation(getActivity(),
+								android.R.anim.slide_in_left,
+								android.R.anim.slide_out_right);
+				startActivity(i, options.toBundle());
 				return true;
 			default:
 				return super.onOptionsItemSelected(item);
diff --git a/briar-android/src/org/briarproject/android/sharing/BaseMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/BaseMessageFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb05d19bcf5bc89659b711cb6b1549b5b0da6ab2
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/sharing/BaseMessageFragment.java
@@ -0,0 +1,98 @@
+package org.briarproject.android.sharing;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.StringRes;
+import android.support.annotation.UiThread;
+import android.support.design.widget.Snackbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.briarproject.R;
+import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.android.view.LargeTextInputView;
+import org.briarproject.android.view.TextInputView.TextInputListener;
+import org.briarproject.api.nullsafety.NotNullByDefault;
+import org.briarproject.util.StringUtils;
+
+import static android.support.design.widget.Snackbar.LENGTH_SHORT;
+import static org.briarproject.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
+import static org.briarproject.util.StringUtils.truncateUtf8;
+
+public abstract class BaseMessageFragment extends BaseFragment
+		implements TextInputListener {
+
+	protected LargeTextInputView message;
+	private MessageFragmentListener listener;
+
+	@Override
+	public void onAttach(Context context) {
+		super.onAttach(context);
+		listener = (MessageFragmentListener) context;
+	}
+
+	@Override
+	public View onCreateView(LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
+
+		// inflate view
+		View v = inflater.inflate(R.layout.fragment_message, container,
+				false);
+		message = (LargeTextInputView) v.findViewById(R.id.messageView);
+		message.setButtonText(getString(getButtonText()));
+		message.setHint(getHintText());
+		message.setListener(this);
+
+		return v;
+	}
+
+	protected void setTitle(int res) {
+		listener.setTitle(res);
+	}
+
+	@StringRes
+	protected abstract int getButtonText();
+	@StringRes
+	protected abstract int getHintText();
+
+	@Override
+	public void onStart() {
+		super.onStart();
+		message.showSoftKeyboard();
+	}
+
+	@Override
+	public void onSendClick(String msg) {
+		if (StringUtils.isTooLong(msg, listener.getMaximumMessageLength())) {
+			Snackbar.make(message, R.string.text_too_long, LENGTH_SHORT).show();
+			return;
+		}
+
+		// disable button to prevent accidental double actions
+		message.setSendButtonEnabled(false);
+		message.hideSoftKeyboard();
+
+		msg = truncateUtf8(msg, MAX_INVITATION_MESSAGE_LENGTH);
+		if(!listener.onButtonClick(msg)) {
+			message.setSendButtonEnabled(true);
+			message.showSoftKeyboard();
+		}
+	}
+
+	@UiThread
+	@NotNullByDefault
+	public interface MessageFragmentListener {
+
+		void onBackPressed();
+
+		void setTitle(@StringRes int titleRes);
+
+		/** Returns true when the button click has been consumed. */
+		boolean onButtonClick(String message);
+
+		int getMaximumMessageLength();
+
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..2dad460eae6a37d1599d94dd428971843b771f0a
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java
@@ -0,0 +1,90 @@
+package org.briarproject.android.sharing;
+
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+import android.support.annotation.UiThread;
+
+import org.briarproject.R;
+import org.briarproject.android.BriarActivity;
+import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DatabaseExecutor;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public abstract class ContactSelectorActivity extends BriarActivity implements
+		BaseFragmentListener, ContactSelectorListener {
+
+	final static String CONTACTS = "contacts";
+
+	protected GroupId groupId;
+	protected Collection<ContactId> contacts;
+
+	@Override
+	public void onCreate(Bundle bundle) {
+		super.onCreate(bundle);
+
+		setContentView(R.layout.activity_fragment_container);
+
+		if (bundle != null) {
+			ArrayList<Integer> intContacts =
+					bundle.getIntegerArrayList(CONTACTS);
+			if (intContacts != null) {
+				contacts = getContactsFromIntegers(intContacts);
+			}
+		}
+	}
+
+	@Override
+	public void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
+		if (contacts != null) {
+			outState.putIntegerArrayList(CONTACTS,
+					getContactsFromIds(contacts));
+		}
+	}
+
+	@CallSuper
+	@UiThread
+	@Override
+	public void contactsSelected(GroupId groupId,
+			Collection<ContactId> contacts) {
+		this.groupId = groupId;
+		this.contacts = contacts;
+	}
+
+	@DatabaseExecutor
+	public abstract boolean isDisabled(GroupId groupId, Contact c)
+			throws DbException;
+
+	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;
+	}
+
+	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;
+	}
+
+	@Override
+	public void onFragmentCreated(String tag) {
+
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java
index 82db75adf15b2ad6ab70e893935628d68601cf9a..03e4da3979fc6de40030705c21b01e471f532ab1 100644
--- a/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java
+++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java
@@ -23,7 +23,6 @@ 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;
@@ -48,7 +47,6 @@ public class ContactSelectorFragment extends BaseFragment implements
 	public static final String TAG = ContactSelectorFragment.class.getName();
 	private static final Logger LOG = Logger.getLogger(TAG);
 
-	private ShareActivity shareActivity;
 	private Menu menu;
 	private BriarRecyclerView list;
 	private ContactSelectorAdapter adapter;
@@ -59,13 +57,11 @@ public class ContactSelectorFragment extends BaseFragment implements
 	volatile ContactManager contactManager;
 	@Inject
 	volatile IdentityManager identityManager;
-	@Inject
-	volatile ForumSharingManager forumSharingManager;
 
 	private volatile GroupId groupId;
+	private volatile ContactSelectorListener listener;
 
 	public static ContactSelectorFragment newInstance(GroupId groupId) {
-
 		Bundle args = new Bundle();
 		args.putByteArray(GROUP_ID, groupId.getBytes());
 		ContactSelectorFragment fragment = new ContactSelectorFragment();
@@ -81,14 +77,13 @@ public class ContactSelectorFragment extends BaseFragment implements
 	@Override
 	public void onAttach(Context context) {
 		super.onAttach(context);
-		shareActivity = (ShareActivity) context;
+		listener = (ContactSelectorListener) context;
 	}
 
 	@Override
 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
 
-		setHasOptionsMenu(true);
 		Bundle args = getArguments();
 		byte[] b = args.getByteArray(GROUP_ID);
 		if (b == null) throw new IllegalStateException("No GroupId");
@@ -139,6 +134,7 @@ public class ContactSelectorFragment extends BaseFragment implements
 
 	@Override
 	public void onSaveInstanceState(Bundle outState) {
+		super.onSaveInstanceState(outState);
 		if (adapter != null) {
 			selectedContacts = adapter.getSelectedContactIds();
 			outState.putIntegerArrayList(CONTACTS,
@@ -148,7 +144,7 @@ public class ContactSelectorFragment extends BaseFragment implements
 
 	@Override
 	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-		inflater.inflate(R.menu.forum_share_actions, menu);
+		inflater.inflate(R.menu.contact_selection_actions, menu);
 		super.onCreateOptionsMenu(menu, inflater);
 		this.menu = menu;
 		// hide sharing action initially, if no contact is selected
@@ -159,12 +155,9 @@ public class ContactSelectorFragment extends BaseFragment implements
 	public boolean onOptionsItemSelected(final MenuItem item) {
 		// Handle presses on the action bar items
 		switch (item.getItemId()) {
-			case android.R.id.home:
-				shareActivity.onBackPressed();
-				return true;
-			case R.id.action_share_forum:
+			case R.id.action_contacts_selected:
 				selectedContacts = adapter.getSelectedContactIds();
-				shareActivity.showMessageScreen(groupId, selectedContacts);
+				listener.contactsSelected(groupId, selectedContacts);
 				return true;
 			default:
 				return super.onOptionsItemSelected(item);
@@ -185,7 +178,7 @@ public class ContactSelectorFragment extends BaseFragment implements
 	}
 
 	private void loadContacts(@Nullable final Collection<ContactId> selection) {
-		shareActivity.runOnDbThread(new Runnable() {
+		listener.runOnDbThread(new Runnable() {
 			@Override
 			public void run() {
 				try {
@@ -199,7 +192,7 @@ public class ContactSelectorFragment extends BaseFragment implements
 						boolean selected = selection != null &&
 								selection.contains(c.getId());
 						// do we have already some sharing with that contact?
-						boolean disabled = shareActivity.isDisabled(groupId, c);
+						boolean disabled = listener.isDisabled(groupId, c);
 						contacts.add(new SelectableContactListItem(c,
 								localAuthor, groupId, selected, disabled));
 					}
@@ -216,7 +209,7 @@ public class ContactSelectorFragment extends BaseFragment implements
 	}
 
 	private void displayContacts(final List<ContactListItem> contacts) {
-		shareActivity.runOnUiThreadUnlessDestroyed(new Runnable() {
+		listener.runOnUiThreadUnlessDestroyed(new Runnable() {
 			@Override
 			public void run() {
 				if (contacts.isEmpty()) list.showData();
@@ -228,7 +221,7 @@ public class ContactSelectorFragment extends BaseFragment implements
 
 	private void updateMenuItem() {
 		if (menu == null) return;
-		MenuItem item = menu.findItem(R.id.action_share_forum);
+		MenuItem item = menu.findItem(R.id.action_contacts_selected);
 		if (item == null) return;
 
 		selectedContacts = adapter.getSelectedContactIds();
diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..8016c45e9050d5c69843afa8c73bb100b8e3cf3b
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java
@@ -0,0 +1,28 @@
+package org.briarproject.android.sharing;
+
+import android.support.annotation.UiThread;
+
+import org.briarproject.android.DestroyableContext;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DatabaseExecutor;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+
+public interface ContactSelectorListener extends DestroyableContext {
+
+	@Deprecated
+	void runOnDbThread(Runnable runnable);
+
+	@DatabaseExecutor
+	boolean isDisabled(GroupId groupId, Contact c) throws DbException;
+
+	@UiThread
+	void contactsSelected(GroupId groupId, Collection<ContactId> contacts);
+
+	@UiThread
+	void onBackPressed();
+
+}
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java
index d79a7ed2eee4ae5141a8020c45f211e6a1eee627..e89fe277e1251b88c9cdbffe644fbfafbdc92697 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareActivity.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java
@@ -2,96 +2,110 @@ package org.briarproject.android.sharing;
 
 import android.content.Intent;
 import android.os.Bundle;
-import android.view.View;
+import android.support.annotation.StringRes;
+import android.support.annotation.UiThread;
+import android.widget.Toast;
 
 import org.briarproject.R;
-import org.briarproject.android.BriarActivity;
-import org.briarproject.android.fragment.BaseFragment;
-import org.briarproject.api.contact.Contact;
+import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener;
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DatabaseExecutor;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.sync.GroupId;
+import org.jetbrains.annotations.NotNull;
 
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
+import java.util.logging.Logger;
 
-public abstract class ShareActivity extends BriarActivity implements
-		BaseFragment.BaseFragmentListener {
+import static android.widget.Toast.LENGTH_SHORT;
+import static java.util.logging.Level.WARNING;
 
-	final static String CONTACTS = "contacts";
+public abstract class ShareActivity extends ContactSelectorActivity implements
+		MessageFragmentListener {
 
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
+	private final static Logger LOG =
+			Logger.getLogger(ShareActivity.class.getName());
 
-		setContentView(R.layout.activity_share);
+	@Override
+	public void onCreate(Bundle bundle) {
+		super.onCreate(bundle);
 
 		Intent i = getIntent();
 		byte[] b = i.getByteArrayExtra(GROUP_ID);
 		if (b == null) throw new IllegalStateException("No GroupId");
-		GroupId groupId = new GroupId(b);
+		groupId = new GroupId(b);
 
-		if (savedInstanceState == null) {
+		if (bundle == null) {
 			ContactSelectorFragment contactSelectorFragment =
 					ContactSelectorFragment.newInstance(groupId);
 			getSupportFragmentManager().beginTransaction()
-					.add(R.id.shareContainer, contactSelectorFragment)
+					.add(R.id.fragmentContainer, contactSelectorFragment)
 					.commit();
 		}
 	}
 
-	abstract ShareMessageFragment getMessageFragment(GroupId groupId,
-			Collection<ContactId> contacts);
-
-	abstract boolean isDisabled(GroupId groupId, Contact c) throws DbException;
-
-	void showMessageScreen(GroupId groupId, Collection<ContactId> contacts) {
-		ShareMessageFragment messageFragment =
-				getMessageFragment(groupId, contacts);
+	@UiThread
+	@Override
+	public void contactsSelected(GroupId groupId,
+			Collection<ContactId> contacts) {
+		super.contactsSelected(groupId, contacts);
 
+		BaseMessageFragment messageFragment = getMessageFragment();
 		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.shareContainer, messageFragment,
+				.replace(R.id.fragmentContainer, messageFragment,
 						ContactSelectorFragment.TAG)
 				.addToBackStack(null)
 				.commit();
 	}
 
-	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;
-	}
+	abstract BaseMessageFragment getMessageFragment();
 
-	void sharingSuccessful(View v) {
+	@UiThread
+	@Override
+	public boolean onButtonClick(@NotNull String message) {
+		share(groupId, contacts, message);
 		setResult(RESULT_OK);
-		hideSoftKeyboard(v);
 		supportFinishAfterTransition();
+		return true;
 	}
 
-	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 share(final GroupId g, final Collection<ContactId> contacts,
+			final String msg) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					for (ContactId c : contacts) {
+						share(g, c, msg);
+					}
+				} catch (DbException e) {
+					// TODO proper error handling
+					sharingError();
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
+			}
+		});
 	}
 
-	@Override
-	public void onFragmentCreated(String tag) {
-
+	@DatabaseExecutor
+	protected abstract void share(GroupId g, ContactId c, String msg)
+			throws DbException;
+
+	private void sharingError() {
+		runOnUiThreadUnlessDestroyed(new Runnable() {
+			@Override
+			public void run() {
+				int res = getSharingError();
+				Toast.makeText(ShareActivity.this, res, LENGTH_SHORT).show();
+			}
+		});
 	}
 
+	protected abstract @StringRes int getSharingError();
+
 }
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java
index 696ea145228995a4a8bffbbb8b87b4df1813bd5b..7d5cdeef3e63ae5e211dc9dabbe283bafd0f654f 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java
@@ -1,5 +1,6 @@
 package org.briarproject.android.sharing;
 
+import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.api.blogs.BlogSharingManager;
 import org.briarproject.api.contact.Contact;
@@ -7,18 +8,19 @@ import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.sync.GroupId;
 
-import java.util.Collection;
-
 import javax.inject.Inject;
 
+import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
+
 public class ShareBlogActivity extends ShareActivity {
 
+	// Fields that are accessed from background threads must be volatile
 	@Inject
 	volatile BlogSharingManager blogSharingManager;
 
-	ShareMessageFragment getMessageFragment(GroupId groupId,
-			Collection<ContactId> contacts) {
-		return ShareBlogMessageFragment.newInstance(groupId, contacts);
+	@Override
+	BaseMessageFragment getMessageFragment() {
+		return ShareBlogMessageFragment.newInstance();
 	}
 
 	@Override
@@ -26,10 +28,24 @@ public class ShareBlogActivity extends ShareActivity {
 		component.inject(this);
 	}
 
-	/**
-	 * This must only be called from a DbThread
-	 */
-	boolean isDisabled(GroupId groupId, Contact c) throws DbException {
+	@Override
+	public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
 		return !blogSharingManager.canBeShared(groupId, c);
 	}
+
+	@Override
+	protected void share(GroupId g, ContactId c, String msg)
+			throws DbException {
+		blogSharingManager.sendInvitation(g, c, msg);
+	}
+
+	@Override
+	protected int getSharingError() {
+		return R.string.blogs_sharing_error;
+	}
+
+	@Override
+	public int getMaximumMessageLength() {
+		return MAX_MESSAGE_BODY_LENGTH;
+	}
 }
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java
index d9f944ab0c91654912f33c1797d34a67cffbfabd..ccee4d8fecb028dcbf9e2ec970fd2115f7e5c2af 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java
@@ -1,41 +1,20 @@
 package org.briarproject.android.sharing;
 
 import android.os.Bundle;
+import android.support.annotation.StringRes;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Toast;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
-import org.briarproject.api.blogs.BlogSharingManager;
-import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.db.DbException;
-import org.briarproject.api.sync.GroupId;
 
-import java.util.Collection;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import static android.widget.Toast.LENGTH_SHORT;
-import static java.util.logging.Level.WARNING;
-
-public class ShareBlogMessageFragment extends ShareMessageFragment {
+public class ShareBlogMessageFragment extends BaseMessageFragment {
 
 	public final static String TAG = ShareBlogMessageFragment.class.getName();
-	private static final Logger LOG = Logger.getLogger(TAG);
-
-	// Fields that are accessed from background threads must be volatile
-	@Inject
-	protected volatile BlogSharingManager blogSharingManager;
 
-	public static ShareBlogMessageFragment newInstance(GroupId groupId,
-			Collection<ContactId> contacts) {
-
-		ShareBlogMessageFragment fragment = new ShareBlogMessageFragment();
-		fragment.setArguments(getArguments(groupId, contacts));
-		return fragment;
+	public static ShareBlogMessageFragment newInstance() {
+		return new ShareBlogMessageFragment();
 	}
 
 	@Override
@@ -43,48 +22,29 @@ public class ShareBlogMessageFragment extends ShareMessageFragment {
 			Bundle savedInstanceState) {
 
 		setTitle(R.string.blogs_sharing_share);
-
-		View v = super.onCreateView(inflater, container, savedInstanceState);
-		ui.message.setButtonText(getString(R.string.blogs_sharing_button));
-		return v;
+		return super.onCreateView(inflater, container, savedInstanceState);
 	}
 
 	@Override
-	public void injectFragment(ActivityComponent component) {
-		component.inject(this);
+	@StringRes
+	protected int getButtonText() {
+		return R.string.blogs_sharing_button;
 	}
 
 	@Override
-	public String getUniqueTag() {
-		return TAG;
+	@StringRes
+	protected int getHintText() {
+		return R.string.forum_share_message;
 	}
 
 	@Override
-	protected void share(final String msg) {
-		listener.runOnDbThread(new Runnable() {
-			@Override
-			public void run() {
-				try {
-					for (ContactId c : getContacts()) {
-						blogSharingManager.sendInvitation(getGroupId(), c, msg);
-					}
-				} catch (DbException e) {
-					sharingError();
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, e.toString(), e);
-				}
-			}
-		});
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
 	}
 
 	@Override
-	protected void sharingError() {
-		listener.runOnUiThreadUnlessDestroyed(new Runnable() {
-			@Override
-			public void run() {
-				int res = R.string.blogs_sharing_error;
-				Toast.makeText(getContext(), res, LENGTH_SHORT).show();
-			}
-		});
+	public String getUniqueTag() {
+		return TAG;
 	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java
index 51a5d5a22ae80e96be3c815faee189bb5d9a9763..492e3615b23b4903a61d433d04779170e7e557f4 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java
@@ -1,5 +1,6 @@
 package org.briarproject.android.sharing;
 
+import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
@@ -7,17 +8,19 @@ import org.briarproject.api.db.DbException;
 import org.briarproject.api.forum.ForumSharingManager;
 import org.briarproject.api.sync.GroupId;
 
-import java.util.Collection;
-
 import javax.inject.Inject;
 
+import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
+
 public class ShareForumActivity extends ShareActivity {
+
+	// Fields that are accessed from background threads must be volatile
 	@Inject
 	volatile ForumSharingManager forumSharingManager;
 
-	ShareMessageFragment getMessageFragment(GroupId groupId,
-			Collection<ContactId> contacts) {
-		return ShareForumMessageFragment.newInstance(groupId, contacts);
+	@Override
+	BaseMessageFragment getMessageFragment() {
+		return ShareForumMessageFragment.newInstance();
 	}
 
 	@Override
@@ -25,10 +28,24 @@ public class ShareForumActivity extends ShareActivity {
 		component.inject(this);
 	}
 
-	/**
-	 * This must only be called from a DbThread
-	 */
-	boolean isDisabled(GroupId groupId, Contact c) throws DbException {
+	@Override
+	public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
 		return !forumSharingManager.canBeShared(groupId, c);
 	}
+
+	@Override
+	protected void share(GroupId g, ContactId c, String msg)
+			throws DbException {
+		forumSharingManager.sendInvitation(g, c, msg);
+	}
+
+	@Override
+	protected int getSharingError() {
+		return R.string.forum_share_error;
+	}
+
+	@Override
+	public int getMaximumMessageLength() {
+		return MAX_MESSAGE_BODY_LENGTH;
+	}
 }
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java
index 8414096f01073fbfbf44532af73030591cbe634f..a3faeb630de35101d606b194260be9aa3971c100 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java
@@ -1,41 +1,20 @@
 package org.briarproject.android.sharing;
 
 import android.os.Bundle;
+import android.support.annotation.StringRes;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Toast;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
-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.Collection;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import static android.widget.Toast.LENGTH_SHORT;
-import static java.util.logging.Level.WARNING;
-
-public class ShareForumMessageFragment extends ShareMessageFragment {
+public class ShareForumMessageFragment extends BaseMessageFragment {
 
 	public final static String TAG = ShareForumMessageFragment.class.getName();
-	private static final Logger LOG = Logger.getLogger(TAG);
-
-	// Fields that are accessed from background threads must be volatile
-	@Inject
-	protected volatile ForumSharingManager forumSharingManager;
 
-	public static ShareForumMessageFragment newInstance(GroupId groupId,
-			Collection<ContactId> contacts) {
-
-		ShareForumMessageFragment fragment = new ShareForumMessageFragment();
-		fragment.setArguments(getArguments(groupId, contacts));
-		return fragment;
+	public static ShareForumMessageFragment newInstance() {
+		return new ShareForumMessageFragment();
 	}
 
 	@Override
@@ -47,42 +26,25 @@ public class ShareForumMessageFragment extends ShareMessageFragment {
 	}
 
 	@Override
-	public void injectFragment(ActivityComponent component) {
-		component.inject(this);
+	@StringRes
+	protected int getButtonText() {
+		return R.string.forum_share_button;
 	}
 
 	@Override
-	public String getUniqueTag() {
-		return TAG;
+	@StringRes
+	protected int getHintText() {
+		return R.string.forum_share_message;
 	}
 
 	@Override
-	protected void share(final String msg) {
-		listener.runOnDbThread(new Runnable() {
-			@Override
-			public void run() {
-				try {
-					for (ContactId c : getContacts()) {
-						forumSharingManager.
-								sendInvitation(getGroupId(), c, msg);
-					}
-				} catch (DbException e) {
-					sharingError();
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, e.toString(), e);
-				}
-			}
-		});
+	public void injectFragment(ActivityComponent component) {
+		component.inject(this);
 	}
 
 	@Override
-	protected void sharingError() {
-		listener.runOnUiThreadUnlessDestroyed(new Runnable() {
-			@Override
-			public void run() {
-				int res = R.string.forum_share_error;
-				Toast.makeText(getContext(), res, LENGTH_SHORT).show();
-			}
-		});
+	public String getUniqueTag() {
+		return TAG;
 	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java
deleted file mode 100644
index ea79398cf0167b62722e3fdbad7417760fa6cc38..0000000000000000000000000000000000000000
--- a/briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.briarproject.android.sharing;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-import org.briarproject.R;
-import org.briarproject.android.fragment.BaseFragment;
-import org.briarproject.android.view.LargeTextInputView;
-import org.briarproject.android.view.TextInputView.TextInputListener;
-import org.briarproject.api.blogs.BlogSharingManager;
-import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.forum.ForumSharingManager;
-import org.briarproject.api.sync.GroupId;
-import org.briarproject.util.StringUtils;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-import javax.inject.Inject;
-
-import static org.briarproject.android.sharing.ShareActivity.CONTACTS;
-import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds;
-import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
-import static org.briarproject.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
-
-abstract class ShareMessageFragment extends BaseFragment
-		implements TextInputListener {
-
-	protected ViewHolder ui;
-	private ShareActivity shareActivity;
-
-	// Fields that are accessed from background threads must be volatile
-	@Inject
-	protected volatile ForumSharingManager forumSharingManager;
-	@Inject
-	protected volatile BlogSharingManager blogSharingManager;
-	private volatile GroupId groupId;
-	private volatile Collection<ContactId> contacts;
-
-	protected static Bundle getArguments(GroupId groupId,
-			Collection<ContactId> contacts) {
-
-		Bundle args = new Bundle();
-		args.putByteArray(GROUP_ID, groupId.getBytes());
-		args.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts));
-		return args;
-	}
-
-	@Override
-	public void onAttach(Context context) {
-		super.onAttach(context);
-		shareActivity = (ShareActivity) context;
-	}
-
-	@Override
-	public View onCreateView(LayoutInflater inflater, ViewGroup container,
-			Bundle savedInstanceState) {
-
-		// allow for "up" button to act as back button
-		setHasOptionsMenu(true);
-
-		// 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 = ShareActivity.getContactsFromIntegers(intContacts);
-
-		// inflate view
-		View v = inflater.inflate(R.layout.fragment_share_message, container,
-				false);
-		ui = new ViewHolder(v);
-		ui.message.setListener(this);
-
-		return v;
-	}
-
-	@Override
-	public void onStart() {
-		super.onStart();
-		ui.message.showSoftKeyboard();
-	}
-
-	@Override
-	public boolean onOptionsItemSelected(final MenuItem item) {
-		switch (item.getItemId()) {
-			case android.R.id.home:
-				shareActivity.onBackPressed();
-				return true;
-			default:
-				return super.onOptionsItemSelected(item);
-		}
-	}
-
-	protected void setTitle(int res) {
-		shareActivity.setTitle(res);
-	}
-
-	@Override
-	public void onSendClick(String msg) {
-		// disable button to prevent accidental double invitations
-		ui.message.setSendButtonEnabled(false);
-
-		msg = StringUtils.truncateUtf8(msg, MAX_INVITATION_MESSAGE_LENGTH);
-		share(msg);
-
-		// don't wait for the invitation to be made before finishing activity
-		shareActivity.sharingSuccessful(ui.message);
-	}
-
-	abstract void share(final String msg);
-
-	abstract void sharingError();
-
-	protected Collection<ContactId> getContacts() {
-		return contacts;
-	}
-
-	protected GroupId getGroupId() {
-		return groupId;
-	}
-
-	protected static class ViewHolder {
-		protected final LargeTextInputView message;
-
-		private ViewHolder(View v) {
-			message = (LargeTextInputView) v
-					.findViewById(R.id.invitationMessageView);
-		}
-	}
-}
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
index 6a1ec4883dfcc6ea3c8563aef42f556741b41de0..a8eefd250f474069e20a40fb458af1013c43976f 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
@@ -253,6 +253,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
 				replyItem != null ? replyItem.getId() : null, handler);
 		textInput.hideSoftKeyboard();
 		textInput.setVisibility(GONE);
+		textInput.setText("");
 		adapter.setReplyItem(null);
 	}
 
diff --git a/briar-android/src/org/briarproject/android/view/TextInputView.java b/briar-android/src/org/briarproject/android/view/TextInputView.java
index dade999a0863fa0bf121e257b3e96cc550332cd5..ff75c84bfd064aaa22fb115e82fba23494e993be 100644
--- a/briar-android/src/org/briarproject/android/view/TextInputView.java
+++ b/briar-android/src/org/briarproject/android/view/TextInputView.java
@@ -101,7 +101,6 @@ public class TextInputView extends KeyboardAwareLinearLayout
 			public void onClick(View v) {
 				if (listener != null) {
 					listener.onSendClick(ui.editText.getText().toString());
-					ui.editText.setText("");
 				}
 			}
 		});
diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupConstants.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupConstants.java
index 989844d0a72b0addbb4e3b8ba3f8a627b209dc68..def6c3f69bc660707c6ed088532dc7dab5908d29 100644
--- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupConstants.java
+++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupConstants.java
@@ -19,4 +19,9 @@ public interface PrivateGroupConstants {
 	 */
 	int MAX_GROUP_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
 
+	/**
+	 * The maximum length of a group invitation message in bytes.
+	 */
+	int MAX_GROUP_INVITATION_MSG_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
+
 }
diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
index 956a42344649b2d88cdbf0924d4c5ab1cda0986d..f2253c99a7c0c1999c1910146602e586ba4cf153 100644
--- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
+++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
@@ -18,6 +18,9 @@ public interface PrivateGroupManager extends MessageTracker {
 	@NotNull
 	ClientId getClientId();
 
+	/** Adds a new private group. */
+	GroupId addPrivateGroup(String name) throws DbException;
+
 	/** Removes a dissolved private group. */
 	void removePrivateGroup(GroupId g) throws DbException;
 
diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
index 6f40bd9c6c9c069c4e9de80d9cbfa7c887626b53..05c2d4021c4c766a2cdbcb89baea7e792a594aeb 100644
--- a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.security.GeneralSecurityException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.logging.Logger;
@@ -70,6 +71,21 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 		return CLIENT_ID;
 	}
 
+	@Override
+	public GroupId addPrivateGroup(String name) throws DbException {
+		PrivateGroup group;
+		Transaction txn = db.startTransaction(false);
+		try {
+			LocalAuthor a = identityManager.getLocalAuthor(txn);
+			group = privateGroupFactory.createPrivateGroup(name, a);
+			db.addGroup(txn, group.getGroup());
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return group.getId();
+	}
+
 	@Override
 	public void removePrivateGroup(GroupId g) throws DbException {
 
@@ -137,7 +153,24 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 	@NotNull
 	@Override
 	public Collection<PrivateGroup> getPrivateGroups() throws DbException {
-		return Collections.emptyList();
+		Collection<Group> groups;
+		Transaction txn = db.startTransaction(true);
+		try {
+			groups = db.getGroups(txn, getClientId());
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		try {
+			Collection<PrivateGroup> privateGroups =
+					new ArrayList<PrivateGroup>(groups.size());
+			for (Group g : groups) {
+				privateGroups.add(privateGroupFactory.parsePrivateGroup(g));
+			}
+			return privateGroups;
+		} catch (FormatException e) {
+			throw new DbException(e);
+		}
 	}
 
 	@Override