From 42175dca7a52cd70b3a93745834ae4fe1b7c3faa Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Tue, 18 Oct 2016 10:22:33 -0200
Subject: [PATCH] Show group invitations and responses in private conversation

---
 briar-android/res/values/strings.xml          |  6 ++
 .../android/contact/ContactListFragment.java  |  5 +-
 .../android/contact/ConversationActivity.java | 93 +++++++++++--------
 .../android/contact/ConversationItem.java     | 51 ++++++++--
 .../contact/ConversationRequestItem.java      |  2 +-
 .../briarproject/api/clients/BaseMessage.java |  9 +-
 .../api/privategroup/GroupMessage.java        |  9 +-
 .../invitation/GroupInvitationItem.java       |  5 +
 .../invitation/GroupInvitationRequest.java    |  3 +-
 .../invitation/GroupInvitationResponse.java   |  3 +-
 .../api/sharing/InvitationItem.java           |  3 +-
 .../api/sharing/SharingInvitationItem.java    |  5 +
 12 files changed, 141 insertions(+), 53 deletions(-)

diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index bd35788a27..c82dab5efc 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -169,12 +169,18 @@
 
 	<!-- Private Group Invitations -->
 	<string name="groups_invitations_title">Group Invitations</string>
+	<string name="groups_invitations_invitation_sent">You have invited %1$s to your group "%2$s".</string>
+	<string name="groups_invitations_invitation_received">%1$s has invited you to join the group "%2$s".</string>
 	<string name="groups_invitations_joined">Joined Group</string>
 	<string name="groups_invitations_declined">Group Invitation Declined</string>
 	<plurals name="groups_invitations_open">
 		<item quantity="one">%d open group invitation</item>
 		<item quantity="other">%d open group invitations</item>
 	</plurals>
+	<string name="groups_invitations_response_accepted_sent">You accepted the group invitation from %s.</string>
+	<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>
 
 	<!-- 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/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index deb7944037..a5c7aef1df 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -286,8 +286,9 @@ public class ContactListFragment extends BaseFragment implements EventListener {
 			updateItem(m.getContactId(),
 					ConversationItem.from(getContext(), "", ir));
 		} else if (e instanceof InvitationRequestReceivedEvent) {
-			LOG.info("Invitation request received, updating item");
-			InvitationRequestReceivedEvent m = (InvitationRequestReceivedEvent) e;
+			LOG.info("Invitation Request received, update item");
+			InvitationRequestReceivedEvent m =
+					(InvitationRequestReceivedEvent) e;
 			InvitationRequest ir = m.getRequest();
 			updateItem(m.getContactId(),
 					ConversationItem.from(getContext(), "", ir));
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index 5eac456b6e..ea618642f3 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -67,6 +67,7 @@ import org.briarproject.api.messaging.PrivateMessage;
 import org.briarproject.api.messaging.PrivateMessageFactory;
 import org.briarproject.api.messaging.PrivateMessageHeader;
 import org.briarproject.api.plugins.ConnectionRegistry;
+import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
 import org.briarproject.api.settings.Settings;
 import org.briarproject.api.settings.SettingsManager;
 import org.briarproject.api.sharing.InvitationMessage;
@@ -146,6 +147,8 @@ public class ConversationActivity extends BriarActivity
 	volatile ForumSharingManager forumSharingManager;
 	@Inject
 	volatile BlogSharingManager blogSharingManager;
+	@Inject
+	volatile GroupInvitationManager groupInvitationManager;
 
 	private volatile GroupId groupId = null;
 	private volatile ContactId contactId = null;
@@ -351,10 +354,14 @@ public class ConversationActivity extends BriarActivity
 					Collection<InvitationMessage> blogInvitations =
 							blogSharingManager
 									.getInvitationMessages(contactId);
+					Collection<InvitationMessage> groupInvitations =
+							groupInvitationManager
+									.getInvitationMessages(contactId);
 					List<InvitationMessage> invitations = new ArrayList<>(
 							forumInvitations.size() + blogInvitations.size());
 					invitations.addAll(forumInvitations);
 					invitations.addAll(blogInvitations);
+					invitations.addAll(groupInvitations);
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
 						LOG.info("Loading messages took " + duration + " ms");
@@ -398,44 +405,46 @@ public class ConversationActivity extends BriarActivity
 			Collection<PrivateMessageHeader> headers,
 			Collection<IntroductionMessage> introductions,
 			Collection<InvitationMessage> invitations) {
-		int size = headers.size() + introductions.size() + invitations.size();
+		int size =
+				headers.size() + introductions.size() + invitations.size();
 		List<ConversationItem> items = new ArrayList<>(size);
-
-			for (PrivateMessageHeader h : headers) {
-				ConversationItem item = ConversationItem.from(h);
-				String body = bodyCache.get(h.getId());
-				if (body == null) loadMessageBody(h.getId());
-				else ((PartialItem) item).setText(body);
-				items.add(item);
-			}
-			for (IntroductionMessage m : introductions) {
-				ConversationItem item;
-				if (m instanceof IntroductionRequest) {
-					item = ConversationItem
-							.from(ConversationActivity.this,
-									contactName,
-									(IntroductionRequest) m);
-				} else {
-					item = ConversationItem
-							.from(ConversationActivity.this,
-									contactName,
-									(IntroductionResponse) m);
-				}
-				items.add(item);
-			}
-			for (InvitationMessage i : invitations) {
-				if (i instanceof InvitationRequest) {
-					InvitationRequest r = (InvitationRequest) i;
-					items.add(ConversationItem
-							.from(ConversationActivity.this,
-									contactName, r));
-				} else if (i instanceof InvitationResponse) {
-					InvitationResponse r = (InvitationResponse) i;
-					items.add(ConversationItem
-							.from(ConversationActivity.this,
-									contactName, r));
-				}
-			}
+		for (PrivateMessageHeader h : headers) {
+			ConversationItem item = ConversationItem.from(h);
+			String body = bodyCache.get(h.getId());
+			if (body == null) loadMessageBody(h.getId());
+			else ((PartialItem) item).setText(body);
+			items.add(item);
+		}
+		for (IntroductionMessage m : introductions) {
+			ConversationItem item;
+			if (m instanceof IntroductionRequest) {
+				item = ConversationItem
+						.from(ConversationActivity.this,
+								contactName,
+								(IntroductionRequest) m);
+			} else {
+				item = ConversationItem
+						.from(ConversationActivity.this,
+								contactName,
+								(IntroductionResponse) m);
+			}
+			items.add(item);
+		}
+		for (InvitationMessage i : invitations) {
+			ConversationItem item;
+			if (i instanceof InvitationRequest) {
+				InvitationRequest r = (InvitationRequest) i;
+				item = ConversationItem
+						.from(ConversationActivity.this,
+								contactName, r);
+			} else {
+				InvitationResponse r = (InvitationResponse) i;
+				item = ConversationItem
+						.from(ConversationActivity.this,
+								contactName, r);
+			}
+			items.add(item);
+		}
 		return items;
 	}
 
@@ -850,6 +859,9 @@ public class ConversationActivity extends BriarActivity
 						case BLOG:
 							respondToBlogRequest(item.getSessionId(), accept);
 							break;
+						case GROUP:
+							respondToGroupRequest(item.getSessionId(), accept);
+							break;
 						default:
 							throw new IllegalArgumentException(
 									"Unknown Request Type");
@@ -889,6 +901,13 @@ public class ConversationActivity extends BriarActivity
 		loadMessages();
 	}
 
+	@DatabaseExecutor
+	private void respondToGroupRequest(SessionId id, boolean accept)
+			throws DbException {
+		groupInvitationManager.respondToInvitation(id, accept);
+		loadMessages();
+	}
+
 	private void introductionResponseError() {
 		runOnUiThreadUnlessDestroyed(new Runnable() {
 			@Override
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationItem.java b/briar-android/src/org/briarproject/android/contact/ConversationItem.java
index 9d1749ae87..e687935f2d 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationItem.java
@@ -6,12 +6,15 @@ import android.support.annotation.StringRes;
 import org.briarproject.R;
 import org.briarproject.android.contact.ConversationRequestItem.RequestType;
 import org.briarproject.api.blogs.BlogInvitationRequest;
+import org.briarproject.api.blogs.BlogInvitationResponse;
 import org.briarproject.api.forum.ForumInvitationRequest;
 import org.briarproject.api.forum.ForumInvitationResponse;
 import org.briarproject.api.introduction.IntroductionRequest;
 import org.briarproject.api.introduction.IntroductionResponse;
 import org.briarproject.api.messaging.PrivateMessageHeader;
 import org.briarproject.api.nullsafety.NotNullByDefault;
+import org.briarproject.api.privategroup.invitation.GroupInvitationRequest;
+import org.briarproject.api.privategroup.invitation.GroupInvitationResponse;
 import org.briarproject.api.sharing.InvitationRequest;
 import org.briarproject.api.sharing.InvitationResponse;
 import org.briarproject.api.sync.GroupId;
@@ -22,6 +25,7 @@ import javax.annotation.concurrent.NotThreadSafe;
 
 import static org.briarproject.android.contact.ConversationRequestItem.RequestType.BLOG;
 import static org.briarproject.android.contact.ConversationRequestItem.RequestType.FORUM;
+import static org.briarproject.android.contact.ConversationRequestItem.RequestType.GROUP;
 import static org.briarproject.android.contact.ConversationRequestItem.RequestType.INTRODUCTION;
 
 @NotThreadSafe
@@ -145,10 +149,17 @@ abstract class ConversationItem {
 				text = ctx.getString(R.string.forum_invitation_sent,
 						((ForumInvitationRequest) ir).getForumName(),
 						contactName);
-			} else {
+			} else if (ir instanceof BlogInvitationRequest) {
 				text = ctx.getString(R.string.blogs_sharing_invitation_sent,
 						((BlogInvitationRequest) ir).getBlogAuthorName(),
 						contactName);
+			} else if (ir instanceof GroupInvitationRequest) {
+				text = ctx.getString(
+						R.string.groups_invitations_invitation_sent,
+						contactName,
+						((GroupInvitationRequest) ir).getGroupName());
+			} else {
+				throw new IllegalArgumentException("Unknown InvitationRequest");
 			}
 			return new ConversationNoticeOutItem(ir.getId(), ir.getGroupId(),
 					text, ir.getMessage(), ir.getTimestamp(), ir.isSent(),
@@ -161,11 +172,19 @@ abstract class ConversationItem {
 						contactName,
 						((ForumInvitationRequest) ir).getForumName());
 				type = FORUM;
-			} else {
+			} else if (ir instanceof BlogInvitationRequest) {
 				text = ctx.getString(R.string.blogs_sharing_invitation_received,
 						contactName,
 						((BlogInvitationRequest) ir).getBlogAuthorName());
 				type = BLOG;
+			} else if (ir instanceof GroupInvitationRequest) {
+				text = ctx.getString(
+						R.string.groups_invitations_invitation_received,
+						contactName,
+						((GroupInvitationRequest) ir).getGroupName());
+				type = GROUP;
+			} else {
+				throw new IllegalArgumentException("Unknown InvitationRequest");
 			}
 			if (!ir.isAvailable()) {
 				return new ConversationNoticeInItem(ir.getId(), ir.getGroupId(),
@@ -185,14 +204,24 @@ abstract class ConversationItem {
 			if (ir.wasAccepted()) {
 				if (ir instanceof ForumInvitationResponse) {
 					res = R.string.forum_invitation_response_accepted_sent;
-				} else {
+				} else if (ir instanceof BlogInvitationResponse) {
 					res = R.string.blogs_sharing_response_accepted_sent;
+				} else if (ir instanceof GroupInvitationResponse) {
+					res = R.string.groups_invitations_response_accepted_sent;
+				} else {
+					throw new IllegalArgumentException(
+							"Unknown InvitationResponse");
 				}
 			} else {
 				if (ir instanceof ForumInvitationResponse) {
 					res = R.string.forum_invitation_response_declined_sent;
-				} else {
+				} else if (ir instanceof BlogInvitationResponse) {
 					res = R.string.blogs_sharing_response_declined_sent;
+				} else if (ir instanceof GroupInvitationResponse) {
+					res = R.string.groups_invitations_response_declined_sent;
+				} else {
+					throw new IllegalArgumentException(
+							"Unknown InvitationResponse");
 				}
 			}
 			String text = ctx.getString(res, contactName);
@@ -202,14 +231,24 @@ abstract class ConversationItem {
 			if (ir.wasAccepted()) {
 				if (ir instanceof ForumInvitationResponse) {
 					res = R.string.forum_invitation_response_accepted_received;
-				} else {
+				} else if (ir instanceof BlogInvitationResponse) {
 					res = R.string.blogs_sharing_response_accepted_received;
+				} else if (ir instanceof GroupInvitationResponse) {
+					res = R.string.groups_invitations_response_accepted_received;
+				} else {
+					throw new IllegalArgumentException(
+							"Unknown InvitationResponse");
 				}
 			} else {
 					if (ir instanceof ForumInvitationResponse) {
 						res = R.string.forum_invitation_response_declined_received;
-					} else {
+					} else if (ir instanceof BlogInvitationResponse) {
 						res = R.string.blogs_sharing_response_declined_received;
+					} else if (ir instanceof GroupInvitationResponse) {
+						res = R.string.groups_invitations_response_declined_received;
+					} else {
+						throw new IllegalArgumentException(
+								"Unknown InvitationResponse");
 					}
 			}
 			String text = ctx.getString(res, contactName);
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationRequestItem.java b/briar-android/src/org/briarproject/android/contact/ConversationRequestItem.java
index 9999350d42..2b8521f7f4 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationRequestItem.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationRequestItem.java
@@ -12,7 +12,7 @@ import javax.annotation.concurrent.NotThreadSafe;
 @NotNullByDefault
 class ConversationRequestItem extends ConversationNoticeInItem {
 
-	enum RequestType { INTRODUCTION, FORUM, BLOG };
+	enum RequestType { INTRODUCTION, FORUM, BLOG, GROUP };
 	private final RequestType requestType;
 	private final SessionId sessionId;
 	private boolean answered;
diff --git a/briar-api/src/org/briarproject/api/clients/BaseMessage.java b/briar-api/src/org/briarproject/api/clients/BaseMessage.java
index 776bfcf5c9..fbfb563377 100644
--- a/briar-api/src/org/briarproject/api/clients/BaseMessage.java
+++ b/briar-api/src/org/briarproject/api/clients/BaseMessage.java
@@ -1,21 +1,26 @@
 package org.briarproject.api.clients;
 
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+@NotNullByDefault
 public abstract class BaseMessage {
 
 	private final Message message;
+	@Nullable
 	private final MessageId parent;
 
-	public BaseMessage(@NotNull Message message, @Nullable MessageId parent) {
+	public BaseMessage(Message message, @Nullable MessageId parent) {
 		this.message = message;
 		this.parent = parent;
 	}
 
-	@NotNull
 	public Message getMessage() {
 		return message;
 	}
diff --git a/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java b/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java
index 22457de2eb..06f277460e 100644
--- a/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java
+++ b/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java
@@ -2,17 +2,22 @@ package org.briarproject.api.privategroup;
 
 import org.briarproject.api.clients.BaseMessage;
 import org.briarproject.api.identity.Author;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+@NotNullByDefault
 public class GroupMessage extends BaseMessage {
 
 	private final Author author;
 
-	public GroupMessage(@NotNull Message message, @Nullable MessageId parent,
-			@NotNull Author author) {
+	public GroupMessage(Message message, @Nullable MessageId parent,
+			Author author) {
 		super(message, parent);
 		this.author = author;
 	}
diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationItem.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationItem.java
index c7f350be45..16688b37c7 100644
--- a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationItem.java
+++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationItem.java
@@ -1,9 +1,14 @@
 package org.briarproject.api.privategroup.invitation;
 
 import org.briarproject.api.contact.Contact;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sharing.InvitationItem;
 import org.briarproject.api.sharing.Shareable;
 
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+@NotNullByDefault
 public class GroupInvitationItem extends InvitationItem {
 
 	private final Contact creator;
diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationRequest.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationRequest.java
index 3c9b25760f..3115a6bc27 100644
--- a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationRequest.java
+++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationRequest.java
@@ -8,9 +8,10 @@ import org.briarproject.api.sharing.InvitationRequest;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 
+import javax.annotation.concurrent.Immutable;
 import javax.annotation.concurrent.ThreadSafe;
 
-@ThreadSafe
+@Immutable
 @NotNullByDefault
 public class GroupInvitationRequest extends InvitationRequest {
 
diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationResponse.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationResponse.java
index 554e731bcd..dda5d554ac 100644
--- a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationResponse.java
+++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationResponse.java
@@ -9,9 +9,10 @@ import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.jetbrains.annotations.NotNull;
 
+import javax.annotation.concurrent.Immutable;
 import javax.annotation.concurrent.ThreadSafe;
 
-@ThreadSafe
+@Immutable
 @NotNullByDefault
 public class GroupInvitationResponse extends InvitationResponse {
 
diff --git a/briar-api/src/org/briarproject/api/sharing/InvitationItem.java b/briar-api/src/org/briarproject/api/sharing/InvitationItem.java
index a813982b10..423edf3b32 100644
--- a/briar-api/src/org/briarproject/api/sharing/InvitationItem.java
+++ b/briar-api/src/org/briarproject/api/sharing/InvitationItem.java
@@ -3,9 +3,10 @@ package org.briarproject.api.sharing;
 import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.GroupId;
 
+import javax.annotation.concurrent.Immutable;
 import javax.annotation.concurrent.ThreadSafe;
 
-@ThreadSafe
+@Immutable
 @NotNullByDefault
 public abstract class InvitationItem {
 
diff --git a/briar-api/src/org/briarproject/api/sharing/SharingInvitationItem.java b/briar-api/src/org/briarproject/api/sharing/SharingInvitationItem.java
index 8993f23200..ddbc3cba45 100644
--- a/briar-api/src/org/briarproject/api/sharing/SharingInvitationItem.java
+++ b/briar-api/src/org/briarproject/api/sharing/SharingInvitationItem.java
@@ -1,9 +1,14 @@
 package org.briarproject.api.sharing;
 
 import org.briarproject.api.contact.Contact;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 
 import java.util.Collection;
 
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+@NotNullByDefault
 public class SharingInvitationItem extends InvitationItem {
 
 	private final Collection<Contact> newSharers;
-- 
GitLab