From 1aa701ceeeaa4e2a2ff4bfa36844d42f2fa8b88e Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Tue, 30 Apr 2013 17:24:04 +0100
Subject: [PATCH] Save and restore UI state when screen is rotated.

Fixes issue #3611920, but keep the issue open because more activities
will be added.
---
 .../sf/briar/android/HomeScreenActivity.java  |  1 +
 .../net/sf/briar/android/SetupActivity.java   |  3 ++
 .../android/blogs/ConfigureBlogActivity.java  |  9 ++--
 .../android/blogs/CreateBlogActivity.java     |  3 ++
 .../android/blogs/WriteBlogPostActivity.java  | 44 ++++++++++++++++++-
 .../groups/ConfigureGroupActivity.java        |  9 ++--
 .../android/groups/CreateGroupActivity.java   |  3 ++
 .../groups/WriteGroupPostActivity.java        | 44 ++++++++++++++++++-
 .../identity/CreateIdentityActivity.java      |  1 +
 .../invitation/AddContactActivity.java        |  4 +-
 .../android/invitation/CodeEntryWidget.java   |  1 +
 .../messages/WritePrivateMessageActivity.java | 19 +++++++-
 12 files changed, 128 insertions(+), 13 deletions(-)

diff --git a/briar-android/src/net/sf/briar/android/HomeScreenActivity.java b/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
index 2798b2693e..02ae13fcc5 100644
--- a/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
+++ b/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
@@ -203,6 +203,7 @@ public class HomeScreenActivity extends RoboActivity {
 		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);
diff --git a/briar-android/src/net/sf/briar/android/SetupActivity.java b/briar-android/src/net/sf/briar/android/SetupActivity.java
index f54e4e6746..e272774b7b 100644
--- a/briar-android/src/net/sf/briar/android/SetupActivity.java
+++ b/briar-android/src/net/sf/briar/android/SetupActivity.java
@@ -79,6 +79,7 @@ public class SetupActivity extends RoboActivity implements OnClickListener {
 				enableOrDisableContinueButton();
 			}
 		};
+		nicknameEntry.setId(1);
 		nicknameEntry.setMaxLines(1);
 		int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_WORDS;
 		nicknameEntry.setInputType(inputType);
@@ -98,6 +99,7 @@ public class SetupActivity extends RoboActivity implements OnClickListener {
 				enableOrDisableContinueButton();
 			}
 		};
+		passwordEntry.setId(2);
 		passwordEntry.setMaxLines(1);
 		inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
 		passwordEntry.setInputType(inputType);
@@ -117,6 +119,7 @@ public class SetupActivity extends RoboActivity implements OnClickListener {
 				enableOrDisableContinueButton();
 			}
 		};
+		passwordConfirmation.setId(3);
 		passwordConfirmation.setMaxLines(1);
 		inputType = TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD;
 		passwordConfirmation.setInputType(inputType);
diff --git a/briar-android/src/net/sf/briar/android/blogs/ConfigureBlogActivity.java b/briar-android/src/net/sf/briar/android/blogs/ConfigureBlogActivity.java
index 420eebf2bc..3c5699330b 100644
--- a/briar-android/src/net/sf/briar/android/blogs/ConfigureBlogActivity.java
+++ b/briar-android/src/net/sf/briar/android/blogs/ConfigureBlogActivity.java
@@ -87,6 +87,7 @@ SelectContactsDialog.Listener {
 		layout.setGravity(CENTER_HORIZONTAL);
 
 		subscribeCheckBox = new CheckBox(this);
+		subscribeCheckBox.setId(1);
 		subscribeCheckBox.setText(R.string.subscribe_to_this_blog);
 		subscribeCheckBox.setChecked(subscribed);
 		subscribeCheckBox.setOnClickListener(this);
@@ -96,21 +97,21 @@ SelectContactsDialog.Listener {
 		radioGroup.setOrientation(VERTICAL);
 
 		visibleToAll = new RadioButton(this);
-		visibleToAll.setId(1);
+		visibleToAll.setId(2);
 		visibleToAll.setText(R.string.blog_visible_to_all);
 		visibleToAll.setEnabled(subscribed);
 		visibleToAll.setOnClickListener(this);
 		radioGroup.addView(visibleToAll);
 
 		visibleToSome = new RadioButton(this);
-		visibleToSome.setId(2);
+		visibleToSome.setId(3);
 		visibleToSome.setText(R.string.blog_visible_to_some);
 		visibleToSome.setEnabled(subscribed);
 		visibleToSome.setOnClickListener(this);
 		radioGroup.addView(visibleToSome);
 
-		if(!subscribed || all) radioGroup.check(1);
-		else radioGroup.check(2);
+		if(!subscribed || all) radioGroup.check(visibleToAll.getId());
+		else radioGroup.check(visibleToSome.getId());
 		layout.addView(radioGroup);
 
 		doneButton = new Button(this);
diff --git a/briar-android/src/net/sf/briar/android/blogs/CreateBlogActivity.java b/briar-android/src/net/sf/briar/android/blogs/CreateBlogActivity.java
index fe7d99ae64..1d3f5deb46 100644
--- a/briar-android/src/net/sf/briar/android/blogs/CreateBlogActivity.java
+++ b/briar-android/src/net/sf/briar/android/blogs/CreateBlogActivity.java
@@ -99,6 +99,7 @@ SelectContactsDialog.Listener {
 				enableOrDisableCreateButton();
 			}
 		};
+		nameEntry.setId(1);
 		nameEntry.setMaxLines(1);
 		nameEntry.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES);
 		nameEntry.setOnEditorActionListener(this);
@@ -108,11 +109,13 @@ SelectContactsDialog.Listener {
 		radioGroup.setOrientation(VERTICAL);
 
 		visibleToAll = new RadioButton(this);
+		visibleToAll.setId(2);
 		visibleToAll.setText(R.string.blog_visible_to_all);
 		visibleToAll.setOnClickListener(this);
 		radioGroup.addView(visibleToAll);
 
 		visibleToSome = new RadioButton(this);
+		visibleToSome.setId(3);
 		visibleToSome.setText(R.string.blog_visible_to_some);
 		visibleToSome.setOnClickListener(this);
 		radioGroup.addView(visibleToSome);
diff --git a/briar-android/src/net/sf/briar/android/blogs/WriteBlogPostActivity.java b/briar-android/src/net/sf/briar/android/blogs/WriteBlogPostActivity.java
index e8242ba17c..de0bc88d54 100644
--- a/briar-android/src/net/sf/briar/android/blogs/WriteBlogPostActivity.java
+++ b/briar-android/src/net/sf/briar/android/blogs/WriteBlogPostActivity.java
@@ -24,6 +24,7 @@ import net.sf.briar.android.identity.LocalAuthorItem;
 import net.sf.briar.android.identity.LocalAuthorItemComparator;
 import net.sf.briar.android.identity.LocalAuthorSpinnerAdapter;
 import net.sf.briar.android.widgets.HorizontalSpace;
+import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.crypto.CryptoComponent;
@@ -67,13 +68,14 @@ implements OnItemSelectedListener, OnClickListener {
 	private Spinner fromSpinner = null, toSpinner = null;
 	private ImageButton sendButton = null;
 	private EditText content = null;
+	private AuthorId localAuthorId = null;
+	private GroupId localGroupId = null;
 
 	// Fields that are accessed from background threads must be volatile
 	@Inject private volatile DatabaseComponent db;
 	@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
 	private volatile LocalAuthor localAuthor = null;
 	private volatile LocalGroup localGroup = null;
-	private volatile GroupId localGroupId = null;
 	private volatile MessageId parentId = null;
 
 	@Override
@@ -86,6 +88,13 @@ implements OnItemSelectedListener, OnClickListener {
 		b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
 		if(b != null) parentId = new MessageId(b);
 
+		if(state != null) {
+			b = state.getByteArray("net.sf.briar.LOCAL_AUTHOR_ID");
+			if(b != null) localAuthorId = new AuthorId(b);
+			b = state.getByteArray("net.sf.briar.LOCAL_GROUP_ID");
+			if(b != null) localGroupId = new GroupId(b);
+		}
+
 		LinearLayout layout = new LinearLayout(this);
 		layout.setLayoutParams(MATCH_WRAP);
 		layout.setOrientation(VERTICAL);
@@ -136,6 +145,7 @@ implements OnItemSelectedListener, OnClickListener {
 		layout.addView(header);
 
 		content = new EditText(this);
+		content.setId(1);
 		int inputType = TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE
 				| TYPE_TEXT_FLAG_CAP_SENTENCES;
 		content.setInputType(inputType);
@@ -187,6 +197,17 @@ implements OnItemSelectedListener, OnClickListener {
 					fromAdapter.add(new LocalAuthorItem(a));
 				fromAdapter.sort(LocalAuthorItemComparator.INSTANCE);
 				fromAdapter.notifyDataSetChanged();
+				int count = fromAdapter.getCount();
+				for(int i = 0; i < count; i++) {
+					LocalAuthorItem item = fromAdapter.getItem(i);
+					if(item == LocalAuthorItem.ANONYMOUS) continue;
+					if(item == LocalAuthorItem.NEW) continue;
+					if(item.getLocalAuthor().getId().equals(localAuthorId)) {
+						localAuthor = item.getLocalAuthor();
+						fromSpinner.setSelection(i);
+						break;
+					}
+				}
 			}
 		});
 	}
@@ -226,6 +247,7 @@ implements OnItemSelectedListener, OnClickListener {
 					LocalGroupItem item = toAdapter.getItem(i);
 					if(item == LocalGroupItem.NEW) continue;
 					if(item.getLocalGroup().getId().equals(localGroupId)) {
+						localGroup = item.getLocalGroup();
 						toSpinner.setSelection(i);
 						break;
 					}
@@ -234,6 +256,19 @@ implements OnItemSelectedListener, OnClickListener {
 		});
 	}
 
+	@Override
+	public void onSaveInstanceState(Bundle state) {
+		super.onSaveInstanceState(state);
+		if(localAuthorId != null) {
+			byte[] b =  localAuthorId.getBytes();
+			state.putByteArray("net.sf.briar.LOCAL_AUTHOR_ID", b);
+		}
+		if(localGroupId != null) {
+			byte[] b =  localGroupId.getBytes();
+			state.putByteArray("net.sf.briar.LOCAL_GROUP_ID", b);
+		}
+	}
+
 	@Override
 	public void onDestroy() {
 		super.onDestroy();
@@ -246,15 +281,20 @@ implements OnItemSelectedListener, OnClickListener {
 			LocalAuthorItem item = fromAdapter.getItem(position);
 			if(item == LocalAuthorItem.ANONYMOUS) {
 				localAuthor = null;
+				localAuthorId = null;
 			} else if(item == LocalAuthorItem.NEW) {
 				localAuthor = null;
+				localAuthorId = null;
 				startActivity(new Intent(this, CreateIdentityActivity.class));
 			} else {
 				localAuthor = item.getLocalAuthor();
+				localAuthorId = localAuthor.getId();
 			}
 		} else if(parent == toSpinner) {
 			LocalGroupItem item = toAdapter.getItem(position);
 			if(item == LocalGroupItem.NEW) {
+				localGroup = null;
+				localGroupId = null;
 				startActivity(new Intent(this, CreateBlogActivity.class));
 			} else {
 				localGroup = item.getLocalGroup();
@@ -267,6 +307,7 @@ implements OnItemSelectedListener, OnClickListener {
 	public void onNothingSelected(AdapterView<?> parent) {
 		if(parent == fromSpinner) {
 			localAuthor = null;
+			localAuthorId = null;
 		} else if(parent == toSpinner) {
 			localGroup = null;
 			localGroupId = null;
@@ -287,6 +328,7 @@ implements OnItemSelectedListener, OnClickListener {
 		finish();
 	}
 
+	// FIXME: This should happen on a CryptoExecutor thread
 	private Message createMessage(byte[] body) throws IOException,
 	GeneralSecurityException {
 		KeyParser keyParser = crypto.getSignatureKeyParser();
diff --git a/briar-android/src/net/sf/briar/android/groups/ConfigureGroupActivity.java b/briar-android/src/net/sf/briar/android/groups/ConfigureGroupActivity.java
index d432b28334..6ba4670e75 100644
--- a/briar-android/src/net/sf/briar/android/groups/ConfigureGroupActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/ConfigureGroupActivity.java
@@ -85,6 +85,7 @@ SelectContactsDialog.Listener {
 		layout.setGravity(CENTER_HORIZONTAL);
 
 		subscribeCheckBox = new CheckBox(this);
+		subscribeCheckBox.setId(1);
 		subscribeCheckBox.setText(R.string.subscribe_to_this_group);
 		subscribeCheckBox.setChecked(subscribed);
 		subscribeCheckBox.setOnClickListener(this);
@@ -94,21 +95,21 @@ SelectContactsDialog.Listener {
 		radioGroup.setOrientation(VERTICAL);
 
 		visibleToAll = new RadioButton(this);
-		visibleToAll.setId(1);
+		visibleToAll.setId(2);
 		visibleToAll.setText(R.string.group_visible_to_all);
 		visibleToAll.setEnabled(subscribed);
 		visibleToAll.setOnClickListener(this);
 		radioGroup.addView(visibleToAll);
 
 		visibleToSome = new RadioButton(this);
-		visibleToSome.setId(2);
+		visibleToSome.setId(3);
 		visibleToSome.setText(R.string.group_visible_to_some);
 		visibleToSome.setEnabled(subscribed);
 		visibleToSome.setOnClickListener(this);
 		radioGroup.addView(visibleToSome);
 
-		if(!subscribed || all) radioGroup.check(1);
-		else radioGroup.check(2);
+		if(!subscribed || all) radioGroup.check(visibleToAll.getId());
+		else radioGroup.check(visibleToSome.getId());
 		layout.addView(radioGroup);
 
 		doneButton = new Button(this);
diff --git a/briar-android/src/net/sf/briar/android/groups/CreateGroupActivity.java b/briar-android/src/net/sf/briar/android/groups/CreateGroupActivity.java
index 04bd359254..7793a6a8a4 100644
--- a/briar-android/src/net/sf/briar/android/groups/CreateGroupActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/CreateGroupActivity.java
@@ -94,6 +94,7 @@ SelectContactsDialog.Listener {
 				enableOrDisableCreateButton();
 			}
 		};
+		nameEntry.setId(1);
 		nameEntry.setMaxLines(1);
 		nameEntry.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES);
 		nameEntry.setOnEditorActionListener(this);
@@ -103,11 +104,13 @@ SelectContactsDialog.Listener {
 		radioGroup.setOrientation(VERTICAL);
 
 		visibleToAll = new RadioButton(this);
+		visibleToAll.setId(2);
 		visibleToAll.setText(R.string.blog_visible_to_all);
 		visibleToAll.setOnClickListener(this);
 		radioGroup.addView(visibleToAll);
 
 		visibleToSome = new RadioButton(this);
+		visibleToSome.setId(3);
 		visibleToSome.setText(R.string.blog_visible_to_some);
 		visibleToSome.setOnClickListener(this);
 		radioGroup.addView(visibleToSome);
diff --git a/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
index cb05d3266c..779857e43e 100644
--- a/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
@@ -27,6 +27,7 @@ import net.sf.briar.android.identity.LocalAuthorItem;
 import net.sf.briar.android.identity.LocalAuthorItemComparator;
 import net.sf.briar.android.identity.LocalAuthorSpinnerAdapter;
 import net.sf.briar.android.widgets.HorizontalSpace;
+import net.sf.briar.api.AuthorId;
 import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.crypto.CryptoComponent;
@@ -70,13 +71,14 @@ implements OnItemSelectedListener, OnClickListener {
 	private Spinner fromSpinner = null, toSpinner = null;
 	private ImageButton sendButton = null;
 	private EditText content = null;
+	private AuthorId localAuthorId = null;
+	private GroupId groupId = null;
 
 	// Fields that are accessed from background threads must be volatile
 	@Inject private volatile DatabaseComponent db;
 	@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
 	private volatile LocalAuthor localAuthor = null;
 	private volatile Group group = null;
-	private volatile GroupId groupId = null;
 	private volatile MessageId parentId = null;
 
 	@Override
@@ -89,6 +91,13 @@ implements OnItemSelectedListener, OnClickListener {
 		b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
 		if(b != null) parentId = new MessageId(b);
 
+		if(state != null) {
+			b = state.getByteArray("net.sf.briar.LOCAL_AUTHOR_ID");
+			if(b != null) localAuthorId = new AuthorId(b);
+			b = state.getByteArray("net.sf.briar.GROUP_ID");
+			if(b != null) groupId = new GroupId(b);
+		}
+
 		LinearLayout layout = new LinearLayout(this);
 		layout.setLayoutParams(MATCH_WRAP);
 		layout.setOrientation(VERTICAL);
@@ -139,6 +148,7 @@ implements OnItemSelectedListener, OnClickListener {
 		layout.addView(header);
 
 		content = new EditText(this);
+		content.setId(1);
 		int inputType = TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE
 				| TYPE_TEXT_FLAG_CAP_SENTENCES;
 		content.setInputType(inputType);
@@ -190,6 +200,17 @@ implements OnItemSelectedListener, OnClickListener {
 					fromAdapter.add(new LocalAuthorItem(a));
 				fromAdapter.sort(LocalAuthorItemComparator.INSTANCE);
 				fromAdapter.notifyDataSetChanged();
+				int count = fromAdapter.getCount();
+				for(int i = 0; i < count; i++) {
+					LocalAuthorItem item = fromAdapter.getItem(i);
+					if(item == LocalAuthorItem.ANONYMOUS) continue;
+					if(item == LocalAuthorItem.NEW) continue;
+					if(item.getLocalAuthor().getId().equals(localAuthorId)) {
+						localAuthor = item.getLocalAuthor();
+						fromSpinner.setSelection(i);
+						break;
+					}
+				}
 			}
 		});
 	}
@@ -231,6 +252,7 @@ implements OnItemSelectedListener, OnClickListener {
 					GroupItem g = toAdapter.getItem(i);
 					if(g == GroupItem.NEW) continue;
 					if(g.getGroup().getId().equals(groupId)) {
+						group = g.getGroup();
 						toSpinner.setSelection(i);
 						break;
 					}
@@ -239,6 +261,19 @@ implements OnItemSelectedListener, OnClickListener {
 		});
 	}
 
+	@Override
+	public void onSaveInstanceState(Bundle state) {
+		super.onSaveInstanceState(state);
+		if(localAuthorId != null) {
+			byte[] b =  localAuthorId.getBytes();
+			state.putByteArray("net.sf.briar.LOCAL_AUTHOR_ID", b);
+		}
+		if(groupId != null) {
+			byte[] b =  groupId.getBytes();
+			state.putByteArray("net.sf.briar.GROUP_ID", b);
+		}
+	}
+
 	@Override
 	public void onDestroy() {
 		super.onDestroy();
@@ -251,15 +286,20 @@ implements OnItemSelectedListener, OnClickListener {
 			LocalAuthorItem item = fromAdapter.getItem(position);
 			if(item == LocalAuthorItem.ANONYMOUS) {
 				localAuthor = null;
+				localAuthorId = null;
 			} else if(item == LocalAuthorItem.NEW) {
 				localAuthor = null;
+				localAuthorId = null;
 				startActivity(new Intent(this, CreateIdentityActivity.class));
 			} else {
 				localAuthor = item.getLocalAuthor();
+				localAuthorId = localAuthor.getId();
 			}
 		} else if(parent == toSpinner) {
 			GroupItem item = toAdapter.getItem(position);
 			if(item == GroupItem.NEW) {
+				group = null;
+				groupId = null;
 				startActivity(new Intent(this, CreateGroupActivity.class));
 			} else {
 				group = item.getGroup();
@@ -272,6 +312,7 @@ implements OnItemSelectedListener, OnClickListener {
 	public void onNothingSelected(AdapterView<?> parent) {
 		if(parent == fromSpinner) {
 			localAuthor = null;
+			localAuthorId = null;
 		} else if(parent == toSpinner) {
 			group = null;
 			groupId = null;
@@ -292,6 +333,7 @@ implements OnItemSelectedListener, OnClickListener {
 		finish();
 	}
 
+	// FIXME: This should happen on a CryptoExecutor thread
 	private Message createMessage(byte[] body) throws IOException,
 	GeneralSecurityException {
 		if(localAuthor == null) {
diff --git a/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java b/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
index eeadc22e68..9a331a0381 100644
--- a/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
+++ b/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
@@ -88,6 +88,7 @@ implements OnEditorActionListener, OnClickListener {
 					createButton.setEnabled(getText().length() > 0);
 			}
 		};
+		nicknameEntry.setId(1);
 		nicknameEntry.setMaxLines(1);
 		int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_WORDS;
 		nicknameEntry.setInputType(inputType);
diff --git a/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java b/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
index 43378046d1..1ade68d616 100644
--- a/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
+++ b/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java
@@ -174,8 +174,8 @@ implements InvitationListener {
 	public void onSaveInstanceState(Bundle state) {
 		super.onSaveInstanceState(state);
 		if(localAuthorId != null) {
-			state.putByteArray("net.sf.briar.LOCAL_AUTHOR_ID",
-					localAuthorId.getBytes());
+			byte[] b = localAuthorId.getBytes();
+			state.putByteArray("net.sf.briar.LOCAL_AUTHOR_ID", b);
 		}
 		state.putInt("net.sf.briar.LOCAL_CODE", localInvitationCode);
 		state.putInt("net.sf.briar.REMOTE_CODE", remoteInvitationCode);
diff --git a/briar-android/src/net/sf/briar/android/invitation/CodeEntryWidget.java b/briar-android/src/net/sf/briar/android/invitation/CodeEntryWidget.java
index 657cb5c3d9..d9b2901e91 100644
--- a/briar-android/src/net/sf/briar/android/invitation/CodeEntryWidget.java
+++ b/briar-android/src/net/sf/briar/android/invitation/CodeEntryWidget.java
@@ -54,6 +54,7 @@ implements OnEditorActionListener, OnClickListener {
 					continueButton.setEnabled(getText().length() == 6);
 			}
 		};
+		codeEntry.setId(1);
 		codeEntry.setTextSize(26);
 		codeEntry.setOnEditorActionListener(this);
 		codeEntry.setMinEms(5);
diff --git a/briar-android/src/net/sf/briar/android/messages/WritePrivateMessageActivity.java b/briar-android/src/net/sf/briar/android/messages/WritePrivateMessageActivity.java
index 734652ef9f..a948891e21 100644
--- a/briar-android/src/net/sf/briar/android/messages/WritePrivateMessageActivity.java
+++ b/briar-android/src/net/sf/briar/android/messages/WritePrivateMessageActivity.java
@@ -83,6 +83,11 @@ implements OnItemSelectedListener, OnClickListener {
 		byte[] b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
 		if(b != null) parentId = new MessageId(b);
 
+		if(state != null) {
+			id = state.getInt("net.sf.briar.CONTACT_ID", -1);
+			if(id != -1) contactId = new ContactId(id);
+		}
+
 		LinearLayout layout = new LinearLayout(this);
 		layout.setLayoutParams(MATCH_WRAP);
 		layout.setOrientation(VERTICAL);
@@ -127,6 +132,7 @@ implements OnItemSelectedListener, OnClickListener {
 		layout.addView(header);
 
 		content = new EditText(this);
+		content.setId(1);
 		int inputType = TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE
 				| TYPE_TEXT_FLAG_CAP_SENTENCES;
 		content.setInputType(inputType);
@@ -188,6 +194,13 @@ implements OnItemSelectedListener, OnClickListener {
 		});
 	}
 
+	@Override
+	public void onSaveInstanceState(Bundle state) {
+		super.onSaveInstanceState(state);
+		if(contactId != null)
+			state.putInt("net.sf.briar.CONTACT_ID", contactId.getInt());
+	}
+
 	@Override
 	public void onDestroy() {
 		super.onDestroy();
@@ -198,12 +211,16 @@ implements OnItemSelectedListener, OnClickListener {
 			long id) {
 		ContactItem item = adapter.getItem(position);
 		if(item == ContactItem.NEW) {
+			contactId = null;
+			localAuthor = null;
 			startActivity(new Intent(this, AddContactActivity.class));
 		} else {
 			Contact c = item.getContact();
-			loadLocalAuthor(c.getLocalAuthorId());
 			contactId = c.getId();
+			localAuthor = null;
+			loadLocalAuthor(c.getLocalAuthorId());
 		}
+		sendButton.setEnabled(false);
 	}
 
 	private void loadLocalAuthor(final AuthorId a) {
-- 
GitLab