From f1ebbc28f3f8e5f030acbf37a397ce155dcd707c Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Fri, 12 Apr 2013 20:17:34 +0100
Subject: [PATCH] Separated WriteGroupMessageActivity into group and blog
 activities.

---
 briar-android/AndroidManifest.xml             |   8 +-
 .../briar/android/groups/GroupActivity.java   |  13 +-
 .../android/groups/GroupListActivity.java     |   6 +-
 .../groups/LocalGroupSpinnerAdapter.java      |  40 +++
 .../groups/ReadGroupMessageActivity.java      |   3 +-
 .../android/groups/WriteBlogPostActivity.java | 333 ++++++++++++++++++
 ...ivity.java => WriteGroupPostActivity.java} |  56 +--
 7 files changed, 422 insertions(+), 37 deletions(-)
 create mode 100644 briar-android/src/net/sf/briar/android/groups/LocalGroupSpinnerAdapter.java
 create mode 100644 briar-android/src/net/sf/briar/android/groups/WriteBlogPostActivity.java
 rename briar-android/src/net/sf/briar/android/groups/{WriteGroupMessageActivity.java => WriteGroupPostActivity.java} (88%)

diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index a034123a8c..fc74fb36e3 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -65,8 +65,12 @@
 			android:label="@string/app_name" >
 		</activity>
 		<activity
-			android:name=".android.groups.WriteGroupMessageActivity"
-			android:label="@string/app_name" >
+			android:name=".android.groups.WriteBlogPostActivity"
+			android:label="@string/compose_blog_title" >
+		</activity>
+		<activity
+			android:name=".android.groups.WriteGroupPostActivity"
+			android:label="@string/compose_group_title" >
 		</activity>
 		<activity
 			android:name=".android.identity.CreateIdentityActivity"
diff --git a/briar-android/src/net/sf/briar/android/groups/GroupActivity.java b/briar-android/src/net/sf/briar/android/groups/GroupActivity.java
index 289c85c592..b72e3d2f50 100644
--- a/briar-android/src/net/sf/briar/android/groups/GroupActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/GroupActivity.java
@@ -208,10 +208,15 @@ OnClickListener, OnItemClickListener {
 	}
 
 	public void onClick(View view) {
-		Intent i = new Intent(this, WriteGroupMessageActivity.class);
-		i.putExtra("net.sf.briar.RESTRICTED", restricted);
-		i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
-		startActivity(i);
+		if(restricted) {
+			Intent i = new Intent(this, WriteBlogPostActivity.class);
+			i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
+			startActivity(i);
+		} else {
+			Intent i = new Intent(this, WriteGroupPostActivity.class);
+			i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
+			startActivity(i);
+		}
 	}
 
 	public void onItemClick(AdapterView<?> parent, View view, int position,
diff --git a/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java b/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java
index 8f58340846..5ca3267d5b 100644
--- a/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java
@@ -224,10 +224,10 @@ implements OnClickListener, DatabaseListener, NoGroupsDialog.Listener {
 				dialog.setListener(this);
 				dialog.setRestricted(restricted);
 				dialog.show(getSupportFragmentManager(), "NoGroupsDialog");
+			} else if(restricted) {
+				startActivity(new Intent(this, WriteBlogPostActivity.class));
 			} else {
-				Intent i = new Intent(this, WriteGroupMessageActivity.class);
-				i.putExtra("net.sf.briar.RESTRICTED", restricted);
-				startActivity(i);
+				startActivity(new Intent(this, WriteGroupPostActivity.class));
 			}
 		}
 	}
diff --git a/briar-android/src/net/sf/briar/android/groups/LocalGroupSpinnerAdapter.java b/briar-android/src/net/sf/briar/android/groups/LocalGroupSpinnerAdapter.java
new file mode 100644
index 0000000000..34a2f3ffa9
--- /dev/null
+++ b/briar-android/src/net/sf/briar/android/groups/LocalGroupSpinnerAdapter.java
@@ -0,0 +1,40 @@
+package net.sf.briar.android.groups;
+
+import java.util.ArrayList;
+
+import net.sf.briar.R;
+import net.sf.briar.api.messaging.LocalGroup;
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+class LocalGroupSpinnerAdapter extends ArrayAdapter<LocalGroup>
+implements SpinnerAdapter {
+
+	LocalGroupSpinnerAdapter(Context context) {
+		super(context, android.R.layout.simple_spinner_item,
+				new ArrayList<LocalGroup>());
+	}
+
+	@Override
+	public View getView(int position, View convertView, ViewGroup parent) {
+		TextView name = new TextView(getContext());
+		name.setTextSize(18);
+		name.setMaxLines(1);
+		Resources res = getContext().getResources();
+		int pad = res.getInteger(R.integer.spinner_padding);
+		name.setPadding(pad, pad, pad, pad);
+		name.setText(getItem(position).getName());
+		return name;
+	}
+
+	@Override
+	public View getDropDownView(int position, View convertView,
+			ViewGroup parent) {
+		return getView(position, convertView, parent);
+	}
+}
diff --git a/briar-android/src/net/sf/briar/android/groups/ReadGroupMessageActivity.java b/briar-android/src/net/sf/briar/android/groups/ReadGroupMessageActivity.java
index 001f1ccc3e..ab2299af0a 100644
--- a/briar-android/src/net/sf/briar/android/groups/ReadGroupMessageActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/ReadGroupMessageActivity.java
@@ -325,7 +325,8 @@ implements OnClickListener {
 			setResult(RESULT_NEXT);
 			finish();
 		} else if(view == replyButton) {
-			Intent i = new Intent(this, WriteGroupMessageActivity.class);
+			// FIXME: Restricted/unrestricted
+			Intent i = new Intent(this, WriteGroupPostActivity.class);
 			i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
 			i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
 			startActivity(i);
diff --git a/briar-android/src/net/sf/briar/android/groups/WriteBlogPostActivity.java b/briar-android/src/net/sf/briar/android/groups/WriteBlogPostActivity.java
new file mode 100644
index 0000000000..f620244855
--- /dev/null
+++ b/briar-android/src/net/sf/briar/android/groups/WriteBlogPostActivity.java
@@ -0,0 +1,333 @@
+package net.sf.briar.android.groups;
+
+import static android.text.InputType.TYPE_CLASS_TEXT;
+import static android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
+import static android.view.Gravity.CENTER_VERTICAL;
+import static android.widget.LinearLayout.HORIZONTAL;
+import static android.widget.LinearLayout.VERTICAL;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+import static net.sf.briar.android.identity.LocalAuthorItem.ANONYMOUS;
+import static net.sf.briar.android.identity.LocalAuthorItem.NEW;
+import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.util.Collection;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import net.sf.briar.R;
+import net.sf.briar.android.BriarActivity;
+import net.sf.briar.android.BriarService;
+import net.sf.briar.android.BriarService.BriarServiceConnection;
+import net.sf.briar.android.identity.CreateIdentityActivity;
+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.LocalAuthor;
+import net.sf.briar.api.android.BundleEncrypter;
+import net.sf.briar.api.android.DatabaseUiExecutor;
+import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.KeyParser;
+import net.sf.briar.api.db.DatabaseComponent;
+import net.sf.briar.api.db.DbException;
+import net.sf.briar.api.messaging.GroupId;
+import net.sf.briar.api.messaging.LocalGroup;
+import net.sf.briar.api.messaging.Message;
+import net.sf.briar.api.messaging.MessageFactory;
+import net.sf.briar.api.messaging.MessageId;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.google.inject.Inject;
+
+public class WriteBlogPostActivity extends BriarActivity
+implements OnItemSelectedListener, OnClickListener {
+
+	private static final Logger LOG =
+			Logger.getLogger(WriteBlogPostActivity.class.getName());
+
+	private final BriarServiceConnection serviceConnection =
+			new BriarServiceConnection();
+
+	@Inject private BundleEncrypter bundleEncrypter;
+	@Inject private CryptoComponent crypto;
+	@Inject private MessageFactory messageFactory;
+	private LocalAuthorSpinnerAdapter fromAdapter = null;
+	private LocalGroupSpinnerAdapter toAdapter = null;
+	private Spinner fromSpinner = null, toSpinner = null;
+	private ImageButton sendButton = null;
+	private EditText content = 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
+	public void onCreate(Bundle state) {
+		super.onCreate(null);
+
+		Intent i = getIntent();
+		byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
+		if(b != null) localGroupId = new GroupId(b);
+		b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
+		if(b != null) parentId = new MessageId(b);
+
+		LinearLayout layout = new LinearLayout(this);
+		layout.setLayoutParams(MATCH_WRAP);
+		layout.setOrientation(VERTICAL);
+
+		LinearLayout header = new LinearLayout(this);
+		header.setLayoutParams(MATCH_WRAP);
+		header.setOrientation(HORIZONTAL);
+		header.setGravity(CENTER_VERTICAL);
+
+		TextView from = new TextView(this);
+		from.setTextSize(18);
+		from.setPadding(10, 10, 10, 10);
+		from.setText(R.string.from);
+		header.addView(from);
+
+		fromAdapter = new LocalAuthorSpinnerAdapter(this, true);
+		fromSpinner = new Spinner(this);
+		fromSpinner.setAdapter(fromAdapter);
+		fromSpinner.setOnItemSelectedListener(this);
+		header.addView(fromSpinner);
+
+		header.addView(new HorizontalSpace(this));
+
+		sendButton = new ImageButton(this);
+		sendButton.setBackgroundResource(0);
+		sendButton.setImageResource(R.drawable.social_send_now);
+		sendButton.setEnabled(false); // Enabled when a group is selected
+		sendButton.setOnClickListener(this);
+		header.addView(sendButton);
+		layout.addView(header);
+
+		header = new LinearLayout(this);
+		header.setLayoutParams(MATCH_WRAP);
+		header.setOrientation(HORIZONTAL);
+		header.setGravity(CENTER_VERTICAL);
+
+		TextView to = new TextView(this);
+		to.setTextSize(18);
+		to.setPadding(10, 0, 0, 10);
+		to.setText(R.string.to);
+		header.addView(to);
+
+		toAdapter = new LocalGroupSpinnerAdapter(this);
+		toSpinner = new Spinner(this);
+		toSpinner.setAdapter(toAdapter);
+		toSpinner.setOnItemSelectedListener(this);
+		header.addView(toSpinner);
+		layout.addView(header);
+
+		content = new EditText(this);
+		content.setPadding(10, 10, 10, 10);
+		int inputType = TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES;
+		content.setInputType(inputType);
+		if(state != null && bundleEncrypter.decrypt(state)) {
+			Parcelable p = state.getParcelable("net.sf.briar.CONTENT");
+			if(p != null) content.onRestoreInstanceState(p);
+		}
+		layout.addView(content);
+
+		setContentView(layout);
+
+		// Bind to the service so we can wait for it to start
+		bindService(new Intent(BriarService.class.getName()),
+				serviceConnection, 0);
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		loadLocalAuthors();
+		loadLocalGroups();
+	}
+
+	private void loadLocalAuthors() {
+		dbUiExecutor.execute(new Runnable() {
+			public void run() {
+				try {
+					serviceConnection.waitForStartup();
+					long now = System.currentTimeMillis();
+					Collection<LocalAuthor> localAuthors = db.getLocalAuthors();
+					long duration = System.currentTimeMillis() - now;
+					if(LOG.isLoggable(INFO))
+						LOG.info("Loading authors took " + duration + " ms");
+					displayLocalAuthors(localAuthors);
+				} catch(DbException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				} catch(InterruptedException e) {
+					LOG.info("Interrupted while waiting for service");
+					Thread.currentThread().interrupt();
+				}
+			}
+		});
+	}
+
+	private void displayLocalAuthors(
+			final Collection<LocalAuthor> localAuthors) {
+		runOnUiThread(new Runnable() {
+			public void run() {
+				fromAdapter.clear();
+				for(LocalAuthor a : localAuthors)
+					fromAdapter.add(new LocalAuthorItem(a));
+				fromAdapter.sort(LocalAuthorItemComparator.INSTANCE);
+				fromAdapter.notifyDataSetChanged();
+			}
+		});
+	}
+
+	private void loadLocalGroups() {
+		dbUiExecutor.execute(new Runnable() {
+			public void run() {
+				try {
+					serviceConnection.waitForStartup();
+					long now = System.currentTimeMillis();
+					Collection<LocalGroup> groups = db.getLocalGroups();
+					long duration = System.currentTimeMillis() - now;
+					if(LOG.isLoggable(INFO))
+						LOG.info("Loading groups took " + duration + " ms");
+					displayLocalGroups(groups);
+				} catch(DbException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				} catch(InterruptedException e) {
+					LOG.info("Interrupted while waiting for service");
+					Thread.currentThread().interrupt();
+				}
+			}
+		});
+	}
+
+	private void displayLocalGroups(final Collection<LocalGroup> groups) {
+		runOnUiThread(new Runnable() {
+			public void run() {
+				if(groups.isEmpty()) finish();
+				int index = -1;
+				for(LocalGroup g : groups) {
+					if(g.getId().equals(localGroupId)) {
+						localGroup = g;
+						index = toAdapter.getCount();
+					}
+					toAdapter.add(g);
+				}
+				if(index != -1) toSpinner.setSelection(index);
+			}
+		});
+	}
+
+	@Override
+	public void onSaveInstanceState(Bundle state) {
+		Parcelable p = content.onSaveInstanceState();
+		state.putParcelable("net.sf.briar.CONTENT", p);
+		bundleEncrypter.encrypt(state);
+	}
+
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+		unbindService(serviceConnection);
+	}
+
+	public void onItemSelected(AdapterView<?> parent, View view, int position,
+			long id) {
+		if(parent == fromSpinner) {
+			LocalAuthorItem item = fromAdapter.getItem(position);
+			if(item == ANONYMOUS) {
+				localAuthor = null;
+			} else if(item == NEW) {
+				localAuthor = null;
+				startActivity(new Intent(this, CreateIdentityActivity.class));
+			} else {
+				localAuthor = item.getLocalAuthor();
+			}
+		} else if(parent == toSpinner) {
+			localGroup = toAdapter.getItem(position);
+			localGroupId = localGroup.getId();
+			sendButton.setEnabled(true);
+		}
+	}
+
+	public void onNothingSelected(AdapterView<?> parent) {
+		if(parent == fromSpinner) {
+			localAuthor = null;
+		} else if(parent == toSpinner) {
+			localGroup = null;
+			localGroupId = null;
+			sendButton.setEnabled(false);
+		}
+	}
+
+	public void onClick(View view) {
+		if(localGroup == null) throw new IllegalStateException();
+		try {
+			byte[] b = content.getText().toString().getBytes("UTF-8");
+			storeMessage(createMessage(b));
+		} catch(GeneralSecurityException e) {
+			throw new RuntimeException(e);
+		} catch(IOException e) {
+			throw new RuntimeException(e);
+		}
+		finish();
+	}
+
+	private Message createMessage(byte[] body) throws IOException,
+	GeneralSecurityException {
+		KeyParser keyParser = crypto.getSignatureKeyParser();
+		byte[] groupKeyBytes = localGroup.getPrivateKey();
+		PrivateKey groupKey = keyParser.parsePrivateKey(groupKeyBytes);
+		if(localAuthor == null) {
+			return messageFactory.createAnonymousMessage(parentId, localGroup,
+					groupKey, "text/plain", body);
+		} else {
+			byte[] authorKeyBytes = localAuthor.getPrivateKey();
+			PrivateKey authorKey = keyParser.parsePrivateKey(authorKeyBytes);
+			return messageFactory.createPseudonymousMessage(parentId,
+					localGroup, groupKey, localAuthor, authorKey, "text/plain",
+					body);
+		}
+	}
+
+	private void storeMessage(final Message m) {
+		dbUiExecutor.execute(new Runnable() {
+			public void run() {
+				try {
+					serviceConnection.waitForStartup();
+					long now = System.currentTimeMillis();
+					db.addLocalGroupMessage(m);
+					long duration = System.currentTimeMillis() - now;
+					if(LOG.isLoggable(INFO))
+						LOG.info("Storing message took " + duration + " ms");
+				} catch(DbException e) {
+					if(LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				} catch(InterruptedException e) {
+					if(LOG.isLoggable(INFO))
+						LOG.info("Interrupted while waiting for service");
+					Thread.currentThread().interrupt();
+				}
+			}
+		});
+	}
+}
diff --git a/briar-android/src/net/sf/briar/android/groups/WriteGroupMessageActivity.java b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
similarity index 88%
rename from briar-android/src/net/sf/briar/android/groups/WriteGroupMessageActivity.java
rename to briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
index b3c8e80230..18dbb71b88 100644
--- a/briar-android/src/net/sf/briar/android/groups/WriteGroupMessageActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
@@ -12,8 +12,8 @@ import static net.sf.briar.android.identity.LocalAuthorItem.NEW;
 import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP;
 
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -33,6 +33,8 @@ import net.sf.briar.android.widgets.HorizontalSpace;
 import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.BundleEncrypter;
 import net.sf.briar.api.android.DatabaseUiExecutor;
+import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.KeyParser;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.messaging.Group;
@@ -55,16 +57,18 @@ import android.widget.TextView;
 
 import com.google.inject.Inject;
 
-public class WriteGroupMessageActivity extends BriarActivity
+public class WriteGroupPostActivity extends BriarActivity
 implements OnItemSelectedListener, OnClickListener {
 
 	private static final Logger LOG =
-			Logger.getLogger(WriteGroupMessageActivity.class.getName());
+			Logger.getLogger(WriteGroupPostActivity.class.getName());
 
 	private final BriarServiceConnection serviceConnection =
 			new BriarServiceConnection();
 
 	@Inject private BundleEncrypter bundleEncrypter;
+	@Inject private CryptoComponent crypto;
+	@Inject private MessageFactory messageFactory;
 	private LocalAuthorSpinnerAdapter fromAdapter = null;
 	private GroupSpinnerAdapter toAdapter = null;
 	private Spinner fromSpinner = null, toSpinner = null;
@@ -74,8 +78,6 @@ implements OnItemSelectedListener, OnClickListener {
 	// Fields that are accessed from background threads must be volatile
 	@Inject private volatile DatabaseComponent db;
 	@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
-	@Inject private volatile MessageFactory messageFactory;
-	private volatile boolean restricted = false;
 	private volatile LocalAuthor localAuthor = null;
 	private volatile Group group = null;
 	private volatile GroupId groupId = null;
@@ -86,9 +88,6 @@ implements OnItemSelectedListener, OnClickListener {
 		super.onCreate(null);
 
 		Intent i = getIntent();
-		restricted = i.getBooleanExtra("net.sf.briar.RESTRICTED", false);
-		if(restricted) setTitle(R.string.compose_blog_title);
-		else setTitle(R.string.compose_group_title);
 		byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
 		if(b != null) groupId = new GroupId(b);
 		b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
@@ -209,17 +208,12 @@ implements OnItemSelectedListener, OnClickListener {
 					serviceConnection.waitForStartup();
 					List<Group> groups = new ArrayList<Group>();
 					long now = System.currentTimeMillis();
-					if(restricted) {
-						groups.addAll(db.getLocalGroups());
-					} else {
-						for(Group g : db.getSubscriptions())
-							if(!g.isRestricted()) groups.add(g);
-					}
+					for(Group g : db.getSubscriptions())
+						if(!g.isRestricted()) groups.add(g);
 					long duration = System.currentTimeMillis() - now;
 					if(LOG.isLoggable(INFO))
 						LOG.info("Loading groups took " + duration + " ms");
-					groups = Collections.unmodifiableList(groups);
-					displayGroups(groups);
+					displayGroups(Collections.unmodifiableList(groups));
 				} catch(DbException e) {
 					if(LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
@@ -294,22 +288,34 @@ implements OnItemSelectedListener, OnClickListener {
 		if(group == null) throw new IllegalStateException();
 		try {
 			byte[] b = content.getText().toString().getBytes("UTF-8");
-			storeMessage(localAuthor, group, b);
-		} catch(UnsupportedEncodingException e) {
+			storeMessage(createMessage(b));
+		} catch(GeneralSecurityException e) {
+			throw new RuntimeException(e);
+		} catch(IOException e) {
 			throw new RuntimeException(e);
 		}
 		finish();
 	}
 
-	private void storeMessage(final LocalAuthor localAuthor, final Group group,
-			final byte[] body) {
+	private Message createMessage(byte[] body) throws IOException,
+	GeneralSecurityException {
+		if(localAuthor == null) {
+			return messageFactory.createAnonymousMessage(parentId, group,
+					"text/plain", body);
+		} else {
+			KeyParser keyParser = crypto.getSignatureKeyParser();
+			byte[] authorKeyBytes = localAuthor.getPrivateKey();
+			PrivateKey authorKey = keyParser.parsePrivateKey(authorKeyBytes);
+			return messageFactory.createPseudonymousMessage(parentId,
+					group, localAuthor, authorKey, "text/plain", body);
+		}
+	}
+
+	private void storeMessage(final Message m) {
 		dbUiExecutor.execute(new Runnable() {
 			public void run() {
 				try {
 					serviceConnection.waitForStartup();
-					// FIXME: Anonymous/pseudonymous, restricted/unrestricted
-					Message m = messageFactory.createAnonymousMessage(parentId,
-							group, "text/plain", body);
 					long now = System.currentTimeMillis();
 					db.addLocalGroupMessage(m);
 					long duration = System.currentTimeMillis() - now;
@@ -318,14 +324,10 @@ implements OnItemSelectedListener, OnClickListener {
 				} catch(DbException e) {
 					if(LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
-				} catch(GeneralSecurityException e) {
-					throw new RuntimeException(e);
 				} catch(InterruptedException e) {
 					if(LOG.isLoggable(INFO))
 						LOG.info("Interrupted while waiting for service");
 					Thread.currentThread().interrupt();
-				} catch(IOException e) {
-					throw new RuntimeException(e);
 				}
 			}
 		});
-- 
GitLab