All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil Maven / Gradle / Ivy

There is a newer version: 2.15.3_unofficial_107
Show newest version
package org.whispersystems.signalservice.api.groupsv2;

import org.signal.libsignal.protocol.logging.Log;
import org.signal.storageservice.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.storageservice.protos.groups.local.DecryptedModifyMemberRole;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMemberRemoval;
import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember;
import org.signal.storageservice.protos.groups.local.EnabledState;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceIds;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import okio.ByteString;

public final class DecryptedGroupUtil {

  private static final String TAG = DecryptedGroupUtil.class.getSimpleName();

  public static ArrayList toAciListWithUnknowns(Collection membersList) {
    ArrayList serviceIdList = new ArrayList<>(membersList.size());

    for (DecryptedMember member : membersList) {
      serviceIdList.add(ACI.parseOrUnknown(member.aciBytes));
    }

    return serviceIdList;
  }

  /** Converts the list of members to ACI's, filtering out unknown ACI's. */
  public static ArrayList toAciList(Collection membersList) {
    ArrayList serviceIdList = new ArrayList<>(membersList.size());

    for (DecryptedMember member : membersList) {
      ACI aci = ACI.parseOrNull(member.aciBytes);

      if (aci != null) {
        serviceIdList.add(aci);
      }
    }

    return serviceIdList;
  }

  public static Set membersToAciByteStringSet(Collection membersList) {
    Set aciList = new HashSet<>(membersList.size());

    for (DecryptedMember member : membersList) {
      aciList.add(member.aciBytes);
    }

    return aciList;
  }

  /**
   * Can return non-decryptable member ACIs as unknown ACIs.
   */
  public static ArrayList pendingToServiceIdList(Collection membersList) {
    ArrayList serviceIdList = new ArrayList<>(membersList.size());

    for (DecryptedPendingMember member : membersList) {
      ServiceId serviceId = ServiceId.parseOrNull(member.serviceIdBytes);
      if (serviceId != null) {
        serviceIdList.add(serviceId);
      } else {
        serviceIdList.add(ACI.UNKNOWN);
      }
    }

    return serviceIdList;
  }

  /**
   * Will not return any non-decryptable member ACIs.
   */
  public static ArrayList removedMembersServiceIdList(DecryptedGroupChange groupChange) {
    List     deletedMembers = groupChange.deleteMembers;
    ArrayList serviceIdList  = new ArrayList<>(deletedMembers.size());

    for (ByteString member : deletedMembers) {
      ServiceId serviceId = ServiceId.parseOrNull(member);

      if (serviceId != null) {
        serviceIdList.add(serviceId);
      }
    }

    return serviceIdList;
  }

  /**
   * Will not return any non-decryptable member ACIs.
   */
  public static ArrayList removedPendingMembersServiceIdList(DecryptedGroupChange groupChange) {
    List deletedPendingMembers = groupChange.deletePendingMembers;
    ArrayList                serviceIdList         = new ArrayList<>(deletedPendingMembers.size());

    for (DecryptedPendingMemberRemoval member : deletedPendingMembers) {
      ServiceId serviceId = ServiceId.parseOrNull(member.serviceIdBytes);

      if(serviceId != null) {
        serviceIdList.add(serviceId);
      }
    }

    return serviceIdList;
  }

  /**
   * Will not return any non-decryptable member ACIs.
   */
  public static ArrayList removedRequestingMembersServiceIdList(DecryptedGroupChange groupChange) {
    List     deleteRequestingMembers = groupChange.deleteRequestingMembers;
    ArrayList serviceIdList           = new ArrayList<>(deleteRequestingMembers.size());

    for (ByteString member : deleteRequestingMembers) {
      ServiceId serviceId = ServiceId.parseOrNull(member);

      if(serviceId != null) {
        serviceIdList.add(serviceId);
      }
    }

    return serviceIdList;
  }

  public static Set bannedMembersToServiceIdSet(Collection membersList) {
    Set serviceIdSet = new HashSet<>(membersList.size());

    for (DecryptedBannedMember member : membersList) {
      ServiceId serviceId = ServiceId.parseOrNull(member.serviceIdBytes);
      if (serviceId != null) {
        serviceIdSet.add(serviceId);
      }
    }

    return serviceIdSet;
  }

  /**
   * The ACI of the member that made the change.
   */
  public static Optional editorServiceId(DecryptedGroupChange change) {
    return Optional.ofNullable(change != null ? ServiceId.parseOrNull(change.editorServiceIdBytes) : null);
  }

  public static Optional findMemberByAci(Collection members, ACI aci) {
    ByteString aciBytes = aci.toByteString();

    for (DecryptedMember member : members) {
      if (aciBytes.equals(member.aciBytes)) {
        return Optional.of(member);
      }
    }

    return Optional.empty();
  }

  public static Optional findPendingByServiceId(Collection members, ServiceId serviceId) {
    ByteString serviceIdBinary = serviceId.toByteString();

    for (DecryptedPendingMember member : members) {
      if (serviceIdBinary.equals(member.serviceIdBytes)) {
        return Optional.of(member);
      }
    }

    return Optional.empty();
  }

  public static Optional findPendingByServiceIds(Collection members, ServiceIds serviceIds) {
    for (DecryptedPendingMember member : members) {
      if (serviceIds.matches(member.serviceIdBytes)) {
        return Optional.of(member);
      }
    }

    return Optional.empty();
  }

  private static int findPendingIndexByServiceIdCipherText(List members, ByteString cipherText) {
    for (int i = 0; i < members.size(); i++) {
      DecryptedPendingMember member = members.get(i);
      if (cipherText.equals(member.serviceIdCipherText)) {
        return i;
      }
    }

    return -1;
  }

  private static int findPendingIndexByServiceId(List members, ByteString serviceIdBinary) {
    for (int i = 0; i < members.size(); i++) {
      DecryptedPendingMember member = members.get(i);
      if (serviceIdBinary.equals(member.serviceIdBytes)) {
        return i;
      }
    }

    return -1;
  }

  public static Optional findRequestingByAci(Collection members, ACI aci) {
    ByteString aciBytes = aci.toByteString();

    for (DecryptedRequestingMember member : members) {
      if (aciBytes.equals(member.aciBytes)) {
        return Optional.of(member);
      }
    }

    return Optional.empty();
  }

  public static Optional findRequestingByServiceIds(Collection members, ServiceIds serviceIds) {
    for (DecryptedRequestingMember member : members) {
      if (serviceIds.matches(member.aciBytes)) {
        return Optional.of(member);
      }
    }

    return Optional.empty();
  }

  public static boolean isPendingOrRequesting(DecryptedGroup group, ServiceIds serviceIds) {
    return findPendingByServiceIds(group.pendingMembers, serviceIds).isPresent() ||
           findRequestingByServiceIds(group.requestingMembers, serviceIds).isPresent();
  }

  public static boolean isRequesting(DecryptedGroup group, ACI aci) {
    return findRequestingByAci(group.requestingMembers, aci).isPresent();
  }

  /**
   * Removes the aci from the full members of a group.
   * 

* Generally not expected to have to do this, just in the case of leaving a group where you cannot * get the new group state as you are not in the group any longer. */ public static DecryptedGroup removeMember(DecryptedGroup group, ACI aci, int revision) { DecryptedGroup.Builder builder = group.newBuilder(); ByteString aciByteString = aci.toByteString(); boolean removed = false; ArrayList decryptedMembers = new ArrayList<>(builder.members); Iterator membersList = decryptedMembers.iterator(); while (membersList.hasNext()) { if (aciByteString.equals(membersList.next().aciBytes)) { membersList.remove(); removed = true; } } if (removed) { return builder.members(decryptedMembers) .revision(revision) .build(); } else { return group; } } public static DecryptedGroup apply(DecryptedGroup group, DecryptedGroupChange change) throws NotAbleToApplyGroupV2ChangeException { if (change.revision != group.revision + 1) { throw new NotAbleToApplyGroupV2ChangeException(); } return applyWithoutRevisionCheck(group, change); } public static DecryptedGroup applyWithoutRevisionCheck(DecryptedGroup group, DecryptedGroupChange change) throws NotAbleToApplyGroupV2ChangeException { DecryptedGroup.Builder builder = group.newBuilder() .revision(change.revision); applyAddMemberAction(builder, change.newMembers); applyDeleteMemberActions(builder, change.deleteMembers); applyModifyMemberRoleActions(builder, change.modifyMemberRoles); applyModifyMemberProfileKeyActions(builder, change.modifiedProfileKeys); applyAddPendingMemberActions(builder, change.newPendingMembers); applyDeletePendingMemberActions(builder, change.deletePendingMembers); applyPromotePendingMemberActions(builder, change.promotePendingMembers); applyModifyTitleAction(builder, change); applyModifyDescriptionAction(builder, change); applyModifyIsAnnouncementGroupAction(builder, change); applyModifyAvatarAction(builder, change); applyModifyDisappearingMessagesTimerAction(builder, change); applyModifyAttributesAccessControlAction(builder, change); applyModifyMembersAccessControlAction(builder, change); applyModifyAddFromInviteLinkAccessControlAction(builder, change); applyAddRequestingMembers(builder, change.newRequestingMembers); applyDeleteRequestingMembers(builder, change.deleteRequestingMembers); applyPromoteRequestingMemberActions(builder, change.promoteRequestingMembers); applyInviteLinkPassword(builder, change); applyAddBannedMembersActions(builder, change.newBannedMembers); applyDeleteBannedMembersActions(builder, change.deleteBannedMembers); applyPromotePendingPniAciMemberActions(builder, change.promotePendingPniAciMembers); return builder.build(); } private static void applyAddMemberAction(DecryptedGroup.Builder builder, List newMembersList) { if (newMembersList.isEmpty()) return; LinkedHashMap members = new LinkedHashMap<>(); for (DecryptedMember member : builder.members) { members.put(member.aciBytes, member); } for (DecryptedMember member : newMembersList) { members.put(member.aciBytes, member); } builder.members(new ArrayList<>(members.values())); removePendingAndRequestingMembersNowInGroup(builder); } private static void applyDeleteMemberActions(DecryptedGroup.Builder builder, List deleteMembersList) { List members = new ArrayList<>(builder.members); for (ByteString removedMember : deleteMembersList) { int index = indexOfAci(members, removedMember); if (index == -1) { Log.w(TAG, "Deleted member on change not found in group"); continue; } members.remove(index); } builder.members(members); } private static void applyModifyMemberRoleActions(DecryptedGroup.Builder builder, List modifyMemberRolesList) throws NotAbleToApplyGroupV2ChangeException { List members = new ArrayList<>(builder.members); for (DecryptedModifyMemberRole modifyMemberRole : modifyMemberRolesList) { int index = indexOfAci(members, modifyMemberRole.aciBytes); if (index == -1) { throw new NotAbleToApplyGroupV2ChangeException(); } Member.Role role = modifyMemberRole.role; ensureKnownRole(role); members.set(index, members.get(index).newBuilder().role(role).build()); } builder.members(members); } private static void applyModifyMemberProfileKeyActions(DecryptedGroup.Builder builder, List modifiedProfileKeysList) throws NotAbleToApplyGroupV2ChangeException { List members = new ArrayList<>(builder.members); for (DecryptedMember modifyProfileKey : modifiedProfileKeysList) { int index = indexOfAci(members, modifyProfileKey.aciBytes); if (index == -1) { throw new NotAbleToApplyGroupV2ChangeException(); } members.set(index, withNewProfileKey(members.get(index), modifyProfileKey.profileKey)); } builder.members(members); } private static void applyAddPendingMemberActions(DecryptedGroup.Builder builder, List newPendingMembersList) throws NotAbleToApplyGroupV2ChangeException { Set fullMemberSet = getMemberAciSet(builder.members); Set pendingMemberCipherTexts = getPendingMemberCipherTextSet(builder.pendingMembers); List pendingMembers = new ArrayList<>(builder.pendingMembers); for (DecryptedPendingMember pendingMember : newPendingMembersList) { if (fullMemberSet.contains(pendingMember.serviceIdBytes)) { throw new NotAbleToApplyGroupV2ChangeException(); } if (!pendingMemberCipherTexts.contains(pendingMember.serviceIdCipherText)) { pendingMembers.add(pendingMember); } } builder.pendingMembers(pendingMembers); } private static void applyDeletePendingMemberActions(DecryptedGroup.Builder builder, List deletePendingMembersList) { List pendingMembers = new ArrayList<>(builder.pendingMembers); for (DecryptedPendingMemberRemoval removedMember : deletePendingMembersList) { int index = findPendingIndexByServiceIdCipherText(pendingMembers, removedMember.serviceIdCipherText); if (index == -1) { Log.w(TAG, "Deleted pending member on change not found in group"); continue; } pendingMembers.remove(index); } builder.pendingMembers(pendingMembers); } private static void applyPromotePendingMemberActions(DecryptedGroup.Builder builder, List promotePendingMembersList) throws NotAbleToApplyGroupV2ChangeException { List members = new ArrayList<>(builder.members); List pendingMembers = new ArrayList<>(builder.pendingMembers); for (DecryptedMember newMember : promotePendingMembersList) { int index = findPendingIndexByServiceId(pendingMembers, newMember.aciBytes); if (index == -1) { throw new NotAbleToApplyGroupV2ChangeException(); } pendingMembers.remove(index); members.add(newMember); } builder.pendingMembers(pendingMembers); builder.members(members); } private static void applyModifyTitleAction(DecryptedGroup.Builder builder, DecryptedGroupChange change) { if (change.newTitle != null) { builder.title(change.newTitle.value_); } } private static void applyModifyDescriptionAction(DecryptedGroup.Builder builder, DecryptedGroupChange change) { if (change.newDescription != null) { builder.description(change.newDescription.value_); } } private static void applyModifyIsAnnouncementGroupAction(DecryptedGroup.Builder builder, DecryptedGroupChange change) { if (change.newIsAnnouncementGroup != EnabledState.UNKNOWN) { builder.isAnnouncementGroup(change.newIsAnnouncementGroup); } } private static void applyModifyAvatarAction(DecryptedGroup.Builder builder, DecryptedGroupChange change) { if (change.newAvatar != null) { builder.avatar(change.newAvatar.value_); } } private static void applyModifyDisappearingMessagesTimerAction(DecryptedGroup.Builder builder, DecryptedGroupChange change) { if (change.newTimer != null) { builder.disappearingMessagesTimer(change.newTimer); } } private static void applyModifyAttributesAccessControlAction(DecryptedGroup.Builder builder, DecryptedGroupChange change) { AccessControl.AccessRequired newAccessLevel = change.newAttributeAccess; if (newAccessLevel != AccessControl.AccessRequired.UNKNOWN) { AccessControl.Builder accessControlBuilder = builder.accessControl != null ? builder.accessControl.newBuilder() : new AccessControl.Builder(); builder.accessControl(accessControlBuilder.attributes(change.newAttributeAccess).build()); } } private static void applyModifyMembersAccessControlAction(DecryptedGroup.Builder builder, DecryptedGroupChange change) { AccessControl.AccessRequired newAccessLevel = change.newMemberAccess; if (newAccessLevel != AccessControl.AccessRequired.UNKNOWN) { AccessControl.Builder accessControlBuilder = builder.accessControl != null ? builder.accessControl.newBuilder() : new AccessControl.Builder(); builder.accessControl(accessControlBuilder.members(change.newMemberAccess).build()); } } private static void applyModifyAddFromInviteLinkAccessControlAction(DecryptedGroup.Builder builder, DecryptedGroupChange change) { AccessControl.AccessRequired newAccessLevel = change.newInviteLinkAccess; if (newAccessLevel != AccessControl.AccessRequired.UNKNOWN) { AccessControl.Builder accessControlBuilder = builder.accessControl != null ? builder.accessControl.newBuilder() : new AccessControl.Builder(); builder.accessControl(accessControlBuilder.addFromInviteLink(newAccessLevel).build()); } } private static void applyAddRequestingMembers(DecryptedGroup.Builder builder, List newRequestingMembers) { List requestingMembers = new ArrayList<>(builder.requestingMembers); requestingMembers.addAll(newRequestingMembers); builder.requestingMembers(requestingMembers); } private static void applyDeleteRequestingMembers(DecryptedGroup.Builder builder, List deleteRequestingMembersList) { List requestingMembers = new ArrayList<>(builder.requestingMembers); for (ByteString removedMember : deleteRequestingMembersList) { int index = indexOfAciInRequestingList(requestingMembers, removedMember); if (index == -1) { Log.w(TAG, "Deleted member on change not found in group"); continue; } requestingMembers.remove(index); } builder.requestingMembers(requestingMembers); } private static void applyPromoteRequestingMemberActions(DecryptedGroup.Builder builder, List promoteRequestingMembers) throws NotAbleToApplyGroupV2ChangeException { List members = new ArrayList<>(builder.members); List requestingMembers = new ArrayList<>(builder.requestingMembers); for (DecryptedApproveMember approvedMember : promoteRequestingMembers) { int index = indexOfAciInRequestingList(requestingMembers, approvedMember.aciBytes); if (index == -1) { Log.w(TAG, "Deleted member on change not found in group"); continue; } DecryptedRequestingMember requestingMember = requestingMembers.get(index); Member.Role role = approvedMember.role; ensureKnownRole(role); requestingMembers.remove(index); members.add(new DecryptedMember.Builder() .aciBytes(approvedMember.aciBytes) .profileKey(requestingMember.profileKey) .role(role) .build()); } builder.members(members); builder.requestingMembers(requestingMembers); } private static void applyInviteLinkPassword(DecryptedGroup.Builder builder, DecryptedGroupChange change) { if (change.newInviteLinkPassword.size() > 0) { builder.inviteLinkPassword(change.newInviteLinkPassword); } } private static void applyAddBannedMembersActions(DecryptedGroup.Builder builder, List newBannedMembersList) { Set bannedMemberServiceIdSet = getBannedMemberServiceIdSet(builder.bannedMembers); List bannedMembers = new ArrayList<>(builder.bannedMembers); for (DecryptedBannedMember member : newBannedMembersList) { if (bannedMemberServiceIdSet.contains(member.serviceIdBytes)) { Log.w(TAG, "Banned member already in banned list"); } else { bannedMembers.add(member); } } builder.bannedMembers(bannedMembers); } private static void applyDeleteBannedMembersActions(DecryptedGroup.Builder builder, List deleteMembersList) { List bannedMembers = new ArrayList<>(builder.bannedMembers); for (DecryptedBannedMember removedMember : deleteMembersList) { int index = indexOfServiceIdInBannedMemberList(bannedMembers, removedMember.serviceIdBytes); if (index == -1) { Log.w(TAG, "Deleted banned member on change not found in banned list"); continue; } bannedMembers.remove(index); } builder.bannedMembers(bannedMembers); } private static void applyPromotePendingPniAciMemberActions(DecryptedGroup.Builder builder, List promotePendingPniAciMembersList) throws NotAbleToApplyGroupV2ChangeException { List members = new ArrayList<>(builder.members); List pendingMembers = new ArrayList<>(builder.pendingMembers); for (DecryptedMember newMember : promotePendingPniAciMembersList) { int index = findPendingIndexByServiceId(pendingMembers, newMember.pniBytes); if (index == -1) { throw new NotAbleToApplyGroupV2ChangeException(); } pendingMembers.remove(index); members.add(newMember); } builder.members(members); builder.pendingMembers(pendingMembers); } private static DecryptedMember withNewProfileKey(DecryptedMember member, ByteString profileKey) { return member.newBuilder() .profileKey(profileKey) .build(); } private static Set getMemberAciSet(List membersList) { Set memberAcis = new HashSet<>(membersList.size()); for (DecryptedMember members : membersList) { memberAcis.add(members.aciBytes); } return memberAcis; } private static Set getPendingMemberCipherTextSet(List pendingMemberList) { Set pendingMemberCipherTexts = new HashSet<>(pendingMemberList.size()); for (DecryptedPendingMember pendingMember : pendingMemberList) { pendingMemberCipherTexts.add(pendingMember.serviceIdCipherText); } return pendingMemberCipherTexts; } private static Set getBannedMemberServiceIdSet(List bannedMemberList) { Set memberServiceIds = new HashSet<>(bannedMemberList.size()); for (DecryptedBannedMember member : bannedMemberList) { memberServiceIds.add(member.serviceIdBytes); } return memberServiceIds; } private static void removePendingAndRequestingMembersNowInGroup(DecryptedGroup.Builder builder) { Set allMembers = membersToAciByteStringSet(builder.members); List pendingMembers = new ArrayList<>(builder.pendingMembers); for (int i = pendingMembers.size() - 1; i >= 0; i--) { DecryptedPendingMember pendingMember = pendingMembers.get(i); if (allMembers.contains(pendingMember.serviceIdBytes)) { pendingMembers.remove(i); } } builder.pendingMembers(pendingMembers); List requestingMembers = new ArrayList<>(builder.requestingMembers); for (int i = requestingMembers.size() - 1; i >= 0; i--) { DecryptedRequestingMember requestingMember = requestingMembers.get(i); if (allMembers.contains(requestingMember.aciBytes)) { requestingMembers.remove(i); } } builder.requestingMembers(requestingMembers); } private static void ensureKnownRole(Member.Role role) throws NotAbleToApplyGroupV2ChangeException { if (role != Member.Role.ADMINISTRATOR && role != Member.Role.DEFAULT) { throw new NotAbleToApplyGroupV2ChangeException(); } } private static int indexOfAci(List memberList, ByteString aci) { for (int i = 0; i < memberList.size(); i++) { if (aci.equals(memberList.get(i).aciBytes)) { return i; } } return -1; } private static int indexOfAciInRequestingList(List memberList, ByteString aci) { for (int i = 0; i < memberList.size(); i++) { if (aci.equals(memberList.get(i).aciBytes)) { return i; } } return -1; } private static int indexOfServiceIdInBannedMemberList(List memberList, ByteString serviceIdBinary) { for (int i = 0; i < memberList.size(); i++) { if (serviceIdBinary.equals(memberList.get(i).serviceIdBytes)) { return i; } } return -1; } public static boolean changeIsEmpty(DecryptedGroupChange change) { return change.modifiedProfileKeys.size() == 0 && // field 6 changeIsEmptyExceptForProfileKeyChanges(change); } /* * When updating this, update {@link #changeIsEmptyExceptForBanChangesAndOptionalProfileKeyChanges(DecryptedGroupChange)} */ public static boolean changeIsEmptyExceptForProfileKeyChanges(DecryptedGroupChange change) { return change.newMembers.size() == 0 && // field 3 change.deleteMembers.size() == 0 && // field 4 change.modifyMemberRoles.size() == 0 && // field 5 change.newPendingMembers.size() == 0 && // field 7 change.deletePendingMembers.size() == 0 && // field 8 change.promotePendingMembers.size() == 0 && // field 9 change.newTitle == null && // field 10 change.newAvatar == null && // field 11 change.newTimer == null && // field 12 isEmpty(change.newAttributeAccess) && // field 13 isEmpty(change.newMemberAccess) && // field 14 isEmpty(change.newInviteLinkAccess) && // field 15 change.newRequestingMembers.size() == 0 && // field 16 change.deleteRequestingMembers.size() == 0 && // field 17 change.promoteRequestingMembers.size() == 0 && // field 18 change.newInviteLinkPassword.size() == 0 && // field 19 change.newDescription == null && // field 20 isEmpty(change.newIsAnnouncementGroup) && // field 21 change.newBannedMembers.size() == 0 && // field 22 change.deleteBannedMembers.size() == 0 && // field 23 change.promotePendingPniAciMembers.size() == 0; // field 24 } public static boolean changeIsEmptyExceptForBanChangesAndOptionalProfileKeyChanges(DecryptedGroupChange change) { return (change.newBannedMembers.size() != 0 || change.deleteBannedMembers.size() != 0) && change.newMembers.size() == 0 && // field 3 change.deleteMembers.size() == 0 && // field 4 change.modifyMemberRoles.size() == 0 && // field 5 change.newPendingMembers.size() == 0 && // field 7 change.deletePendingMembers.size() == 0 && // field 8 change.promotePendingMembers.size() == 0 && // field 9 change.newTitle == null && // field 10 change.newAvatar == null && // field 11 change.newTimer == null && // field 12 isEmpty(change.newAttributeAccess) && // field 13 isEmpty(change.newMemberAccess) && // field 14 isEmpty(change.newInviteLinkAccess) && // field 15 change.newRequestingMembers.size() == 0 && // field 16 change.deleteRequestingMembers.size() == 0 && // field 17 change.promoteRequestingMembers.size() == 0 && // field 18 change.newInviteLinkPassword.size() == 0 && // field 19 change.newDescription == null && // field 20 isEmpty(change.newIsAnnouncementGroup) && // field 21 change.promotePendingPniAciMembers.size() == 0; // field 24 } static boolean isEmpty(AccessControl.AccessRequired newAttributeAccess) { return newAttributeAccess == AccessControl.AccessRequired.UNKNOWN; } static boolean isEmpty(EnabledState enabledState) { return enabledState == EnabledState.UNKNOWN; } public static boolean changeIsSilent(DecryptedGroupChange plainGroupChange) { return changeIsEmptyExceptForProfileKeyChanges(plainGroupChange) || changeIsEmptyExceptForBanChangesAndOptionalProfileKeyChanges(plainGroupChange); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy