diff --git a/res/values/strings.xml b/res/values/strings.xml
index a6cd89e760..73605e508f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1073,7 +1073,12 @@
- %1$s joined the group.
- %1$s joined the group.
+
+ - %1$s was removed from the group.
+ - %1$s were removed from the group.
+
Group name is now \'%1$s\'.
+ You were removed from the group.
Make your profile name and photo visible to this group?
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
index 2d8c98bee0..5e3e103d23 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
@@ -175,7 +175,7 @@ public class ConversationUpdateItem extends LinearLayout
icon.setImageResource(R.drawable.ic_group_grey600_24dp);
icon.clearColorFilter();
- GroupUtil.getDescription(getContext(), messageRecord.getBody()).addListener(this);
+ GroupUtil.getDescription(getContext(), messageRecord.getBody(), messageRecord.getRecipient()).addListener(this);
body.setText(messageRecord.getDisplayBody(getContext()));
title.setVisibility(GONE);
diff --git a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
index 0531562b73..a2b324c7cb 100644
--- a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
@@ -92,7 +92,7 @@ public abstract class MessageRecord extends DisplayRecord {
if (isGroupUpdate() && isOutgoing()) {
return new SpannableString(context.getString(R.string.MessageRecord_you_updated_group));
} else if (isGroupUpdate()) {
- return new SpannableString(GroupUtil.getDescription(context, getBody()).toString(getIndividualRecipient()));
+ return new SpannableString(GroupUtil.getDescription(context, getBody(), getRecipient()).toString(getIndividualRecipient()));
} else if (isGroupQuit() && isOutgoing()) {
return new SpannableString(context.getString(R.string.MessageRecord_left_group));
} else if (isGroupQuit()) {
diff --git a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
index 49df8e0e37..f184231a72 100644
--- a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
+++ b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
@@ -141,7 +141,9 @@ public class GroupMessageProcessor {
}
if (missingMembers.size() > 0) {
- // TODO We should tell added and missing about each-other.
+ for (Address removedMember : missingMembers) {
+ builder.addMembers(removedMember.serialize());
+ }
}
if (group.getName().isPresent() || group.getAvatar().isPresent()) {
diff --git a/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt
index dfc732474c..ac5bde0b08 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt
@@ -45,6 +45,10 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
fun getFriendRequestStatus(threadID: Long): LokiThreadFriendRequestStatus {
if (threadID < 0) { return LokiThreadFriendRequestStatus.NONE }
+ // Loki - Friend request logic doesn't apply to group chats, always treat them as friends
+ val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID)
+ if (recipient != null && recipient.isGroupRecipient) { return LokiThreadFriendRequestStatus.FRIENDS; }
+
val database = databaseHelper.readableDatabase
val result = database.get(friendRequestTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor ->
cursor.getInt(friendRequestStatus)
diff --git a/src/org/thoughtcrime/securesms/util/GroupUtil.java b/src/org/thoughtcrime/securesms/util/GroupUtil.java
index b8d2500a32..3dc6c363a7 100644
--- a/src/org/thoughtcrime/securesms/util/GroupUtil.java
+++ b/src/org/thoughtcrime/securesms/util/GroupUtil.java
@@ -7,10 +7,13 @@ import android.support.annotation.WorkerThread;
import com.google.protobuf.ByteString;
+import network.loki.messenger.BuildConfig;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.database.Address;
+import org.thoughtcrime.securesms.database.Database;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
+import org.thoughtcrime.securesms.database.GroupDatabase.*;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -111,17 +114,25 @@ public class GroupUtil {
}
- public static @NonNull GroupDescription getDescription(@NonNull Context context, @Nullable String encodedGroup) {
+ public static @NonNull GroupDescription getDescription(@NonNull Context context, @Nullable String encodedGroup, @Nullable Recipient groupRecipient) {
+ // Make sure we always are passing a group recipient
+ if (BuildConfig.DEBUG && groupRecipient != null && !groupRecipient.isGroupRecipient()) {
+ throw new AssertionError();
+ }
+
if (encodedGroup == null) {
- return new GroupDescription(context, null);
+ return new GroupDescription(context, null, null);
}
try {
- GroupContext groupContext = GroupContext.parseFrom(Base64.decode(encodedGroup));
- return new GroupDescription(context, groupContext);
+ GroupContext groupContext = GroupContext.parseFrom(Base64.decode(encodedGroup));
+ GroupRecord groupRecord = groupRecipient != null
+ ? DatabaseFactory.getGroupDatabase(context).getGroup(groupRecipient.getAddress().toGroupString()).orNull()
+ : null;
+ return new GroupDescription(context, groupContext, groupRecord);
} catch (IOException e) {
Log.w(TAG, e);
- return new GroupDescription(context, null);
+ return new GroupDescription(context, null, null);
}
}
@@ -129,24 +140,50 @@ public class GroupUtil {
@NonNull private final Context context;
@Nullable private final GroupContext groupContext;
- @Nullable private final List members;
+ private final List members;
+ private final List removedMembers;
+ private boolean ourDeviceWasRemoved;
- public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) {
+ public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) { this(context, groupContext, null); }
+ public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext, @Nullable GroupRecord groupRecord) {
this.context = context.getApplicationContext();
this.groupContext = groupContext;
- if (groupContext == null || groupContext.getMembersList().isEmpty()) {
- this.members = null;
- } else {
- this.members = new LinkedList<>();
+ this.members = new LinkedList<>();
+ this.removedMembers = new LinkedList<>();
+ this.ourDeviceWasRemoved = false;
+
+ if (groupContext != null && !groupContext.getMembersList().isEmpty()) {
+ List memberList = groupContext.getMembersList();
+ List currentMembers = groupRecord != null ? groupRecord.getMembers() : null;
+
+ // Add them to the member or removed members lists
+ for (String member : memberList) {
+ Address address = Address.fromSerialized(member);
+ Recipient recipient = Recipient.from(context, address, true);
+ if (currentMembers == null || currentMembers.contains(address)) {
+ this.members.add(recipient);
+ } else {
+ this.removedMembers.add(recipient);
+ }
+ }
- for (String member : groupContext.getMembersList()) {
- this.members.add(Recipient.from(context, Address.fromExternal(context, member), true));
+ // Check if our device was removed
+ if (!removedMembers.isEmpty()) {
+ String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
+ String hexEncodedPublicKey = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : TextSecurePreferences.getLocalNumber(context);
+ Recipient self = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false);
+ ourDeviceWasRemoved = removedMembers.contains(self);
}
}
}
public String toString(Recipient sender) {
+ // Show the local removed message
+ if (ourDeviceWasRemoved) {
+ return context.getString(R.string.GroupUtil_you_were_removed_from_group);
+ }
+
StringBuilder description = new StringBuilder();
description.append(context.getString(R.string.MessageRecord_s_updated_group, sender.toShortString()));
@@ -156,14 +193,20 @@ public class GroupUtil {
String title = groupContext.getName();
- if (members != null) {
+ if (!members.isEmpty()) {
description.append("\n");
description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_joined_the_group,
members.size(), toString(members)));
}
+ if (!removedMembers.isEmpty()) {
+ description.append("\n");
+ description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_removed_from_the_group,
+ removedMembers.size(), toString(removedMembers)));
+ }
+
if (title != null && !title.trim().isEmpty()) {
- if (members != null) description.append(" ");
+ if (!members.isEmpty()) description.append(" ");
else description.append("\n");
description.append(context.getString(R.string.GroupUtil_group_name_is_now, title));
}
@@ -172,7 +215,7 @@ public class GroupUtil {
}
public void addListener(RecipientModifiedListener listener) {
- if (this.members != null) {
+ if (!this.members.isEmpty()) {
for (Recipient member : this.members) {
member.addListener(listener);
}