diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index ba01be5bfcd1ad5e5581f9f0089c6eb7aaed7674..6b12578c8ce86fa3baf171ca742e1740819d6be8 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -48,5 +48,9 @@
 			android:name=".android.messages.ConversationListActivity"
 			android:label="@string/messages_title" >
 		</activity>
+		<activity
+			android:name=".android.messages.ReadMessageActivity"
+			android:label="@string/messages_title" >
+		</activity>
 	</application>
 </manifest>
diff --git a/briar-android/res/drawable-hdpi/social_reply.png b/briar-android/res/drawable-hdpi/social_reply.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0fcbf356e9d6363ca546c2d803f15f01cbcb21d
Binary files /dev/null and b/briar-android/res/drawable-hdpi/social_reply.png differ
diff --git a/briar-android/res/drawable-mdpi/social_reply.png b/briar-android/res/drawable-mdpi/social_reply.png
new file mode 100644
index 0000000000000000000000000000000000000000..75723bca5710f2c2f62fb99c5413ca143a049109
Binary files /dev/null and b/briar-android/res/drawable-mdpi/social_reply.png differ
diff --git a/briar-android/res/drawable-xhdpi/social_reply.png b/briar-android/res/drawable-xhdpi/social_reply.png
new file mode 100644
index 0000000000000000000000000000000000000000..1dc01d7df2c545b5cd55a8cd8da5768e7bccf80c
Binary files /dev/null and b/briar-android/res/drawable-xhdpi/social_reply.png differ
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 a6e7e3f32133dbf091c4b20b71f13534e019b6e1..d0d7ace2fd4ff981747460c4ba862834f7c43c0a 100644
--- a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
@@ -88,8 +88,6 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 		// Bind to the service so we can wait for the DB to be opened
 		bindService(new Intent(BriarService.class.getName()),
 				serviceConnection, 0);
-		// Load the contact list from the DB
-		reloadContactList();
 
 		// Add some fake contacts to the database in a background thread
 		// FIXME: Remove this
@@ -118,6 +116,12 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
 		});
 	}
 
+	@Override
+	public void onResume() {
+		super.onResume();
+		reloadContactList();
+	}
+
 	@Override
 	public void onDestroy() {
 		super.onDestroy();
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 1521492c9360d2e3ab9f9433c74a2eb36633f578..45bc63639dff68091b7eac35a8ed0bc876b8039d 100644
--- a/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java
@@ -1,6 +1,6 @@
 package net.sf.briar.android.contact;
 
-import static android.view.Gravity.CENTER;
+import static android.view.Gravity.CENTER_VERTICAL;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.widget.LinearLayout.HORIZONTAL;
 
@@ -35,7 +35,7 @@ implements OnItemClickListener {
 		Context ctx = getContext();
 		LinearLayout layout = new LinearLayout(ctx);
 		layout.setOrientation(HORIZONTAL);
-		layout.setGravity(CENTER);
+		layout.setGravity(CENTER_VERTICAL);
 
 		ImageView bulb = new ImageView(ctx);
 		bulb.setPadding(5, 5, 5, 5);
diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java b/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java
index 82ae8a74feac97838fa8cd4f07e059b741dfcb5d..5188bbbe7e303dbcd0e605ffa2c1658ff1614d4d 100644
--- a/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java
+++ b/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java
@@ -28,6 +28,8 @@ 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.Button;
 import android.widget.LinearLayout;
 import android.widget.LinearLayout.LayoutParams;
@@ -36,7 +38,7 @@ import android.widget.ListView;
 import com.google.inject.Inject;
 
 public class ConversationActivity extends BriarActivity
-implements OnClickListener, DatabaseListener {
+implements DatabaseListener, OnClickListener, OnItemClickListener {
 
 	private static final Logger LOG =
 			Logger.getLogger(ConversationActivity.class.getName());
@@ -48,6 +50,7 @@ implements OnClickListener, DatabaseListener {
 	@Inject @DatabaseExecutor private Executor dbExecutor;
 
 	private ConversationAdapter adapter = null;
+	private String contactName = null;
 	private volatile ContactId contactId = null;
 
 	@Override
@@ -55,12 +58,12 @@ implements OnClickListener, DatabaseListener {
 		super.onCreate(null);
 
 		Intent i = getIntent();
+		contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
+		if(contactName == null) throw new IllegalStateException();
+		setTitle(contactName);
 		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");
-		if(contactName == null) throw new IllegalStateException();
-		setTitle(contactName);
 
 		LinearLayout layout = new LinearLayout(this);
 		layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
@@ -72,7 +75,7 @@ implements OnClickListener, DatabaseListener {
 		// Give me all the width and all the unused height
 		list.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT, 1f));
 		list.setAdapter(adapter);
-		list.setOnItemClickListener(adapter);
+		list.setOnItemClickListener(this);
 		layout.addView(list);
 
 		Button composeButton = new Button(this);
@@ -92,7 +95,11 @@ implements OnClickListener, DatabaseListener {
 		// Bind to the service so we can wait for the DB to be opened
 		bindService(new Intent(BriarService.class.getName()),
 				serviceConnection, 0);
-		// Load the message headers from the DB
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
 		reloadMessageHeaders();
 	}
 
@@ -103,10 +110,6 @@ implements OnClickListener, DatabaseListener {
 		unbindService(serviceConnection);
 	}
 
-	public void onClick(View view) {
-		// FIXME: Hook this button up to an activity
-	}
-
 	public void eventOccurred(DatabaseEvent e) {
 		if(e instanceof MessageAddedEvent) {
 			if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
@@ -152,4 +155,20 @@ implements OnClickListener, DatabaseListener {
 			}
 		});
 	}
+
+	public void onClick(View view) {
+		// FIXME: Hook this button up to an activity
+	}
+
+	public void onItemClick(AdapterView<?> parent, View view, int position,
+			long id) {
+		PrivateMessageHeader item = adapter.getItem(position);
+		Intent i = new Intent(this, ReadMessageActivity.class);
+		i.putExtra("net.sf.briar.CONTACT_NAME", contactName);
+		i.putExtra("net.sf.briar.MESSAGE_ID", item.getId().getBytes());
+		i.putExtra("net.sf.briar.CONTENT_TYPE", item.getContentType());
+		i.putExtra("net.sf.briar.TIMESTAMP", item.getTimestamp());
+		i.putExtra("net.sf.briar.STARRED", item.isStarred());
+		startActivity(i);
+	}
 }
diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationAdapter.java b/briar-android/src/net/sf/briar/android/messages/ConversationAdapter.java
index 31fdd1dcc84606d179b171ef7192393583a6f65a..8f2b7a96b22bc31401e969a43ac882a32d01ab54 100644
--- a/briar-android/src/net/sf/briar/android/messages/ConversationAdapter.java
+++ b/briar-android/src/net/sf/briar/android/messages/ConversationAdapter.java
@@ -1,7 +1,7 @@
 package net.sf.briar.android.messages;
 
 import static android.graphics.Typeface.BOLD;
-import static android.view.Gravity.CENTER;
+import static android.view.Gravity.CENTER_VERTICAL;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.widget.LinearLayout.HORIZONTAL;
 import static java.text.DateFormat.SHORT;
@@ -14,16 +14,13 @@ import android.content.Context;
 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.LinearLayout.LayoutParams;
 import android.widget.TextView;
 
-class ConversationAdapter extends ArrayAdapter<PrivateMessageHeader>
-implements OnItemClickListener {
+class ConversationAdapter extends ArrayAdapter<PrivateMessageHeader> {
 
 	ConversationAdapter(Context ctx) {
 		super(ctx, android.R.layout.simple_expandable_list_item_1,
@@ -36,12 +33,11 @@ implements OnItemClickListener {
 		Context ctx = getContext();
 		LinearLayout layout = new LinearLayout(ctx);
 		layout.setOrientation(HORIZONTAL);
-		layout.setGravity(CENTER);
+		layout.setGravity(CENTER_VERTICAL);
 
 		ImageView star = new ImageView(ctx);
 		star.setPadding(5, 5, 5, 5);
-		if(item.getStarred())
-			star.setImageResource(R.drawable.rating_important);
+		if(item.isStarred()) star.setImageResource(R.drawable.rating_important);
 		else star.setImageResource(R.drawable.rating_not_important);
 		layout.addView(star);
 
@@ -57,7 +53,8 @@ implements OnItemClickListener {
 		subject.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT,
 				1));
 		subject.setTextSize(14);
-		if(!item.getRead()) subject.setTypeface(null, BOLD);
+		subject.setMaxLines(2);
+		if(!item.isRead()) subject.setTypeface(null, BOLD);
 		subject.setText(item.getSubject());
 		layout.addView(subject);
 
@@ -70,9 +67,4 @@ implements OnItemClickListener {
 
 		return layout;
 	}
-
-	public void onItemClick(AdapterView<?> parent, View view, int position,
-			long id) {
-		// FIXME
-	}
 }
diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationItem.java b/briar-android/src/net/sf/briar/android/messages/ConversationItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..2640bc105efcf064bd501251b0e8fd1fe81086cb
--- /dev/null
+++ b/briar-android/src/net/sf/briar/android/messages/ConversationItem.java
@@ -0,0 +1,67 @@
+package net.sf.briar.android.messages;
+
+import net.sf.briar.api.db.PrivateMessageHeader;
+import net.sf.briar.api.messaging.MessageId;
+
+class ConversationItem {
+
+	private final PrivateMessageHeader header;
+	private final byte[] body;
+	private final boolean expanded;
+
+	ConversationItem(PrivateMessageHeader header) {
+		this.header = header;
+		body = null;
+		expanded = false;
+	}
+
+	// Collapse an existing item
+	ConversationItem(ConversationItem item) {
+		this.header = item.header;
+		body = null;
+		expanded = false;
+	}
+
+	// Expand an existing item
+	ConversationItem(ConversationItem item, byte[] body) {
+		this.header = item.header;
+		this.body = body;
+		expanded = true;
+	}
+
+	MessageId getId() {
+		return header.getId();
+	}
+
+	String getContentType() {
+		return header.getContentType();
+	}
+
+	String getSubject() {
+		return header.getSubject();
+	}
+
+	long getTimestamp() {
+		return header.getTimestamp();
+	}
+
+	boolean isRead() {
+		return header.isRead();
+	}
+
+	boolean isStarred() {
+		return header.isStarred();
+	}
+
+	boolean isIncoming() {
+		return header.isIncoming();
+	}
+
+	byte[] getBody() {
+		return body;
+	}
+
+	boolean isExpanded() {
+		return expanded;
+	}
+}
diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java b/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java
index b54a41827e8eac042306b726b5c5e5c812924aee..a0d337641a2ae5e6d926ca7e1c598b5ffad71b8f 100644
--- a/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java
+++ b/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java
@@ -92,8 +92,6 @@ implements OnClickListener, DatabaseListener {
 		// Bind to the service so we can wait for the DB to be opened
 		bindService(new Intent(BriarService.class.getName()),
 				serviceConnection, 0);
-		// Load the message headers from the DB
-		reloadMessageHeaders();
 
 		// Add some fake messages to the database in a background thread
 		// FIXME: Remove this
@@ -115,13 +113,10 @@ implements OnClickListener, DatabaseListener {
 								"text/plain",
 								"First message is short".getBytes("UTF-8"));
 						db.addLocalPrivateMessage(m, contactId);
-						db.setReadFlag(m.getId(), true);
-						db.setStarredFlag(m.getId(), true);
-						Thread.sleep(1000);
 						m = messageFactory.createPrivateMessage(m.getId(),
 								"image/jpeg", new byte[1000]);
 						db.receiveMessage(contactId, m);
-						Thread.sleep(1000);
+						db.setReadFlag(m.getId(), true);
 						m = messageFactory.createPrivateMessage(m.getId(),
 								"text/plain",
 								("Third message is quite long to test line"
@@ -129,6 +124,7 @@ implements OnClickListener, DatabaseListener {
 								+ " all that fun stuff").getBytes("UTF-8"));
 						db.addLocalPrivateMessage(m, contactId);
 						db.setReadFlag(m.getId(), true);
+						db.setStarredFlag(m.getId(), true);
 					}
 				} catch(DbException e) {
 					if(LOG.isLoggable(WARNING))
@@ -148,6 +144,12 @@ implements OnClickListener, DatabaseListener {
 		});
 	}
 
+	@Override
+	public void onResume() {
+		super.onResume();
+		reloadMessageHeaders();
+	}
+
 	@Override
 	public void onDestroy() {
 		super.onDestroy();
diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationListAdapter.java b/briar-android/src/net/sf/briar/android/messages/ConversationListAdapter.java
index 234af5266ebee97f2c156057edc33287b35062b7..42b68a5d0dfd6d1054f3f171683f32d52ae28421 100644
--- a/briar-android/src/net/sf/briar/android/messages/ConversationListAdapter.java
+++ b/briar-android/src/net/sf/briar/android/messages/ConversationListAdapter.java
@@ -1,7 +1,7 @@
 package net.sf.briar.android.messages;
 
 import static android.graphics.Typeface.BOLD;
-import static android.view.Gravity.CENTER;
+import static android.view.Gravity.CENTER_VERTICAL;
 import static android.view.Gravity.LEFT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.widget.LinearLayout.HORIZONTAL;
@@ -38,12 +38,11 @@ implements OnItemClickListener {
 		Context ctx = getContext();
 		LinearLayout layout = new LinearLayout(ctx);
 		layout.setOrientation(HORIZONTAL);
-		layout.setGravity(CENTER);
+		layout.setGravity(CENTER_VERTICAL);
 
 		ImageView star = new ImageView(ctx);
 		star.setPadding(5, 5, 5, 5);
-		if(item.getStarred())
-			star.setImageResource(R.drawable.rating_important);
+		if(item.isStarred()) star.setImageResource(R.drawable.rating_important);
 		else star.setImageResource(R.drawable.rating_not_important);
 		layout.addView(star);
 
@@ -61,7 +60,8 @@ implements OnItemClickListener {
 
 		TextView subject = new TextView(ctx);
 		subject.setTextSize(14);
-		if(!item.getRead()) subject.setTypeface(null, BOLD);
+		subject.setMaxLines(2);
+		if(!item.isRead()) subject.setTypeface(null, BOLD);
 		subject.setText(item.getSubject());
 		innerLayout.addView(subject);
 		layout.addView(innerLayout);
diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationListItem.java b/briar-android/src/net/sf/briar/android/messages/ConversationListItem.java
index 24b2e63a44c12544c84077da0a3d19a015820ba5..d0842efca03d8ed00093c4ea0fc413d423e08020 100644
--- a/briar-android/src/net/sf/briar/android/messages/ConversationListItem.java
+++ b/briar-android/src/net/sf/briar/android/messages/ConversationListItem.java
@@ -23,10 +23,10 @@ class ConversationListItem {
 		subject = headers.get(0).getSubject();
 		timestamp = headers.get(0).getTimestamp();
 		length = headers.size();
-		boolean allRead = false, anyStarred = false;
+		boolean allRead = true, anyStarred = false;
 		for(PrivateMessageHeader h : headers) {
-			allRead &= h.getRead();
-			anyStarred |= h.getStarred();
+			allRead &= h.isRead();
+			anyStarred |= h.isStarred();
 		}
 		read = allRead;
 		starred = anyStarred;
@@ -48,11 +48,11 @@ class ConversationListItem {
 		return timestamp;
 	}
 
-	boolean getRead() {
+	boolean isRead() {
 		return read;
 	}
 
-	boolean getStarred() {
+	boolean isStarred() {
 		return starred;
 	}
 
diff --git a/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java b/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea3f0dffd0a2058c6d55b9327b4e78e0f10c985d
--- /dev/null
+++ b/briar-android/src/net/sf/briar/android/messages/ReadMessageActivity.java
@@ -0,0 +1,201 @@
+package net.sf.briar.android.messages;
+
+import static android.view.Gravity.CENTER_VERTICAL;
+import static android.view.Gravity.RIGHT;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.widget.LinearLayout.HORIZONTAL;
+import static android.widget.LinearLayout.VERTICAL;
+import static java.text.DateFormat.SHORT;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import net.sf.briar.R;
+import net.sf.briar.android.BriarActivity;
+import net.sf.briar.android.BriarService;
+import net.sf.briar.android.BriarService.BriarServiceConnection;
+import net.sf.briar.api.db.DatabaseComponent;
+import net.sf.briar.api.db.DatabaseExecutor;
+import net.sf.briar.api.db.DbException;
+import net.sf.briar.api.messaging.MessageId;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+
+import com.google.inject.Inject;
+
+public class ReadMessageActivity extends BriarActivity
+implements OnClickListener {
+
+	private static final Logger LOG =
+			Logger.getLogger(ReadMessageActivity.class.getName());
+
+	private final BriarServiceConnection serviceConnection =
+			new BriarServiceConnection();
+
+	@Inject private DatabaseComponent db;
+	@Inject @DatabaseExecutor private Executor dbExecutor;
+
+	private MessageId messageId = null;
+	private boolean starred = false;
+	private ImageButton starButton = null, replyButton = null;
+
+	@Override
+	public void onCreate(Bundle state) {
+		super.onCreate(null);
+
+		Intent i = getIntent();
+		String contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
+		if(contactName == null) throw new IllegalStateException();
+		setTitle(contactName);
+		byte[] id = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
+		if(id == null) throw new IllegalStateException();
+		messageId = new MessageId(id);
+		String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
+		if(contentType == null) throw new IllegalStateException();
+		long timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
+		if(timestamp == -1) throw new IllegalStateException();
+		starred = i.getBooleanExtra("net.sf.briar.STARRED", false);
+
+		LinearLayout layout = new LinearLayout(this);
+		layout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+		layout.setOrientation(VERTICAL);
+
+		LinearLayout header = new LinearLayout(this);
+		header.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+		header.setOrientation(HORIZONTAL);
+		header.setGravity(CENTER_VERTICAL);
+
+		starButton = new ImageButton(this);
+		starButton.setPadding(5, 5, 5, 5);
+		starButton.setBackgroundResource(0);
+		if(starred) starButton.setImageResource(R.drawable.rating_important);
+		else starButton.setImageResource(R.drawable.rating_not_important);
+		starButton.setOnClickListener(this);
+		header.addView(starButton);
+
+		replyButton = new ImageButton(this);
+		replyButton.setPadding(5, 5, 5, 5);
+		replyButton.setBackgroundResource(0);
+		replyButton.setImageResource(R.drawable.social_reply);
+		replyButton.setOnClickListener(this);
+		header.addView(replyButton);
+
+		TextView date = new TextView(this);
+		// Give me all the unused width
+		date.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1));
+		date.setTextSize(14);
+		date.setPadding(10, 0, 10, 0);
+		date.setGravity(RIGHT);
+		long now = System.currentTimeMillis();
+		date.setText(DateUtils.formatSameDayTime(timestamp, now, SHORT, SHORT));
+		header.addView(date);
+		layout.addView(header);
+
+		if(contentType.equals("text/plain")) {
+			// Load and display the message body
+			TextView content = new TextView(this);
+			content.setPadding(10, 10, 10, 10);
+			layout.addView(content);
+			loadMessageBody(messageId, content);
+		}
+
+		setContentView(layout);
+
+		// Bind to the service so we can wait for the DB to be opened
+		bindService(new Intent(BriarService.class.getName()),
+				serviceConnection, 0);
+	}
+
+	private void loadMessageBody(final MessageId id, final TextView view) {
+		dbExecutor.execute(new Runnable() {
+			public void run() {
+				try {
+					// Wait for the service to be bound and started
+					serviceConnection.waitForStartup();
+					// Load the message body from the database
+					byte[] body = db.getMessageBody(id);
+					final String text = new String(body, "UTF-8");
+					// Display the message body
+					runOnUiThread(new Runnable() {
+						public void run() {
+							view.setText(text);
+						}
+					});
+				} catch(DbException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				} catch(InterruptedException e) {
+					if(LOG.isLoggable(INFO))
+						LOG.info("Interrupted while waiting for service");
+					Thread.currentThread().interrupt();
+				} catch(UnsupportedEncodingException e) {
+					throw new RuntimeException(e);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		final MessageId id = messageId;
+		dbExecutor.execute(new Runnable() {
+			public void run() {
+				try {
+					// Wait for the service to be bound and started
+					serviceConnection.waitForStartup();
+					// Mark the message as read
+					db.setReadFlag(id, true);
+				} catch(DbException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				} catch(InterruptedException e) {
+					if(LOG.isLoggable(INFO))
+						LOG.info("Interrupted while waiting for service");
+					Thread.currentThread().interrupt();
+				}
+			}
+		});
+	}
+
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		unbindService(serviceConnection);
+	}
+
+	@Override
+	public void onClick(View view) {
+		if(view == starButton) {
+			final MessageId id = messageId;
+			final boolean starredNow = !starred;
+			dbExecutor.execute(new Runnable() {
+				public void run() {
+					try {
+						db.setStarredFlag(id, starredNow);
+					} catch(DbException e) {
+						if(LOG.isLoggable(WARNING))
+							LOG.log(WARNING, e.toString(), e);
+					}
+				}
+			});
+			starred = starredNow;
+			if(starred)
+				starButton.setImageResource(R.drawable.rating_important);
+			else starButton.setImageResource(R.drawable.rating_not_important);
+		} else if(view == replyButton) {
+			// FIXME: Hook this up to an activity
+		}
+	}
+}
diff --git a/briar-api/src/net/sf/briar/api/db/MessageHeader.java b/briar-api/src/net/sf/briar/api/db/MessageHeader.java
index 2cbab3f51ce17268a48723f3ee44eefd757ef02f..95a927d3e7fdf8ae7334c1da297c16570cd8f414 100644
--- a/briar-api/src/net/sf/briar/api/db/MessageHeader.java
+++ b/briar-api/src/net/sf/briar/api/db/MessageHeader.java
@@ -49,12 +49,12 @@ public abstract class MessageHeader {
 	}
 
 	/** Returns true if the message has been read. */
-	public boolean getRead() {
+	public boolean isRead() {
 		return read;
 	}
 
 	/** Returns true if the message has been starred. */
-	public boolean getStarred() {
+	public boolean isStarred() {
 		return starred;
 	}
 }
diff --git a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
index 31262080834f69942f71c45b16a5da37354dddcf..b59bdd71d1cdd945125170d8043a718fc5ee1d8b 100644
--- a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
+++ b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
@@ -1310,13 +1310,13 @@ public class H2DatabaseTest extends BriarTestCase {
 		GroupMessageHeader header = it.next();
 		if(messageId.equals(header.getId())) {
 			assertHeadersMatch(message, header);
-			assertTrue(header.getRead());
-			assertFalse(header.getStarred());
+			assertTrue(header.isRead());
+			assertFalse(header.isStarred());
 			messageFound = true;
 		} else if(messageId1.equals(header.getId())) {
 			assertHeadersMatch(message1, header);
-			assertFalse(header.getRead());
-			assertFalse(header.getStarred());
+			assertFalse(header.isRead());
+			assertFalse(header.isStarred());
 			message1Found = true;
 		} else {
 			fail();
@@ -1326,13 +1326,13 @@ public class H2DatabaseTest extends BriarTestCase {
 		header = it.next();
 		if(messageId.equals(header.getId())) {
 			assertHeadersMatch(message, header);
-			assertTrue(header.getRead());
-			assertFalse(header.getStarred());
+			assertTrue(header.isRead());
+			assertFalse(header.isStarred());
 			messageFound = true;
 		} else if(messageId1.equals(header.getId())) {
 			assertHeadersMatch(message1, header);
-			assertFalse(header.getRead());
-			assertFalse(header.getStarred());
+			assertFalse(header.isRead());
+			assertFalse(header.isStarred());
 			message1Found = true;
 		} else {
 			fail();