diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
index 5a1d1868acaa85cf6c01492185d46ef76db4a6a7..0116728d618854535b6808ffed3121fa7e991570 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ContactExchangeActivity.java
@@ -1,6 +1,8 @@
 package org.briarproject.briar.android.keyagreement;
 
 import android.os.Bundle;
+import android.support.annotation.UiThread;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import org.briarproject.bramble.api.contact.ContactExchangeListener;
@@ -12,6 +14,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
 import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
 import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
 import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.briar.R;
 import org.briarproject.briar.R.string;
 import org.briarproject.briar.android.activity.ActivityComponent;
 
@@ -22,6 +25,7 @@ import javax.inject.Inject;
 
 import static android.widget.Toast.LENGTH_LONG;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
@@ -48,11 +52,6 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
 		getSupportActionBar().setTitle(string.add_contact_title);
 	}
 
-	@Override
-	void keyAgreementFinished(KeyAgreementResult result) {
-		runOnUiThreadUnlessDestroyed(() -> startContactExchange(result));
-	}
-
 	private void startContactExchange(KeyAgreementResult result) {
 		runOnDbThread(() -> {
 			LocalAuthor localAuthor;
@@ -60,7 +59,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
 			try {
 				localAuthor = identityManager.getLocalAuthor();
 			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+				logException(LOG, WARNING, e);
 				contactExchangeFailed();
 				return;
 			}
@@ -105,4 +104,42 @@ public class ContactExchangeActivity extends KeyAgreementActivity implements
 			finish();
 		});
 	}
+
+	@UiThread
+	@Override
+	public void keyAgreementFailed() {
+		// TODO show failure somewhere persistent?
+		Toast.makeText(this, R.string.connection_failed,
+				LENGTH_LONG).show();
+	}
+
+	@UiThread
+	@Override
+	public void keyAgreementWaiting(TextView status) {
+		status.setText(R.string.waiting_for_contact_to_scan);
+	}
+
+	@UiThread
+	@Override
+	public void keyAgreementStarted(TextView status) {
+		status.setText(R.string.authenticating_with_device);
+	}
+
+	@UiThread
+	@Override
+	public void keyAgreementAborted(boolean remoteAborted) {
+		// TODO show abort somewhere persistent?
+		Toast.makeText(this,
+				remoteAborted ? R.string.connection_aborted_remote :
+						R.string.connection_aborted_local, LENGTH_LONG)
+				.show();
+	}
+
+	@UiThread
+	@Override
+	public void keyAgreementFinished(TextView status,
+			KeyAgreementResult result) {
+		status.setText(R.string.exchanging_contact_details);
+		startContactExchange(result);
+	}
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
index 0a25f92377e78ab52329fe4b1bb5dc7212b19d07..5f722eb336dae683266b194e36ddf72962c23ecd 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java
@@ -16,11 +16,7 @@ import android.support.v7.widget.Toolbar;
 import android.view.MenuItem;
 import android.widget.Toast;
 
-import org.briarproject.bramble.api.event.Event;
 import org.briarproject.bramble.api.event.EventBus;
-import org.briarproject.bramble.api.event.EventListener;
-import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
-import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent;
 import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
 import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
 import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
@@ -47,15 +43,14 @@ import static android.bluetooth.BluetoothAdapter.STATE_ON;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION.SDK_INT;
 import static android.widget.Toast.LENGTH_LONG;
-import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.LogUtils.logException;
 import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ENABLE_BLUETOOTH;
 import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
 
 @MethodsNotNullByDefault
 @ParametersNotNullByDefault
 public abstract class KeyAgreementActivity extends BriarActivity implements
-		BaseFragmentListener, IntroScreenSeenListener, EventListener {
+		BaseFragmentListener, IntroScreenSeenListener,
+		KeyAgreementFragment.KeyAgreementEventListener {
 
 	private enum BluetoothState {
 		UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED
@@ -99,18 +94,6 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
 		if (bluetoothReceiver != null) unregisterReceiver(bluetoothReceiver);
 	}
 
-	@Override
-	public void onStart() {
-		super.onStart();
-		eventBus.addListener(this);
-	}
-
-	@Override
-	protected void onStop() {
-		super.onStop();
-		eventBus.removeListener(this);
-	}
-
 	@Override
 	public boolean onOptionsItemSelected(MenuItem item) {
 		switch (item.getItemId()) {
@@ -194,7 +177,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
 		// FIXME #824
 		FragmentManager fm = getSupportFragmentManager();
 		if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
-			BaseFragment f = KeyAgreementFragment.newInstance();
+			BaseFragment f = KeyAgreementFragment.newInstance(this);
 			fm.beginTransaction()
 					.replace(R.id.fragmentContainer, f, f.getUniqueTag())
 					.addToBackStack(f.getUniqueTag())
@@ -264,79 +247,6 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
 		}
 	}
 
-	@Override
-	public void eventOccurred(Event e) {
-		if (e instanceof KeyAgreementFinishedEvent) {
-			KeyAgreementFinishedEvent event = (KeyAgreementFinishedEvent) e;
-			keyAgreementFinished(event.getResult());
-		}
-	}
-
-<<<<<<<HEAD
-
-	private void keyAgreementFinished(KeyAgreementResult result) {
-		runOnUiThreadUnlessDestroyed(() -> startContactExchange(result));
-	}
-
-	private void startContactExchange(KeyAgreementResult result) {
-		runOnDbThread(() -> {
-			LocalAuthor localAuthor;
-			// Load the local pseudonym
-			try {
-				localAuthor = identityManager.getLocalAuthor();
-			} catch (DbException e) {
-				logException(LOG, WARNING, e);
-				contactExchangeFailed();
-				return;
-			}
-
-			// Exchange contact details
-			contactExchangeTask.startExchange(KeyAgreementActivity.this,
-					localAuthor, result.getMasterKey(),
-					result.getConnection(), result.getTransportId(),
-					result.wasAlice());
-		});
-	}
-
-	@Override
-	public void contactExchangeSucceeded(Author remoteAuthor) {
-		runOnUiThreadUnlessDestroyed(() -> {
-			String contactName = remoteAuthor.getName();
-			String format = getString(string.contact_added_toast);
-			String text = String.format(format, contactName);
-			Toast.makeText(KeyAgreementActivity.this, text, LENGTH_LONG).show();
-			supportFinishAfterTransition();
-		});
-	}
-
-	@Override
-	public void duplicateContact(Author remoteAuthor) {
-		runOnUiThreadUnlessDestroyed(() -> {
-			String contactName = remoteAuthor.getName();
-			String format = getString(string.contact_already_exists);
-			String text = String.format(format, contactName);
-			Toast.makeText(KeyAgreementActivity.this, text, LENGTH_LONG).show();
-			finish();
-		});
-	}
-
-	@Override
-	public void contactExchangeFailed() {
-		runOnUiThreadUnlessDestroyed(() -> {
-			Toast.makeText(KeyAgreementActivity.this,
-					string.contact_exchange_failed, LENGTH_LONG).show();
-			finish();
-		});
-	}
-=======
-
-	abstract void keyAgreementFinished(KeyAgreementResult result);
->>>>>>>927ab9571...
-	Add some
-	abstraction to
-	the keyagreement
-	ui
-
 	private class BluetoothStateReceiver extends BroadcastReceiver {
 
 		@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementFragment.java
index 222750ece54726cd441ca3881517ccd09219d1a1..0c8b2233ec7ca01624955b670b5621c95d2b06b0 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementFragment.java
@@ -18,6 +18,7 @@ import com.google.zxing.Result;
 import org.briarproject.bramble.api.UnsupportedVersionException;
 import org.briarproject.bramble.api.event.Event;
 import org.briarproject.bramble.api.event.EventBus;
+import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
 import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
 import org.briarproject.bramble.api.keyagreement.Payload;
 import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
@@ -87,10 +88,12 @@ public class KeyAgreementFragment extends BaseEventFragment
 	private boolean gotRemotePayload;
 	private volatile boolean gotLocalPayload;
 	private KeyAgreementTask task;
+	private KeyAgreementEventListener listener;
 
-	public static KeyAgreementFragment newInstance() {
+	public static KeyAgreementFragment newInstance(KeyAgreementEventListener listener) {
 		Bundle args = new Bundle();
 		KeyAgreementFragment fragment = new KeyAgreementFragment();
+		fragment.listener = listener;
 		fragment.setArguments(args);
 		return fragment;
 	}
@@ -265,29 +268,27 @@ public class KeyAgreementFragment extends BaseEventFragment
 			KeyAgreementAbortedEvent event = (KeyAgreementAbortedEvent) e;
 			keyAgreementAborted(event.didRemoteAbort());
 		} else if (e instanceof KeyAgreementFinishedEvent) {
-			keyAgreementFinished();
+			keyAgreementFinished(((KeyAgreementFinishedEvent) e).getResult());
 		}
 	}
 
 	private void keyAgreementFailed() {
 		runOnUiThreadUnlessDestroyed(() -> {
 			reset();
-			// TODO show failure somewhere persistent?
-			Toast.makeText(getActivity(), R.string.connection_failed,
-					LENGTH_LONG).show();
+			listener.keyAgreementFailed();
 		});
 	}
 
 	private void keyAgreementWaiting() {
 		runOnUiThreadUnlessDestroyed(
-				() -> status.setText(R.string.waiting_for_contact_to_scan));
+				() -> listener.keyAgreementWaiting(status));
 	}
 
 	private void keyAgreementStarted() {
 		runOnUiThreadUnlessDestroyed(() -> {
 			qrCodeView.setVisibility(INVISIBLE);
 			statusView.setVisibility(VISIBLE);
-			status.setText(R.string.authenticating_with_device);
+			listener.keyAgreementStarted(status);
 		});
 	}
 
@@ -297,18 +298,14 @@ public class KeyAgreementFragment extends BaseEventFragment
 			qrCodeView.setVisibility(VISIBLE);
 			statusView.setVisibility(INVISIBLE);
 			status.setText("");
-			// TODO show abort somewhere persistent?
-			Toast.makeText(getActivity(),
-					remoteAborted ? R.string.connection_aborted_remote :
-							R.string.connection_aborted_local, LENGTH_LONG)
-					.show();
+			listener.keyAgreementAborted(remoteAborted);
 		});
 	}
 
-	private void keyAgreementFinished() {
+	private void keyAgreementFinished(KeyAgreementResult result) {
 		runOnUiThreadUnlessDestroyed(() -> {
 			statusView.setVisibility(VISIBLE);
-			status.setText(R.string.exchanging_contact_details);
+			listener.keyAgreementFinished(status, result);
 		});
 	}
 
@@ -343,4 +340,22 @@ public class KeyAgreementFragment extends BaseEventFragment
 	protected void finish() {
 		getActivity().getSupportFragmentManager().popBackStack();
 	}
+
+	interface KeyAgreementEventListener {
+
+		@UiThread
+		void keyAgreementFailed();
+
+		@UiThread
+		void keyAgreementWaiting(TextView status);
+
+		@UiThread
+		void keyAgreementStarted(TextView status);
+
+		@UiThread
+		void keyAgreementAborted(boolean remoteAborted);
+
+		@UiThread
+		void keyAgreementFinished(TextView status, KeyAgreementResult result);
+	}
 }