mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-06 22:38:21 +00:00
Migrate GV1 to GV2 on to server. Allow query of group status.
This commit is contained in:
parent
31e137cf6d
commit
985a220fca
@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -80,6 +79,14 @@ public final class GroupManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
public static void migrateGroupToServer(@NonNull Context context,
|
||||||
|
@NonNull GroupId.V1 groupIdV1)
|
||||||
|
throws IOException, GroupChangeFailedException, MembershipNotSuitableForV2Exception, GroupAlreadyExistsException
|
||||||
|
{
|
||||||
|
new GroupManagerV2(context).migrateGroupOnToServer(groupIdV1);
|
||||||
|
}
|
||||||
|
|
||||||
private static Set<RecipientId> getMemberIds(Collection<Recipient> recipients) {
|
private static Set<RecipientId> getMemberIds(Collection<Recipient> recipients) {
|
||||||
Set<RecipientId> results = new HashSet<>(recipients.size());
|
Set<RecipientId> results = new HashSet<>(recipients.size());
|
||||||
|
|
||||||
@ -164,6 +171,21 @@ public final class GroupManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
public static V2GroupServerStatus v2GroupStatus(@NonNull Context context,
|
||||||
|
@NonNull GroupMasterKey groupMasterKey)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
new GroupManagerV2(context).groupServerQuery(groupMasterKey);
|
||||||
|
return V2GroupServerStatus.FULL_OR_PENDING_MEMBER;
|
||||||
|
} catch (GroupNotAMemberException e) {
|
||||||
|
return V2GroupServerStatus.NOT_A_MEMBER;
|
||||||
|
} catch (GroupDoesNotExistException e) {
|
||||||
|
return V2GroupServerStatus.DOES_NOT_EXIST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static void setMemberAdmin(@NonNull Context context,
|
public static void setMemberAdmin(@NonNull Context context,
|
||||||
@NonNull GroupId.V2 groupId,
|
@NonNull GroupId.V2 groupId,
|
||||||
@ -303,7 +325,7 @@ public final class GroupManager {
|
|||||||
} else {
|
} else {
|
||||||
GroupDatabase.GroupRecord groupRecord = DatabaseFactory.getGroupDatabase(context).requireGroup(groupId);
|
GroupDatabase.GroupRecord groupRecord = DatabaseFactory.getGroupDatabase(context).requireGroup(groupId);
|
||||||
List<RecipientId> members = groupRecord.getMembers();
|
List<RecipientId> members = groupRecord.getMembers();
|
||||||
byte[] avatar = groupRecord.hasAvatar() ? Util.readFully(AvatarHelper.getAvatar(context, groupRecord.getRecipientId())) : null;
|
byte[] avatar = groupRecord.hasAvatar() ? AvatarHelper.getAvatarBytes(context, groupRecord.getRecipientId()) : null;
|
||||||
Set<RecipientId> recipientIds = new HashSet<>(members);
|
Set<RecipientId> recipientIds = new HashSet<>(members);
|
||||||
int originalSize = recipientIds.size();
|
int originalSize = recipientIds.size();
|
||||||
|
|
||||||
@ -388,4 +410,13 @@ public final class GroupManager {
|
|||||||
ENABLED,
|
ENABLED,
|
||||||
ENABLED_WITH_APPROVAL
|
ENABLED_WITH_APPROVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum V2GroupServerStatus {
|
||||||
|
/** The group does not exist. The expected pre-migration state for V1 groups. */
|
||||||
|
DOES_NOT_EXIST,
|
||||||
|
/** Group exists but self is not in the group. */
|
||||||
|
NOT_A_MEMBER,
|
||||||
|
/** Self is a full or pending member of the group. */
|
||||||
|
FULL_OR_PENDING_MEMBER
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,31 @@ final class GroupManagerV2 {
|
|||||||
return new GroupUpdater(groupId, GroupsV2ProcessingLock.acquireGroupProcessingLock());
|
return new GroupUpdater(groupId, GroupsV2ProcessingLock.acquireGroupProcessingLock());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
void groupServerQuery(@NonNull GroupMasterKey groupMasterKey)
|
||||||
|
throws GroupNotAMemberException, IOException, GroupDoesNotExistException
|
||||||
|
{
|
||||||
|
new GroupsV2StateProcessor(context).forGroup(groupMasterKey)
|
||||||
|
.getCurrentGroupStateFromServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
void migrateGroupOnToServer(@NonNull GroupId.V1 groupIdV1)
|
||||||
|
throws IOException, MembershipNotSuitableForV2Exception, GroupAlreadyExistsException, GroupChangeFailedException
|
||||||
|
{
|
||||||
|
GroupMasterKey groupMasterKey = groupIdV1.deriveV2MigrationMasterKey();
|
||||||
|
GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
||||||
|
GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupIdV1);
|
||||||
|
String name = groupRecord.getTitle();
|
||||||
|
byte[] avatar = groupRecord.hasAvatar() ? AvatarHelper.getAvatarBytes(context, groupRecord.getRecipientId()) : null;
|
||||||
|
int messageTimer = Recipient.resolved(groupRecord.getRecipientId()).getExpireMessages();
|
||||||
|
Set<RecipientId> memberIds = Stream.of(groupRecord.getMembers())
|
||||||
|
.filterNot(m -> m.equals(Recipient.self().getId()))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
createGroupOnServer(groupSecretParams, name, avatar, memberIds, Member.Role.ADMINISTRATOR, messageTimer);
|
||||||
|
}
|
||||||
|
|
||||||
final class GroupCreator extends LockOwner {
|
final class GroupCreator extends LockOwner {
|
||||||
|
|
||||||
GroupCreator(@NonNull Closeable lock) {
|
GroupCreator(@NonNull Closeable lock) {
|
||||||
@ -150,59 +175,43 @@ final class GroupManagerV2 {
|
|||||||
@Nullable byte[] avatar)
|
@Nullable byte[] avatar)
|
||||||
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception
|
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception
|
||||||
{
|
{
|
||||||
if (!GroupsV2CapabilityChecker.allAndSelfSupportGroupsV2AndUuid(members)) {
|
return createGroup(name, avatar, members);
|
||||||
throw new MembershipNotSuitableForV2Exception("At least one potential new member does not support GV2 or UUID capabilities");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GroupCandidate self = groupCandidateHelper.recipientIdToCandidate(Recipient.self().getId());
|
@WorkerThread
|
||||||
Set<GroupCandidate> candidates = new HashSet<>(groupCandidateHelper.recipientIdsToCandidates(members));
|
private @NonNull GroupManager.GroupActionResult createGroup(@Nullable String name,
|
||||||
|
@Nullable byte[] avatar,
|
||||||
if (SignalStore.internalValues().gv2ForceInvites()) {
|
@NonNull Collection<RecipientId> members)
|
||||||
candidates = GroupCandidate.withoutProfileKeyCredentials(candidates);
|
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception
|
||||||
}
|
{
|
||||||
|
GroupSecretParams groupSecretParams = GroupSecretParams.generate();
|
||||||
if (!self.hasProfileKeyCredential()) {
|
DecryptedGroup decryptedGroup;
|
||||||
Log.w(TAG, "Cannot create a V2 group as self does not have a versioned profile");
|
|
||||||
throw new MembershipNotSuitableForV2Exception("Cannot create a V2 group as self does not have a versioned profile");
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupsV2Operations.NewGroup newGroup = groupsV2Operations.createNewGroup(name,
|
|
||||||
Optional.fromNullable(avatar),
|
|
||||||
self,
|
|
||||||
candidates);
|
|
||||||
|
|
||||||
GroupSecretParams groupSecretParams = newGroup.getGroupSecretParams();
|
|
||||||
GroupMasterKey masterKey = groupSecretParams.getMasterKey();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
groupsV2Api.putNewGroup(newGroup, authorization.getAuthorizationForToday(Recipient.self().requireUuid(), groupSecretParams));
|
decryptedGroup = createGroupOnServer(groupSecretParams, name, avatar, members, Member.Role.DEFAULT, 0);
|
||||||
|
} catch (GroupAlreadyExistsException e) {
|
||||||
DecryptedGroup decryptedGroup = groupsV2Api.getGroup(groupSecretParams, ApplicationDependencies.getGroupsV2Authorization().getAuthorizationForToday(Recipient.self().requireUuid(), groupSecretParams));
|
|
||||||
if (decryptedGroup == null) {
|
|
||||||
throw new GroupChangeFailedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupId.V2 groupId = groupDatabase.create(masterKey, decryptedGroup);
|
|
||||||
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
|
||||||
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
|
||||||
|
|
||||||
AvatarHelper.setAvatar(context, groupRecipientId, avatar != null ? new ByteArrayInputStream(avatar) : null);
|
|
||||||
groupDatabase.onAvatarUpdated(groupId, avatar != null);
|
|
||||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient.getId(), true);
|
|
||||||
|
|
||||||
DecryptedGroupChange groupChange = DecryptedGroupChange.newBuilder(GroupChangeReconstruct.reconstructGroupChange(DecryptedGroup.newBuilder().build(), decryptedGroup))
|
|
||||||
.setEditor(UuidUtil.toByteString(selfUuid))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
RecipientAndThread recipientAndThread = sendGroupUpdate(masterKey, new GroupMutation(null, groupChange, decryptedGroup), null);
|
|
||||||
|
|
||||||
return new GroupManager.GroupActionResult(recipientAndThread.groupRecipient,
|
|
||||||
recipientAndThread.threadId,
|
|
||||||
decryptedGroup.getMembersCount() - 1,
|
|
||||||
getPendingMemberRecipientIds(decryptedGroup.getPendingMembersList()));
|
|
||||||
} catch (VerificationFailedException | InvalidGroupStateException | GroupExistsException e) {
|
|
||||||
throw new GroupChangeFailedException(e);
|
throw new GroupChangeFailedException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GroupMasterKey masterKey = groupSecretParams.getMasterKey();
|
||||||
|
GroupId.V2 groupId = groupDatabase.create(masterKey, decryptedGroup);
|
||||||
|
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
||||||
|
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||||
|
|
||||||
|
AvatarHelper.setAvatar(context, groupRecipientId, avatar != null ? new ByteArrayInputStream(avatar) : null);
|
||||||
|
groupDatabase.onAvatarUpdated(groupId, avatar != null);
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient.getId(), true);
|
||||||
|
|
||||||
|
DecryptedGroupChange groupChange = DecryptedGroupChange.newBuilder(GroupChangeReconstruct.reconstructGroupChange(DecryptedGroup.newBuilder().build(), decryptedGroup))
|
||||||
|
.setEditor(UuidUtil.toByteString(selfUuid))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
RecipientAndThread recipientAndThread = sendGroupUpdate(masterKey, new GroupMutation(null, groupChange, decryptedGroup), null);
|
||||||
|
|
||||||
|
return new GroupManager.GroupActionResult(recipientAndThread.groupRecipient,
|
||||||
|
recipientAndThread.threadId,
|
||||||
|
decryptedGroup.getMembersCount() - 1,
|
||||||
|
getPendingMemberRecipientIds(decryptedGroup.getPendingMembersList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +238,7 @@ final class GroupManagerV2 {
|
|||||||
@NonNull GroupManager.GroupActionResult addMembers(@NonNull Collection<RecipientId> newMembers)
|
@NonNull GroupManager.GroupActionResult addMembers(@NonNull Collection<RecipientId> newMembers)
|
||||||
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException, MembershipNotSuitableForV2Exception
|
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException, MembershipNotSuitableForV2Exception
|
||||||
{
|
{
|
||||||
if (!GroupsV2CapabilityChecker.allSupportGroupsV2AndUuid(newMembers)) {
|
if (!GroupsV2CapabilityChecker.allHaveUuidAndSupportGroupsV2(newMembers)) {
|
||||||
throw new MembershipNotSuitableForV2Exception("At least one potential new member does not support GV2 or UUID capabilities");
|
throw new MembershipNotSuitableForV2Exception("At least one potential new member does not support GV2 or UUID capabilities");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,6 +595,56 @@ final class GroupManagerV2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private @NonNull DecryptedGroup createGroupOnServer(@NonNull GroupSecretParams groupSecretParams,
|
||||||
|
@Nullable String name,
|
||||||
|
@Nullable byte[] avatar,
|
||||||
|
@NonNull Collection<RecipientId> members,
|
||||||
|
@NonNull Member.Role memberRole,
|
||||||
|
int disappearingMessageTimerSeconds)
|
||||||
|
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception, GroupAlreadyExistsException
|
||||||
|
{
|
||||||
|
if (!GroupsV2CapabilityChecker.allAndSelfHaveUuidAndSupportGroupsV2(members)) {
|
||||||
|
throw new MembershipNotSuitableForV2Exception("At least one potential new member does not support GV2 capability or we don't have their UUID");
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupCandidate self = groupCandidateHelper.recipientIdToCandidate(Recipient.self().getId());
|
||||||
|
Set<GroupCandidate> candidates = new HashSet<>(groupCandidateHelper.recipientIdsToCandidates(members));
|
||||||
|
|
||||||
|
if (SignalStore.internalValues().gv2ForceInvites()) {
|
||||||
|
Log.w(TAG, "Forcing GV2 invites due to internal setting");
|
||||||
|
candidates = GroupCandidate.withoutProfileKeyCredentials(candidates);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.hasProfileKeyCredential()) {
|
||||||
|
Log.w(TAG, "Cannot create a V2 group as self does not have a versioned profile");
|
||||||
|
throw new MembershipNotSuitableForV2Exception("Cannot create a V2 group as self does not have a versioned profile");
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupsV2Operations.NewGroup newGroup = groupsV2Operations.createNewGroup(groupSecretParams,
|
||||||
|
name,
|
||||||
|
Optional.fromNullable(avatar),
|
||||||
|
self,
|
||||||
|
candidates,
|
||||||
|
memberRole,
|
||||||
|
disappearingMessageTimerSeconds);
|
||||||
|
|
||||||
|
try {
|
||||||
|
groupsV2Api.putNewGroup(newGroup, authorization.getAuthorizationForToday(Recipient.self().requireUuid(), groupSecretParams));
|
||||||
|
|
||||||
|
DecryptedGroup decryptedGroup = groupsV2Api.getGroup(groupSecretParams, ApplicationDependencies.getGroupsV2Authorization().getAuthorizationForToday(Recipient.self().requireUuid(), groupSecretParams));
|
||||||
|
if (decryptedGroup == null) {
|
||||||
|
throw new GroupChangeFailedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return decryptedGroup;
|
||||||
|
} catch (VerificationFailedException | InvalidGroupStateException e) {
|
||||||
|
throw new GroupChangeFailedException(e);
|
||||||
|
} catch (GroupExistsException e) {
|
||||||
|
throw new GroupAlreadyExistsException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class GroupJoiner extends LockOwner {
|
final class GroupJoiner extends LockOwner {
|
||||||
private final GroupId.V2 groupId;
|
private final GroupId.V2 groupId;
|
||||||
private final GroupLinkPassword password;
|
private final GroupLinkPassword password;
|
||||||
@ -777,7 +836,7 @@ final class GroupManagerV2 {
|
|||||||
private @NonNull GroupChange joinGroupOnServer(boolean requestToJoin, int currentRevision)
|
private @NonNull GroupChange joinGroupOnServer(boolean requestToJoin, int currentRevision)
|
||||||
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception, GroupLinkNotActiveException, GroupJoinAlreadyAMemberException
|
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception, GroupLinkNotActiveException, GroupJoinAlreadyAMemberException
|
||||||
{
|
{
|
||||||
if (!GroupsV2CapabilityChecker.allAndSelfSupportGroupsV2AndUuid(Collections.singleton(Recipient.self().getId()))) {
|
if (!GroupsV2CapabilityChecker.allAndSelfHaveUuidAndSupportGroupsV2(Collections.singleton(Recipient.self().getId()))) {
|
||||||
throw new MembershipNotSuitableForV2Exception("Self does not support GV2 or UUID capabilities");
|
throw new MembershipNotSuitableForV2Exception("Self does not support GV2 or UUID capabilities");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,18 +52,18 @@ public final class GroupsV2CapabilityChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
static boolean allAndSelfSupportGroupsV2AndUuid(@NonNull Collection<RecipientId> recipientIds)
|
static boolean allAndSelfHaveUuidAndSupportGroupsV2(@NonNull Collection<RecipientId> recipientIds)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
HashSet<RecipientId> recipientIdsSet = new HashSet<>(recipientIds);
|
HashSet<RecipientId> recipientIdsSet = new HashSet<>(recipientIds);
|
||||||
|
|
||||||
recipientIdsSet.add(Recipient.self().getId());
|
recipientIdsSet.add(Recipient.self().getId());
|
||||||
|
|
||||||
return allSupportGroupsV2AndUuid(recipientIdsSet);
|
return allHaveUuidAndSupportGroupsV2(recipientIdsSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
static boolean allSupportGroupsV2AndUuid(@NonNull Collection<RecipientId> recipientIds)
|
static boolean allHaveUuidAndSupportGroupsV2(@NonNull Collection<RecipientId> recipientIds)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Set<RecipientId> recipientIdsSet = new HashSet<>(recipientIds);
|
Set<RecipientId> recipientIdsSet = new HashSet<>(recipientIds);
|
||||||
|
@ -96,6 +96,11 @@ public class AvatarHelper {
|
|||||||
return ModernDecryptingPartInputStream.createFor(attachmentSecret, avatarFile, 0);
|
return ModernDecryptingPartInputStream.createFor(attachmentSecret, avatarFile, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] getAvatarBytes(@NonNull Context context, @NonNull RecipientId recipientId) throws IOException {
|
||||||
|
return hasAvatar(context, recipientId) ? Util.readFully(getAvatar(context, recipientId))
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of the avatar on disk.
|
* Returns the size of the avatar on disk.
|
||||||
*/
|
*/
|
||||||
|
@ -79,23 +79,26 @@ public final class GroupsV2Operations {
|
|||||||
* @param self You will be member 0 and the only admin.
|
* @param self You will be member 0 and the only admin.
|
||||||
* @param members Members must not contain self. Members will be non-admin members of the group.
|
* @param members Members must not contain self. Members will be non-admin members of the group.
|
||||||
*/
|
*/
|
||||||
public NewGroup createNewGroup(final String title,
|
public NewGroup createNewGroup(final GroupSecretParams groupSecretParams,
|
||||||
|
final String title,
|
||||||
final Optional<byte[]> avatar,
|
final Optional<byte[]> avatar,
|
||||||
final GroupCandidate self,
|
final GroupCandidate self,
|
||||||
final Set<GroupCandidate> members) {
|
final Set<GroupCandidate> members,
|
||||||
|
final Member.Role memberRole,
|
||||||
|
final int disappearingMessageTimerSeconds)
|
||||||
|
{
|
||||||
|
|
||||||
if (members.contains(self)) {
|
if (members.contains(self)) {
|
||||||
throw new IllegalArgumentException("Members must not contain self");
|
throw new IllegalArgumentException("Members must not contain self");
|
||||||
}
|
}
|
||||||
|
|
||||||
final GroupSecretParams groupSecretParams = GroupSecretParams.generate(random);
|
final GroupOperations groupOperations = forGroup(groupSecretParams);
|
||||||
final GroupOperations groupOperations = forGroup(groupSecretParams);
|
|
||||||
|
|
||||||
Group.Builder group = Group.newBuilder()
|
Group.Builder group = Group.newBuilder()
|
||||||
.setRevision(0)
|
.setRevision(0)
|
||||||
.setPublicKey(ByteString.copyFrom(groupSecretParams.getPublicParams().serialize()))
|
.setPublicKey(ByteString.copyFrom(groupSecretParams.getPublicParams().serialize()))
|
||||||
.setTitle(groupOperations.encryptTitle(title))
|
.setTitle(groupOperations.encryptTitle(title))
|
||||||
.setDisappearingMessagesTimer(groupOperations.encryptTimer(0))
|
.setDisappearingMessagesTimer(groupOperations.encryptTimer(disappearingMessageTimerSeconds))
|
||||||
.setAccessControl(AccessControl.newBuilder()
|
.setAccessControl(AccessControl.newBuilder()
|
||||||
.setAttributes(AccessControl.AccessRequired.MEMBER)
|
.setAttributes(AccessControl.AccessRequired.MEMBER)
|
||||||
.setMembers(AccessControl.AccessRequired.MEMBER));
|
.setMembers(AccessControl.AccessRequired.MEMBER));
|
||||||
@ -103,13 +106,12 @@ public final class GroupsV2Operations {
|
|||||||
group.addMembers(groupOperations.member(self.getProfileKeyCredential().get(), Member.Role.ADMINISTRATOR));
|
group.addMembers(groupOperations.member(self.getProfileKeyCredential().get(), Member.Role.ADMINISTRATOR));
|
||||||
|
|
||||||
for (GroupCandidate credential : members) {
|
for (GroupCandidate credential : members) {
|
||||||
Member.Role newMemberRole = Member.Role.DEFAULT;
|
|
||||||
ProfileKeyCredential profileKeyCredential = credential.getProfileKeyCredential().orNull();
|
ProfileKeyCredential profileKeyCredential = credential.getProfileKeyCredential().orNull();
|
||||||
|
|
||||||
if (profileKeyCredential != null) {
|
if (profileKeyCredential != null) {
|
||||||
group.addMembers(groupOperations.member(profileKeyCredential, newMemberRole));
|
group.addMembers(groupOperations.member(profileKeyCredential, memberRole));
|
||||||
} else {
|
} else {
|
||||||
group.addPendingMembers(groupOperations.invitee(credential.getUuid(), newMemberRole));
|
group.addPendingMembers(groupOperations.invitee(credential.getUuid(), memberRole));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user