diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index 50b17cc2986cb5be7a47083856506109cd17f9c8..853ea69d7117e5c51187e8f22d15fabdfcc91357 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -46,7 +46,7 @@
 	<string name="exchanging_contact_details">Exchanging contact details\u2026</string>
 	<string name="codes_do_not_match">Codes do not match</string>
 	<string name="interfering">This could mean that someone is trying to interfere with your connection</string>
-	<string name="contact_added">Contact added</string>
+	<string name="contact_added_toast">Contact added</string>
 	<string name="done_button">Done</string>
 	<string name="messages_title">Messages</string>
 	<string name="no_messages">(No messages)</string>
@@ -81,4 +81,6 @@
 	<string name="no_contacts">You don\'t have any contacts. Add a contact now?</string>
 	<string name="add_button">Add</string>
 	<string name="cancel_button">Cancel</string>
+	<string name="message_sent_toast">Message sent</string>
+	<string name="post_sent_toast">Post sent</string>
 </resources>
diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
index f1e3316e44efd04af9909658d268bec37df825f2..1b64b76352c5f4c594380e9ae5bf17dcfdcebef7 100644
--- a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
@@ -21,6 +21,7 @@ import net.sf.briar.R;
 import net.sf.briar.android.invitation.AddContactActivity;
 import net.sf.briar.android.util.HorizontalBorder;
 import net.sf.briar.android.util.ListLoadingProgressBar;
+import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.Contact;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.android.DatabaseUiExecutor;
@@ -35,6 +36,7 @@ import net.sf.briar.api.db.event.DatabaseListener;
 import net.sf.briar.api.db.event.MessageAddedEvent;
 import net.sf.briar.api.db.event.MessageExpiredEvent;
 import net.sf.briar.api.lifecycle.LifecycleManager;
+import net.sf.briar.api.messaging.GroupId;
 import net.sf.briar.api.transport.ConnectionListener;
 import net.sf.briar.api.transport.ConnectionRegistry;
 import roboguice.activity.RoboActivity;
@@ -42,12 +44,15 @@ import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 
 public class ContactListActivity extends RoboActivity
-implements OnClickListener, DatabaseListener, ConnectionListener {
+implements OnClickListener, OnItemClickListener, DatabaseListener,
+ConnectionListener {
 
 	private static final Logger LOG =
 			Logger.getLogger(ContactListActivity.class.getName());
@@ -76,7 +81,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 		// Give me all the width and all the unused height
 		list.setLayoutParams(MATCH_WRAP_1);
 		list.setAdapter(adapter);
-		list.setOnItemClickListener(adapter);
+		list.setOnItemClickListener(this);
 		layout.addView(list);
 
 		// Show a progress bar while the list is loading
@@ -115,9 +120,10 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 						Long lastConnected = times.get(c.getId());
 						if(lastConnected == null) continue;
 						try {
+							GroupId inbox = db.getInboxGroupId(c.getId());
 							Collection<MessageHeader> headers =
 									db.getInboxMessageHeaders(c.getId());
-							displayContact(c, lastConnected, headers);
+							displayContact(c, lastConnected, inbox, headers);
 						} catch(NoSuchContactException e) {
 							if(LOG.isLoggable(INFO))
 								LOG.info("Contact removed");
@@ -151,7 +157,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 	}
 
 	private void displayContact(final Contact c, final long lastConnected,
-			final Collection<MessageHeader> headers) {
+			final GroupId inbox, final Collection<MessageHeader> headers) {
 		runOnUiThread(new Runnable() {
 			public void run() {
 				list.setVisibility(VISIBLE);
@@ -162,7 +168,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 				if(item != null) adapter.remove(item);
 				// Add a new item
 				adapter.add(new ContactListItem(c, connected, lastConnected,
-						headers));
+						inbox, headers));
 				adapter.sort(ItemComparator.INSTANCE);
 				adapter.notifyDataSetChanged();
 			}
@@ -182,7 +188,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 		int count = adapter.getCount();
 		for(int i = 0; i < count; i++) {
 			ContactListItem item = adapter.getItem(i);
-			if(item.getContactId().equals(c)) return item;
+			if(item.getContact().getId().equals(c)) return item;
 		}
 		return null; // Not found
 	}
@@ -198,6 +204,21 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 		startActivity(new Intent(this, AddContactActivity.class));
 	}
 
+	public void onItemClick(AdapterView<?> parent, View view, int position,
+			long id) {
+		ContactListItem item = adapter.getItem(position);
+		ContactId contactId = item.getContact().getId();
+		String contactName = item.getContact().getAuthor().getName();
+		GroupId inbox = item.getInboxGroupId();
+		AuthorId localAuthorId = item.getContact().getLocalAuthorId();
+		Intent i = new Intent(this, ConversationActivity.class);
+		i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
+		i.putExtra("net.sf.briar.CONTACT_NAME", contactName);
+		i.putExtra("net.sf.briar.GROUP_ID", inbox.getBytes());
+		i.putExtra("net.sf.briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
+		startActivity(i);
+	}
+
 	public void eventOccurred(DatabaseEvent e) {
 		if(e instanceof ContactAddedEvent) {
 			loadContacts();
@@ -257,7 +278,10 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 		runOnUiThread(new Runnable() {
 			public void run() {
 				ContactListItem item = findItem(c);
-				if(item != null) adapter.remove(item);
+				if(item != null) {
+					adapter.remove(item);
+					adapter.notifyDataSetChanged();
+				}
 			}
 		});
 	}
@@ -288,8 +312,9 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 		private static final ItemComparator INSTANCE = new ItemComparator();
 
 		public int compare(ContactListItem a, ContactListItem b) {
-			return String.CASE_INSENSITIVE_ORDER.compare(a.getContactName(),
-					b.getContactName());
+			String aName = a.getContact().getAuthor().getName();
+			String bName = b.getContact().getAuthor().getName();
+			return String.CASE_INSENSITIVE_ORDER.compare(aName, bName);
 		}
 	}
 }
diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java b/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java
index 2c3a988268cd7da93809aabecfad30d02ad89495..8f4dbe2dfe6e980e597b62068399294d185ffd80 100644
--- a/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java
@@ -8,21 +8,17 @@ import java.util.ArrayList;
 
 import net.sf.briar.R;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
 import android.text.Html;
 import android.text.format.DateUtils;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-class ContactListAdapter extends ArrayAdapter<ContactListItem>
-implements OnItemClickListener {
+class ContactListAdapter extends ArrayAdapter<ContactListItem> {
 
 	ContactListAdapter(Context ctx) {
 		super(ctx, android.R.layout.simple_expandable_list_item_1,
@@ -54,7 +50,7 @@ implements OnItemClickListener {
 		name.setMaxLines(1);
 		name.setPadding(0, 10, 10, 10);
 		int unread = item.getUnreadCount();
-		String contactName = item.getContactName();
+		String contactName = item.getContact().getAuthor().getName();
 		if(unread > 0) name.setText(contactName + " (" + unread + ")");
 		else name.setText(contactName);
 		layout.addView(name);
@@ -74,15 +70,4 @@ implements OnItemClickListener {
 
 		return layout;
 	}
-
-	public void onItemClick(AdapterView<?> parent, View view, int position,
-			long id) {
-		ContactListItem item = getItem(position);
-		Intent i = new Intent(getContext(), ConversationActivity.class);
-		i.putExtra("net.sf.briar.CONTACT_ID", item.getContactId().getInt());
-		i.putExtra("net.sf.briar.CONTACT_NAME", item.getContactName());
-		i.putExtra("net.sf.briar.LOCAL_AUTHOR_ID",
-				item.getLocalAuthorId().getBytes());
-		getContext().startActivity(i);
-	}
 }
diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListItem.java b/briar-android/src/net/sf/briar/android/contact/ContactListItem.java
index f075db23afc3d10eed7cd814ec4fcc744d76792d..ca137ba0f737142f24926469f94957c64a5c77dc 100644
--- a/briar-android/src/net/sf/briar/android/contact/ContactListItem.java
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListItem.java
@@ -2,15 +2,15 @@ package net.sf.briar.android.contact;
 
 import java.util.Collection;
 
-import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.Contact;
-import net.sf.briar.api.ContactId;
 import net.sf.briar.api.db.MessageHeader;
+import net.sf.briar.api.messaging.GroupId;
 
 // This class is not thread-safe
 class ContactListItem {
 
 	private final Contact contact;
+	private final GroupId inbox;
 	private boolean connected;
 	private long lastConnected;
 	private boolean empty;
@@ -18,8 +18,9 @@ class ContactListItem {
 	private int unread;
 
 	ContactListItem(Contact contact, boolean connected, long lastConnected,
-			Collection<MessageHeader> headers) {
+			GroupId inbox, Collection<MessageHeader> headers) {
 		this.contact = contact;
+		this.inbox = inbox;
 		this.connected = connected;
 		this.lastConnected = lastConnected;
 		setHeaders(headers);
@@ -41,12 +42,8 @@ class ContactListItem {
 		return contact;
 	}
 
-	ContactId getContactId() {
-		return contact.getId();
-	}
-
-	String getContactName() {
-		return contact.getAuthor().getName();
+	GroupId getInboxGroupId() {
+		return inbox;
 	}
 
 	long getLastConnected() {
@@ -65,10 +62,6 @@ class ContactListItem {
 		this.connected = connected;
 	}
 
-	AuthorId getLocalAuthorId() {
-		return contact.getLocalAuthorId();
-	}
-
 	boolean isEmpty() {
 		return empty;
 	}
diff --git a/briar-android/src/net/sf/briar/android/contact/ConversationActivity.java b/briar-android/src/net/sf/briar/android/contact/ConversationActivity.java
index 40f55c9333d0e92dc36a23c67ebcf84ec2407588..29d32232870ae4763186e8a59e914a024edd8ad8 100644
--- a/briar-android/src/net/sf/briar/android/contact/ConversationActivity.java
+++ b/briar-android/src/net/sf/briar/android/contact/ConversationActivity.java
@@ -18,6 +18,7 @@ import javax.inject.Inject;
 import net.sf.briar.R;
 import net.sf.briar.android.util.HorizontalBorder;
 import net.sf.briar.android.util.ListLoadingProgressBar;
+import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.db.DatabaseComponent;
@@ -60,6 +61,7 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
 	@Inject private volatile LifecycleManager lifecycleManager;
 	private volatile ContactId contactId = null;
 	private volatile GroupId groupId = null;
+	private volatile AuthorId localAuthorId = null;
 
 	@Override
 	public void onCreate(Bundle state) {
@@ -72,6 +74,12 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
 		contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
 		if(contactName == null) throw new IllegalStateException();
 		setTitle(contactName);
+		byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
+		if(b == null) throw new IllegalStateException();
+		groupId = new GroupId(b);
+		b = i.getByteArrayExtra("net.sf.briar.LOCAL_AUTHOR_ID");
+		if(b == null) throw new IllegalStateException();
+		localAuthorId = new AuthorId(b);
 
 		LinearLayout layout = new LinearLayout(this);
 		layout.setLayoutParams(MATCH_MATCH);
@@ -116,7 +124,6 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
 				try {
 					lifecycleManager.waitForDatabase();
 					long now = System.currentTimeMillis();
-					groupId = db.getInboxGroup(contactId);
 					Collection<MessageHeader> headers =
 							db.getInboxMessageHeaders(contactId);
 					long duration = System.currentTimeMillis() - now;
@@ -201,8 +208,8 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
 				});
 			}
 		} else if(e instanceof MessageAddedEvent) {
-			ContactId source = ((MessageAddedEvent) e).getContactId();
-			if(source == null || source.equals(contactId)) {
+			GroupId g = ((MessageAddedEvent) e).getGroup().getId();
+			if(g.equals(groupId)) {
 				if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
 				loadHeaders();
 			}
@@ -214,8 +221,9 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
 
 	public void onClick(View view) {
 		Intent i = new Intent(this, WritePrivateMessageActivity.class);
-		i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
+		i.putExtra("net.sf.briar.CONTACT_NAME", contactName);
 		i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
+		i.putExtra("net.sf.briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
 		startActivity(i);
 	}
 
@@ -229,7 +237,8 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
 		Intent i = new Intent(this, ReadPrivateMessageActivity.class);
 		i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
 		i.putExtra("net.sf.briar.CONTACT_NAME", contactName);
-		i.putExtra("net.sf.briar.GROUP_ID", header.getGroupId().getBytes());
+		i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
+		i.putExtra("net.sf.briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
 		i.putExtra("net.sf.briar.AUTHOR_NAME", header.getAuthor().getName());
 		i.putExtra("net.sf.briar.MESSAGE_ID", header.getId().getBytes());
 		i.putExtra("net.sf.briar.CONTENT_TYPE", header.getContentType());
diff --git a/briar-android/src/net/sf/briar/android/contact/ReadPrivateMessageActivity.java b/briar-android/src/net/sf/briar/android/contact/ReadPrivateMessageActivity.java
index 6c970f4bbf16795612f11626952165f8d41f3855..92c8f01da337bddea21b9272cfe0843e8efe30c0 100644
--- a/briar-android/src/net/sf/briar/android/contact/ReadPrivateMessageActivity.java
+++ b/briar-android/src/net/sf/briar/android/contact/ReadPrivateMessageActivity.java
@@ -20,7 +20,7 @@ import javax.inject.Inject;
 import net.sf.briar.R;
 import net.sf.briar.android.util.HorizontalBorder;
 import net.sf.briar.android.util.HorizontalSpace;
-import net.sf.briar.api.ContactId;
+import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
@@ -50,8 +50,9 @@ implements OnClickListener {
 	private static final Logger LOG =
 			Logger.getLogger(ReadPrivateMessageActivity.class.getName());
 
-	private ContactId contactId = null;
-	private boolean read;
+	private String contactName = null;
+	private AuthorId localAuthorId = null;
+	private boolean read = false;
 	private ImageButton readButton = null, prevButton = null, nextButton = null;
 	private ImageButton replyButton = null;
 	private TextView content = null;
@@ -69,15 +70,15 @@ implements OnClickListener {
 		super.onCreate(state);
 
 		Intent i = getIntent();
-		int id = i.getIntExtra("net.sf.briar.CONTACT_ID", -1);
-		if(id == -1) throw new IllegalStateException();
-		contactId = new ContactId(id);
-		String contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
+		contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
 		if(contactName == null) throw new IllegalStateException();
 		setTitle(contactName);
+		byte[] b = i.getByteArrayExtra("net.sf.briar.LOCAL_AUTHOR_ID");
+		if(b == null) throw new IllegalStateException();
+		localAuthorId = new AuthorId(b);
 		String authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
 		if(authorName == null) throw new IllegalStateException();
-		byte[] b = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
+		b = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
 		if(b == null) throw new IllegalStateException();
 		messageId = new MessageId(b);
 		b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
@@ -266,8 +267,10 @@ implements OnClickListener {
 			finish();
 		} else if(view == replyButton) {
 			Intent i = new Intent(this, WritePrivateMessageActivity.class);
-			i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
+			i.putExtra("net.sf.briar.CONTACT_NAME", contactName);
 			i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
+			i.putExtra("net.sf.briar.LOCAL_AUTHOR_ID",
+					localAuthorId.getBytes());
 			i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
 			i.putExtra("net.sf.briar.TIMESTAMP", timestamp);
 			startActivity(i);
diff --git a/briar-android/src/net/sf/briar/android/contact/WritePrivateMessageActivity.java b/briar-android/src/net/sf/briar/android/contact/WritePrivateMessageActivity.java
index c84b21622122795bb4ab1fe831d56f0eba2ac5e9..b8df2aad6d2286106da0876a90223bb5c7245ffe 100644
--- a/briar-android/src/net/sf/briar/android/contact/WritePrivateMessageActivity.java
+++ b/briar-android/src/net/sf/briar/android/contact/WritePrivateMessageActivity.java
@@ -5,6 +5,7 @@ import static android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
 import static android.view.Gravity.CENTER_VERTICAL;
 import static android.widget.LinearLayout.HORIZONTAL;
 import static android.widget.LinearLayout.VERTICAL;
+import static android.widget.Toast.LENGTH_LONG;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
@@ -19,10 +20,10 @@ import javax.inject.Inject;
 
 import net.sf.briar.R;
 import net.sf.briar.android.util.HorizontalSpace;
-import net.sf.briar.api.Contact;
-import net.sf.briar.api.ContactId;
+import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.DatabaseUiExecutor;
+import net.sf.briar.api.crypto.CryptoExecutor;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.NoSuchContactException;
@@ -44,6 +45,7 @@ import android.widget.EditText;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+import android.widget.Toast;
 
 public class WritePrivateMessageActivity extends RoboActivity
 implements OnClickListener {
@@ -59,12 +61,13 @@ implements OnClickListener {
 	@Inject private volatile DatabaseComponent db;
 	@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
 	@Inject private volatile LifecycleManager lifecycleManager;
+	@Inject @CryptoExecutor private volatile Executor cryptoExecutor;
 	@Inject private volatile MessageFactory messageFactory;
-	private volatile ContactId contactId = null;
+	private volatile String contactName = null;
 	private volatile GroupId groupId = null;
+	private volatile AuthorId localAuthorId = null;
 	private volatile MessageId parentId = null;
 	private volatile long timestamp = -1;
-	private volatile Contact contact = null;
 	private volatile LocalAuthor localAuthor = null;
 	private volatile Group group = null;
 
@@ -73,12 +76,14 @@ implements OnClickListener {
 		super.onCreate(state);
 
 		Intent i = getIntent();
-		int id = i.getIntExtra("net.sf.briar.CONTACT_ID", -1);
-		if(id == -1) throw new IllegalStateException();
-		contactId = new ContactId(id);
+		contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
+		if(contactName == null) throw new IllegalStateException();
 		byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
 		if(b == null) throw new IllegalStateException();
 		groupId = new GroupId(b);
+		b = i.getByteArrayExtra("net.sf.briar.LOCAL_AUTHOR_ID");
+		if(b == null) throw new IllegalStateException();
+		localAuthorId = new AuthorId(b);
 		b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
 		if(b != null) parentId = new MessageId(b);
 		timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
@@ -111,7 +116,8 @@ implements OnClickListener {
 		to = new TextView(this);
 		to.setTextSize(18);
 		to.setPadding(10, 0, 10, 10);
-		to.setText(R.string.to);
+		String format = getResources().getString(R.string.format_to);
+		to.setText(String.format(format, contactName));
 		layout.addView(to);
 
 		content = new EditText(this);
@@ -127,22 +133,21 @@ implements OnClickListener {
 	@Override
 	public void onResume() {
 		super.onResume();
-		loadAuthorsAndGroup();
+		loadAuthorAndGroup();
 	}
 
-	private void loadAuthorsAndGroup() {
+	private void loadAuthorAndGroup() {
 		dbUiExecutor.execute(new Runnable() {
 			public void run() {
 				try {
 					lifecycleManager.waitForDatabase();
 					long now = System.currentTimeMillis();
-					contact = db.getContact(contactId);
-					localAuthor = db.getLocalAuthor(contact.getLocalAuthorId());
+					localAuthor = db.getLocalAuthor(localAuthorId);
 					group = db.getGroup(groupId);
 					long duration = System.currentTimeMillis() - now;
 					if(LOG.isLoggable(INFO))
 						LOG.info("Load took " + duration + " ms");
-					displayAuthors();
+					displayLocalAuthor();
 				} catch(NoSuchContactException e) {
 					finish();
 				} catch(NoSuchSubscriptionException e) {
@@ -158,42 +163,54 @@ implements OnClickListener {
 		});
 	}
 
-	private void displayAuthors() {
+	private void displayLocalAuthor() {
 		runOnUiThread(new Runnable() {
 			public void run() {
 				Resources res = getResources();
 				String format = res.getString(R.string.format_from);
 				String name = localAuthor.getName();
 				from.setText(String.format(format, name));
-				format = res.getString(R.string.format_to);
-				name = contact.getAuthor().getName();
-				to.setText(String.format(format, name));
 				sendButton.setEnabled(true);
 			}
 		});
 	}
 
 	public void onClick(View view) {
-		if(contact == null || localAuthor == null)
-			throw new IllegalStateException();
+		if(localAuthor == null) throw new IllegalStateException();
 		try {
-			storeMessage(content.getText().toString().getBytes("UTF-8"));
+			createMessage(content.getText().toString().getBytes("UTF-8"));
 		} catch(UnsupportedEncodingException e) {
 			throw new RuntimeException(e);
 		}
+		Toast.makeText(this, R.string.message_sent_toast, LENGTH_LONG).show();
 		finish();
 	}
 
-	private void storeMessage(final byte[] body) {
+	private void createMessage(final byte[] body) {
+		cryptoExecutor.execute(new Runnable() {
+			public void run() {
+				// Don't use an earlier timestamp than the parent
+				long time = System.currentTimeMillis();
+				time = Math.max(time, timestamp + 1);
+				Message m;
+				try {
+					m = messageFactory.createAnonymousMessage(parentId, group,
+							"text/plain", time, body);
+				} catch(GeneralSecurityException e) {
+					throw new RuntimeException(e);
+				} catch(IOException e) {
+					throw new RuntimeException(e);
+				}
+				storeMessage(m);
+			}
+		});
+	}
+
+	private void storeMessage(final Message m) {
 		dbUiExecutor.execute(new Runnable() {
 			public void run() {
 				try {
 					lifecycleManager.waitForDatabase();
-					// Don't use an earlier timestamp than the parent
-					long time = System.currentTimeMillis();
-					time = Math.max(time, timestamp + 1);
-					Message m = messageFactory.createAnonymousMessage(parentId,
-							group, "text/plain", time, body);
 					long now = System.currentTimeMillis();
 					db.addLocalMessage(m);
 					long duration = System.currentTimeMillis() - now;
@@ -202,14 +219,10 @@ implements OnClickListener {
 				} catch(DbException e) {
 					if(LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
-				} catch(GeneralSecurityException e) {
-					throw new RuntimeException(e);
 				} catch(InterruptedException e) {
 					if(LOG.isLoggable(INFO))
 						LOG.info("Interrupted while waiting for database");
 					Thread.currentThread().interrupt();
-				} catch(IOException e) {
-					throw new RuntimeException(e);
 				}
 			}
 		});
diff --git a/briar-android/src/net/sf/briar/android/groups/ReadGroupPostActivity.java b/briar-android/src/net/sf/briar/android/groups/ReadGroupPostActivity.java
index 34da4493e83b97f790f3bcfadab55c1817b31470..ed44aa9835a14a8a4429137558e1694fdb4d0f03 100644
--- a/briar-android/src/net/sf/briar/android/groups/ReadGroupPostActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/ReadGroupPostActivity.java
@@ -76,11 +76,11 @@ implements OnClickListener {
 		b = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
 		if(b == null) throw new IllegalStateException();
 		messageId = new MessageId(b);
-		String authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
 		String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
 		if(contentType == null) throw new IllegalStateException();
 		timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
 		if(timestamp == -1) throw new IllegalStateException();
+		String authorName = i.getStringExtra("net.sf.briar.AUTHOR_NAME");
 
 		if(state == null) {
 			read = false;
diff --git a/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
index 21e4860152e29ee73cbc184ae3026a661aa16b72..190c0f80ae4b3faf4aef9075e373219f6631b656 100644
--- a/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
@@ -5,11 +5,13 @@ import static android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
 import static android.view.Gravity.CENTER_VERTICAL;
 import static android.widget.LinearLayout.HORIZONTAL;
 import static android.widget.LinearLayout.VERTICAL;
+import static android.widget.Toast.LENGTH_LONG;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 import java.util.Collection;
 import java.util.concurrent.Executor;
@@ -27,6 +29,7 @@ import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.CryptoExecutor;
 import net.sf.briar.api.crypto.KeyParser;
 import net.sf.briar.api.crypto.PrivateKey;
 import net.sf.briar.api.db.DatabaseComponent;
@@ -51,6 +54,7 @@ import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.Spinner;
 import android.widget.TextView;
+import android.widget.Toast;
 
 public class WriteGroupPostActivity extends RoboActivity
 implements OnItemSelectedListener, OnClickListener {
@@ -58,8 +62,6 @@ implements OnItemSelectedListener, OnClickListener {
 	private static final Logger LOG =
 			Logger.getLogger(WriteGroupPostActivity.class.getName());
 
-	@Inject private CryptoComponent crypto;
-	@Inject private MessageFactory messageFactory;
 	private LocalAuthorSpinnerAdapter adapter = null;
 	private Spinner spinner = null;
 	private ImageButton sendButton = null;
@@ -72,6 +74,9 @@ implements OnItemSelectedListener, OnClickListener {
 	@Inject private volatile DatabaseComponent db;
 	@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
 	@Inject private volatile LifecycleManager lifecycleManager;
+	@Inject @CryptoExecutor private volatile Executor cryptoExecutor;
+	@Inject private volatile CryptoComponent crypto;
+	@Inject private volatile MessageFactory messageFactory;
 	private volatile MessageId parentId = null;
 	private volatile long timestamp = -1;
 	private volatile LocalAuthor localAuthor = null;
@@ -85,7 +90,7 @@ implements OnItemSelectedListener, OnClickListener {
 		byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
 		if(b == null) throw new IllegalStateException();
 		groupId = new GroupId(b);
-		
+
 		b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
 		if(b != null) parentId = new MessageId(b);
 		timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
@@ -207,62 +212,67 @@ implements OnItemSelectedListener, OnClickListener {
 			byte[] b =  localAuthorId.getBytes();
 			state.putByteArray("net.sf.briar.LOCAL_AUTHOR_ID", b);
 		}
-		if(groupId != null) {
-			byte[] b =  groupId.getBytes();
-			state.putByteArray("net.sf.briar.GROUP_ID", b);
-		}
 	}
 
 	public void onItemSelected(AdapterView<?> parent, View view, int position,
 			long id) {
-			LocalAuthorItem item = adapter.getItem(position);
-			if(item == LocalAuthorItem.ANONYMOUS) {
-				localAuthor = null;
-				localAuthorId = null;
-			} else if(item == LocalAuthorItem.NEW) {
-				localAuthor = null;
-				localAuthorId = null;
-				startActivity(new Intent(this, CreateIdentityActivity.class));
-			} else {
-				localAuthor = item.getLocalAuthor();
-				localAuthorId = localAuthor.getId();
-			}
+		LocalAuthorItem item = adapter.getItem(position);
+		if(item == LocalAuthorItem.ANONYMOUS) {
+			localAuthor = null;
+			localAuthorId = null;
+		} else if(item == LocalAuthorItem.NEW) {
+			localAuthor = null;
+			localAuthorId = null;
+			startActivity(new Intent(this, CreateIdentityActivity.class));
+		} else {
+			localAuthor = item.getLocalAuthor();
+			localAuthorId = localAuthor.getId();
+		}
 	}
 
 	public void onNothingSelected(AdapterView<?> parent) {
-			localAuthor = null;
-			localAuthorId = null;
+		localAuthor = null;
+		localAuthorId = null;
 	}
 
 	public void onClick(View view) {
 		if(group == null) throw new IllegalStateException();
 		try {
-			byte[] b = content.getText().toString().getBytes("UTF-8");
-			storeMessage(createMessage(b));
-		} catch(GeneralSecurityException e) {
-			throw new RuntimeException(e);
-		} catch(IOException e) {
+			createMessage(content.getText().toString().getBytes("UTF-8"));
+		} catch(UnsupportedEncodingException e) {
 			throw new RuntimeException(e);
 		}
+		Toast.makeText(this, R.string.post_sent_toast, LENGTH_LONG).show();
 		finish();
 	}
 
-	// FIXME: This should happen on a CryptoExecutor thread
-	private Message createMessage(byte[] body) throws IOException,
-	GeneralSecurityException {
-		// Don't use an earlier timestamp than the parent
-		long time = System.currentTimeMillis();
-		time = Math.max(time, timestamp + 1);
-		if(localAuthor == null) {
-			return messageFactory.createAnonymousMessage(parentId, group,
-					"text/plain", time, body);
-		} else {
-			KeyParser keyParser = crypto.getSignatureKeyParser();
-			byte[] authorKeyBytes = localAuthor.getPrivateKey();
-			PrivateKey authorKey = keyParser.parsePrivateKey(authorKeyBytes);
-			return messageFactory.createPseudonymousMessage(parentId,
-					group, localAuthor, authorKey, "text/plain", time, body);
-		}
+	private void createMessage(final byte[] body) {
+		cryptoExecutor.execute(new Runnable() {
+			public void run() {
+				// Don't use an earlier timestamp than the parent
+				long time = System.currentTimeMillis();
+				time = Math.max(time, timestamp + 1);
+				Message m;
+				try {
+					if(localAuthor == null) {
+						m = messageFactory.createAnonymousMessage(parentId,
+								group, "text/plain", time, body);
+					} else {
+						KeyParser keyParser = crypto.getSignatureKeyParser();
+						byte[] b = localAuthor.getPrivateKey();
+						PrivateKey authorKey = keyParser.parsePrivateKey(b);
+						m = messageFactory.createPseudonymousMessage(parentId,
+								group, localAuthor, authorKey, "text/plain",
+								time, body);
+					}
+				} catch(GeneralSecurityException e) {
+					throw new RuntimeException(e);
+				} catch(IOException e) {
+					throw new RuntimeException(e);
+				}
+				storeMessage(m);
+			}
+		});
 	}
 
 	private void storeMessage(final Message m) {
diff --git a/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java b/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
index 25fe6b53cf79209aff48963c774aec091a52eb03..64ebbcad80791981ae6cb472a4bee0155da5bf9d 100644
--- a/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
+++ b/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
@@ -169,7 +169,7 @@ implements InvitationListener {
 	}
 
 	private void showToastAndFinish() {
-		Toast.makeText(this, R.string.contact_added, LENGTH_LONG).show();
+		Toast.makeText(this, R.string.contact_added_toast, LENGTH_LONG).show();
 		finish();
 	}