diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..ece37d7282581872f2004d3048462a9c544b5f07
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/CrashFragment.java
@@ -0,0 +1,40 @@
+package org.briarproject.briar.android.reporting;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.briar.R;
+
+import static java.util.Objects.requireNonNull;
+
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+public class CrashFragment extends Fragment {
+
+	@Nullable
+	@Override
+	public View onCreateView(LayoutInflater inflater,
+			@Nullable ViewGroup container,
+			@Nullable Bundle savedInstanceState) {
+		View v = inflater
+				.inflate(R.layout.fragment_crash, container, false);
+
+		v.findViewById(R.id.acceptButton).setOnClickListener(view ->
+				getDevReportActivity().showReportForm(true));
+		v.findViewById(R.id.declineButton).setOnClickListener(view ->
+				getDevReportActivity().closeReport());
+
+		return v;
+	}
+
+	private DevReportActivity getDevReportActivity() {
+		return (DevReportActivity) requireNonNull(getActivity());
+	}
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/DevReportActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/DevReportActivity.java
index d20ef85737754feae03cf319aedaeaea1ba4ff53..1388016c7b85313b6caa167a435e101f82dddf0e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/DevReportActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/DevReportActivity.java
@@ -2,86 +2,32 @@ package org.briarproject.briar.android.reporting;
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.os.AsyncTask;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
 import android.support.v7.app.AppCompatDelegate;
 import android.support.v7.widget.Toolbar;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
-import org.acra.ReportField;
-import org.acra.collector.CrashReportData;
 import org.acra.dialog.BaseCrashReportDialog;
-import org.acra.file.CrashReportPersister;
-import org.acra.model.Element;
+import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
 import org.briarproject.briar.R;
 import org.briarproject.briar.android.Localizer;
 import org.briarproject.briar.android.util.UserFeedback;
-import org.json.JSONException;
 
 import java.io.File;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.logging.Logger;
 
 import static android.os.Build.VERSION.SDK_INT;
-import static android.view.View.GONE;
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.inputmethod.InputMethodManager.SHOW_FORCED;
-import static java.util.logging.Level.WARNING;
+import static java.util.Objects.requireNonNull;
 import static org.acra.ACRAConstants.EXTRA_REPORT_FILE;
-import static org.acra.ReportField.ANDROID_VERSION;
-import static org.acra.ReportField.APP_VERSION_CODE;
-import static org.acra.ReportField.APP_VERSION_NAME;
-import static org.acra.ReportField.PACKAGE_NAME;
-import static org.acra.ReportField.REPORT_ID;
-import static org.acra.ReportField.STACK_TRACE;
 import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
 
-public class DevReportActivity extends BaseCrashReportDialog
-		implements CompoundButton.OnCheckedChangeListener {
-
-	private static final Logger LOG =
-			Logger.getLogger(DevReportActivity.class.getName());
-
-	private static final String STATE_REVIEWING = "reviewing";
-	private static final Set<ReportField> requiredFields = new HashSet<>();
-
-	static {
-		requiredFields.add(REPORT_ID);
-		requiredFields.add(APP_VERSION_CODE);
-		requiredFields.add(APP_VERSION_NAME);
-		requiredFields.add(PACKAGE_NAME);
-		requiredFields.add(ANDROID_VERSION);
-		requiredFields.add(STACK_TRACE);
-	}
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+public class DevReportActivity extends BaseCrashReportDialog {
 
 	private AppCompatDelegate delegate;
-	private Set<ReportField> excludedFields = new HashSet<>();
-	private EditText userCommentView = null;
-	private EditText userEmailView = null;
-	private CheckBox includeDebugReport = null;
-	private Button chevron = null;
-	private LinearLayout report = null;
-	private View progress = null;
-	private MenuItem sendReport = null;
-	private boolean reviewing = false;
 
 	private AppCompatDelegate getDelegate() {
 		if (delegate == null) {
@@ -110,68 +56,21 @@ public class DevReportActivity extends BaseCrashReportDialog
 	}
 
 	@Override
-	public void init(Bundle state) {
+	public void init(@Nullable Bundle state) {
 		super.init(state);
 
 		if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);
 
 		getDelegate().setContentView(R.layout.activity_dev_report);
 
-		Toolbar tb = findViewById(R.id.toolbar);
-		getDelegate().setSupportActionBar(tb);
-
-		View requestReport = findViewById(R.id.request_report);
-		View reportForm = findViewById(R.id.report_form);
-		userCommentView = findViewById(R.id.user_comment);
-		userEmailView = findViewById(R.id.user_email);
-		includeDebugReport = findViewById(R.id.include_debug_report);
-		chevron = findViewById(R.id.chevron);
-		report = findViewById(R.id.report_content);
-		progress = findViewById(R.id.progress_wheel);
-
-		//noinspection ConstantConditions
-		getDelegate().getSupportActionBar().setTitle(
-				isFeedback() ? R.string.feedback_title :
-						R.string.crash_report_title);
-		userCommentView.setHint(isFeedback() ? R.string.enter_feedback :
-				R.string.describe_crash);
-
-		if (isFeedback()) {
-			includeDebugReport
-					.setText(getString(R.string.include_debug_report_feedback));
-			reportForm.setVisibility(VISIBLE);
-			requestReport.setVisibility(INVISIBLE);
-		} else {
-			includeDebugReport.setChecked(true);
-			reportForm.setVisibility(INVISIBLE);
-			requestReport.setVisibility(VISIBLE);
-		}
-
-		findViewById(R.id.acceptButton).setOnClickListener(v -> {
-			reviewing = true;
-			reportForm.setVisibility(VISIBLE);
-			requestReport.setVisibility(INVISIBLE);
-			((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
-					.showSoftInput(userCommentView, SHOW_FORCED);
-		});
-		findViewById(R.id.declineButton).setOnClickListener(v -> closeReport());
-		chevron.setOnClickListener(v -> {
-			boolean show =
-					chevron.getText().equals(getString(R.string.show));
-			if (show) {
-				chevron.setText(R.string.hide);
-				refresh();
-			} else {
-				chevron.setText(R.string.show);
-				report.setVisibility(GONE);
-			}
-		});
+		Toolbar toolbar = findViewById(R.id.toolbar);
+		getDelegate().setSupportActionBar(toolbar);
 
-		if (state != null)
-			reviewing = state.getBoolean(STATE_REVIEWING, isFeedback());
+		String title = getString(isFeedback() ? R.string.feedback_title :
+				R.string.crash_report_title);
+		requireNonNull(getDelegate().getSupportActionBar()).setTitle(title);
 
-		if (!isFeedback() && !reviewing)
-			requestReport.setVisibility(VISIBLE);
+		if (state == null) showReportForm(isFeedback());
 	}
 
 	@Override
@@ -181,47 +80,17 @@ public class DevReportActivity extends BaseCrashReportDialog
 	}
 
 	@Override
-	public void onPostCreate(Bundle state) {
+	public void onPostCreate(@Nullable Bundle state) {
 		super.onPostCreate(state);
 		getDelegate().onPostCreate(state);
 	}
 
-	@Override
-	public void onStart() {
-		super.onStart();
-		if (chevron.isSelected()) refresh();
-	}
-
 	@Override
 	protected void onPostResume() {
 		super.onPostResume();
 		getDelegate().onPostResume();
 	}
 
-	@Override
-	public boolean onCreateOptionsMenu(Menu menu) {
-		// Inflate the menu items for use in the action bar
-		MenuInflater inflater = getDelegate().getMenuInflater();
-		inflater.inflate(R.menu.dev_report_actions, menu);
-		sendReport = menu.findItem(R.id.action_send_report);
-		return super.onCreateOptionsMenu(menu);
-	}
-
-	@Override
-	public boolean onOptionsItemSelected(MenuItem item) {
-		// Handle presses on the action bar items
-		switch (item.getItemId()) {
-			case android.R.id.home:
-				onBackPressed();
-				return true;
-			case R.id.action_send_report:
-				processReport();
-				return true;
-			default:
-				return super.onOptionsItemSelected(item);
-		}
-	}
-
 	@Override
 	public void onTitleChanged(CharSequence title, int color) {
 		super.onTitleChanged(title, color);
@@ -234,12 +103,6 @@ public class DevReportActivity extends BaseCrashReportDialog
 		getDelegate().onConfigurationChanged(newConfig);
 	}
 
-	@Override
-	public void onSaveInstanceState(Bundle state) {
-		super.onSaveInstanceState(state);
-		state.putBoolean(STATE_REVIEWING, reviewing);
-	}
-
 	@Override
 	public void onStop() {
 		super.onStop();
@@ -257,132 +120,33 @@ public class DevReportActivity extends BaseCrashReportDialog
 		closeReport();
 	}
 
-	@Override
-	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-		ReportField field = (ReportField) buttonView.getTag();
-		if (field != null) {
-			if (isChecked) excludedFields.remove(field);
-			else excludedFields.add(field);
-		}
+	void sendCrashReport(String comment, String email) {
+		sendCrash(comment, email);
 	}
 
-	@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
 	private boolean isFeedback() {
 		return getException() instanceof UserFeedback;
 	}
 
-	private void refresh() {
-		report.setVisibility(INVISIBLE);
-		progress.setVisibility(VISIBLE);
-		report.removeAllViews();
-		new AsyncTask<Void, Void, CrashReportData>() {
-
-			@Override
-			protected CrashReportData doInBackground(Void... args) {
-				File reportFile = (File) getIntent().getSerializableExtra(
-						EXTRA_REPORT_FILE);
-				CrashReportPersister persister = new CrashReportPersister();
-				try {
-					return persister.load(reportFile);
-				} catch (IOException | JSONException e) {
-					LOG.log(WARNING, "Could not load report file", e);
-					return null;
-				}
-			}
-
-			@Override
-			protected void onPostExecute(CrashReportData crashData) {
-				LayoutInflater inflater = getLayoutInflater();
-				if (crashData != null) {
-					for (Entry<ReportField, Element> e : crashData.entrySet()) {
-						ReportField field = e.getKey();
-						String value = e.getValue().toString()
-								.replaceAll("\\\\n", "\n");
-						boolean required = requiredFields.contains(field);
-						boolean excluded = excludedFields.contains(field);
-						View v = inflater.inflate(R.layout.list_item_crash,
-								report, false);
-						CheckBox cb = v.findViewById(R.id.include_in_report);
-						cb.setTag(field);
-						cb.setChecked(required || !excluded);
-						cb.setEnabled(!required);
-						cb.setOnCheckedChangeListener(DevReportActivity.this);
-						cb.setText(field.toString());
-						TextView content = v.findViewById(R.id.content);
-						content.setText(value);
-						report.addView(v);
-					}
-				} else {
-					View v = inflater.inflate(
-							android.R.layout.simple_list_item_1, report, false);
-					TextView error = v.findViewById(android.R.id.text1);
-					error.setText(R.string.could_not_load_report_data);
-					report.addView(v);
-				}
-				report.setVisibility(VISIBLE);
-				progress.setVisibility(GONE);
-			}
-		}.execute();
-	}
-
-	private void processReport() {
-		userCommentView.setEnabled(false);
-		userEmailView.setEnabled(false);
-		sendReport.setEnabled(false);
-		progress.setVisibility(VISIBLE);
-		boolean includeReport = !isFeedback() || includeDebugReport.isChecked();
-		new AsyncTask<Void, Void, Boolean>() {
-
-			@Override
-			protected Boolean doInBackground(Void... args) {
-				File reportFile = (File) getIntent().getSerializableExtra(
-						EXTRA_REPORT_FILE);
-				CrashReportPersister persister = new CrashReportPersister();
-				try {
-					CrashReportData data = persister.load(reportFile);
-					if (includeReport) {
-						for (ReportField field : excludedFields) {
-							LOG.info("Removing field " + field.name());
-							data.remove(field);
-						}
-					} else {
-						Iterator<Entry<ReportField, Element>> iter =
-								data.entrySet().iterator();
-						while (iter.hasNext()) {
-							Entry<ReportField, Element> e = iter.next();
-							if (!requiredFields.contains(e.getKey())) {
-								iter.remove();
-							}
-						}
-					}
-					persister.store(data, reportFile);
-					return true;
-				} catch (IOException | JSONException e) {
-					LOG.log(WARNING, "Error processing report file", e);
-					return false;
-				}
-			}
-
-			@Override
-			protected void onPostExecute(Boolean success) {
-				if (success) {
-					// Retrieve user's comment and email address, if any
-					String comment = "";
-					if (userCommentView != null)
-						comment = userCommentView.getText().toString();
-					String email = "";
-					if (userEmailView != null) {
-						email = userEmailView.getText().toString();
-					}
-					sendCrash(comment, email);
-				}
-				finish();
-			}
-		}.execute();
+	void showReportForm(boolean showReportForm) {
+		Fragment f;
+		if (showReportForm) {
+			File file =
+					(File) getIntent().getSerializableExtra(EXTRA_REPORT_FILE);
+			f = ReportFormFragment.newInstance(isFeedback(), file);
+			requireNonNull(getDelegate().getSupportActionBar()).show();
+		} else {
+			f = new CrashFragment();
+			requireNonNull(getDelegate().getSupportActionBar()).hide();
+		}
+		getSupportFragmentManager().beginTransaction()
+				.replace(R.id.fragmentContainer, f)
+				.commit();
 	}
 
-	private void closeReport() {
+	void closeReport() {
 		cancelReports();
 		finish();
 	}
+
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java
new file mode 100644
index 0000000000000000000000000000000000000000..a90e7201201b8bf52dda1746629604030ad85755
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/ReportFormFragment.java
@@ -0,0 +1,286 @@
+package org.briarproject.briar.android.reporting;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.acra.ReportField;
+import org.acra.collector.CrashReportData;
+import org.acra.file.CrashReportPersister;
+import org.acra.model.Element;
+import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.briar.R;
+import org.json.JSONException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import static android.view.MenuItem.SHOW_AS_ACTION_ALWAYS;
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static java.util.Objects.requireNonNull;
+import static java.util.logging.Level.WARNING;
+import static java.util.logging.Logger.getLogger;
+import static org.acra.ACRAConstants.EXTRA_REPORT_FILE;
+import static org.acra.ReportField.ANDROID_VERSION;
+import static org.acra.ReportField.APP_VERSION_CODE;
+import static org.acra.ReportField.APP_VERSION_NAME;
+import static org.acra.ReportField.PACKAGE_NAME;
+import static org.acra.ReportField.REPORT_ID;
+import static org.acra.ReportField.STACK_TRACE;
+
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+public class ReportFormFragment extends Fragment
+		implements CompoundButton.OnCheckedChangeListener {
+
+	private static final Logger LOG =
+			getLogger(ReportFormFragment.class.getName());
+	private static final String IS_FEEDBACK = "isFeedback";
+	private static final Set<ReportField> requiredFields = new HashSet<>();
+	private static final Set<ReportField> excludedFields = new HashSet<>();
+	static {
+		requiredFields.add(REPORT_ID);
+		requiredFields.add(APP_VERSION_CODE);
+		requiredFields.add(APP_VERSION_NAME);
+		requiredFields.add(PACKAGE_NAME);
+		requiredFields.add(ANDROID_VERSION);
+		requiredFields.add(STACK_TRACE);
+	}
+
+	private boolean isFeedback;
+	private File reportFile;
+
+	private EditText userCommentView;
+	private EditText userEmailView;
+	private CheckBox includeDebugReport;
+	private Button chevron;
+	private LinearLayout report;
+	private View progress;
+	@Nullable
+	private MenuItem sendReport;
+
+	static ReportFormFragment newInstance(boolean isFeedback,
+			File reportFile) {
+		ReportFormFragment f = new ReportFormFragment();
+		Bundle args = new Bundle();
+		args.putBoolean(IS_FEEDBACK, isFeedback);
+		args.putSerializable(EXTRA_REPORT_FILE, reportFile);
+		f.setArguments(args);
+		return f;
+	}
+
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setHasOptionsMenu(true);
+	}
+
+	@Nullable
+	@Override
+	public View onCreateView(LayoutInflater inflater,
+			@Nullable ViewGroup container,
+			@Nullable Bundle savedInstanceState) {
+		View v = inflater.inflate(R.layout.fragment_report_form, container,
+				false);
+
+		userCommentView = v.findViewById(R.id.user_comment);
+		userEmailView = v.findViewById(R.id.user_email);
+		includeDebugReport = v.findViewById(R.id.include_debug_report);
+		chevron = v.findViewById(R.id.chevron);
+		report = v.findViewById(R.id.report_content);
+		progress = v.findViewById(R.id.progress_wheel);
+
+		Bundle args = requireNonNull(getArguments());
+		isFeedback = args.getBoolean(IS_FEEDBACK);
+		reportFile =
+				(File) requireNonNull(args.getSerializable(EXTRA_REPORT_FILE));
+
+		if (isFeedback) {
+			includeDebugReport
+					.setText(getString(R.string.include_debug_report_feedback));
+			userCommentView.setHint(R.string.enter_feedback);
+		} else {
+			includeDebugReport.setChecked(true);
+			userCommentView.setHint(R.string.describe_crash);
+		}
+
+		chevron.setOnClickListener(view -> {
+			boolean show = chevron.getText().equals(getString(R.string.show));
+			if (show) {
+				chevron.setText(R.string.hide);
+				refresh();
+			} else {
+				chevron.setText(R.string.show);
+				report.setVisibility(GONE);
+			}
+		});
+
+		return v;
+	}
+
+	@Override
+	public void onStart() {
+		super.onStart();
+		if (chevron.isSelected()) refresh();
+	}
+
+	@Override
+	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+		inflater.inflate(R.menu.dev_report_actions, menu);
+		sendReport = menu.findItem(R.id.action_send_report);
+		// calling setShowAsAction() shouldn't be needed, but for some reason is
+		sendReport.setShowAsAction(SHOW_AS_ACTION_ALWAYS);
+		super.onCreateOptionsMenu(menu, inflater);
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		if (item.getItemId() == R.id.action_send_report) {
+			processReport();
+			return true;
+		}
+		return super.onOptionsItemSelected(item);
+	}
+
+	@Override
+	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+		ReportField field = (ReportField) buttonView.getTag();
+		if (field != null) {
+			if (isChecked) excludedFields.remove(field);
+			else excludedFields.add(field);
+		}
+	}
+
+	private void refresh() {
+		report.setVisibility(INVISIBLE);
+		progress.setVisibility(VISIBLE);
+		report.removeAllViews();
+		new AsyncTask<Void, Void, CrashReportData>() {
+
+			@Override
+			protected CrashReportData doInBackground(Void... args) {
+				CrashReportPersister persister = new CrashReportPersister();
+				try {
+					return persister.load(reportFile);
+				} catch (IOException | JSONException e) {
+					LOG.log(WARNING, "Could not load report file", e);
+					return null;
+				}
+			}
+
+			@Override
+			protected void onPostExecute(CrashReportData crashData) {
+				LayoutInflater inflater = getLayoutInflater();
+				if (crashData != null) {
+					for (Map.Entry<ReportField, Element> e : crashData.entrySet()) {
+						ReportField field = e.getKey();
+						String value = e.getValue().toString()
+								.replaceAll("\\\\n", "\n");
+						boolean required = requiredFields.contains(field);
+						boolean excluded = excludedFields.contains(field);
+						View v = inflater.inflate(R.layout.list_item_crash,
+								report, false);
+						CheckBox cb = v.findViewById(R.id.include_in_report);
+						cb.setTag(field);
+						cb.setChecked(required || !excluded);
+						cb.setEnabled(!required);
+						cb.setOnCheckedChangeListener(ReportFormFragment.this);
+						cb.setText(field.toString());
+						TextView content = v.findViewById(R.id.content);
+						content.setText(value);
+						report.addView(v);
+					}
+				} else {
+					View v = inflater.inflate(
+							android.R.layout.simple_list_item_1, report, false);
+					TextView error = v.findViewById(android.R.id.text1);
+					error.setText(R.string.could_not_load_report_data);
+					report.addView(v);
+				}
+				report.setVisibility(VISIBLE);
+				progress.setVisibility(GONE);
+			}
+		}.execute();
+	}
+
+	private void processReport() {
+		userCommentView.setEnabled(false);
+		userEmailView.setEnabled(false);
+		requireNonNull(sendReport).setEnabled(false);
+		progress.setVisibility(VISIBLE);
+		boolean includeReport = !isFeedback || includeDebugReport.isChecked();
+		new AsyncTask<Void, Void, Boolean>() {
+
+			@Override
+			protected Boolean doInBackground(Void... args) {
+				CrashReportPersister persister = new CrashReportPersister();
+				try {
+					CrashReportData data = persister.load(reportFile);
+					if (includeReport) {
+						for (ReportField field : excludedFields) {
+							LOG.info("Removing field " + field.name());
+							data.remove(field);
+						}
+					} else {
+						Iterator<Map.Entry<ReportField, Element>> iter =
+								data.entrySet().iterator();
+						while (iter.hasNext()) {
+							Map.Entry<ReportField, Element> e = iter.next();
+							if (!requiredFields.contains(e.getKey())) {
+								iter.remove();
+							}
+						}
+					}
+					persister.store(data, reportFile);
+					return true;
+				} catch (IOException | JSONException e) {
+					LOG.log(WARNING, "Error processing report file", e);
+					return false;
+				}
+			}
+
+			@Override
+			protected void onPostExecute(Boolean success) {
+				if (success) {
+					// Retrieve user's comment and email address, if any
+					String comment = "";
+					if (userCommentView != null)
+						comment = userCommentView.getText().toString();
+					String email = "";
+					if (userEmailView != null) {
+						email = userEmailView.getText().toString();
+					}
+					getDevReportActivity().sendCrashReport(comment, email);
+				}
+				if (getActivity() != null) getActivity().finish();
+			}
+		}.execute();
+	}
+
+	private DevReportActivity getDevReportActivity() {
+		return (DevReportActivity) requireNonNull(getActivity());
+	}
+
+}
diff --git a/briar-android/src/main/res/layout/activity_dev_report.xml b/briar-android/src/main/res/layout/activity_dev_report.xml
index 5f7271d556929a1a6a94ee762990c40f9ed0f079..c06205fafcfa7d9013163ec7c31e4f5c56a10df9 100644
--- a/briar-android/src/main/res/layout/activity_dev_report.xml
+++ b/briar-android/src/main/res/layout/activity_dev_report.xml
@@ -1,244 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
+<LinearLayout
 	xmlns:android="http://schemas.android.com/apk/res/android"
-	xmlns:app="http://schemas.android.com/apk/res-auto"
-	xmlns:tools="http://schemas.android.com/tools"
 	android:layout_width="match_parent"
-	android:layout_height="match_parent">
+	android:layout_height="match_parent"
+	android:orientation="vertical">
 
-	<android.support.constraint.ConstraintLayout
-		android:id="@+id/report_form"
+	<include
+		android:id="@+id/appBar"
+		layout="@layout/toolbar"
 		android:layout_width="match_parent"
-		android:layout_height="match_parent"
-		android:visibility="visible"
-		tools:context=".android.reporting.DevReportActivity"
-		tools:visibility="invisible">
+		android:layout_height="wrap_content"/>
 
-		<include
-			android:id="@+id/appBar"
-			layout="@layout/toolbar"
-			android:layout_width="0dp"
-			android:layout_height="wrap_content"
-			app:layout_constraintEnd_toEndOf="parent"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toTopOf="parent"/>
-
-		<android.support.design.widget.TextInputLayout
-			android:id="@+id/user_comment_layout"
-			android:layout_width="0dp"
-			android:layout_height="wrap_content"
-			android:layout_marginEnd="@dimen/margin_large"
-			android:layout_marginLeft="@dimen/margin_large"
-			android:layout_marginRight="@dimen/margin_large"
-			android:layout_marginStart="@dimen/margin_large"
-			android:layout_marginTop="@dimen/margin_large"
-			app:hintEnabled="false"
-			app:layout_constraintEnd_toEndOf="parent"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toBottomOf="@+id/appBar">
-
-			<android.support.design.widget.TextInputEditText
-				android:id="@+id/user_comment"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:inputType="textMultiLine|textCapSentences"
-				android:maxLines="5"
-				tools:hint="@string/describe_crash"/>
-
-		</android.support.design.widget.TextInputLayout>
-
-		<android.support.design.widget.TextInputLayout
-			android:id="@+id/user_email_layout"
-			android:layout_width="0dp"
-			android:layout_height="wrap_content"
-			android:layout_marginEnd="@dimen/margin_large"
-			android:layout_marginLeft="@dimen/margin_large"
-			android:layout_marginRight="@dimen/margin_large"
-			android:layout_marginStart="@dimen/margin_large"
-			app:hintEnabled="false"
-			app:layout_constraintEnd_toEndOf="parent"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toBottomOf="@+id/user_comment_layout">
-
-			<android.support.design.widget.TextInputEditText
-				android:id="@+id/user_email"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:hint="@string/optional_contact_email"
-				android:inputType="textEmailAddress"
-				android:maxLines="1"/>
-
-		</android.support.design.widget.TextInputLayout>
-
-		<CheckBox
-			android:id="@+id/include_debug_report"
-			android:layout_width="0dp"
-			android:layout_height="wrap_content"
-			android:layout_marginLeft="@dimen/margin_large"
-			android:layout_marginStart="@dimen/margin_large"
-			android:checked="false"
-			android:text="@string/include_debug_report_crash"
-			app:layout_constraintBottom_toBottomOf="@+id/chevron"
-			app:layout_constraintEnd_toStartOf="@+id/chevron"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toTopOf="@+id/chevron"/>
-
-		<Button
-			android:id="@+id/chevron"
-			style="@style/BriarButtonFlat.Positive"
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
-			android:text="@string/show"
-			app:layout_constraintEnd_toEndOf="parent"
-			app:layout_constraintTop_toBottomOf="@+id/user_email_layout"/>
-
-		<ScrollView
-			android:id="@+id/report_scroll"
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toBottomOf="@id/include_debug_report">
-
-			<LinearLayout
-				android:id="@+id/report_content"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:gravity="center_horizontal"
-				android:orientation="vertical"
-				android:paddingBottom="@dimen/listitem_height_one_line_avatar"
-				android:paddingEnd="@dimen/margin_large"
-				android:paddingStart="@dimen/margin_large"
-				android:paddingTop="@dimen/margin_small"
-				android:visibility="gone"
-				tools:visibility="visible"/>
-
-		</ScrollView>
-
-		<ProgressBar
-			android:id="@+id/progress_wheel"
-			style="?android:attr/progressBarStyleLarge"
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
-			android:indeterminate="true"
-			android:visibility="gone"
-			app:layout_constraintBottom_toBottomOf="parent"
-			app:layout_constraintEnd_toEndOf="parent"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toBottomOf="@+id/include_debug_report"
-			tools:visibility="visible"/>
-
-	</android.support.constraint.ConstraintLayout>
-
-	<ScrollView
-		android:id="@+id/request_report"
+	<FrameLayout
+		android:id="@+id/fragmentContainer"
 		android:layout_width="match_parent"
-		android:layout_height="match_parent"
-		android:fillViewport="true"
-		android:visibility="invisible"
-		tools:visibility="visible">
-
-		<android.support.constraint.ConstraintLayout
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:padding="@dimen/margin_large">
-
-			<android.support.v7.widget.AppCompatImageView
-				android:id="@+id/errorIcon"
-				android:layout_width="wrap_content"
-				android:layout_height="wrap_content"
-				android:layout_margin="16dp"
-				android:src="@drawable/ic_crash"
-				app:layout_constraintEnd_toEndOf="parent"
-				app:layout_constraintStart_toStartOf="parent"
-				app:layout_constraintTop_toTopOf="parent"
-				app:layout_constraintBottom_toTopOf="@+id/crashed"
-				app:layout_constraintVertical_chainStyle="packed"
-				app:tint="?attr/colorControlNormal"
-				tools:ignore="ContentDescription"/>
-
-			<TextView
-				android:id="@+id/crashed"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:gravity="center"
-				android:text="@string/briar_crashed"
-				android:textColor="?android:attr/textColorSecondary"
-				android:textSize="@dimen/text_size_large"
-				app:layout_constraintBottom_toTopOf="@+id/fault"
-				app:layout_constraintEnd_toEndOf="parent"
-				app:layout_constraintStart_toStartOf="parent"
-				app:layout_constraintTop_toBottomOf="@+id/errorIcon"
-				tools:layout_editor_absoluteY="8dp"/>
-
-			<TextView
-				android:id="@+id/fault"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:layout_marginTop="@dimen/margin_large"
-				android:gravity="center"
-				android:text="@string/not_your_fault"
-				android:textColor="?android:attr/textColorSecondary"
-				android:textSize="@dimen/text_size_large"
-				app:layout_constraintBottom_toTopOf="@+id/pleaseSend"
-				app:layout_constraintEnd_toEndOf="parent"
-				app:layout_constraintStart_toStartOf="parent"
-				app:layout_constraintTop_toBottomOf="@+id/crashed"/>
-
-			<TextView
-				android:id="@+id/pleaseSend"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:layout_marginTop="@dimen/margin_large"
-				android:gravity="center"
-				android:text="@string/please_send_report"
-				android:textColor="?android:attr/textColorSecondary"
-				android:textSize="@dimen/text_size_large"
-				app:layout_constraintBottom_toTopOf="@+id/encrypted"
-				app:layout_constraintEnd_toEndOf="parent"
-				app:layout_constraintStart_toStartOf="parent"
-				app:layout_constraintTop_toBottomOf="@+id/fault"/>
-
-			<TextView
-				android:id="@+id/encrypted"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:layout_marginTop="@dimen/margin_large"
-				android:gravity="center"
-				android:text="@string/report_is_encrypted"
-				android:textColor="?android:attr/textColorSecondary"
-				android:textSize="@dimen/text_size_large"
-				app:layout_constraintBottom_toTopOf="@+id/acceptButton"
-				app:layout_constraintEnd_toEndOf="parent"
-				app:layout_constraintStart_toStartOf="parent"
-				app:layout_constraintTop_toBottomOf="@+id/pleaseSend"/>
-
-			<Button
-				android:id="@+id/declineButton"
-				style="@style/BriarButtonFlat.Negative"
-				android:layout_width="0dp"
-				android:layout_height="wrap_content"
-				android:text="@string/close"
-				app:layout_constraintBottom_toBottomOf="@+id/acceptButton"
-				app:layout_constraintEnd_toStartOf="@+id/acceptButton"
-				app:layout_constraintHorizontal_weight="1"
-				app:layout_constraintStart_toStartOf="parent"
-				app:layout_constraintTop_toTopOf="@+id/acceptButton"/>
-
-			<Button
-				android:id="@+id/acceptButton"
-				style="@style/BriarButtonFlat.Positive"
-				android:layout_width="0dp"
-				android:layout_height="wrap_content"
-				android:layout_marginTop="@dimen/margin_large"
-				android:text="@string/send_report"
-				app:layout_constraintBottom_toBottomOf="parent"
-				app:layout_constraintEnd_toEndOf="parent"
-				app:layout_constraintHorizontal_weight="1"
-				app:layout_constraintStart_toEndOf="@+id/declineButton"
-				app:layout_constraintTop_toBottomOf="@+id/encrypted"/>
-
-		</android.support.constraint.ConstraintLayout>
-
-	</ScrollView>
+		android:layout_height="match_parent"/>
 
-</FrameLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/briar-android/src/main/res/layout/fragment_crash.xml b/briar-android/src/main/res/layout/fragment_crash.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8dd38ac7769e96e504e814c82387c413b666616a
--- /dev/null
+++ b/briar-android/src/main/res/layout/fragment_crash.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto"
+	xmlns:tools="http://schemas.android.com/tools"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent">
+
+	<ScrollView
+		android:id="@+id/request_report"
+		android:layout_width="match_parent"
+		android:layout_height="match_parent"
+		android:fillViewport="true">
+
+		<android.support.constraint.ConstraintLayout
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:padding="@dimen/margin_large">
+
+			<android.support.v7.widget.AppCompatImageView
+				android:id="@+id/errorIcon"
+				android:layout_width="wrap_content"
+				android:layout_height="wrap_content"
+				android:layout_margin="16dp"
+				android:src="@drawable/ic_crash"
+				app:layout_constraintBottom_toTopOf="@+id/crashed"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toTopOf="parent"
+				app:layout_constraintVertical_chainStyle="packed"
+				app:tint="?attr/colorControlNormal"
+				tools:ignore="ContentDescription"/>
+
+			<TextView
+				android:id="@+id/crashed"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:gravity="center"
+				android:text="@string/briar_crashed"
+				android:textColor="?android:attr/textColorSecondary"
+				android:textSize="@dimen/text_size_large"
+				app:layout_constraintBottom_toTopOf="@+id/fault"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toBottomOf="@+id/errorIcon"
+				tools:layout_editor_absoluteY="8dp"/>
+
+			<TextView
+				android:id="@+id/fault"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:layout_marginTop="@dimen/margin_large"
+				android:gravity="center"
+				android:text="@string/not_your_fault"
+				android:textColor="?android:attr/textColorSecondary"
+				android:textSize="@dimen/text_size_large"
+				app:layout_constraintBottom_toTopOf="@+id/pleaseSend"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toBottomOf="@+id/crashed"/>
+
+			<TextView
+				android:id="@+id/pleaseSend"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:layout_marginTop="@dimen/margin_large"
+				android:gravity="center"
+				android:text="@string/please_send_report"
+				android:textColor="?android:attr/textColorSecondary"
+				android:textSize="@dimen/text_size_large"
+				app:layout_constraintBottom_toTopOf="@+id/encrypted"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toBottomOf="@+id/fault"/>
+
+			<TextView
+				android:id="@+id/encrypted"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:layout_marginTop="@dimen/margin_large"
+				android:gravity="center"
+				android:text="@string/report_is_encrypted"
+				android:textColor="?android:attr/textColorSecondary"
+				android:textSize="@dimen/text_size_large"
+				app:layout_constraintBottom_toTopOf="@+id/acceptButton"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toBottomOf="@+id/pleaseSend"/>
+
+			<Button
+				android:id="@+id/declineButton"
+				style="@style/BriarButtonFlat.Negative"
+				android:layout_width="0dp"
+				android:layout_height="wrap_content"
+				android:text="@string/close"
+				app:layout_constraintBottom_toBottomOf="@+id/acceptButton"
+				app:layout_constraintEnd_toStartOf="@+id/acceptButton"
+				app:layout_constraintHorizontal_weight="1"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toTopOf="@+id/acceptButton"/>
+
+			<Button
+				android:id="@+id/acceptButton"
+				style="@style/BriarButtonFlat.Positive"
+				android:layout_width="0dp"
+				android:layout_height="wrap_content"
+				android:layout_marginTop="@dimen/margin_large"
+				android:text="@string/send_report"
+				app:layout_constraintBottom_toBottomOf="parent"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintHorizontal_weight="1"
+				app:layout_constraintStart_toEndOf="@+id/declineButton"
+				app:layout_constraintTop_toBottomOf="@+id/encrypted"/>
+
+		</android.support.constraint.ConstraintLayout>
+
+	</ScrollView>
+
+</android.support.constraint.ConstraintLayout>
\ No newline at end of file
diff --git a/briar-android/src/main/res/layout/fragment_report_form.xml b/briar-android/src/main/res/layout/fragment_report_form.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b69eea3ae78fd8fa303be4af422de992accba24a
--- /dev/null
+++ b/briar-android/src/main/res/layout/fragment_report_form.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	xmlns:app="http://schemas.android.com/apk/res-auto"
+	xmlns:tools="http://schemas.android.com/tools">
+
+	<android.support.design.widget.TextInputLayout
+		android:id="@+id/user_comment_layout"
+		android:layout_width="0dp"
+		android:layout_height="wrap_content"
+		android:layout_marginEnd="@dimen/margin_large"
+		android:layout_marginLeft="@dimen/margin_large"
+		android:layout_marginRight="@dimen/margin_large"
+		android:layout_marginStart="@dimen/margin_large"
+		android:layout_marginTop="@dimen/margin_large"
+		app:hintEnabled="false"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toTopOf="parent">
+
+		<android.support.design.widget.TextInputEditText
+			android:id="@+id/user_comment"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:inputType="textMultiLine|textCapSentences"
+			android:maxLines="5"
+			tools:hint="@string/describe_crash"/>
+
+	</android.support.design.widget.TextInputLayout>
+
+	<android.support.design.widget.TextInputLayout
+		android:id="@+id/user_email_layout"
+		android:layout_width="0dp"
+		android:layout_height="wrap_content"
+		android:layout_marginEnd="@dimen/margin_large"
+		android:layout_marginLeft="@dimen/margin_large"
+		android:layout_marginRight="@dimen/margin_large"
+		android:layout_marginStart="@dimen/margin_large"
+		app:hintEnabled="false"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toBottomOf="@+id/user_comment_layout">
+
+		<android.support.design.widget.TextInputEditText
+			android:id="@+id/user_email"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:hint="@string/optional_contact_email"
+			android:inputType="textEmailAddress"
+			android:maxLines="1"/>
+
+	</android.support.design.widget.TextInputLayout>
+
+	<CheckBox
+		android:id="@+id/include_debug_report"
+		android:layout_width="0dp"
+		android:layout_height="wrap_content"
+		android:layout_marginLeft="@dimen/margin_large"
+		android:layout_marginStart="@dimen/margin_large"
+		android:checked="false"
+		android:text="@string/include_debug_report_crash"
+		app:layout_constraintBottom_toBottomOf="@+id/chevron"
+		app:layout_constraintEnd_toStartOf="@+id/chevron"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toTopOf="@+id/chevron"/>
+
+	<Button
+		android:id="@+id/chevron"
+		style="@style/BriarButtonFlat.Positive"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:text="@string/show"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintTop_toBottomOf="@+id/user_email_layout"/>
+
+	<ScrollView
+		android:id="@+id/report_scroll"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toBottomOf="@id/include_debug_report">
+
+		<LinearLayout
+			android:id="@+id/report_content"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:gravity="center_horizontal"
+			android:orientation="vertical"
+			android:paddingBottom="@dimen/listitem_height_one_line_avatar"
+			android:paddingEnd="@dimen/margin_large"
+			android:paddingStart="@dimen/margin_large"
+			android:paddingTop="@dimen/margin_small"
+			android:visibility="gone"
+			tools:visibility="visible"/>
+
+	</ScrollView>
+
+	<ProgressBar
+		android:id="@+id/progress_wheel"
+		style="?android:attr/progressBarStyleLarge"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:indeterminate="true"
+		android:visibility="gone"
+		app:layout_constraintBottom_toBottomOf="parent"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toBottomOf="@+id/include_debug_report"
+		tools:visibility="visible"/>
+
+</android.support.constraint.ConstraintLayout>