diff --git a/briar-android/src/org/briarproject/android/BaseActivity.java b/briar-android/src/org/briarproject/android/BaseActivity.java
index 02395fbf10775009a93b15cc21c7c8f0c587e01e..d1899b64095f0446586d431b26c5cdd1a956acdb 100644
--- a/briar-android/src/org/briarproject/android/BaseActivity.java
+++ b/briar-android/src/org/briarproject/android/BaseActivity.java
@@ -2,12 +2,16 @@ package org.briarproject.android;
 
 import android.os.Bundle;
 import android.os.IBinder;
+import android.support.annotation.CallSuper;
 import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
 import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 
 import org.briarproject.android.controller.ActivityLifecycleController;
+import org.briarproject.android.controller.handler.ContextResultHandler;
+import org.briarproject.android.controller.handler.DestroyableContextManager;
 import org.briarproject.android.forum.ForumModule;
 
 import java.util.ArrayList;
@@ -18,12 +22,14 @@ import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
 import static org.briarproject.android.TestingConstants.PREVENT_SCREENSHOTS;
 
 public abstract class BaseActivity extends AppCompatActivity
-		implements DestroyableContext {
+		implements DestroyableContext, DestroyableContextManager {
 
 	protected ActivityComponent activityComponent;
 
 	private final List<ActivityLifecycleController> lifecycleControllers =
 			new ArrayList<>();
+	private List<ContextResultHandler> contextResultHandlers =
+			new ArrayList<>();
 	private boolean destroyed = false;
 
 	public abstract void injectActivity(ActivityComponent component);
@@ -32,6 +38,7 @@ public abstract class BaseActivity extends AppCompatActivity
 		lifecycleControllers.add(alc);
 	}
 
+	@SuppressWarnings("unchecked")
 	@Override
 	public void onCreate(@Nullable Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
@@ -52,6 +59,39 @@ public abstract class BaseActivity extends AppCompatActivity
 		for (ActivityLifecycleController alc : lifecycleControllers) {
 			alc.onActivityCreate(this);
 		}
+
+		if (getLastCustomNonConfigurationInstance() != null) {
+			contextResultHandlers =
+					(List<ContextResultHandler>) getLastCustomNonConfigurationInstance();
+			for (ContextResultHandler crh : contextResultHandlers)
+				crh.setDestroyableContextManager(this);
+		}
+
+	}
+
+	@UiThread
+	public void addContextResultHandler(ContextResultHandler crh) {
+		contextResultHandlers.add(crh);
+	}
+
+
+	@UiThread
+	public void removeContextResultHandler(String tag) {
+		List<ContextResultHandler> handlersToRemove = new ArrayList<>();
+		for (ContextResultHandler crh : contextResultHandlers) {
+			if (crh.getTag().equals(tag))
+				contextResultHandlers.remove(crh);
+				handlersToRemove.add(crh);
+		}
+		contextResultHandlers.removeAll(handlersToRemove);
+	}
+
+	@UiThread
+	public boolean containsContextResultHandler(String tag) {
+		for (ContextResultHandler crh : contextResultHandlers) {
+			if (crh.getTag().equals(tag)) return true;
+		}
+		return false;
 	}
 
 	public ActivityComponent getActivityComponent() {
@@ -67,6 +107,12 @@ public abstract class BaseActivity extends AppCompatActivity
 		return new ForumModule();
 	}
 
+	@CallSuper
+	@Override
+	public Object onRetainCustomNonConfigurationInstance() {
+		return contextResultHandlers;
+	}
+
 	@Override
 	protected void onStart() {
 		super.onStart();
@@ -87,6 +133,9 @@ public abstract class BaseActivity extends AppCompatActivity
 	protected void onDestroy() {
 		super.onDestroy();
 		destroyed = true;
+		for (ContextResultHandler crh : contextResultHandlers) {
+			crh.setDestroyableContextManager(null);
+		}
 		for (ActivityLifecycleController alc : lifecycleControllers) {
 			alc.onActivityDestroy();
 		}
diff --git a/briar-android/src/org/briarproject/android/BriarActivity.java b/briar-android/src/org/briarproject/android/BriarActivity.java
index 9d162b62d6702b4863c888f1afafd51041235d14..66e991ab714eeb8e381d9e8a9c896aced276aff5 100644
--- a/briar-android/src/org/briarproject/android/BriarActivity.java
+++ b/briar-android/src/org/briarproject/android/BriarActivity.java
@@ -3,10 +3,12 @@ package org.briarproject.android;
 import android.annotation.SuppressLint;
 import android.content.Intent;
 import android.os.Build;
+import android.support.annotation.NonNull;
 
 import org.briarproject.android.controller.BriarController;
 import org.briarproject.android.controller.DbController;
-import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.controller.handler.DestroyableContextManager;
+import org.briarproject.android.controller.handler.UiContextResultHandler;
 import org.briarproject.android.panic.ExitActivity;
 
 import java.util.logging.Logger;
@@ -28,6 +30,8 @@ public abstract class BriarActivity extends BaseActivity {
 
 	public static final int REQUEST_PASSWORD = 1;
 
+	protected static final String TAG_SIGN_OUT = "briar.SIGN_OUT";
+
 	private static final Logger LOG =
 			Logger.getLogger(BriarActivity.class.getName());
 
@@ -58,13 +62,18 @@ public abstract class BriarActivity extends BaseActivity {
 	}
 
 	protected void signOut(final boolean removeFromRecentApps) {
-		briarController.signOut(new UiResultHandler<Void>(this) {
-			@Override
-			public void onResultUi(Void result) {
-				if (removeFromRecentApps) startExitActivity();
-				else finishAndExit();
-			}
-		});
+		briarController
+				.signOut(new UiContextResultHandler<Void>(this, TAG_SIGN_OUT) {
+					@Override
+					public void onResultUi(@NonNull Void result,
+							@NonNull DestroyableContextManager context) {
+						if (removeFromRecentApps) {
+							((BriarActivity) context).startExitActivity();
+						} else {
+							((BriarActivity)context).finishAndExit();
+						}
+					}
+				});
 	}
 
 	protected void signOut() {
diff --git a/briar-android/src/org/briarproject/android/ChangePasswordActivity.java b/briar-android/src/org/briarproject/android/ChangePasswordActivity.java
index 4c93924ea80b997bd8637596b80d1cc327fbddc4..565eadb1551c4e9a5fd382d5c6ba2629fd62eb6f 100644
--- a/briar-android/src/org/briarproject/android/ChangePasswordActivity.java
+++ b/briar-android/src/org/briarproject/android/ChangePasswordActivity.java
@@ -18,7 +18,8 @@ import android.widget.Toast;
 import org.briarproject.R;
 import org.briarproject.android.controller.PasswordController;
 import org.briarproject.android.controller.SetupController;
-import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.controller.handler.DestroyableContextManager;
+import org.briarproject.android.controller.handler.UiContextResultHandler;
 import org.briarproject.android.util.AndroidUtils;
 import org.briarproject.android.util.StrengthMeter;
 
@@ -32,6 +33,8 @@ public class ChangePasswordActivity extends BaseActivity
 		implements OnClickListener,
 		OnEditorActionListener {
 
+	private static final String TAG_PASSWORD_CHANGE = "briar.PASSWORD_CHANGE";
+
 	@Inject
 	protected PasswordController passwordController;
 	@Inject
@@ -91,6 +94,11 @@ public class ChangePasswordActivity extends BaseActivity
 		newPasswordConfirmation.addTextChangedListener(tw);
 		newPasswordConfirmation.setOnEditorActionListener(this);
 		changePasswordButton.setOnClickListener(this);
+
+		if (containsContextResultHandler(TAG_PASSWORD_CHANGE)) {
+			changePasswordButton.setVisibility(INVISIBLE);
+			progress.setVisibility(VISIBLE);
+		}
 	}
 
 	@Override
@@ -131,22 +139,27 @@ public class ChangePasswordActivity extends BaseActivity
 		// Replace the button with a progress bar
 		changePasswordButton.setVisibility(INVISIBLE);
 		progress.setVisibility(VISIBLE);
+
 		passwordController.changePassword(currentPassword.getText().toString(),
 				newPassword.getText().toString(),
-				new UiResultHandler<Boolean>(this) {
+				new UiContextResultHandler<Boolean>(this, TAG_PASSWORD_CHANGE) {
 					@Override
-					public void onResultUi(@NonNull Boolean result) {
+					public void onResultUi(@NonNull Boolean result,
+							@NonNull DestroyableContextManager context) {
+						ChangePasswordActivity cpa =
+								(ChangePasswordActivity) getContextManager();
 						if (result) {
-							Toast.makeText(ChangePasswordActivity.this,
-									R.string.password_changed,
+							Toast.makeText(cpa, R.string.password_changed,
 									Toast.LENGTH_LONG).show();
-							setResult(RESULT_OK);
-							finish();
+							cpa.setResult(RESULT_OK);
+							cpa.finish();
 						} else {
-							tryAgain();
+							cpa.tryAgain();
 						}
 					}
+
 				});
+
 	}
 
 	private void tryAgain() {
diff --git a/briar-android/src/org/briarproject/android/NavDrawerActivity.java b/briar-android/src/org/briarproject/android/NavDrawerActivity.java
index c80310aa0cac726e87c3c3801544a8cd611018f3..571a2251311bc4ee875ee29c9fdab73cef672a99 100644
--- a/briar-android/src/org/briarproject/android/NavDrawerActivity.java
+++ b/briar-android/src/org/briarproject/android/NavDrawerActivity.java
@@ -129,6 +129,12 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
 		if (getIntent() != null) {
 			onNewIntent(getIntent());
 		}
+
+		if (containsContextResultHandler(TAG_SIGN_OUT)) {
+			// User is signing out of Briar and rotated the device before the
+			// operation could finish, restore the UI state
+			showLoadingScreen(true, R.string.progress_title_logout);
+		}
 	}
 
 	private void welcomeMessageCheck() {
diff --git a/briar-android/src/org/briarproject/android/PasswordActivity.java b/briar-android/src/org/briarproject/android/PasswordActivity.java
index 751b6adcdb97b7dff491fdefc13a3f1dea512786..ea8b634dba908d8e4042ecd77fa8036846a57f3e 100644
--- a/briar-android/src/org/briarproject/android/PasswordActivity.java
+++ b/briar-android/src/org/briarproject/android/PasswordActivity.java
@@ -18,9 +18,12 @@ import android.widget.TextView.OnEditorActionListener;
 
 import org.briarproject.R;
 import org.briarproject.android.controller.PasswordController;
-import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.controller.handler.DestroyableContextManager;
+import org.briarproject.android.controller.handler.UiContextResultHandler;
 import org.briarproject.android.util.AndroidUtils;
 
+import java.util.logging.Logger;
+
 import javax.inject.Inject;
 
 import static android.content.Intent.ACTION_MAIN;
@@ -30,6 +33,11 @@ import static android.view.View.VISIBLE;
 
 public class PasswordActivity extends BaseActivity {
 
+	private static final String TAG_PASSWORD = "briar.PASSWORD";
+
+	private static final Logger LOG =
+			Logger.getLogger(NavDrawerActivity.class.getName());
+
 	@Inject
 	protected PasswordController passwordController;
 
@@ -77,6 +85,12 @@ public class PasswordActivity extends BaseActivity {
 			public void afterTextChanged(Editable s) {
 			}
 		});
+		if (containsContextResultHandler(TAG_PASSWORD)) {
+			signInButton.setVisibility(INVISIBLE);
+			progress.setVisibility(VISIBLE);
+		}
+
+		LOG.info("not tryAgain() " + this.hashCode());
 	}
 
 	@Override
@@ -125,21 +139,28 @@ public class PasswordActivity extends BaseActivity {
 		hideSoftKeyboard(password);
 		signInButton.setVisibility(INVISIBLE);
 		progress.setVisibility(VISIBLE);
-		passwordController.validatePassword(password.getText().toString(),
-				new UiResultHandler<Boolean>(this) {
+
+		UiContextResultHandler<Boolean> crh =
+				new UiContextResultHandler<Boolean>(this, TAG_PASSWORD) {
 					@Override
-					public void onResultUi(@NonNull Boolean result) {
+					public void onResultUi(@NonNull Boolean result,
+							@NonNull DestroyableContextManager context) {
+						PasswordActivity pa =
+								((PasswordActivity) getContextManager());
 						if (result) {
-							setResult(RESULT_OK);
-							finish();
+							pa.setResult(RESULT_OK);
+							pa.finish();
 						} else {
-							tryAgain();
+							pa.tryAgain();
 						}
 					}
-				});
+
+				};
+		passwordController.validatePassword(password.getText().toString(), crh);
 	}
 
 	private void tryAgain() {
+		LOG.info("tryAgain() " + this.hashCode());
 		AndroidUtils.setError(input, getString(R.string.try_again), true);
 		signInButton.setVisibility(VISIBLE);
 		progress.setVisibility(INVISIBLE);
diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java
index 0dcce3d72a28a3d4eeed9bee8584cf1c97e38a99..92bfefbdd182ffd3e28b1fe7bd157880e65c595f 100644
--- a/briar-android/src/org/briarproject/android/SetupActivity.java
+++ b/briar-android/src/org/briarproject/android/SetupActivity.java
@@ -2,6 +2,7 @@ package org.briarproject.android;
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
 import android.support.design.widget.TextInputLayout;
 import android.text.Editable;
 import android.text.TextWatcher;
@@ -16,7 +17,8 @@ import android.widget.TextView.OnEditorActionListener;
 
 import org.briarproject.R;
 import org.briarproject.android.controller.SetupController;
-import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.controller.handler.DestroyableContextManager;
+import org.briarproject.android.controller.handler.UiContextResultHandler;
 import org.briarproject.android.util.AndroidUtils;
 import org.briarproject.android.util.StrengthMeter;
 import org.briarproject.util.StringUtils;
@@ -33,6 +35,8 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENG
 public class SetupActivity extends BaseActivity implements OnClickListener,
 		OnEditorActionListener {
 
+	protected static final String TAG_SETUP = "briar.SETUP";
+
 	@Inject
 	protected SetupController setupController;
 
@@ -87,6 +91,14 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
 		passwordConfirmation.addTextChangedListener(tw);
 		passwordConfirmation.setOnEditorActionListener(this);
 		createAccountButton.setOnClickListener(this);
+
+		if (containsContextResultHandler(TAG_SETUP)) {
+			// Activity has been re-created due to an orientation change,
+			// update the result handler with the current context and restore
+			// the UI state
+			createAccountButton.setVisibility(INVISIBLE);
+			progress.setVisibility(VISIBLE);
+		}
 	}
 
 	@Override
@@ -135,13 +147,16 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
 		String nickname = nicknameEntry.getText().toString();
 		String password = passwordEntry.getText().toString();
 
-		setupController.storeAuthorInfo(nickname, password,
-				new UiResultHandler<Void>(this) {
-					@Override
-					public void onResultUi(Void result) {
-						showMain();
-					}
-				});
+		setupController
+				.storeAuthorInfo(nickname, password,
+						new UiContextResultHandler<Void>(this, TAG_SETUP) {
+							@Override
+							public void onResultUi(@NonNull Void result,
+									@NonNull DestroyableContextManager context) {
+								((SetupActivity)context).showMain();
+							}
+
+						});
 	}
 
 	private void showMain() {
diff --git a/briar-android/src/org/briarproject/android/controller/BriarControllerImpl.java b/briar-android/src/org/briarproject/android/controller/BriarControllerImpl.java
index f49d6485f7b58e4673e06f4f40e1125275e85610..b3d834c54c731910ebfa754b388b7fc76aa4c7cf 100644
--- a/briar-android/src/org/briarproject/android/controller/BriarControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/controller/BriarControllerImpl.java
@@ -71,6 +71,7 @@ public class BriarControllerImpl implements BriarController {
 			@Override
 			public void run() {
 				try {
+					Thread.sleep(5000);
 					// Wait for the service to finish starting up
 					IBinder binder = serviceConnection.waitForBinder();
 					BriarService service =
diff --git a/briar-android/src/org/briarproject/android/controller/handler/ContextResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/ContextResultHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..94fc446afc2a85b4c1357ae9571112ee19be9d91
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/controller/handler/ContextResultHandler.java
@@ -0,0 +1,7 @@
+package org.briarproject.android.controller.handler;
+
+public interface ContextResultHandler<R> extends ResultHandler<R> {
+	void setDestroyableContextManager(DestroyableContextManager listener);
+	String getTag();
+	DestroyableContextManager getContextManager();
+}
diff --git a/briar-android/src/org/briarproject/android/controller/handler/DestroyableContextManager.java b/briar-android/src/org/briarproject/android/controller/handler/DestroyableContextManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5d0652f3f2d61a18ca4fc97f28456c8a5e33399
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/controller/handler/DestroyableContextManager.java
@@ -0,0 +1,12 @@
+package org.briarproject.android.controller.handler;
+
+import org.briarproject.android.DestroyableContext;
+
+public interface DestroyableContextManager extends DestroyableContext {
+
+	void addContextResultHandler(ContextResultHandler crh);
+
+	void removeContextResultHandler(String tag);
+
+	boolean containsContextResultHandler(String tag);
+}
diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiContextExceptionResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiContextExceptionResultHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..dca6213f333adbbbc049c6d9337f4e0935ac7be9
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/controller/handler/UiContextExceptionResultHandler.java
@@ -0,0 +1,68 @@
+package org.briarproject.android.controller.handler;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+
+import org.briarproject.api.nullsafety.NotNullByDefault;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * This class defines a result handler with a callback that runs on the UI thread
+ * and is retained when orientation changes occur. It ensures that the callback
+ * is always run in the latest context.
+ * <p>
+ * Note that this event handler should be used carefully with callbacks that
+ * depend on global variables as those variables might be destroyed or lose their
+ * state during the orientation change.
+ *
+ * @param <R> The result's object type
+ */
+@Immutable
+@NotNullByDefault
+public abstract class UiContextExceptionResultHandler<R, E extends Exception>
+		extends UiContextResultHandler<R>
+		implements ResultExceptionHandler<R, E> {
+
+	@Nullable
+	private E exception;
+
+	protected UiContextExceptionResultHandler(
+			DestroyableContextManager listener, String tag) {
+		super(listener, tag);
+	}
+
+	@Override
+	public void setDestroyableContextManager(
+			DestroyableContextManager listener) {
+		boolean isSwitchingFromNullToNonNull =
+				this.listener == null && listener != null;
+		super.setDestroyableContextManager(listener);
+		if (isSwitchingFromNullToNonNull)
+			runException();
+	}
+
+	@Override
+	public void onException(final E exception) {
+		this.exception = exception;
+		listener.runOnUiThreadUnlessDestroyed(new Runnable() {
+			@Override
+			public void run() {
+				runException();
+			}
+		});
+	}
+
+	@UiThread
+	private void runException() {
+		if (exception != null && listener != null) {
+			onExceptionUi(exception, listener);
+			listener.removeContextResultHandler(getTag());
+			exception = null;
+		}
+	}
+
+	@UiThread
+	public abstract void onExceptionUi(E exception,
+			DestroyableContextManager context);
+}
diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiContextResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiContextResultHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6a3f2df0f6943560908e2bdb7e19ccaa811571a
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/controller/handler/UiContextResultHandler.java
@@ -0,0 +1,84 @@
+package org.briarproject.android.controller.handler;
+
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+
+import org.briarproject.api.nullsafety.NotNullByDefault;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * This class defines a result handler with a callback that runs on the UI thread
+ * and is retained when orientation changes occur. It ensures that the callback
+ * is always run in the latest context.
+ * <p>
+ * Note that this event handler should be used carefully with callbacks that
+ * depend on global variables as those variables might be destroyed or lose their
+ * state during the orientation change.
+ *
+ * @param <R> The result's object type
+ */
+@Immutable
+@NotNullByDefault
+public abstract class UiContextResultHandler<R>
+		implements ContextResultHandler<R> {
+
+	private final String tag;
+	protected DestroyableContextManager listener;
+	@Nullable
+	private R result;
+
+	@UiThread
+	protected UiContextResultHandler(DestroyableContextManager listener,
+			String tag) {
+		this.listener = listener;
+		this.tag = tag;
+		listener.addContextResultHandler(this);
+	}
+
+	@UiThread
+	@Override
+	public void setDestroyableContextManager(
+			DestroyableContextManager listener) {
+		// Check if the listener is switching from null to non null
+		boolean isSwitchingFromNullToNonNull =
+				this.listener == null && listener != null;
+		this.listener = listener;
+		if (isSwitchingFromNullToNonNull)
+			runResult();
+	}
+
+	@Override
+	public String getTag() {
+		return tag;
+	}
+
+	@Override
+	public DestroyableContextManager getContextManager() {
+		return listener;
+	}
+
+	@Override
+	public void onResult(final R result) {
+		this.result = result;
+		listener.runOnUiThreadUnlessDestroyed(new Runnable() {
+			@Override
+			public void run() {
+				runResult();
+			}
+		});
+	}
+
+	@UiThread
+	private void runResult() {
+		if (result != null && listener != null) {
+			onResultUi(result, listener);
+			listener.removeContextResultHandler(tag);
+			result = null;
+		}
+	}
+
+	@UiThread
+	public abstract void onResultUi(R result,
+			DestroyableContextManager context);
+}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index 0fe164367f3e1b8c26bd18f64e15ad88d59e8cf9..941ce6c7f7950219f0cf8a28eea7c643724c983e 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -18,7 +18,8 @@ import android.widget.Toast;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
-import org.briarproject.android.controller.handler.UiResultExceptionHandler;
+import org.briarproject.android.controller.handler.DestroyableContextManager;
+import org.briarproject.android.controller.handler.UiContextExceptionResultHandler;
 import org.briarproject.android.sharing.ForumSharingStatusActivity;
 import org.briarproject.android.sharing.ShareForumActivity;
 import org.briarproject.android.threaded.ThreadItemAdapter;
@@ -44,6 +45,7 @@ public class ForumActivity extends
 		ThreadListActivity<Forum, ThreadItemAdapter<ForumItem>, ForumItem, ForumPostHeader> {
 
 	private static final int REQUEST_FORUM_SHARED = 3;
+	private static final String TAG_FORUM = "briar.FORUM";
 
 	@Inject
 	ForumController forumController;
@@ -168,19 +170,22 @@ public class ForumActivity extends
 
 	private void deleteForum() {
 		forumController.deleteNamedGroup(
-				new UiResultExceptionHandler<Void, DbException>(this) {
+				new UiContextExceptionResultHandler<Void, DbException>(this,
+						TAG_FORUM) {
 					@Override
-					public void onResultUi(Void v) {
-						Toast.makeText(ForumActivity.this,
+					public void onResultUi(Void result,
+							DestroyableContextManager context) {
+						Toast.makeText((ForumActivity)context,
 								R.string.forum_left_toast, LENGTH_SHORT).show();
 					}
 
 					@Override
-					public void onExceptionUi(DbException exception) {
-						// TODO proper error handling
-						finish();
+					public void onExceptionUi(DbException exception,
+							DestroyableContextManager context) {
+						((ForumActivity)context).finish();
 					}
 				});
+
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java
index 2f7125331bd0182ae0d897723fe662c6ef726414..1b4c81eb0c6b47e6280d0ed3780e54dc6ce4d446 100644
--- a/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java
@@ -2,7 +2,8 @@ package org.briarproject.android.privategroup.creation;
 
 import org.briarproject.R;
 import org.briarproject.android.contactselection.ContactSelectorActivity;
-import org.briarproject.android.controller.handler.UiResultExceptionHandler;
+import org.briarproject.android.controller.handler.DestroyableContextManager;
+import org.briarproject.android.controller.handler.UiContextExceptionResultHandler;
 import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.db.DbException;
@@ -21,6 +22,8 @@ import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_
 public abstract class BaseGroupInviteActivity
 		extends ContactSelectorActivity implements MessageFragmentListener {
 
+	private static final String TAG_GROUP_INVITEE = "briar.GROUP_INVITEE";
+
 	@Inject
 	CreateGroupController controller;
 
@@ -43,19 +46,26 @@ public abstract class BaseGroupInviteActivity
 	public boolean onButtonClick(@NotNull String message) {
 		if (groupId == null)
 			throw new IllegalStateException("GroupId was not initialized");
+
 		controller.sendInvitation(groupId, contacts, message,
-				new UiResultExceptionHandler<Void, DbException>(this) {
+				new UiContextExceptionResultHandler<Void, DbException>(this,
+						TAG_GROUP_INVITEE) {
 					@Override
-					public void onResultUi(Void result) {
-						setResult(RESULT_OK);
-						supportFinishAfterTransition();
+					public void onExceptionUi(DbException exception,
+							DestroyableContextManager context) {
+						((BaseGroupInviteActivity) context)
+								.setResult(RESULT_CANCELED);
+						((BaseGroupInviteActivity) context)
+								.finish();
 					}
 
 					@Override
-					public void onExceptionUi(DbException exception) {
-						// TODO proper error handling
-						setResult(RESULT_CANCELED);
-						finish();
+					public void onResultUi(Void result,
+							DestroyableContextManager context) {
+						((BaseGroupInviteActivity) context)
+								.setResult(RESULT_OK);
+						((BaseGroupInviteActivity) context)
+								.supportFinishAfterTransition();
 					}
 				});
 		return true;
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
index 461989c81c91c04d433a5bf65facb72f52346b85..7ecb06c4c8a376cac7d7e53c04d02bee3c4d5b1e 100644
--- a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
@@ -7,7 +7,8 @@ import android.support.v4.app.ActivityOptionsCompat;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
-import org.briarproject.android.controller.handler.UiResultExceptionHandler;
+import org.briarproject.android.controller.handler.DestroyableContextManager;
+import org.briarproject.android.controller.handler.UiContextExceptionResultHandler;
 import org.briarproject.android.privategroup.conversation.GroupActivity;
 import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener;
 import org.briarproject.api.db.DbException;
@@ -18,6 +19,8 @@ import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
 public class CreateGroupActivity extends BaseGroupInviteActivity implements
 		CreateGroupListener, MessageFragmentListener {
 
+	private static final String TAG_CREATE_GROUP = "briar.CREATE_GROUP";
+
 	@Override
 	public void injectActivity(ActivityComponent component) {
 		component.inject(this);
@@ -49,17 +52,20 @@ public class CreateGroupActivity extends BaseGroupInviteActivity implements
 	@Override
 	public void onGroupNameChosen(String name) {
 		controller.createGroup(name,
-				new UiResultExceptionHandler<GroupId, DbException>(this) {
+				new UiContextExceptionResultHandler<GroupId, DbException>(this,
+						TAG_CREATE_GROUP) {
 					@Override
-					public void onResultUi(GroupId g) {
-						groupId = g;
-						switchToContactSelectorFragment(g);
+					public void onExceptionUi(DbException exception,
+							DestroyableContextManager context) {
+						((CreateGroupActivity) context).finish();
 					}
 
 					@Override
-					public void onExceptionUi(DbException exception) {
-						// TODO proper error handling
-						finish();
+					public void onResultUi(GroupId g,
+							DestroyableContextManager context) {
+						((CreateGroupActivity) context).groupId = g;
+						((CreateGroupActivity) context)
+								.switchToContactSelectorFragment(g);
 					}
 				});
 	}