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); }