Write previous group state to the database for advanced change messages.
parent
b40fd7b243
commit
38fa58c0a3
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.database.model;
|
|||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
@ -81,7 +82,11 @@ final class GroupsV2UpdateMessageProducer {
|
|||
}
|
||||
}
|
||||
|
||||
List<UpdateDescription> describeChanges(@NonNull DecryptedGroupChange change) {
|
||||
List<UpdateDescription> describeChanges(@Nullable DecryptedGroup previousGroupState, @NonNull DecryptedGroupChange change) {
|
||||
if (DecryptedGroup.getDefaultInstance().equals(previousGroupState)) {
|
||||
previousGroupState = null;
|
||||
}
|
||||
|
||||
List<UpdateDescription> updates = new LinkedList<>();
|
||||
|
||||
if (change.getEditor().isEmpty() || UuidUtil.UNKNOWN_UUID.equals(UuidUtil.fromByteString(change.getEditor()))) {
|
||||
|
@ -96,7 +101,7 @@ final class GroupsV2UpdateMessageProducer {
|
|||
describeUnknownEditorNewTimer(change, updates);
|
||||
describeUnknownEditorNewAttributeAccess(change, updates);
|
||||
describeUnknownEditorNewMembershipAccess(change, updates);
|
||||
describeUnknownEditorNewGroupInviteLinkAccess(change, updates);
|
||||
describeUnknownEditorNewGroupInviteLinkAccess(previousGroupState, change, updates);
|
||||
describeRequestingMembers(change, updates);
|
||||
describeUnknownEditorRequestingMembersApprovals(change, updates);
|
||||
describeUnknownEditorRequestingMembersDeletes(change, updates);
|
||||
|
@ -119,7 +124,7 @@ final class GroupsV2UpdateMessageProducer {
|
|||
describeNewTimer(change, updates);
|
||||
describeNewAttributeAccess(change, updates);
|
||||
describeNewMembershipAccess(change, updates);
|
||||
describeNewGroupInviteLinkAccess(change, updates);
|
||||
describeNewGroupInviteLinkAccess(previousGroupState, change, updates);
|
||||
describeRequestingMembers(change, updates);
|
||||
describeRequestingMembersApprovals(change, updates);
|
||||
describeRequestingMembersDeletes(change, updates);
|
||||
|
@ -509,7 +514,16 @@ final class GroupsV2UpdateMessageProducer {
|
|||
}
|
||||
}
|
||||
|
||||
private void describeNewGroupInviteLinkAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
|
||||
private void describeNewGroupInviteLinkAccess(@Nullable DecryptedGroup previousGroupState,
|
||||
@NonNull DecryptedGroupChange change,
|
||||
@NonNull List<UpdateDescription> updates)
|
||||
{
|
||||
AccessControl.AccessRequired previousAccessControl = null;
|
||||
|
||||
if (previousGroupState != null) {
|
||||
previousAccessControl = previousGroupState.getAccessControl().getAddFromInviteLink();
|
||||
}
|
||||
|
||||
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
|
||||
boolean groupLinkEnabled = false;
|
||||
|
||||
|
@ -517,17 +531,33 @@ final class GroupsV2UpdateMessageProducer {
|
|||
case ANY:
|
||||
groupLinkEnabled = true;
|
||||
if (editorIsYou) {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_off)));
|
||||
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_admin_approval_for_the_group_link)));
|
||||
} else {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_off)));
|
||||
}
|
||||
} else {
|
||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_off, editor)));
|
||||
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
|
||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_off_admin_approval_for_the_group_link, editor)));
|
||||
} else {
|
||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_off, editor)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ADMINISTRATOR:
|
||||
groupLinkEnabled = true;
|
||||
if (editorIsYou) {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_on)));
|
||||
if (previousAccessControl == AccessControl.AccessRequired.ANY) {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_admin_approval_for_the_group_link)));
|
||||
} else {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_on)));
|
||||
}
|
||||
} else {
|
||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_on, editor)));
|
||||
if (previousAccessControl == AccessControl.AccessRequired.ANY) {
|
||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_admin_approval_for_the_group_link, editor)));
|
||||
} else {
|
||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_on, editor)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UNSATISFIABLE:
|
||||
|
@ -548,13 +578,30 @@ final class GroupsV2UpdateMessageProducer {
|
|||
}
|
||||
}
|
||||
|
||||
private void describeUnknownEditorNewGroupInviteLinkAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
|
||||
private void describeUnknownEditorNewGroupInviteLinkAccess(@Nullable DecryptedGroup previousGroupState,
|
||||
@NonNull DecryptedGroupChange change,
|
||||
@NonNull List<UpdateDescription> updates)
|
||||
{
|
||||
AccessControl.AccessRequired previousAccessControl = null;
|
||||
|
||||
if (previousGroupState != null) {
|
||||
previousAccessControl = previousGroupState.getAccessControl().getAddFromInviteLink();
|
||||
}
|
||||
|
||||
switch (change.getNewInviteLinkAccess()) {
|
||||
case ANY:
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_off)));
|
||||
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_off)));
|
||||
} else {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_off)));
|
||||
}
|
||||
break;
|
||||
case ADMINISTRATOR:
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_on)));
|
||||
if (previousAccessControl == AccessControl.AccessRequired.ANY) {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_on)));
|
||||
} else {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_on)));
|
||||
}
|
||||
break;
|
||||
case UNSATISFIABLE:
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_off)));
|
||||
|
|
|
@ -177,7 +177,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
GroupsV2UpdateMessageProducer updateMessageProducer = new GroupsV2UpdateMessageProducer(context, descriptionStrategy, Recipient.self().getUuid().get());
|
||||
|
||||
if (decryptedGroupV2Context.hasChange() && decryptedGroupV2Context.getGroupState().getRevision() != 0) {
|
||||
return UpdateDescription.concatWithNewLines(updateMessageProducer.describeChanges(decryptedGroupV2Context.getChange()));
|
||||
return UpdateDescription.concatWithNewLines(updateMessageProducer.describeChanges(decryptedGroupV2Context.getPreviousGroupState(), decryptedGroupV2Context.getChange()));
|
||||
} else {
|
||||
return updateMessageProducer.describeNewGroup(decryptedGroupV2Context.getGroupState(), decryptedGroupV2Context.getChange());
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ final class GroupManagerV2 {
|
|||
.setEditor(UuidUtil.toByteString(selfUuid))
|
||||
.build();
|
||||
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdate(masterKey, decryptedGroup, groupChange, null);
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdate(masterKey, new GroupMutation(null, groupChange, decryptedGroup), null);
|
||||
|
||||
return new GroupManager.GroupActionResult(recipientAndThread.groupRecipient,
|
||||
recipientAndThread.threadId,
|
||||
|
@ -505,16 +505,18 @@ final class GroupManagerV2 {
|
|||
private GroupManager.GroupActionResult commitChange(@NonNull GroupChange.Actions.Builder change)
|
||||
throws GroupNotAMemberException, GroupChangeFailedException, IOException, GroupInsufficientRightsException
|
||||
{
|
||||
final GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
|
||||
final GroupDatabase.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
|
||||
final int nextRevision = v2GroupProperties.getGroupRevision() + 1;
|
||||
final GroupChange.Actions changeActions = change.setRevision(nextRevision).build();
|
||||
final GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
|
||||
final GroupDatabase.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
|
||||
final int nextRevision = v2GroupProperties.getGroupRevision() + 1;
|
||||
final GroupChange.Actions changeActions = change.setRevision(nextRevision).build();
|
||||
final DecryptedGroupChange decryptedChange;
|
||||
final DecryptedGroup decryptedGroupState;
|
||||
final DecryptedGroup previousGroupState;
|
||||
|
||||
try {
|
||||
previousGroupState = v2GroupProperties.getDecryptedGroup();
|
||||
decryptedChange = groupOperations.decryptChange(changeActions, selfUuid);
|
||||
decryptedGroupState = DecryptedGroupUtil.apply(v2GroupProperties.getDecryptedGroup(), decryptedChange);
|
||||
decryptedGroupState = DecryptedGroupUtil.apply(previousGroupState, decryptedChange);
|
||||
} catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new IOException(e);
|
||||
|
@ -523,7 +525,8 @@ final class GroupManagerV2 {
|
|||
GroupChange signedGroupChange = commitToServer(changeActions);
|
||||
groupDatabase.update(groupId, decryptedGroupState);
|
||||
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdate(groupMasterKey, decryptedGroupState, decryptedChange, signedGroupChange);
|
||||
GroupMutation groupMutation = new GroupMutation(previousGroupState, decryptedChange, decryptedGroupState);
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdate(groupMasterKey, groupMutation, signedGroupChange);
|
||||
int newMembersCount = decryptedChange.getNewMembersCount();
|
||||
List<RecipientId> newPendingMembers = getPendingMemberRecipientIds(decryptedChange.getNewPendingMembersList());
|
||||
|
||||
|
@ -681,7 +684,7 @@ final class GroupManagerV2 {
|
|||
} else if (requestToJoin) {
|
||||
Log.i(TAG, "Requested to join, cannot send update");
|
||||
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdate(groupMasterKey, decryptedGroup, decryptedChange, signedGroupChange);
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdate(groupMasterKey, new GroupMutation(null, decryptedChange, decryptedGroup), signedGroupChange);
|
||||
|
||||
return new GroupManager.GroupActionResult(groupRecipient,
|
||||
recipientAndThread.threadId,
|
||||
|
@ -706,7 +709,7 @@ final class GroupManagerV2 {
|
|||
System.currentTimeMillis(),
|
||||
decryptedChange);
|
||||
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdate(groupMasterKey, decryptedGroup, decryptedChange, signedGroupChange);
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdate(groupMasterKey, new GroupMutation(null, decryptedChange, decryptedGroup), signedGroupChange);
|
||||
|
||||
return new GroupManager.GroupActionResult(groupRecipient,
|
||||
recipientAndThread.threadId,
|
||||
|
@ -905,7 +908,7 @@ final class GroupManagerV2 {
|
|||
|
||||
groupDatabase.update(groupId, resetRevision(newGroup, decryptedGroup.getRevision()));
|
||||
|
||||
sendGroupUpdate(groupMasterKey, decryptedGroup, decryptedChange, signedGroupChange);
|
||||
sendGroupUpdate(groupMasterKey, new GroupMutation(decryptedGroup, decryptedChange, newGroup), signedGroupChange);
|
||||
} catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) {
|
||||
throw new GroupChangeFailedException(e);
|
||||
}
|
||||
|
@ -959,13 +962,12 @@ final class GroupManagerV2 {
|
|||
}
|
||||
|
||||
private @NonNull RecipientAndThread sendGroupUpdate(@NonNull GroupMasterKey masterKey,
|
||||
@NonNull DecryptedGroup decryptedGroup,
|
||||
@Nullable DecryptedGroupChange plainGroupChange,
|
||||
@NonNull GroupMutation groupMutation,
|
||||
@Nullable GroupChange signedGroupChange)
|
||||
{
|
||||
GroupId.V2 groupId = GroupId.v2(masterKey);
|
||||
Recipient groupRecipient = Recipient.externalGroup(context, groupId);
|
||||
DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, decryptedGroup, plainGroupChange, signedGroupChange);
|
||||
DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, groupMutation, signedGroupChange);
|
||||
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(groupRecipient,
|
||||
decryptedGroupV2Context,
|
||||
null,
|
||||
|
@ -977,8 +979,11 @@ final class GroupManagerV2 {
|
|||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
|
||||
DecryptedGroupChange plainGroupChange = groupMutation.getGroupChange();
|
||||
|
||||
if (plainGroupChange != null && DecryptedGroupUtil.changeIsEmptyExceptForProfileKeyChanges(plainGroupChange)) {
|
||||
ApplicationDependencies.getJobManager().add(PushGroupSilentUpdateSendJob.create(context, groupId, decryptedGroup, outgoingMessage));
|
||||
ApplicationDependencies.getJobManager().add(PushGroupSilentUpdateSendJob.create(context, groupId, groupMutation.getNewGroupState(), outgoingMessage));
|
||||
return new RecipientAndThread(groupRecipient, -1);
|
||||
} else {
|
||||
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package org.thoughtcrime.securesms.groups;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
|
||||
public final class GroupMutation {
|
||||
@Nullable private final DecryptedGroup previousGroupState;
|
||||
@Nullable private final DecryptedGroupChange groupChange;
|
||||
@NonNull private final DecryptedGroup newGroupState;
|
||||
|
||||
public GroupMutation(@Nullable DecryptedGroup previousGroupState, @Nullable DecryptedGroupChange groupChange, @NonNull DecryptedGroup newGroupState) {
|
||||
this.previousGroupState = previousGroupState;
|
||||
this.groupChange = groupChange;
|
||||
this.newGroupState = newGroupState;
|
||||
}
|
||||
|
||||
public @Nullable DecryptedGroup getPreviousGroupState() {
|
||||
return previousGroupState;
|
||||
}
|
||||
|
||||
public @Nullable DecryptedGroupChange getGroupChange() {
|
||||
return groupChange;
|
||||
}
|
||||
|
||||
public @NonNull DecryptedGroup getNewGroupState() {
|
||||
return newGroupState;
|
||||
}
|
||||
}
|
|
@ -49,12 +49,13 @@ public final class GroupProtoUtil {
|
|||
}
|
||||
|
||||
public static DecryptedGroupV2Context createDecryptedGroupV2Context(@NonNull GroupMasterKey masterKey,
|
||||
@NonNull DecryptedGroup decryptedGroup,
|
||||
@Nullable DecryptedGroupChange plainGroupChange,
|
||||
@NonNull GroupMutation groupMutation,
|
||||
@Nullable GroupChange signedServerChange)
|
||||
{
|
||||
int revision = plainGroupChange != null ? plainGroupChange.getRevision() : decryptedGroup.getRevision();
|
||||
SignalServiceProtos.GroupContextV2.Builder contextBuilder = SignalServiceProtos.GroupContextV2.newBuilder()
|
||||
DecryptedGroupChange plainGroupChange = groupMutation.getGroupChange();
|
||||
DecryptedGroup decryptedGroup = groupMutation.getNewGroupState();
|
||||
int revision = plainGroupChange != null ? plainGroupChange.getRevision() : decryptedGroup.getRevision();
|
||||
SignalServiceProtos.GroupContextV2.Builder contextBuilder = SignalServiceProtos.GroupContextV2.newBuilder()
|
||||
.setMasterKey(ByteString.copyFrom(masterKey.serialize()))
|
||||
.setRevision(revision);
|
||||
|
||||
|
@ -66,6 +67,10 @@ public final class GroupProtoUtil {
|
|||
.setContext(contextBuilder.build())
|
||||
.setGroupState(decryptedGroup);
|
||||
|
||||
if (groupMutation.getPreviousGroupState() != null) {
|
||||
builder.setPreviousGroupState(groupMutation.getPreviousGroupState());
|
||||
}
|
||||
|
||||
if (plainGroupChange != null) {
|
||||
builder.setChange(plainGroupChange);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|||
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupMutation;
|
||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||
import org.thoughtcrime.securesms.groups.GroupProtoUtil;
|
||||
import org.thoughtcrime.securesms.groups.GroupsV2Authorization;
|
||||
|
@ -226,9 +227,9 @@ public final class GroupsV2StateProcessor {
|
|||
determineProfileSharing(inputGroupState, newLocalState);
|
||||
if (localState != null && localState.getRevision() == GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION) {
|
||||
Log.i(TAG, "Inserting single update message for restore placeholder");
|
||||
insertUpdateMessages(timestamp, Collections.singleton(new LocalGroupLogEntry(newLocalState, null)));
|
||||
insertUpdateMessages(timestamp, null, Collections.singleton(new LocalGroupLogEntry(newLocalState, null)));
|
||||
} else {
|
||||
insertUpdateMessages(timestamp, advanceGroupStateResult.getProcessedLogEntries());
|
||||
insertUpdateMessages(timestamp, localState, advanceGroupStateResult.getProcessedLogEntries());
|
||||
}
|
||||
persistLearnedProfileKeys(inputGroupState);
|
||||
|
||||
|
@ -260,7 +261,7 @@ public final class GroupsV2StateProcessor {
|
|||
.addDeleteMembers(UuidUtil.toByteString(selfUuid))
|
||||
.build();
|
||||
|
||||
DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, simulatedGroupState, simulatedGroupChange, null);
|
||||
DecryptedGroupV2Context decryptedGroupV2Context = GroupProtoUtil.createDecryptedGroupV2Context(masterKey, new GroupMutation(decryptedGroup, simulatedGroupChange, simulatedGroupState), null);
|
||||
OutgoingGroupUpdateMessage leaveMessage = new OutgoingGroupUpdateMessage(groupRecipient,
|
||||
decryptedGroupV2Context,
|
||||
null,
|
||||
|
@ -362,13 +363,17 @@ public final class GroupsV2StateProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
private void insertUpdateMessages(long timestamp, Collection<LocalGroupLogEntry> processedLogEntries) {
|
||||
private void insertUpdateMessages(long timestamp,
|
||||
@Nullable DecryptedGroup previousGroupState,
|
||||
Collection<LocalGroupLogEntry> processedLogEntries)
|
||||
{
|
||||
for (LocalGroupLogEntry entry : processedLogEntries) {
|
||||
if (entry.getChange() != null && DecryptedGroupUtil.changeIsEmptyExceptForProfileKeyChanges(entry.getChange()) && !DecryptedGroupUtil.changeIsEmpty(entry.getChange())) {
|
||||
Log.d(TAG, "Skipping profile key changes only update message");
|
||||
} else {
|
||||
storeMessage(GroupProtoUtil.createDecryptedGroupV2Context(masterKey, entry.getGroup(), entry.getChange(), null), timestamp);
|
||||
storeMessage(GroupProtoUtil.createDecryptedGroupV2Context(masterKey, new GroupMutation(previousGroupState, entry.getChange(), entry.getGroup()), null), timestamp);
|
||||
}
|
||||
previousGroupState = entry.getGroup();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,9 +28,10 @@ import "SignalService.proto";
|
|||
import "DecryptedGroups.proto";
|
||||
|
||||
message DecryptedGroupV2Context {
|
||||
signalservice.GroupContextV2 context = 1;
|
||||
DecryptedGroupChange change = 2;
|
||||
DecryptedGroup groupState = 3;
|
||||
signalservice.GroupContextV2 context = 1;
|
||||
DecryptedGroupChange change = 2;
|
||||
DecryptedGroup groupState = 3;
|
||||
DecryptedGroup previousGroupState = 4;
|
||||
}
|
||||
|
||||
message TemporalAuthCredentialResponse {
|
||||
|
|
|
@ -1013,6 +1013,12 @@
|
|||
<string name="MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_off">The group link has been turned on with admin approval off.</string>
|
||||
<string name="MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_on">The group link has been turned on with admin approval on.</string>
|
||||
<string name="MessageRecord_the_group_link_has_been_turned_off">The group link has been turned off.</string>
|
||||
<string name="MessageRecord_you_turned_off_admin_approval_for_the_group_link">You turned off admin approval for the group link.</string>
|
||||
<string name="MessageRecord_s_turned_off_admin_approval_for_the_group_link">%1$s turned off admin approval for the group link.</string>
|
||||
<string name="MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_off">The admin approval for the group link has been turned off.</string>
|
||||
<string name="MessageRecord_you_turned_on_admin_approval_for_the_group_link">You turned on admin approval for the group link.</string>
|
||||
<string name="MessageRecord_s_turned_on_admin_approval_for_the_group_link">%1$s turned on admin approval for the group link.</string>
|
||||
<string name="MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_on">The admin approval for the group link has been turned on.</string>
|
||||
|
||||
<!-- GV2 group link reset -->
|
||||
<string name="MessageRecord_you_reset_the_group_link">You reset the group link.</string>
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.database.model;
|
|||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
@ -35,6 +36,7 @@ import static java.util.Collections.emptyList;
|
|||
import static java.util.Collections.singletonList;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -921,6 +923,56 @@ public final class GroupsV2UpdateMessageProducerTest {
|
|||
assertThat(describeChange(change), is(singletonList("The group link has been turned off.")));
|
||||
}
|
||||
|
||||
// Group link with known previous group state
|
||||
|
||||
@Test
|
||||
public void group_link_access_from_unknown_to_administrator() {
|
||||
assertEquals("You turned on the group link with admin approval on.", describeGroupLinkChange(you, AccessControl.AccessRequired.UNKNOWN, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
assertEquals("Alice turned on the group link with admin approval on.", describeGroupLinkChange(alice, AccessControl.AccessRequired.UNKNOWN, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
assertEquals("The group link has been turned on with admin approval on.", describeGroupLinkChange(null, AccessControl.AccessRequired.UNKNOWN, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void group_link_access_from_administrator_to_unsatisfiable() {
|
||||
assertEquals("You turned off the group link.", describeGroupLinkChange(you, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.UNSATISFIABLE));
|
||||
assertEquals("Bob turned off the group link.", describeGroupLinkChange(bob, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.UNSATISFIABLE));
|
||||
assertEquals("The group link has been turned off.", describeGroupLinkChange(null, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.UNSATISFIABLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void group_link_access_from_unsatisfiable_to_administrator() {
|
||||
assertEquals("You turned on the group link with admin approval on.", describeGroupLinkChange(you, AccessControl.AccessRequired.UNSATISFIABLE, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
assertEquals("Alice turned on the group link with admin approval on.", describeGroupLinkChange(alice, AccessControl.AccessRequired.UNSATISFIABLE, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
assertEquals("The group link has been turned on with admin approval on.", describeGroupLinkChange(null, AccessControl.AccessRequired.UNSATISFIABLE, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void group_link_access_from_administrator_to_any() {
|
||||
assertEquals("You turned off admin approval for the group link.", describeGroupLinkChange(you, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.ANY));
|
||||
assertEquals("Bob turned off admin approval for the group link.", describeGroupLinkChange(bob, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.ANY));
|
||||
assertEquals("The admin approval for the group link has been turned off.", describeGroupLinkChange(null, AccessControl.AccessRequired.ADMINISTRATOR, AccessControl.AccessRequired.ANY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void group_link_access_from_any_to_administrator() {
|
||||
assertEquals("You turned on admin approval for the group link.", describeGroupLinkChange(you, AccessControl.AccessRequired.ANY, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
assertEquals("Bob turned on admin approval for the group link.", describeGroupLinkChange(bob, AccessControl.AccessRequired.ANY, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
assertEquals("The admin approval for the group link has been turned on.", describeGroupLinkChange(null, AccessControl.AccessRequired.ANY, AccessControl.AccessRequired.ADMINISTRATOR));
|
||||
}
|
||||
|
||||
private String describeGroupLinkChange(@Nullable UUID editor, @NonNull AccessControl.AccessRequired fromAccess, AccessControl.AccessRequired toAccess){
|
||||
DecryptedGroup previousGroupState = DecryptedGroup.newBuilder()
|
||||
.setAccessControl(AccessControl.newBuilder()
|
||||
.setAddFromInviteLink(fromAccess))
|
||||
.build();
|
||||
DecryptedGroupChange change = (editor != null ? changeBy(editor) : changeByUnknown()).inviteLinkAccess(toAccess)
|
||||
.build();
|
||||
|
||||
List<String> strings = describeChange(previousGroupState, change);
|
||||
assertEquals(1, strings.size());
|
||||
return strings.get(0);
|
||||
}
|
||||
|
||||
// Group link reset
|
||||
|
||||
@Test
|
||||
|
@ -1271,8 +1323,14 @@ public final class GroupsV2UpdateMessageProducerTest {
|
|||
}
|
||||
|
||||
private @NonNull List<String> describeChange(@NonNull DecryptedGroupChange change) {
|
||||
return describeChange(null, change);
|
||||
}
|
||||
|
||||
private @NonNull List<String> describeChange(@Nullable DecryptedGroup previousGroupState,
|
||||
@NonNull DecryptedGroupChange change)
|
||||
{
|
||||
MainThreadUtil.setMainThread(false);
|
||||
return Stream.of(producer.describeChanges(change))
|
||||
return Stream.of(producer.describeChanges(previousGroupState, change))
|
||||
.map(UpdateDescription::getString)
|
||||
.toList();
|
||||
}
|
||||
|
@ -1291,7 +1349,7 @@ public final class GroupsV2UpdateMessageProducerTest {
|
|||
}
|
||||
|
||||
private void assertSingleChangeMentioning(DecryptedGroupChange change, List<UUID> expectedMentions) {
|
||||
List<UpdateDescription> changes = producer.describeChanges(change);
|
||||
List<UpdateDescription> changes = producer.describeChanges(null, change);
|
||||
|
||||
assertThat(changes.size(), is(1));
|
||||
|
||||
|
|
Loading…
Reference in New Issue