From 62040d45b8265716f731f90d59cc17add0db4b1c Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Mon, 7 Nov 2016 16:09:07 -0200
Subject: [PATCH] Create GroupDissolvedEvent and react to it

Also react to incoming group invitations
---
 briar-android/res/values/strings.xml          | 12 ++++----
 .../android/forum/ForumControllerImpl.java    |  3 +-
 .../conversation/GroupActivity.java           | 23 +++++++++++++--
 .../conversation/GroupController.java         |  7 +++++
 .../conversation/GroupControllerImpl.java     | 14 +++++++++-
 .../android/privategroup/list/GroupItem.java  |  4 +++
 .../list/GroupListController.java             |  7 +++++
 .../list/GroupListControllerImpl.java         | 28 +++++++++++++++++++
 .../privategroup/list/GroupListFragment.java  | 16 +++++++++++
 .../threaded/ThreadListControllerImpl.java    |  7 +++--
 .../api/event/GroupDissolvedEvent.java        | 26 +++++++++++++++++
 .../privategroup/PrivateGroupManagerImpl.java |  4 +++
 12 files changed, 139 insertions(+), 12 deletions(-)
 create mode 100644 briar-api/src/org/briarproject/api/event/GroupDissolvedEvent.java

diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index 42c7979248..3d9e47c001 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -155,7 +155,7 @@
 		<item quantity="other">%d messages</item>
 	</plurals>
 	<string name="groups_group_is_empty">This group is empty</string>
-	<string name="groups_group_is_dissolved">This group is dissolved</string>
+	<string name="groups_group_is_dissolved">This group has been dissolved</string>
 	<string name="groups_remove">Remove</string>
 	<string name="groups_create_group_title">Create 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>
@@ -168,11 +168,16 @@
 	<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_member_joined">joined the group.</string>
 	<string name="groups_leave">Leave Group</string>
 	<string name="groups_leave_dialog_title">Confirm Leaving Group</string>
 	<string name="groups_leave_dialog_message">Are you sure that you want to leave this group?</string>
 	<string name="groups_dissolve">Dissolve Group</string>
-	<string name="groups_member_joined">joined the group.</string>
+	<string name="groups_dissolve_dialog_title">Confirm Dissolving Group</string>
+	<string name="groups_dissolve_dialog_message">Are you sure that you want to dissolve this group?\n\nAll other members will not be able to continue their conversation and might not receive the latest messages.</string>
+	<string name="groups_dissolve_button">Dissolve</string>
+	<string name="groups_dissolved_dialog_title">Group Has Been Dissolved</string>
+	<string name="groups_dissolved_dialog_message">The creator of this group has dissolved it.\n\nYou can no longer write messages to the group and might not receive all posts that have been written.</string>
 
 	<!-- Private Group Invitations -->
 	<string name="groups_invitations_title">Group Invitations</string>
@@ -188,9 +193,6 @@
 	<string name="groups_invitations_response_declined_sent">You declined the group invitation from %s.</string>
 	<string name="groups_invitations_response_accepted_received">%s accepted your group invitation.</string>
 	<string name="groups_invitations_response_declined_received">%s declined your group invitation.</string>
-	<string name="groups_dissolve_dialog_title">Confirm Dissolving Group</string>
-	<string name="groups_dissolve_dialog_message">Are you sure that you want to dissolve this group?\n\nAll other members will not be able to continue their conversation and might not receive the latest messages.</string>
-	<string name="groups_dissolve_button">Dissolve</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/forum/ForumControllerImpl.java b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
index a3b7fde893..55c8f817b8 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
@@ -4,6 +4,7 @@ import android.support.annotation.Nullable;
 
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.android.threaded.ThreadListController.ThreadListListener;
 import org.briarproject.android.threaded.ThreadListControllerImpl;
 import org.briarproject.api.clients.MessageTracker.GroupCount;
 import org.briarproject.api.crypto.CryptoExecutor;
@@ -32,7 +33,7 @@ import static java.lang.Math.max;
 import static java.util.logging.Level.WARNING;
 
 public class ForumControllerImpl
-		extends ThreadListControllerImpl<Forum, ForumItem, ForumPostHeader, ForumPost>
+		extends ThreadListControllerImpl<Forum, ForumItem, ForumPostHeader, ForumPost, ThreadListListener<ForumPostHeader>>
 		implements ForumController {
 
 	private static final Logger LOG =
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
index dcdabb5feb..e59f6cdaca 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
@@ -18,8 +18,9 @@ import android.view.MenuItem;
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.controller.handler.UiResultExceptionHandler;
-import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity;
+import org.briarproject.android.privategroup.conversation.GroupController.GroupListener;
 import org.briarproject.android.privategroup.creation.GroupInviteActivity;
+import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity;
 import org.briarproject.android.threaded.ThreadListActivity;
 import org.briarproject.android.threaded.ThreadListController;
 import org.briarproject.api.db.DbException;
@@ -29,11 +30,12 @@ import org.briarproject.api.privategroup.PrivateGroup;
 import javax.inject.Inject;
 
 import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static android.view.View.GONE;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
 
 public class GroupActivity extends
 		ThreadListActivity<PrivateGroup, GroupMessageItem, GroupMessageHeader>
-		implements OnClickListener {
+		implements GroupListener, OnClickListener {
 
 	private final static int REQUEST_INVITE = 2;
 
@@ -200,6 +202,12 @@ public class GroupActivity extends
 		if (writeMenuItem != null) writeMenuItem.setVisible(enabled);
 		textInput.setSendButtonEnabled(enabled);
 		list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f);
+
+		if (!enabled) {
+			textInput.setVisibility(GONE);
+			if (textInput.isKeyboardOpen()) textInput.hideSoftKeyboard();
+			if (textInput.isEmojiDrawerOpen()) textInput.hideEmojiDrawer();
+		}
 	}
 
 	private void showMenuItems() {
@@ -254,4 +262,15 @@ public class GroupActivity extends
 				});
 	}
 
+	@Override
+	public void onGroupDissolved() {
+		setGroupEnabled(false);
+		AlertDialog.Builder builder =
+			new AlertDialog.Builder(this, R.style.BriarDialogTheme);
+		builder.setTitle(getString(R.string.groups_dissolved_dialog_title));
+		builder.setMessage(getString(R.string.groups_dissolved_dialog_message));
+		builder.setNeutralButton(R.string.ok, null);
+		builder.show();
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
index ffdc971fce..18915ab15d 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupController.java
@@ -1,5 +1,7 @@
 package org.briarproject.android.privategroup.conversation;
 
+import android.support.annotation.UiThread;
+
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
 import org.briarproject.android.threaded.ThreadListController;
 import org.briarproject.api.db.DbException;
@@ -16,4 +18,9 @@ public interface GroupController
 	void isDissolved(
 			ResultExceptionHandler<Boolean, DbException> handler);
 
+	interface GroupListener extends ThreadListListener<GroupMessageHeader> {
+		@UiThread
+		void onGroupDissolved();
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
index 8146f200aa..89de803314 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
@@ -4,12 +4,14 @@ import android.support.annotation.Nullable;
 
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.android.privategroup.conversation.GroupController.GroupListener;
 import org.briarproject.android.threaded.ThreadListControllerImpl;
 import org.briarproject.api.crypto.CryptoExecutor;
 import org.briarproject.api.db.DatabaseExecutor;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
+import org.briarproject.api.event.GroupDissolvedEvent;
 import org.briarproject.api.event.GroupMessageAddedEvent;
 import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.identity.LocalAuthor;
@@ -34,7 +36,7 @@ import static java.lang.Math.max;
 import static java.util.logging.Level.WARNING;
 
 public class GroupControllerImpl extends
-		ThreadListControllerImpl<PrivateGroup, GroupMessageItem, GroupMessageHeader, GroupMessage>
+		ThreadListControllerImpl<PrivateGroup, GroupMessageItem, GroupMessageHeader, GroupMessage, GroupListener>
 		implements GroupController {
 
 	private static final Logger LOG =
@@ -78,6 +80,16 @@ public class GroupControllerImpl extends
 					}
 				});
 			}
+		} else if (e instanceof GroupDissolvedEvent) {
+			GroupDissolvedEvent g = (GroupDissolvedEvent) e;
+			if (getGroupId().equals(g.getGroupId())) {
+				listener.runOnUiThreadUnlessDestroyed(new Runnable() {
+					@Override
+					public void run() {
+						listener.onGroupDissolved();
+					}
+				});
+			}
 		}
 	}
 
diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupItem.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupItem.java
index ac75f29cd5..31c6458a65 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupItem.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupItem.java
@@ -70,4 +70,8 @@ class GroupItem {
 		return dissolved;
 	}
 
+	void setDissolved() {
+		dissolved = true;
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupListController.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListController.java
index f102f82e49..71c9f83058 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListController.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListController.java
@@ -38,11 +38,18 @@ public interface GroupListController extends DbController {
 		@UiThread
 		void onGroupMessageAdded(GroupMessageHeader header);
 
+		@UiThread
+		void onGroupInvitationReceived();
+
 		@UiThread
 		void onGroupAdded(GroupId groupId);
 
 		@UiThread
 		void onGroupRemoved(GroupId groupId);
+
+		@UiThread
+		void onGroupDissolved(GroupId groupId);
+
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java
index 4524e484b3..60881e1c89 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java
@@ -13,6 +13,8 @@ import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
 import org.briarproject.api.event.GroupAddedEvent;
+import org.briarproject.api.event.GroupDissolvedEvent;
+import org.briarproject.api.event.GroupInvitationReceivedEvent;
 import org.briarproject.api.event.GroupMessageAddedEvent;
 import org.briarproject.api.event.GroupRemovedEvent;
 import org.briarproject.api.lifecycle.LifecycleManager;
@@ -89,6 +91,10 @@ public class GroupListControllerImpl extends DbControllerImpl
 			GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
 			LOG.info("Private group message added");
 			onGroupMessageAdded(g.getHeader());
+		} else if (e instanceof GroupInvitationReceivedEvent) {
+			GroupInvitationReceivedEvent g = (GroupInvitationReceivedEvent) e;
+			LOG.info("Private group invitation received");
+			onGroupInvitationReceived();
 		} else if (e instanceof GroupAddedEvent) {
 			GroupAddedEvent g = (GroupAddedEvent) e;
 			ClientId id = g.getGroup().getClientId();
@@ -103,6 +109,10 @@ public class GroupListControllerImpl extends DbControllerImpl
 				LOG.info("Private group removed");
 				onGroupRemoved(g.getGroup().getId());
 			}
+		} else if (e instanceof GroupDissolvedEvent) {
+			GroupDissolvedEvent g = (GroupDissolvedEvent) e;
+			LOG.info("Private group dissolved");
+			onGroupDissolved(g.getGroupId());
 		}
 	}
 
@@ -115,6 +125,15 @@ public class GroupListControllerImpl extends DbControllerImpl
 		});
 	}
 
+	private void onGroupInvitationReceived() {
+		listener.runOnUiThreadUnlessDestroyed(new Runnable() {
+			@Override
+			public void run() {
+				listener.onGroupInvitationReceived();
+			}
+		});
+	}
+
 	private void onGroupAdded(final GroupId g) {
 		listener.runOnUiThreadUnlessDestroyed(new Runnable() {
 			@Override
@@ -133,6 +152,15 @@ public class GroupListControllerImpl extends DbControllerImpl
 		});
 	}
 
+	private void onGroupDissolved(final GroupId g) {
+		listener.runOnUiThreadUnlessDestroyed(new Runnable() {
+			@Override
+			public void run() {
+				listener.onGroupDissolved(g);
+			}
+		});
+	}
+
 	@Override
 	public void loadGroups(
 			final ResultExceptionHandler<Collection<GroupItem>, DbException> handler) {
diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
index ec9f94b9f4..6dbcf31499 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
@@ -151,6 +151,11 @@ public class GroupListFragment extends BaseFragment implements
 		}
 	}
 
+	@Override
+	public void onGroupInvitationReceived() {
+		loadAvailableGroups();
+	}
+
 	@UiThread
 	@Override
 	public void onGroupAdded(GroupId groupId) {
@@ -164,6 +169,17 @@ public class GroupListFragment extends BaseFragment implements
 		adapter.removeItem(groupId);
 	}
 
+	@Override
+	public void onGroupDissolved(GroupId groupId) {
+		adapter.incrementRevision();
+		int position = adapter.findItemPosition(groupId);
+		GroupItem item = adapter.getItemAt(position);
+		if (item != null) {
+			item.setDissolved();
+			adapter.updateItemAt(position, item);
+		}
+	}
+
 	@Override
 	public String getUniqueTag() {
 		return TAG;
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java b/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
index 441758258e..3d52c972fa 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
@@ -6,6 +6,7 @@ import android.support.annotation.CallSuper;
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.controller.DbControllerImpl;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.android.threaded.ThreadListController.ThreadListListener;
 import org.briarproject.api.clients.ThreadedMessage;
 import org.briarproject.api.clients.NamedGroup;
 import org.briarproject.api.clients.PostHeader;
@@ -34,7 +35,7 @@ import java.util.logging.Logger;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 
-public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage>
+public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage, L extends ThreadListListener<H>>
 		extends DbControllerImpl
 		implements ThreadListController<G, I, H>, EventListener {
 
@@ -49,7 +50,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 	protected final AndroidNotificationManager notificationManager;
 	protected final Executor cryptoExecutor;
 	protected final Clock clock;
-	protected volatile ThreadListListener<H> listener;
+	protected volatile L listener;
 
 	protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
 			LifecycleManager lifecycleManager, IdentityManager identityManager,
@@ -72,7 +73,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 	@SuppressWarnings("unchecked")
 	@Override
 	public void onActivityCreate(Activity activity) {
-		listener = (ThreadListListener<H>) activity;
+		listener = (L) activity;
 	}
 
 	@CallSuper
diff --git a/briar-api/src/org/briarproject/api/event/GroupDissolvedEvent.java b/briar-api/src/org/briarproject/api/event/GroupDissolvedEvent.java
new file mode 100644
index 0000000000..5b8a3224d3
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/event/GroupDissolvedEvent.java
@@ -0,0 +1,26 @@
+package org.briarproject.api.event;
+
+import org.briarproject.api.nullsafety.NotNullByDefault;
+import org.briarproject.api.sync.GroupId;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * An event that is broadcast when a private group was dissolved
+ * by a remote creator.
+ */
+@Immutable
+@NotNullByDefault
+public class GroupDissolvedEvent extends Event {
+
+	private final GroupId groupId;
+
+	public GroupDissolvedEvent(GroupId groupId) {
+		this.groupId = groupId;
+	}
+
+	public GroupId getGroupId() {
+		return groupId;
+	}
+
+}
diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
index 4b2ab02666..2865aa9382 100644
--- a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
@@ -10,6 +10,8 @@ import org.briarproject.api.data.MetadataParser;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.GroupDissolvedEvent;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.Author.Status;
 import org.briarproject.api.identity.AuthorId;
@@ -176,6 +178,8 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 		} catch (FormatException e) {
 			throw new DbException(e);
 		}
+		Event e = new GroupDissolvedEvent(g);
+		txn.attach(e);
 	}
 
 	@Override
-- 
GitLab