From c83d4bbb39905f2990094f0b2b5dc2b934a4df33 Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Tue, 11 Oct 2016 16:18:21 -0300
Subject: [PATCH] Implement first prototype of private group message threads

---
 briar-android/AndroidManifest.xml             |  11 ++
 briar-android/res/drawable/ic_group_white.xml |   9 +
 briar-android/res/menu/forum_actions.xml      |   2 +-
 briar-android/res/menu/group_actions.xml      |  41 +++++
 briar-android/res/values/strings.xml          |   8 +
 .../android/ActivityComponent.java            |   3 +
 .../briarproject/android/ActivityModule.java  |   9 +
 .../android/AndroidComponent.java             |   3 +
 .../android/forum/ForumControllerImpl.java    |   3 +-
 .../conversation/GroupActivity.java           |  82 +++++++++
 .../conversation/GroupController.java         |  11 ++
 .../conversation/GroupControllerImpl.java     | 165 ++++++++++++++++++
 .../conversation/GroupMessageAdapter.java     |  28 +++
 .../conversation/GroupMessageViewHolder.java  |  14 ++
 .../privategroup/list/GroupListAdapter.java   |   1 -
 .../privategroup/list/GroupViewHolder.java    |  17 +-
 .../android/threaded/ThreadListActivity.java  |   4 +-
 .../org/briarproject/api/forum/ForumPost.java |   1 +
 .../api/privategroup/GroupMessage.java        |  13 +-
 19 files changed, 411 insertions(+), 14 deletions(-)
 create mode 100644 briar-android/res/drawable/ic_group_white.xml
 create mode 100644 briar-android/res/menu/group_actions.xml
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageViewHolder.java

diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 86c5239e54..1bb12bfff4 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -100,6 +100,17 @@
 				/>
 		</activity>
 
+		<activity
+			android:name=".android.privategroup.conversation.GroupActivity"
+			android:label="@string/app_name"
+			android:parentActivityName=".android.NavDrawerActivity"
+			android:windowSoftInputMode="adjustResize|stateHidden">
+			<meta-data
+				android:name="android.support.PARENT_ACTIVITY"
+				android:value=".android.NavDrawerActivity"
+				/>
+		</activity>
+
 		<activity
 			android:name=".android.sharing.InvitationsForumActivity"
 			android:label="@string/forum_invitations_title"
diff --git a/briar-android/res/drawable/ic_group_white.xml b/briar-android/res/drawable/ic_group_white.xml
new file mode 100644
index 0000000000..f6e32475bd
--- /dev/null
+++ b/briar-android/res/drawable/ic_group_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0">
+	<path
+		android:fillColor="#FFFFFFFF"
+		android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
+</vector>
diff --git a/briar-android/res/menu/forum_actions.xml b/briar-android/res/menu/forum_actions.xml
index 2a262cbfa3..cd4e4573c5 100644
--- a/briar-android/res/menu/forum_actions.xml
+++ b/briar-android/res/menu/forum_actions.xml
@@ -7,7 +7,7 @@
 		android:id="@+id/action_forum_compose_post"
 		android:icon="@drawable/forum_item_create_white"
 		android:title="@string/forum_compose_post"
-		app:showAsAction="ifRoom"/>
+		app:showAsAction="always"/>
 
 	<item
 		android:id="@+id/action_forum_share"
diff --git a/briar-android/res/menu/group_actions.xml b/briar-android/res/menu/group_actions.xml
new file mode 100644
index 0000000000..8ebe58a1b3
--- /dev/null
+++ b/briar-android/res/menu/group_actions.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+
+	<item
+		android:id="@+id/action_group_compose_message"
+		android:icon="@drawable/forum_item_create_white"
+		android:title="@string/groups_compose_message"
+		app:showAsAction="always"/>
+
+	<item
+		android:id="@+id/action_group_member_list"
+		android:enabled="false"
+		android:icon="@drawable/ic_group_white"
+		android:title="@string/groups_member_list"
+		app:showAsAction="ifRoom"/>
+
+	<item
+		android:id="@+id/action_group_invite"
+		android:enabled="false"
+		android:icon="@drawable/ic_add_white"
+		android:title="@string/groups_invite_members"
+		app:showAsAction="ifRoom"/>
+
+	<item
+		android:id="@+id/action_group_leave"
+		android:enabled="false"
+		android:icon="@drawable/action_delete_white"
+		android:title="@string/groups_leave"
+		android:visible="false"
+		app:showAsAction="never"/>
+
+	<item
+		android:id="@+id/action_group_dissolve"
+		android:enabled="false"
+		android:icon="@drawable/action_delete_white"
+		android:title="@string/groups_dissolve"
+		app:showAsAction="never"/>
+
+</menu>
\ No newline at end of file
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index 883dceee68..0f541d6fb3 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -155,6 +155,14 @@
 	<string name="groups_group_is_dissolved">This group is dissolved</string>
 	<string name="groups_remove">Remove</string>
 	<string name="groups_add_group_title">Add Private Group</string>
+	<string name="groups_no_messages">This group is empty.\n\nYou can use the pen icon at the top to compose the first message.</string>
+	<string name="groups_compose_message">Compose Message</string>
+	<string name="groups_message_sent">Message sent</string>
+	<string name="groups_message_received">Message received</string>
+	<string name="groups_member_list">Member List</string>
+	<string name="groups_invite_members">Invite Members</string>
+	<string name="groups_leave">Leave Group</string>
+	<string name="groups_dissolve">Dissolve Group</string>
 
 	<!-- Forums -->
 	<string name="no_forums">You don\'t have any forums yet.\n\nWhy don\'t you create a new one yourself by tapping the + icon at the top?\n\nYou can also ask your contacts to share forums with you.</string>
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index d7cab786be..4e66c87aba 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -28,6 +28,7 @@ import org.briarproject.android.keyagreement.KeyAgreementActivity;
 import org.briarproject.android.keyagreement.ShowQrCodeFragment;
 import org.briarproject.android.panic.PanicPreferencesActivity;
 import org.briarproject.android.panic.PanicResponderActivity;
+import org.briarproject.android.privategroup.conversation.GroupActivity;
 import org.briarproject.android.privategroup.list.GroupListFragment;
 import org.briarproject.android.sharing.ContactSelectorFragment;
 import org.briarproject.android.sharing.InvitationsBlogActivity;
@@ -72,6 +73,8 @@ public interface ActivityComponent {
 
 	void inject(InvitationsBlogActivity activity);
 
+	void inject(GroupActivity activity);
+
 	void inject(CreateForumActivity activity);
 
 	void inject(ShareForumActivity activity);
diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index 3a81fc60ff..07cace5662 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -21,6 +21,8 @@ import org.briarproject.android.controller.SetupController;
 import org.briarproject.android.controller.SetupControllerImpl;
 import org.briarproject.android.forum.ForumController;
 import org.briarproject.android.forum.ForumControllerImpl;
+import org.briarproject.android.privategroup.conversation.GroupController;
+import org.briarproject.android.privategroup.conversation.GroupControllerImpl;
 import org.briarproject.android.privategroup.list.GroupListController;
 import org.briarproject.android.privategroup.list.GroupListControllerImpl;
 
@@ -99,6 +101,13 @@ public class ActivityModule {
 		return groupListController;
 	}
 
+	@ActivityScope
+	@Provides
+	protected GroupController provideGroupController(
+			GroupControllerImpl groupController) {
+		return groupController;
+	}
+
 	@ActivityScope
 	@Provides
 	protected ForumController provideForumController(
diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java
index 9b7e6edbaf..32f4524014 100644
--- a/briar-android/src/org/briarproject/android/AndroidComponent.java
+++ b/briar-android/src/org/briarproject/android/AndroidComponent.java
@@ -35,6 +35,7 @@ import org.briarproject.api.messaging.MessagingManager;
 import org.briarproject.api.messaging.PrivateMessageFactory;
 import org.briarproject.api.plugins.ConnectionRegistry;
 import org.briarproject.api.plugins.PluginManager;
+import org.briarproject.api.privategroup.GroupMessageFactory;
 import org.briarproject.api.privategroup.PrivateGroupManager;
 import org.briarproject.api.settings.SettingsManager;
 import org.briarproject.plugins.AndroidPluginsModule;
@@ -96,6 +97,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
 
 	PrivateGroupManager privateGroupManager();
 
+	GroupMessageFactory groupMessageFactory();
+
 	ForumManager forumManager();
 
 	ForumSharingManager forumSharingManager();
diff --git a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
index 442ce23a50..a3b910390d 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
@@ -166,8 +166,7 @@ public class ForumControllerImpl
 									p.getMessage().getTimestamp(),
 									p.getAuthor(), OURSELVES, true);
 
-					resultHandler.onResult(new ForumEntry(h,
-							bodyCache.get(p.getMessage().getId())));
+					resultHandler.onResult(buildItem(h));
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
new file mode 100644
index 0000000000..b00fdde45e
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
@@ -0,0 +1,82 @@
+package org.briarproject.android.privategroup.conversation;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.threaded.ThreadListActivity;
+import org.briarproject.android.threaded.ThreadListController;
+import org.briarproject.api.privategroup.GroupMessageHeader;
+import org.briarproject.api.privategroup.PrivateGroup;
+
+import javax.inject.Inject;
+
+public class GroupActivity extends
+		ThreadListActivity<PrivateGroup, GroupMessageItem, GroupMessageHeader, GroupMessageAdapter> {
+
+	@Inject
+	protected GroupController controller;
+
+	@Override
+	public void injectActivity(ActivityComponent component) {
+		component.inject(this);
+	}
+
+	@Override
+	protected ThreadListController<PrivateGroup, GroupMessageItem, GroupMessageHeader> getController() {
+		return controller;
+	}
+
+	@Override
+	public void onCreate(Bundle state) {
+		super.onCreate(state);
+		list.setEmptyText(R.string.groups_no_messages);
+	}
+
+	@Override
+	protected @LayoutRes int getLayout() {
+		return R.layout.activity_forum;
+	}
+
+	@Override
+	protected GroupMessageAdapter createAdapter(
+			LinearLayoutManager layoutManager) {
+		return new GroupMessageAdapter(this, layoutManager);
+	}
+
+	@Override
+	public boolean onCreateOptionsMenu(Menu menu) {
+		// Inflate the menu items for use in the action bar
+		MenuInflater inflater = getMenuInflater();
+		inflater.inflate(R.menu.group_actions, menu);
+
+		return super.onCreateOptionsMenu(menu);
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(final MenuItem item) {
+		switch (item.getItemId()) {
+			case R.id.action_group_compose_message:
+				showTextInput(null);
+				return true;
+			default:
+				return super.onOptionsItemSelected(item);
+		}
+	}
+
+	@Override
+	protected int getItemPostedString() {
+		return R.string.groups_message_sent;
+	}
+
+	@Override
+	protected int getItemReceivedString() {
+		return R.string.groups_message_received;
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
new file mode 100644
index 0000000000..0292f22787
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
@@ -0,0 +1,11 @@
+package org.briarproject.android.privategroup.conversation;
+
+import org.briarproject.android.threaded.ThreadListController;
+import org.briarproject.api.privategroup.GroupMessageHeader;
+import org.briarproject.api.privategroup.PrivateGroup;
+
+public interface GroupController
+		extends
+		ThreadListController<PrivateGroup, GroupMessageItem, GroupMessageHeader> {
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
new file mode 100644
index 0000000000..40e4d898cd
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
@@ -0,0 +1,165 @@
+package org.briarproject.android.privategroup.conversation;
+
+import android.support.annotation.Nullable;
+
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.android.threaded.ThreadListControllerImpl;
+import org.briarproject.api.FormatException;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.GroupMessageAddedEvent;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.privategroup.GroupMessage;
+import org.briarproject.api.privategroup.GroupMessageFactory;
+import org.briarproject.api.privategroup.GroupMessageHeader;
+import org.briarproject.api.privategroup.PrivateGroup;
+import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.sync.MessageId;
+
+import java.security.GeneralSecurityException;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+import static org.briarproject.api.identity.Author.Status.OURSELVES;
+
+public class GroupControllerImpl
+		extends ThreadListControllerImpl<PrivateGroup, GroupMessageItem, GroupMessageHeader>
+		implements GroupController {
+
+	private static final Logger LOG =
+			Logger.getLogger(GroupControllerImpl.class.getName());
+
+	@Inject
+	volatile GroupMessageFactory groupMessageFactory;
+	@Inject
+	volatile PrivateGroupManager privateGroupManager;
+
+	@Inject
+	GroupControllerImpl() {
+		super();
+	}
+
+	@Override
+	public void onActivityResume() {
+		super.onActivityResume();
+		notificationManager.clearForumPostNotification(groupId);
+	}
+
+	@Override
+	public void eventOccurred(Event e) {
+		super.eventOccurred(e);
+
+		if (e instanceof GroupMessageAddedEvent) {
+			final GroupMessageAddedEvent pe = (GroupMessageAddedEvent) e;
+			if (!pe.isLocal() && pe.getGroupId().equals(groupId)) {
+				LOG.info("Group message received, adding...");
+				final GroupMessageHeader fph = pe.getHeader();
+				updateNewestTimestamp(fph.getTimestamp());
+				listener.runOnUiThreadUnlessDestroyed(new Runnable() {
+					@Override
+					public void run() {
+						listener.onHeaderReceived(fph);
+					}
+				});
+			}
+		}
+	}
+
+	@Override
+	protected PrivateGroup loadGroupItem() throws DbException {
+		return privateGroupManager.getPrivateGroup(groupId);
+	}
+
+	@Override
+	protected Collection<GroupMessageHeader> loadHeaders() throws DbException {
+		return privateGroupManager.getHeaders(groupId);
+	}
+
+	@Override
+	protected void loadBodies(Collection<GroupMessageHeader> headers)
+			throws DbException {
+		for (GroupMessageHeader header : headers) {
+			if (!bodyCache.containsKey(header.getId())) {
+				String body =
+						privateGroupManager.getMessageBody(header.getId());
+				bodyCache.put(header.getId(), body);
+			}
+		}
+	}
+
+	@Override
+	protected void markRead(MessageId id) throws DbException {
+		privateGroupManager.setReadFlag(groupId, id, true);
+	}
+
+	@Override
+	public void send(final String body, @Nullable final MessageId parentId,
+			final ResultExceptionHandler<GroupMessageItem, DbException> handler) {
+		cryptoExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				LOG.info("Create message...");
+				long timestamp = System.currentTimeMillis();
+				timestamp = Math.max(timestamp, newestTimeStamp.get());
+				GroupMessage gm;
+				try {
+					LocalAuthor a = identityManager.getLocalAuthor();
+					gm = groupMessageFactory.createGroupMessage(groupId,
+							timestamp, parentId, a, body);
+				} catch (GeneralSecurityException | FormatException e) {
+					throw new RuntimeException(e);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					handler.onException(e);
+					return;
+				}
+				bodyCache.put(gm.getMessage().getId(), body);
+				storeMessage(gm, handler);
+			}
+		});
+	}
+
+	private void storeMessage(final GroupMessage gm,
+			final ResultExceptionHandler<GroupMessageItem, DbException> handler) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					LOG.info("Store message...");
+					long now = System.currentTimeMillis();
+					privateGroupManager.addLocalMessage(gm);
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Storing message took " + duration + " ms");
+
+					GroupMessageHeader h = new GroupMessageHeader(groupId,
+							gm.getMessage().getId(), gm.getParent(),
+							gm.getMessage().getTimestamp(), gm.getAuthor(),
+							OURSELVES, true);
+
+					handler.onResult(buildItem(h));
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					handler.onException(e);
+				}
+			}
+		});
+	}
+
+	@Override
+	protected void deleteGroupItem(PrivateGroup group) throws DbException {
+		privateGroupManager.removePrivateGroup(group.getId());
+	}
+
+	@Override
+	protected GroupMessageItem buildItem(GroupMessageHeader header) {
+		return new GroupMessageItem(header, bodyCache.get(header.getId()));
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
new file mode 100644
index 0000000000..19ee14adce
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageAdapter.java
@@ -0,0 +1,28 @@
+package org.briarproject.android.privategroup.conversation;
+
+import android.support.annotation.UiThread;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.briarproject.R;
+import org.briarproject.android.threaded.ThreadItemAdapter;
+
+@UiThread
+public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
+
+	public GroupMessageAdapter(ThreadItemListener<GroupMessageItem> listener,
+			LinearLayoutManager layoutManager) {
+		super(listener, layoutManager);
+	}
+
+	@Override
+	public GroupMessageViewHolder onCreateViewHolder(ViewGroup parent,
+			int viewType) {
+		View v = LayoutInflater.from(parent.getContext())
+				.inflate(R.layout.list_item_forum_post, parent, false);
+		return new GroupMessageViewHolder(v);
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageViewHolder.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageViewHolder.java
new file mode 100644
index 0000000000..11825b8056
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupMessageViewHolder.java
@@ -0,0 +1,14 @@
+package org.briarproject.android.privategroup.conversation;
+
+import android.view.View;
+
+import org.briarproject.android.threaded.ThreadItemViewHolder;
+
+public class GroupMessageViewHolder
+		extends ThreadItemViewHolder<GroupMessageItem> {
+
+	public GroupMessageViewHolder(View v) {
+		super(v);
+	}
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupListAdapter.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListAdapter.java
index 20210a486c..b29fc227b6 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListAdapter.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListAdapter.java
@@ -1,7 +1,6 @@
 package org.briarproject.android.privategroup.list;
 
 import android.content.Context;
-import android.support.annotation.Nullable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupViewHolder.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupViewHolder.java
index 12b956df4f..3994a0a1e5 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupViewHolder.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupViewHolder.java
@@ -1,7 +1,9 @@
 package org.briarproject.android.privategroup.list;
 
 import android.content.Context;
+import android.content.Intent;
 import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -10,13 +12,18 @@ import android.widget.Button;
 import android.widget.TextView;
 
 import org.briarproject.R;
+import org.briarproject.android.privategroup.conversation.GroupActivity;
 import org.briarproject.android.util.AndroidUtils;
 import org.briarproject.android.view.TextAvatarView;
+import org.briarproject.api.sync.GroupId;
 import org.jetbrains.annotations.NotNull;
 
 import static android.support.v4.content.ContextCompat.getColor;
+import static android.support.v4.content.ContextCompat.startActivities;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
+import static org.briarproject.android.BriarActivity.GROUP_ID;
+import static org.briarproject.android.BriarActivity.GROUP_NAME;
 
 class GroupViewHolder extends RecyclerView.ViewHolder {
 
@@ -44,7 +51,7 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
 		remove = (Button) v.findViewById(R.id.removeButton);
 	}
 
-	void bindView(Context ctx, @Nullable final GroupItem group,
+	void bindView(final Context ctx, @Nullable final GroupItem group,
 			@NotNull final OnGroupRemoveClickListener listener) {
 		if (group == null) return;
 
@@ -115,15 +122,15 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
 		layout.setOnClickListener(new OnClickListener() {
 			@Override
 			public void onClick(View v) {
-/*
 				Intent i = new Intent(ctx, GroupActivity.class);
-				GroupId id = item.getId();
+				GroupId id = group.getId();
 				i.putExtra(GROUP_ID, id.getBytes());
+				i.putExtra(GROUP_NAME, group.getName());
 				ActivityOptionsCompat options = ActivityOptionsCompat
 						.makeCustomAnimation(ctx, android.R.anim.fade_in,
 								android.R.anim.fade_out);
-				ActivityCompat.startActivity(ctx, i, options.toBundle());
-*/
+				Intent[] intents = {i};
+				startActivities(ctx, intents, options.toBundle());
 			}
 		});
 	}
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
index 6a74779230..e175d3034e 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
@@ -90,8 +90,8 @@ public abstract class ThreadListActivity<G extends BaseGroup, I extends ThreadIt
 		getController().loadGroupItem(
 				new UiResultExceptionHandler<G, DbException>(this) {
 					@Override
-					public void onResultUi(G forum) {
-						setTitle(forum.getName());
+					public void onResultUi(G groupItem) {
+						setTitle(groupItem.getName());
 					}
 
 					@Override
diff --git a/briar-api/src/org/briarproject/api/forum/ForumPost.java b/briar-api/src/org/briarproject/api/forum/ForumPost.java
index aef48c78c4..bbc23e9db0 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumPost.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumPost.java
@@ -9,6 +9,7 @@ import org.jetbrains.annotations.Nullable;
 
 public class ForumPost extends BaseMessage {
 
+	@Nullable
 	private final Author author;
 
 	public ForumPost(@NotNull Message message, @Nullable MessageId parent,
diff --git a/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java b/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java
index 8ffe6c6a46..22457de2eb 100644
--- a/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java
+++ b/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java
@@ -1,17 +1,24 @@
 package org.briarproject.api.privategroup;
 
-import org.briarproject.api.forum.ForumPost;
+import org.briarproject.api.clients.BaseMessage;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-public class GroupMessage extends ForumPost {
+public class GroupMessage extends BaseMessage {
+
+	private final Author author;
 
 	public GroupMessage(@NotNull Message message, @Nullable MessageId parent,
 			@NotNull Author author) {
-		super(message, parent, author);
+		super(message, parent);
+		this.author = author;
+	}
+
+	public Author getAuthor() {
+		return author;
 	}
 
 }
-- 
GitLab