+ public Pair<DecryptedGroup, GroupChange> updateGroupV2(
+ GroupInfoV2 groupInfoV2, String name, String avatarFile
+ ) throws IOException {
+ final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
+ GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
+
+ GroupChange.Actions.Builder change = name != null
+ ? groupOperations.createModifyGroupTitle(name)
+ : GroupChange.Actions.newBuilder();
+
+ if (avatarFile != null) {
+ final byte[] avatarBytes = readAvatarBytes(avatarFile);
+ String avatarCdnKey = groupsV2Api.uploadAvatar(avatarBytes,
+ groupSecretParams,
+ groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams));
+ change.setModifyAvatar(GroupChange.Actions.ModifyAvatarAction.newBuilder().setAvatar(avatarCdnKey));
+ }
+
+ final Optional<UUID> uuid = this.selfAddressProvider.getSelfAddress().getUuid();
+ if (uuid.isPresent()) {
+ change.setSourceUuid(UuidUtil.toByteString(uuid.get()));
+ }
+
+ return commitChange(groupInfoV2, change);
+ }
+
+ public Pair<DecryptedGroup, GroupChange> updateGroupV2(
+ GroupInfoV2 groupInfoV2, Set<SignalServiceAddress> newMembers
+ ) throws IOException {
+ final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
+ GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
+
+ Set<GroupCandidate> candidates = newMembers.stream()
+ .map(member -> new GroupCandidate(member.getUuid().get(),
+ Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member))))
+ .collect(Collectors.toSet());
+
+ final GroupChange.Actions.Builder change = groupOperations.createModifyGroupMembershipChange(candidates,
+ selfAddressProvider.getSelfAddress().getUuid().get());
+
+ final Optional<UUID> uuid = this.selfAddressProvider.getSelfAddress().getUuid();
+ if (uuid.isPresent()) {
+ change.setSourceUuid(UuidUtil.toByteString(uuid.get()));
+ }
+
+ return commitChange(groupInfoV2, change);
+ }
+
+ private Pair<DecryptedGroup, GroupChange> commitChange(
+ GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change
+ ) throws IOException {
+ final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
+ final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
+ final DecryptedGroup previousGroupState = groupInfoV2.getGroup();
+ final int nextRevision = previousGroupState.getRevision() + 1;
+ final GroupChange.Actions changeActions = change.setRevision(nextRevision).build();
+ final DecryptedGroupChange decryptedChange;
+ final DecryptedGroup decryptedGroupState;
+
+ try {
+ decryptedChange = groupOperations.decryptChange(changeActions,
+ selfAddressProvider.getSelfAddress().getUuid().get());
+ decryptedGroupState = DecryptedGroupUtil.apply(previousGroupState, decryptedChange);
+ } catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) {
+ throw new IOException(e);
+ }
+
+ GroupChange signedGroupChange = groupsV2Api.patchGroup(change.build(),
+ groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams),
+ Optional.absent());
+
+ return new Pair<>(decryptedGroupState, signedGroupChange);
+ }
+