diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 17f78577a731ae957f30373f4a0b36ba6eeff7f2..755d5cc6a266a16d13cbd15fb94d14cf8d85abbf 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -30,6 +30,16 @@
 			android:logo="@drawable/logo"
 			android:label="@string/app_name" >
 		</activity>
+		<activity
+			android:name=".android.ExpiredActivity"
+			android:logo="@drawable/logo"
+			android:label="@string/app_name" >
+		</activity>
+		<activity
+			android:name=".android.PasswordActivity"
+			android:logo="@drawable/logo"
+			android:label="@string/app_name" >
+		</activity>
 		<activity
 			android:name=".android.SetupActivity"
 			android:logo="@drawable/logo"
diff --git a/briar-android/src/org/briarproject/android/BriarActivity.java b/briar-android/src/org/briarproject/android/BriarActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..583269766a0b8de9b64d43422d05f04926e86358
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/BriarActivity.java
@@ -0,0 +1,106 @@
+package org.briarproject.android;
+
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static java.util.logging.Level.INFO;
+
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import org.briarproject.android.BriarService.BriarBinder;
+import org.briarproject.android.BriarService.BriarServiceConnection;
+import org.briarproject.api.db.DatabaseConfig;
+
+import roboguice.activity.RoboFragmentActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+public class BriarActivity extends RoboFragmentActivity {
+
+	// This build expires on 7 February 2014
+	private static final long EXPIRY_DATE = 1391731200 * 1000L;
+	private static final int PASSWORD_REQUEST_CODE = 1;
+
+	private static final Logger LOG =
+			Logger.getLogger(BriarActivity.class.getName());
+
+	private final BriarServiceConnection serviceConnection =
+			new BriarServiceConnection();
+
+	@Inject private DatabaseConfig databaseConfig;
+	private boolean bound = false;
+
+	@Override
+	public void onCreate(Bundle state) {
+		super.onCreate(state);
+		if(LOG.isLoggable(INFO)) LOG.info("Created");
+		if(System.currentTimeMillis() >= EXPIRY_DATE) {
+			if(LOG.isLoggable(INFO)) LOG.info("Expired");
+			Intent i = new Intent(this, ExpiredActivity.class);
+			i.setFlags(FLAG_ACTIVITY_NO_ANIMATION);
+			startActivity(i);
+			finish();
+		} else if(databaseConfig.getEncryptionKey() == null) {
+			if(LOG.isLoggable(INFO)) LOG.info("No password");
+			Intent i = new Intent(this, PasswordActivity.class);
+			i.setFlags(FLAG_ACTIVITY_NO_ANIMATION);
+			startActivityForResult(i, PASSWORD_REQUEST_CODE);
+		} else {
+			startAndBindService();
+		}
+	}
+
+	@Override
+	public void onActivityResult(int request, int result, Intent data) {
+		if(request == PASSWORD_REQUEST_CODE) {
+			if(result == RESULT_OK) startAndBindService();
+			else finish();
+		}
+	}
+
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		unbindService();
+	}
+
+	protected void startAndBindService() {
+		startService(new Intent(BriarService.class.getName()));
+		bound = bindService(new Intent(BriarService.class.getName()),
+				serviceConnection, 0);
+	}
+
+	protected void unbindService() {
+		if(bound) unbindService(serviceConnection);
+	}
+
+	protected void quit() {
+		new Thread() {
+			@Override
+			public void run() {
+				try {
+					// Wait for the service to finish starting up
+					IBinder binder = serviceConnection.waitForBinder();
+					BriarService service = ((BriarBinder) binder).getService();
+					service.waitForStartup();
+					// Shut down the service and wait for it to shut down
+					if(LOG.isLoggable(INFO)) LOG.info("Shutting down service");
+					service.shutdown();
+					service.waitForShutdown();
+				} catch(InterruptedException e) {
+					if(LOG.isLoggable(INFO))
+						LOG.info("Interrupted while waiting for service");
+				}
+				// Finish the activity and kill the JVM
+				runOnUiThread(new Runnable() {
+					public void run() {
+						finish();
+						if(LOG.isLoggable(INFO)) LOG.info("Exiting");
+						System.exit(0);
+					}
+				});
+			}
+		}.start();
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/BriarService.java b/briar-android/src/org/briarproject/android/BriarService.java
index 5b827bfd5aa826e3276a0d397c2c7275181df5d2..f84ab4247789cbb93d5e9f2e64dad483cb395e37 100644
--- a/briar-android/src/org/briarproject/android/BriarService.java
+++ b/briar-android/src/org/briarproject/android/BriarService.java
@@ -19,8 +19,10 @@ import org.briarproject.api.db.DatabaseConfig;
 import org.briarproject.api.lifecycle.LifecycleManager;
 
 import roboguice.service.RoboService;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.Binder;
@@ -29,7 +31,9 @@ import android.support.v4.app.NotificationCompat;
 
 public class BriarService extends RoboService {
 
-	private static final int NOTIFICATION_ID = 1;
+	private static final int ONGOING_NOTIFICATION_ID = 1;
+	private static final int FAILURE_NOTIFICATION_ID = 2;
+
 	private static final Logger LOG =
 			Logger.getLogger(BriarService.class.getName());
 
@@ -64,7 +68,7 @@ public class BriarService extends RoboService {
 		i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
 				FLAG_ACTIVITY_SINGLE_TOP);
 		b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
-		startForeground(NOTIFICATION_ID, b.build());
+		startForeground(ONGOING_NOTIFICATION_ID, b.build());
 		// Start the services in a background thread
 		new Thread() {
 			@Override
@@ -73,18 +77,23 @@ public class BriarService extends RoboService {
 					started = true;
 				} else {
 					if(LOG.isLoggable(INFO)) LOG.info("Startup failed");
-					Intent i = new Intent(BriarService.this,
-							HomeScreenActivity.class);
-					i.setFlags(FLAG_ACTIVITY_NEW_TASK |
-							FLAG_ACTIVITY_CLEAR_TOP);
-					i.putExtra("briar.STARTUP_FAILED", true);
-					startActivity(i);
+					showStartupFailureNotification();
 					stopSelf();
 				}
 			}
 		}.start();
 	}
 
+	private void showStartupFailureNotification() {
+		NotificationCompat.Builder b = new NotificationCompat.Builder(this);
+		b.setSmallIcon(android.R.drawable.stat_notify_error);
+		b.setContentTitle(getText(R.string.startup_failed_notification_title));
+		b.setContentText(getText(R.string.startup_failed_notification_text));
+		Object o = getSystemService(Context.NOTIFICATION_SERVICE);
+		NotificationManager nm = (NotificationManager) o;
+		nm.notify(FAILURE_NOTIFICATION_ID, b.build());
+	}
+
 	@Override
 	public int onStartCommand(Intent intent, int flags, int startId) {
 		if(LOG.isLoggable(INFO)) LOG.info("Started");
diff --git a/briar-android/src/org/briarproject/android/ExpiredActivity.java b/briar-android/src/org/briarproject/android/ExpiredActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..453775393d29f122177c007c2a0c31e8065862b6
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/ExpiredActivity.java
@@ -0,0 +1,35 @@
+package org.briarproject.android;
+
+import static android.view.Gravity.CENTER;
+import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH;
+
+import org.briarproject.R;
+import org.briarproject.android.util.LayoutUtils;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class ExpiredActivity extends Activity {
+
+	@Override
+	public void onCreate(Bundle state) {
+		super.onCreate(state);
+
+		LinearLayout layout = new LinearLayout(this);
+		layout.setLayoutParams(MATCH_MATCH);
+		layout.setGravity(CENTER);
+
+		int pad = LayoutUtils.getPadding(this);
+
+		TextView warning = new TextView(this);
+		warning.setGravity(CENTER);
+		warning.setTextSize(18);
+		warning.setPadding(pad, pad, pad, pad);
+		warning.setText(R.string.expiry_warning);
+		layout.addView(warning);
+
+		setContentView(layout);
+	}
+}
diff --git a/briar-android/src/org/briarproject/android/HomeScreenActivity.java b/briar-android/src/org/briarproject/android/HomeScreenActivity.java
index d8a16c69614c227c3fe7ca2c0a8de5a33a1d720f..c299c986a787771b7ff6a15f6867ad01e9735d5c 100644
--- a/briar-android/src/org/briarproject/android/HomeScreenActivity.java
+++ b/briar-android/src/org/briarproject/android/HomeScreenActivity.java
@@ -1,19 +1,11 @@
 package org.briarproject.android;
 
-import static android.text.InputType.TYPE_CLASS_TEXT;
-import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
 import static android.view.Gravity.CENTER;
-import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.View.GONE;
-import static android.view.View.VISIBLE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY;
-import static android.widget.LinearLayout.VERTICAL;
 import static android.widget.Toast.LENGTH_SHORT;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH;
-import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -23,70 +15,38 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import org.briarproject.R;
-import org.briarproject.android.BriarService.BriarBinder;
-import org.briarproject.android.BriarService.BriarServiceConnection;
 import org.briarproject.android.contact.ContactListActivity;
 import org.briarproject.android.groups.GroupListActivity;
-import org.briarproject.android.util.FixedVerticalSpace;
 import org.briarproject.android.util.LayoutUtils;
 import org.briarproject.api.LocalAuthor;
 import org.briarproject.api.android.DatabaseUiExecutor;
 import org.briarproject.api.android.ReferenceManager;
-import org.briarproject.api.crypto.CryptoComponent;
-import org.briarproject.api.crypto.CryptoExecutor;
 import org.briarproject.api.db.DatabaseComponent;
-import org.briarproject.api.db.DatabaseConfig;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.lifecycle.LifecycleManager;
-import org.briarproject.util.StringUtils;
 
-import roboguice.activity.RoboActivity;
-import android.app.NotificationManager;
-import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.os.Bundle;
-import android.os.IBinder;
-import android.support.v4.app.NotificationCompat;
-import android.text.Editable;
-import android.view.KeyEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
 import android.widget.BaseAdapter;
 import android.widget.Button;
-import android.widget.EditText;
 import android.widget.GridView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
 import android.widget.Toast;
 
-public class HomeScreenActivity extends RoboActivity {
-
-	// This build expires on 7 February 2014
-	private static final long EXPIRY_DATE = 1391731200 * 1000L;
+public class HomeScreenActivity extends BriarActivity {
 
 	private static final Logger LOG =
 			Logger.getLogger(HomeScreenActivity.class.getName());
 
-	private final BriarServiceConnection serviceConnection =
-			new BriarServiceConnection();
-
 	@Inject private ReferenceManager referenceManager;
-	@Inject private DatabaseConfig databaseConfig;
 	@Inject @DatabaseUiExecutor private Executor dbUiExecutor;
-	@Inject @CryptoExecutor private Executor cryptoExecutor;
-	private boolean bound = false;
-	private TextView enterPassword = null;
-	private Button continueButton = null;
-	private ProgressBar progress = null;
 
 	// Fields that are accessed from background threads must be volatile
-	@Inject private volatile CryptoComponent crypto;
 	@Inject private volatile DatabaseComponent db;
 	@Inject private volatile LifecycleManager lifecycleManager;
 
@@ -95,17 +55,11 @@ public class HomeScreenActivity extends RoboActivity {
 		super.onCreate(state);
 		if(LOG.isLoggable(INFO)) LOG.info("Created");
 		Intent i = getIntent();
-		boolean failed = i.getBooleanExtra("briar.STARTUP_FAILED", false);
 		long handle = i.getLongExtra("briar.LOCAL_AUTHOR_HANDLE", -1);
-		if(failed) {
-			// LifecycleManager failed to start all necessary services
-			showStartupFailureNotification();
-			finish();
-			if(LOG.isLoggable(INFO)) LOG.info("Exiting");
-			System.exit(0);
-		} else if(System.currentTimeMillis() >= EXPIRY_DATE) {
-			showExpiryWarning();
-		} else if(handle != -1) {
+		if(handle == -1) {
+			// The activity has been launched before
+			showButtons();
+		} else {
 			// The activity was launched from the setup wizard
 			LocalAuthor a = referenceManager.removeReference(handle,
 					LocalAuthor.class);
@@ -117,219 +71,9 @@ public class HomeScreenActivity extends RoboActivity {
 				showSpinner();
 				storeLocalAuthor(a);
 			}
-			startService(new Intent(BriarService.class.getName()));
-			bindService();
-		} else if(databaseConfig.getEncryptionKey() == null) {
-			// The activity was launched from the splash screen
-			showPasswordPrompt();
-		} else {
-			// The activity has been launched before
-			showButtons();
-			bindService();
 		}
 	}
 
-	private void showStartupFailureNotification() {
-		NotificationCompat.Builder b = new NotificationCompat.Builder(this);
-		b.setSmallIcon(android.R.drawable.stat_notify_error);
-		b.setContentTitle(getText(R.string.startup_failed_notification_title));
-		b.setContentText(getText(R.string.startup_failed_notification_text));
-		Object o = getSystemService(Context.NOTIFICATION_SERVICE);
-		NotificationManager nm = (NotificationManager) o;
-		nm.notify(0, b.build());
-	}
-
-	private void showSpinner() {
-		LinearLayout layout = new LinearLayout(this);
-		layout.setLayoutParams(MATCH_MATCH);
-		layout.setGravity(CENTER);
-
-		ProgressBar progress = new ProgressBar(this);
-		progress.setIndeterminate(true);
-		layout.addView(progress);
-
-		setContentView(layout);
-	}
-
-	private void bindService() {
-		bound = bindService(new Intent(BriarService.class.getName()),
-				serviceConnection, 0);
-	}
-
-	private void quit() {
-		new Thread() {
-			@Override
-			public void run() {
-				try {
-					// Wait for the service to finish starting up
-					IBinder binder = serviceConnection.waitForBinder();
-					BriarService service = ((BriarBinder) binder).getService();
-					service.waitForStartup();
-					// Shut down the service and wait for it to shut down
-					if(LOG.isLoggable(INFO)) LOG.info("Shutting down service");
-					service.shutdown();
-					service.waitForShutdown();
-				} catch(InterruptedException e) {
-					if(LOG.isLoggable(INFO))
-						LOG.info("Interrupted while waiting for service");
-				}
-				// Finish the activity and kill the JVM
-				runOnUiThread(new Runnable() {
-					public void run() {
-						finish();
-						if(LOG.isLoggable(INFO)) LOG.info("Exiting");
-						System.exit(0);
-					}
-				});
-			}
-		}.start();
-	}
-
-	private void storeLocalAuthor(final LocalAuthor a) {
-		dbUiExecutor.execute(new Runnable() {
-			public void run() {
-				try {
-					lifecycleManager.waitForDatabase();
-					long now = System.currentTimeMillis();
-					db.addLocalAuthor(a);
-					long duration = System.currentTimeMillis() - now;
-					if(LOG.isLoggable(INFO))
-						LOG.info("Storing author took " + duration + " ms");
-					runOnUiThread(new Runnable() {
-						public void run() {
-							showButtons();
-						}
-					});
-				} 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 database");
-					Thread.currentThread().interrupt();
-				}
-			}
-		});
-	}
-
-	private void showPasswordPrompt() {
-		SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE);
-		String hex = prefs.getString("key", null);
-		if(hex == null) throw new IllegalStateException();
-		final byte[] encrypted = StringUtils.fromHexString(hex);
-
-		LinearLayout layout = new LinearLayout(this);
-		layout.setLayoutParams(MATCH_MATCH);
-		layout.setOrientation(VERTICAL);
-		layout.setGravity(CENTER_HORIZONTAL);
-
-		int pad = LayoutUtils.getPadding(this);
-
-		enterPassword = new TextView(this);
-		enterPassword.setGravity(CENTER);
-		enterPassword.setTextSize(18);
-		enterPassword.setPadding(pad, pad, pad, 0);
-		enterPassword.setText(R.string.enter_password);
-		layout.addView(enterPassword);
-
-		final EditText passwordEntry = new EditText(this);
-		passwordEntry.setId(1);
-		passwordEntry.setMaxLines(1);
-		int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
-		passwordEntry.setInputType(inputType);
-		passwordEntry.setOnEditorActionListener(new OnEditorActionListener() {
-			public boolean onEditorAction(TextView v, int action, KeyEvent e) {
-				validatePassword(encrypted, passwordEntry.getText());
-				return true;
-			}
-		});
-		layout.addView(passwordEntry);
-
-		// Adjusting the padding of buttons and EditTexts has the wrong results
-		layout.addView(new FixedVerticalSpace(this));
-
-		continueButton = new Button(this);
-		continueButton.setLayoutParams(WRAP_WRAP);
-		continueButton.setText(R.string.continue_button);
-		continueButton.setOnClickListener(new OnClickListener() {
-			public void onClick(View v) {
-				validatePassword(encrypted, passwordEntry.getText());
-			}
-		});
-		layout.addView(continueButton);
-
-		progress = new ProgressBar(this);
-		progress.setLayoutParams(WRAP_WRAP);
-		progress.setIndeterminate(true);
-		progress.setVisibility(GONE);
-		layout.addView(progress);
-		setContentView(layout);
-	}
-
-	private void validatePassword(final byte[] encrypted, Editable e) {
-		if(enterPassword == null || continueButton == null || progress == null)
-			return;
-		// Hide the soft keyboard
-		Object o = getSystemService(INPUT_METHOD_SERVICE);
-		((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0);
-		// Replace the button with a progress bar
-		continueButton.setVisibility(GONE);
-		progress.setVisibility(VISIBLE);
-		// Decrypt the database key in a background thread
-		int length = e.length();
-		final char[] password = new char[length];
-		e.getChars(0, length, password, 0);
-		e.delete(0, length);
-		cryptoExecutor.execute(new Runnable() {
-			public void run() {
-				byte[] key = crypto.decryptWithPassword(encrypted, password);
-				if(key == null) {
-					tryAgain();
-				} else {
-					databaseConfig.setEncryptionKey(key);
-					showButtonsAndStartService();
-				}
-			}
-		});
-	}
-
-	private void tryAgain() {
-		runOnUiThread(new Runnable() {
-			public void run() {
-				enterPassword.setText(R.string.try_again);
-				continueButton.setVisibility(VISIBLE);
-				progress.setVisibility(GONE);
-			}
-		});
-	}
-
-	private void showButtonsAndStartService() {
-		runOnUiThread(new Runnable() {
-			public void run() {
-				showButtons();
-				startService(new Intent(BriarService.class.getName()));
-				bindService();
-			}
-		});
-	}
-
-	private void showExpiryWarning() {
-		LinearLayout layout = new LinearLayout(this);
-		layout.setLayoutParams(MATCH_MATCH);
-		layout.setGravity(CENTER);
-
-		int pad = LayoutUtils.getPadding(this);
-
-		TextView warning = new TextView(this);
-		warning.setGravity(CENTER);
-		warning.setTextSize(18);
-		warning.setPadding(pad, pad, pad, pad);
-		warning.setText(R.string.expiry_warning);
-		layout.addView(warning);
-
-		setContentView(layout);
-	}
-
 	private void showButtons() {
 		ListView.LayoutParams matchMatch =
 				new ListView.LayoutParams(MATCH_PARENT, MATCH_PARENT);
@@ -423,9 +167,42 @@ public class HomeScreenActivity extends RoboActivity {
 		setContentView(grid);
 	}
 
-	@Override
-	public void onDestroy() {
-		super.onDestroy();
-		if(bound) unbindService(serviceConnection);
+	private void showSpinner() {
+		LinearLayout layout = new LinearLayout(this);
+		layout.setLayoutParams(MATCH_MATCH);
+		layout.setGravity(CENTER);
+
+		ProgressBar progress = new ProgressBar(this);
+		progress.setIndeterminate(true);
+		layout.addView(progress);
+
+		setContentView(layout);
+	}
+
+	private void storeLocalAuthor(final LocalAuthor a) {
+		dbUiExecutor.execute(new Runnable() {
+			public void run() {
+				try {
+					lifecycleManager.waitForDatabase();
+					long now = System.currentTimeMillis();
+					db.addLocalAuthor(a);
+					long duration = System.currentTimeMillis() - now;
+					if(LOG.isLoggable(INFO))
+						LOG.info("Storing author took " + duration + " ms");
+					runOnUiThread(new Runnable() {
+						public void run() {
+							showButtons();
+						}
+					});
+				} 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 database");
+					Thread.currentThread().interrupt();
+				}
+			}
+		});
 	}
 }
diff --git a/briar-android/src/org/briarproject/android/PasswordActivity.java b/briar-android/src/org/briarproject/android/PasswordActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..485cc19cd355341ab85c1af16c5fbe239302419f
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/PasswordActivity.java
@@ -0,0 +1,154 @@
+package org.briarproject.android;
+
+import static android.text.InputType.TYPE_CLASS_TEXT;
+import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
+import static android.view.Gravity.CENTER;
+import static android.view.Gravity.CENTER_HORIZONTAL;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY;
+import static android.widget.LinearLayout.VERTICAL;
+import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH;
+import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+import org.briarproject.R;
+import org.briarproject.android.util.FixedVerticalSpace;
+import org.briarproject.android.util.LayoutUtils;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.crypto.CryptoExecutor;
+import org.briarproject.api.db.DatabaseConfig;
+import org.briarproject.util.StringUtils;
+
+import roboguice.activity.RoboActivity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.text.Editable;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+public class PasswordActivity extends RoboActivity {
+
+	@Inject private DatabaseConfig databaseConfig;
+	@Inject @CryptoExecutor private Executor cryptoExecutor;
+	private TextView enterPassword = null;
+	private Button continueButton = null;
+	private ProgressBar progress = null;
+
+	// Fields that are accessed from background threads must be volatile
+	@Inject private volatile CryptoComponent crypto;
+
+	@Override
+	public void onCreate(Bundle state) {
+		super.onCreate(state);
+
+		SharedPreferences prefs = getSharedPreferences("db", MODE_PRIVATE);
+		String hex = prefs.getString("key", null);
+		if(hex == null) throw new IllegalStateException();
+		final byte[] encrypted = StringUtils.fromHexString(hex);
+
+		LinearLayout layout = new LinearLayout(this);
+		layout.setLayoutParams(MATCH_MATCH);
+		layout.setOrientation(VERTICAL);
+		layout.setGravity(CENTER_HORIZONTAL);
+
+		int pad = LayoutUtils.getPadding(this);
+
+		enterPassword = new TextView(this);
+		enterPassword.setGravity(CENTER);
+		enterPassword.setTextSize(18);
+		enterPassword.setPadding(pad, pad, pad, 0);
+		enterPassword.setText(R.string.enter_password);
+		layout.addView(enterPassword);
+
+		final EditText passwordEntry = new EditText(this);
+		passwordEntry.setId(1);
+		passwordEntry.setMaxLines(1);
+		int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
+		passwordEntry.setInputType(inputType);
+		passwordEntry.setOnEditorActionListener(new OnEditorActionListener() {
+			public boolean onEditorAction(TextView v, int action, KeyEvent e) {
+				validatePassword(encrypted, passwordEntry.getText());
+				return true;
+			}
+		});
+		layout.addView(passwordEntry);
+
+		// Adjusting the padding of buttons and EditTexts has the wrong results
+		layout.addView(new FixedVerticalSpace(this));
+
+		continueButton = new Button(this);
+		continueButton.setLayoutParams(WRAP_WRAP);
+		continueButton.setText(R.string.continue_button);
+		continueButton.setOnClickListener(new OnClickListener() {
+			public void onClick(View v) {
+				validatePassword(encrypted, passwordEntry.getText());
+			}
+		});
+		layout.addView(continueButton);
+
+		progress = new ProgressBar(this);
+		progress.setLayoutParams(WRAP_WRAP);
+		progress.setIndeterminate(true);
+		progress.setVisibility(GONE);
+		layout.addView(progress);
+		setContentView(layout);
+	}
+
+	private void validatePassword(final byte[] encrypted, Editable e) {
+		if(enterPassword == null || continueButton == null || progress == null)
+			return;
+		// Hide the soft keyboard
+		Object o = getSystemService(INPUT_METHOD_SERVICE);
+		((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0);
+		// Replace the button with a progress bar
+		continueButton.setVisibility(GONE);
+		progress.setVisibility(VISIBLE);
+		// Decrypt the database key in a background thread
+		int length = e.length();
+		final char[] password = new char[length];
+		e.getChars(0, length, password, 0);
+		e.delete(0, length);
+		cryptoExecutor.execute(new Runnable() {
+			public void run() {
+				byte[] key = crypto.decryptWithPassword(encrypted, password);
+				if(key == null) {
+					tryAgain();
+				} else {
+					databaseConfig.setEncryptionKey(key);
+					returnOk();
+				}
+			}
+		});
+	}
+
+	private void tryAgain() {
+		runOnUiThread(new Runnable() {
+			public void run() {
+				enterPassword.setText(R.string.try_again);
+				continueButton.setVisibility(VISIBLE);
+				progress.setVisibility(GONE);
+			}
+		});
+	}
+
+	private void returnOk() {
+		runOnUiThread(new Runnable() {
+			public void run() {
+				setResult(RESULT_OK);
+				finish();
+			}
+		});
+	}
+}