mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 08:09:12 +01:00
Add additional group terminate checks.
This commit is contained in:
@@ -101,6 +101,19 @@ public class TypingStatusRepository {
|
||||
return threadsNotifier;
|
||||
}
|
||||
|
||||
public synchronized void stopAllTypingForThread(long threadId) {
|
||||
Set<Typist> typists = typistMap.remove(threadId);
|
||||
if (typists != null) {
|
||||
for (Typist typist : typists) {
|
||||
Runnable timer = timers.remove(typist);
|
||||
if (timer != null) {
|
||||
ThreadUtil.cancelRunnableOnMain(timer);
|
||||
}
|
||||
}
|
||||
notifyThread(threadId, Collections.emptySet(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
TypingState empty = new TypingState(Collections.emptyList(), false);
|
||||
for (MutableLiveData<TypingState> notifier : notifiers.values()) {
|
||||
|
||||
@@ -1514,10 +1514,10 @@ class ConversationFragment :
|
||||
when {
|
||||
inputReadyState.isClientExpired || inputReadyState.isUnauthorized -> disabledInputView.showAsExpiredOrUnauthorized(inputReadyState.isClientExpired, inputReadyState.isUnauthorized)
|
||||
args.isIncognito -> disabledInputView.showAsIncognito()
|
||||
!inputReadyState.messageRequestState.isAccepted -> disabledInputView.showAsMessageRequest(inputReadyState.conversationRecipient, inputReadyState.messageRequestState)
|
||||
inputReadyState.isTerminatedGroup -> disabledInputView.showAsTerminatedGroup()
|
||||
inputReadyState.isActiveGroup == false -> disabledInputView.showAsNoLongerAMember()
|
||||
!inputReadyState.messageRequestState.isAccepted -> disabledInputView.showAsMessageRequest(inputReadyState.conversationRecipient, inputReadyState.messageRequestState)
|
||||
inputReadyState.isRequestingMember == true -> disabledInputView.showAsRequestingMember()
|
||||
inputReadyState.isActiveGroup == false -> disabledInputView.showAsNoLongerAMember()
|
||||
inputReadyState.isAnnouncementGroup == true && inputReadyState.isAdmin == false -> disabledInputView.showAsAnnouncementGroupAdminsOnly()
|
||||
inputReadyState.conversationRecipient.isReleaseNotes -> disabledInputView.showAsReleaseNotesChannel(inputReadyState.conversationRecipient)
|
||||
inputReadyState.shouldShowInviteToSignal() -> disabledInputView.showAsInviteToSignal(requireContext(), inputReadyState.conversationRecipient, inputReadyState.threadContainsSms)
|
||||
@@ -3609,7 +3609,14 @@ class ConversationFragment :
|
||||
}
|
||||
|
||||
override fun onInviteFriendsToGroupClicked(groupId: GroupId.V2) {
|
||||
GroupLinkInviteFriendsBottomSheetDialogFragment.show(requireActivity().supportFragmentManager, groupId)
|
||||
if (conversationGroupViewModel.groupRecordSnapshot?.isTerminated == true) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage(R.string.conversation_activity__group_action_not_allowed_group_ended)
|
||||
.setPositiveButton(android.R.string.ok) { d, _ -> d.dismiss() }
|
||||
.show()
|
||||
} else {
|
||||
GroupLinkInviteFriendsBottomSheetDialogFragment.show(requireActivity().supportFragmentManager, groupId)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
|
||||
@@ -406,7 +406,7 @@ class ConversationViewModel(
|
||||
val pendingGroupJoinFlow: Flow<PendingGroupJoinRequestsBanner> = groupRecordFlow
|
||||
.map {
|
||||
PendingGroupJoinRequestsBanner(
|
||||
suggestionsSize = it.actionableRequestingMembersCount,
|
||||
suggestionsSize = if (it.isTerminated) 0 else it.actionableRequestingMembersCount,
|
||||
onViewClicked = groupJoinClickListener
|
||||
)
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ private fun TerminatedGroupSheetContent(adminName: String?, onOkClick: () -> Uni
|
||||
.defaultMinSize(minWidth = 220.dp)
|
||||
.padding(bottom = 24.dp)
|
||||
) {
|
||||
Text(text = stringResource(R.string.TerminatedGroupBottomSheet__okay))
|
||||
Text(text = stringResource(android.R.string.ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -787,11 +787,11 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
notifyConversationListListeners()
|
||||
}
|
||||
|
||||
fun update(groupMasterKey: GroupMasterKey, decryptedGroup: DecryptedGroup, groupSendEndorsements: ReceivedGroupSendEndorsements?) {
|
||||
update(GroupId.v2(groupMasterKey), decryptedGroup, groupSendEndorsements)
|
||||
fun update(groupMasterKey: GroupMasterKey, decryptedGroup: DecryptedGroup, groupSendEndorsements: ReceivedGroupSendEndorsements?, terminatorRecipientId: RecipientId? = null) {
|
||||
update(GroupId.v2(groupMasterKey), decryptedGroup, groupSendEndorsements, terminatorRecipientId)
|
||||
}
|
||||
|
||||
fun update(groupId: GroupId.V2, decryptedGroup: DecryptedGroup, receivedGroupSendEndorsements: ReceivedGroupSendEndorsements?) {
|
||||
fun update(groupId: GroupId.V2, decryptedGroup: DecryptedGroup, receivedGroupSendEndorsements: ReceivedGroupSendEndorsements?, terminatorRecipientId: RecipientId? = null) {
|
||||
val groupRecipientId: RecipientId = recipients.getOrInsertFromGroupId(groupId)
|
||||
val existingGroup: Optional<GroupRecord> = getGroup(groupId)
|
||||
val title: String = decryptedGroup.title
|
||||
@@ -801,7 +801,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
contentValues.put(V2_REVISION, decryptedGroup.revision)
|
||||
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.encode())
|
||||
contentValues.put(IS_MEMBER, if (isGroupMember(decryptedGroup)) 1 else 0)
|
||||
contentValues.put(TERMINATED_BY, if (decryptedGroup.terminated) -1 else 0)
|
||||
contentValues.put(TERMINATED_BY, terminatorRecipientId?.toLong() ?: if (decryptedGroup.terminated) -1 else 0)
|
||||
|
||||
if (receivedGroupSendEndorsements != null) {
|
||||
contentValues.put(GROUP_SEND_ENDORSEMENTS_EXPIRATION, receivedGroupSendEndorsements.expirationMs)
|
||||
|
||||
@@ -744,7 +744,8 @@ final class GroupManagerV2 {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
groupDatabase.update(groupId, decryptedGroupState, groupsV2Operations.forGroup(groupSecretParams).receiveGroupSendEndorsements(selfAci, decryptedGroupState, changeResponse.group_send_endorsements_response));
|
||||
RecipientId terminatorRecipientId = (decryptedGroupState.terminated && !previousGroupState.terminated) ? Recipient.self().getId() : null;
|
||||
groupDatabase.update(groupId, decryptedGroupState, groupsV2Operations.forGroup(groupSecretParams).receiveGroupSendEndorsements(selfAci, decryptedGroupState, changeResponse.group_send_endorsements_response), terminatorRecipientId);
|
||||
|
||||
GroupMutation groupMutation = new GroupMutation(previousGroupState, decryptedChange, decryptedGroupState);
|
||||
RecipientAndThread recipientAndThread = sendGroupUpdateHelper.sendGroupUpdate(groupMasterKey, groupMutation, signedGroupChange, sendToMembers);
|
||||
@@ -986,7 +987,7 @@ final class GroupManagerV2 {
|
||||
}
|
||||
} else if (groupAlreadyExists && requestToJoin) {
|
||||
Log.i(TAG, "Group already exists, but we are requesting to join, updating with new placeholder, alreadyPending: " + isAlreadyPendingApproval);
|
||||
groupDatabase.update(groupMasterKey, decryptedGroup, null);
|
||||
groupDatabase.update(groupMasterKey, decryptedGroup, null, null);
|
||||
}
|
||||
|
||||
RecipientId groupRecipientId = SignalDatabase.recipients().getOrInsertFromGroupId(groupId);
|
||||
@@ -1250,7 +1251,7 @@ final class GroupManagerV2 {
|
||||
DecryptedGroupChange decryptedChange = groupOperations.decryptChange(signedGroupChange, DecryptChangeVerificationMode.alreadyTrusted()).get();
|
||||
DecryptedGroup newGroup = DecryptedGroupUtil.applyWithoutRevisionCheck(decryptedGroup, decryptedChange);
|
||||
|
||||
groupDatabase.update(groupId, resetRevision(newGroup, decryptedGroup.revision), null);
|
||||
groupDatabase.update(groupId, resetRevision(newGroup, decryptedGroup.revision), null, null);
|
||||
|
||||
sendGroupUpdateHelper.sendGroupUpdate(groupMasterKey, new GroupMutation(decryptedGroup, decryptedChange, newGroup), signedGroupChange, false);
|
||||
} catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
||||
import org.thoughtcrime.securesms.groups.GroupInsufficientRightsException;
|
||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||
import org.thoughtcrime.securesms.groups.MembershipNotSuitableForV2Exception;
|
||||
import org.whispersystems.signalservice.internal.push.exceptions.GroupTerminatedException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -18,11 +19,13 @@ public enum GroupChangeFailureReason {
|
||||
NOT_A_MEMBER,
|
||||
BUSY,
|
||||
NETWORK,
|
||||
GROUP_TERMINATED,
|
||||
OTHER;
|
||||
|
||||
@SuppressLint("SuspiciousIndentation")
|
||||
public static @NonNull GroupChangeFailureReason fromException(@NonNull Throwable e) {
|
||||
if (e instanceof MembershipNotSuitableForV2Exception) return GroupChangeFailureReason.NOT_GV2_CAPABLE;
|
||||
if (e instanceof GroupTerminatedException) return GroupChangeFailureReason.GROUP_TERMINATED;
|
||||
if (e instanceof IOException) return GroupChangeFailureReason.NETWORK;
|
||||
if (e instanceof GroupNotAMemberException) return GroupChangeFailureReason.NOT_A_MEMBER;
|
||||
if (e instanceof GroupChangeBusyException) return GroupChangeFailureReason.BUSY;
|
||||
|
||||
@@ -21,6 +21,7 @@ public final class GroupErrors {
|
||||
case NOT_A_MEMBER : return R.string.ManageGroupActivity_youre_not_a_member_of_the_group;
|
||||
case BUSY : return R.string.ManageGroupActivity_failed_to_update_the_group_please_retry_later;
|
||||
case NETWORK : return R.string.ManageGroupActivity_failed_to_update_the_group_due_to_a_network_error_please_retry_later;
|
||||
case GROUP_TERMINATED : return R.string.MessageRecord_the_group_was_terminated;
|
||||
default : return R.string.ManageGroupActivity_failed_to_update_the_group;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ package org.thoughtcrime.securesms.groups.ui.invitesandrequests.joining;
|
||||
enum FetchGroupDetailsError {
|
||||
GroupLinkNotActive,
|
||||
BannedFromGroup,
|
||||
NetworkError
|
||||
NetworkError,
|
||||
GroupTerminated
|
||||
}
|
||||
|
||||
@@ -175,6 +175,10 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
groupName.setText(R.string.GroupJoinBottomSheetDialogFragment_link_error);
|
||||
groupDetails.setText(R.string.GroupJoinBottomSheetDialogFragment_joining_via_this_link_failed_try_joining_again_later);
|
||||
break;
|
||||
case GroupTerminated:
|
||||
groupName.setText(R.string.GroupJoinBottomSheetDialogFragment_cant_join_group);
|
||||
groupDetails.setText(R.string.GroupJoinBottomSheetDialogFragment_this_group_has_been_ended);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +188,7 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
case BANNED : return getString(R.string.GroupJoinBottomSheetDialogFragment_you_cant_join_this_group_via_the_group_link_because_an_admin_removed_you);
|
||||
case NETWORK_ERROR : return getString(R.string.GroupJoinBottomSheetDialogFragment_encountered_a_network_error);
|
||||
case LIMIT_REACHED : return getString(R.string.GroupJoinBottomSheetDialogFragment_group_limit_reached);
|
||||
case GROUP_TERMINATED : return getString(R.string.GroupJoinBottomSheetDialogFragment_this_group_has_been_ended);
|
||||
default : return getString(R.string.GroupJoinBottomSheetDialogFragment_unable_to_join_group_please_try_again_later);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.jobs.AvatarGroupsV2DownloadJob;
|
||||
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
||||
import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException;
|
||||
import org.whispersystems.signalservice.internal.push.exceptions.GroupTerminatedException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -38,6 +39,8 @@ final class GroupJoinRepository {
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
callback.onComplete(getGroupDetails());
|
||||
} catch (GroupTerminatedException e) {
|
||||
callback.onError(FetchGroupDetailsError.GroupTerminated);
|
||||
} catch (IOException e) {
|
||||
callback.onError(FetchGroupDetailsError.NetworkError);
|
||||
} catch (GroupLinkNotActiveException e) {
|
||||
@@ -60,6 +63,9 @@ final class GroupJoinRepository {
|
||||
groupDetails.getAvatarBytes());
|
||||
|
||||
callback.onComplete(new JoinGroupSuccess(groupActionResult.getGroupRecipient(), groupActionResult.getThreadId()));
|
||||
} catch (GroupTerminatedException e) {
|
||||
Log.w(TAG, "Group is terminated", e);
|
||||
callback.onError(JoinGroupError.GROUP_TERMINATED);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Network error", e);
|
||||
callback.onError(JoinGroupError.NETWORK_ERROR);
|
||||
|
||||
@@ -7,4 +7,5 @@ enum JoinGroupError {
|
||||
FAILED,
|
||||
LIMIT_REACHED,
|
||||
NETWORK_ERROR,
|
||||
GROUP_TERMINATED,
|
||||
}
|
||||
|
||||
@@ -41,10 +41,12 @@ import org.thoughtcrime.securesms.mms.MmsException
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupChangeReconstruct
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupHistoryPage
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException
|
||||
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException
|
||||
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException
|
||||
import org.whispersystems.signalservice.api.groupsv2.ReceivedGroupSendEndorsements
|
||||
@@ -52,6 +54,7 @@ import org.whispersystems.signalservice.api.groupsv2.getChangedFields
|
||||
import org.whispersystems.signalservice.api.groupsv2.isSilent
|
||||
import org.whispersystems.signalservice.api.push.ServiceIds
|
||||
import org.whispersystems.signalservice.internal.push.exceptions.GroupNotFoundException
|
||||
import org.whispersystems.signalservice.internal.push.exceptions.GroupTerminatedException
|
||||
import org.whispersystems.signalservice.internal.push.exceptions.NotInGroupException
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
@@ -214,6 +217,21 @@ class GroupsV2StateProcessor private constructor(
|
||||
|
||||
if (currentLocalState != null && DecryptedGroupUtil.isPendingOrRequesting(currentLocalState, serviceIds)) {
|
||||
Log.w(TAG, "$logPrefix Unable to query server for group. Server says we're not in group, but we think we are a pending or requesting member")
|
||||
try {
|
||||
groupsApi.getGroupJoinInfo(groupSecretParams, Optional.empty(), groupsV2Authorization.getAuthorizationForToday(serviceIds, groupSecretParams))
|
||||
} catch (e: GroupTerminatedException) {
|
||||
Log.i(TAG, "$logPrefix Group was terminated while join request was pending, marking locally")
|
||||
profileAndMessageHelper.markTerminatedLocally()
|
||||
} catch (e: GroupLinkNotActiveException) {
|
||||
if (e.reason == GroupLinkNotActiveException.Reason.BANNED) {
|
||||
Log.i(TAG, "$logPrefix Join request was rejected (banned) while pending, marking locally")
|
||||
profileAndMessageHelper.markJoinRequestRejectedLocally()
|
||||
} else {
|
||||
Log.i(TAG, "$logPrefix Group link not active while checking pending join for group termination")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "$logPrefix Network error while checking if group terminated", e)
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "$logPrefix Unable to query server for group $groupId server says we're not in group, we agree, inserting leave message")
|
||||
profileAndMessageHelper.leaveGroupLocally(serviceIds)
|
||||
@@ -525,20 +543,20 @@ class GroupsV2StateProcessor private constructor(
|
||||
Log.i(TAG, "$logPrefix Local state (revision: ${currentLocalState?.revision}) does not match, updating to ${updatedGroupState.revision}")
|
||||
}
|
||||
|
||||
saveGroupState(groupStateDiff, updatedGroupState, groupSendEndorsements)
|
||||
|
||||
if (updatedGroupState.terminated && (currentLocalState == null || !currentLocalState.terminated)) {
|
||||
val terminatingChange = groupStateDiff.serverHistory
|
||||
val terminatorRecipientId: RecipientId? = if (updatedGroupState.terminated && (currentLocalState == null || !currentLocalState.terminated)) {
|
||||
groupStateDiff.serverHistory
|
||||
.mapNotNull { it.change }
|
||||
.firstOrNull { it.terminateGroup }
|
||||
?.let { ServiceId.parseOrNull(it.editorServiceIdBytes) }
|
||||
?.let { RecipientId.from(it) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (terminatingChange != null) {
|
||||
val editorServiceId = ServiceId.parseOrNull(terminatingChange.editorServiceIdBytes)
|
||||
if (editorServiceId != null) {
|
||||
val terminatorRecipientId = RecipientId.from(editorServiceId)
|
||||
SignalDatabase.groups.setTerminatedBy(groupId, terminatorRecipientId)
|
||||
}
|
||||
}
|
||||
saveGroupState(groupStateDiff, updatedGroupState, groupSendEndorsements, terminatorRecipientId)
|
||||
|
||||
if (terminatorRecipientId != null) {
|
||||
profileAndMessageHelper.stopAllTypingForGroup()
|
||||
}
|
||||
|
||||
if (currentLocalState == null || currentLocalState.revision == RESTORE_PLACEHOLDER_REVISION) {
|
||||
@@ -561,7 +579,7 @@ class GroupsV2StateProcessor private constructor(
|
||||
return InternalUpdateResult.Updated(updatedGroupState)
|
||||
}
|
||||
|
||||
private fun saveGroupState(groupStateDiff: GroupStateDiff, updatedGroupState: DecryptedGroup, groupSendEndorsements: ReceivedGroupSendEndorsements?) {
|
||||
private fun saveGroupState(groupStateDiff: GroupStateDiff, updatedGroupState: DecryptedGroup, groupSendEndorsements: ReceivedGroupSendEndorsements?, terminatorRecipientId: RecipientId? = null) {
|
||||
val previousGroupState = groupStateDiff.previousGroupState
|
||||
|
||||
if (groupSendEndorsements != null) {
|
||||
@@ -573,12 +591,12 @@ class GroupsV2StateProcessor private constructor(
|
||||
|
||||
if (groupId == null) {
|
||||
Log.w(TAG, "$logPrefix Group create failed, trying to update")
|
||||
SignalDatabase.groups.update(groupMasterKey, updatedGroupState, groupSendEndorsements)
|
||||
SignalDatabase.groups.update(groupMasterKey, updatedGroupState, groupSendEndorsements, terminatorRecipientId)
|
||||
}
|
||||
|
||||
updatedGroupState.avatar.isNotEmpty()
|
||||
} else {
|
||||
SignalDatabase.groups.update(groupMasterKey, updatedGroupState, groupSendEndorsements)
|
||||
SignalDatabase.groups.update(groupMasterKey, updatedGroupState, groupSendEndorsements, terminatorRecipientId)
|
||||
|
||||
updatedGroupState.avatar != previousGroupState.avatar
|
||||
}
|
||||
@@ -735,6 +753,89 @@ class GroupsV2StateProcessor private constructor(
|
||||
SignalDatabase.groups.remove(groupId, Recipient.self().id)
|
||||
}
|
||||
|
||||
fun markTerminatedLocally() {
|
||||
val group = SignalDatabase.groups.getGroup(groupId).orNull()
|
||||
|
||||
if (group == null) {
|
||||
Log.w(TAG, "Group not found when inserting terminated message for $groupId")
|
||||
return
|
||||
}
|
||||
|
||||
if (group.isTerminated) {
|
||||
Log.w(TAG, "Group $groupId is already marked as terminated.")
|
||||
return
|
||||
}
|
||||
|
||||
val groupRecipient = Recipient.externalGroupExact(groupId)
|
||||
|
||||
val decryptedGroup = group.requireV2GroupProperties().decryptedGroup
|
||||
val simulatedGroupState = decryptedGroup.copy(terminated = true)
|
||||
val simulatedGroupChange = DecryptedGroupChange(terminateGroup = true)
|
||||
|
||||
val updateDescription = GroupProtoUtil.createOutgoingGroupV2UpdateDescription(masterKey, GroupMutation(decryptedGroup, simulatedGroupChange, simulatedGroupState), null)
|
||||
val terminateMessage = OutgoingMessage.groupUpdateMessage(groupRecipient, updateDescription, System.currentTimeMillis(), isSelfGroupAdd = false)
|
||||
|
||||
try {
|
||||
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(groupRecipient)
|
||||
val id = SignalDatabase.messages.insertMessageOutbox(terminateMessage, threadId, false, null).messageId
|
||||
SignalDatabase.messages.markAsSent(id, true)
|
||||
SignalDatabase.threads.update(threadId, unarchive = false, allowDeletion = false)
|
||||
} catch (e: MmsException) {
|
||||
Log.w(TAG, "Failed to insert terminated group message for $groupId", e)
|
||||
}
|
||||
|
||||
SignalDatabase.groups.update(masterKey, simulatedGroupState, null)
|
||||
}
|
||||
|
||||
fun markJoinRequestRejectedLocally() {
|
||||
val group = SignalDatabase.groups.getGroup(groupId).orNull()
|
||||
|
||||
if (group == null) {
|
||||
Log.w(TAG, "Group not found when inserting join request rejection message for $groupId")
|
||||
return
|
||||
}
|
||||
|
||||
val decryptedGroup = group.requireV2GroupProperties().decryptedGroup
|
||||
|
||||
if (decryptedGroup.requestingMembers.none { ACI.parseOrNull(it.aciBytes) == aci }) {
|
||||
Log.w(TAG, "Not a requesting member of $groupId, skipping rejection insert")
|
||||
return
|
||||
}
|
||||
|
||||
val groupRecipient = Recipient.externalGroupExact(groupId)
|
||||
|
||||
val simulatedGroupState = decryptedGroup.copy(
|
||||
requestingMembers = decryptedGroup.requestingMembers.filter { ACI.parseOrNull(it.aciBytes) != aci }
|
||||
)
|
||||
val simulatedGroupChange = DecryptedGroupChange(
|
||||
editorServiceIdBytes = ACI.UNKNOWN.toByteString(),
|
||||
deleteRequestingMembers = listOf(aci.toByteString())
|
||||
)
|
||||
|
||||
val updateDescription = GroupProtoUtil.createOutgoingGroupV2UpdateDescription(masterKey, GroupMutation(decryptedGroup, simulatedGroupChange, simulatedGroupState), null)
|
||||
val rejectedMessage = OutgoingMessage.groupUpdateMessage(groupRecipient, updateDescription, System.currentTimeMillis(), isSelfGroupAdd = false)
|
||||
|
||||
try {
|
||||
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(groupRecipient)
|
||||
val id = SignalDatabase.messages.insertMessageOutbox(rejectedMessage, threadId, false, null).messageId
|
||||
SignalDatabase.messages.markAsSent(id, true)
|
||||
SignalDatabase.threads.update(threadId, unarchive = false, allowDeletion = false)
|
||||
} catch (e: MmsException) {
|
||||
Log.w(TAG, "Failed to insert rejected join request message for $groupId", e)
|
||||
}
|
||||
|
||||
SignalDatabase.groups.update(masterKey, simulatedGroupState, null)
|
||||
}
|
||||
|
||||
fun stopAllTypingForGroup() {
|
||||
if (TextSecurePreferences.isTypingIndicatorsEnabled(AppDependencies.application)) {
|
||||
val threadId = SignalDatabase.threads.getThreadIdFor(SignalDatabase.recipients.getOrInsertFromGroupId(groupId))
|
||||
if (threadId != null) {
|
||||
AppDependencies.typingStatusRepository.stopAllTypingForThread(threadId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun persistLearnedProfileKeys(groupStateDiff: GroupStateDiff) {
|
||||
val profileKeys = ProfileKeySet()
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.signal.core.util.Util;
|
||||
import org.signal.core.util.UuidUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.SealedSenderAccessUtil;
|
||||
@@ -35,7 +37,6 @@ import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.MessageUtil;
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
|
||||
import org.signal.core.util.Util;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender.IndividualSendEvents;
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||
@@ -50,7 +51,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||
import org.signal.core.util.UuidUtil;
|
||||
import org.whispersystems.signalservice.internal.push.BodyRange;
|
||||
import org.whispersystems.signalservice.internal.push.DataMessage;
|
||||
|
||||
|
||||
@@ -15,11 +15,14 @@ import androidx.annotation.Nullable;
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.signal.blurhash.BlurHash;
|
||||
import org.signal.core.models.ServiceId.ACI;
|
||||
import org.signal.core.util.Base64;
|
||||
import org.signal.core.util.Hex;
|
||||
import org.signal.core.util.Util;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException;
|
||||
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation;
|
||||
import org.signal.blurhash.BlurHash;
|
||||
import org.thoughtcrime.securesms.TextSecureExpiredException;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
@@ -43,7 +46,6 @@ import org.thoughtcrime.securesms.events.PartProgressEvent;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobTracker;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.BackoffUtil;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage;
|
||||
@@ -57,10 +59,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.signal.core.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.signal.core.util.Util;
|
||||
import org.whispersystems.signalservice.api.messages.AttachmentTransferProgress;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
||||
@@ -68,17 +67,12 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemo
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServicePreview;
|
||||
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
|
||||
import org.signal.core.models.ServiceId.ACI;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.RetryNetworkException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
|
||||
import org.whispersystems.signalservice.internal.push.BodyRange;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
Reference in New Issue
Block a user