mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Add support for admin delete.
This commit is contained in:
committed by
Cody Henthorne
parent
1968438ebb
commit
071fbfd916
@@ -32,7 +32,8 @@ object DeleteDialog {
|
||||
messageRecords: Set<MessageRecord>,
|
||||
title: CharSequence? = null,
|
||||
message: CharSequence = context.resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageRecords.size, messageRecords.size),
|
||||
forceRemoteDelete: Boolean = false
|
||||
forceRemoteDelete: Boolean = false,
|
||||
isAdmin: Boolean = false
|
||||
): Single<Pair<Boolean, Boolean>> = Single.create { emitter ->
|
||||
val builder = MaterialAlertDialogBuilder(context)
|
||||
|
||||
@@ -43,7 +44,7 @@ object DeleteDialog {
|
||||
val isNoteToSelfDelete = isNoteToSelfDelete(messageRecords)
|
||||
|
||||
if (forceRemoteDelete) {
|
||||
builder.setPositiveButton(R.string.ConversationFragment_delete_for_everyone) { _, _ -> deleteForEveryone(messageRecords, emitter) }
|
||||
builder.setPositiveButton(R.string.ConversationFragment_delete_for_everyone) { _, _ -> deleteForEveryone(messageRecords = messageRecords, isAdminDelete = false, emitter = emitter) }
|
||||
} else {
|
||||
val positiveButton = if (isNoteToSelfDelete) {
|
||||
R.string.ConversationFragment_delete
|
||||
@@ -58,7 +59,9 @@ object DeleteDialog {
|
||||
}
|
||||
|
||||
if (MessageConstraintsUtil.isValidRemoteDeleteSend(messageRecords, System.currentTimeMillis()) && !isNoteToSelfDelete) {
|
||||
builder.setNeutralButton(R.string.ConversationFragment_delete_for_everyone) { _, _ -> handleDeleteForEveryone(context, messageRecords, emitter) }
|
||||
builder.setNeutralButton(R.string.ConversationFragment_delete_for_everyone) { _, _ -> handleDeleteForEveryone(context = context, messageRecords = messageRecords, isAdminDelete = false, emitter = emitter) }
|
||||
} else if (MessageConstraintsUtil.isValidAdminDeleteSend(messageRecords, System.currentTimeMillis(), isAdmin)) {
|
||||
builder.setNeutralButton(R.string.ConversationFragment_delete_for_everyone) { _, _ -> handleAdminDeleteForEveryone(context, messageRecords, emitter) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,15 +74,27 @@ object DeleteDialog {
|
||||
return messageRecords.all { messageRecord: MessageRecord -> messageRecord.isOutgoing && messageRecord.toRecipient.isSelf }
|
||||
}
|
||||
|
||||
private fun handleDeleteForEveryone(context: Context, messageRecords: Set<MessageRecord>, emitter: SingleEmitter<Pair<Boolean, Boolean>>) {
|
||||
private fun handleAdminDeleteForEveryone(context: Context, messageRecords: Set<MessageRecord>, emitter: SingleEmitter<Pair<Boolean, Boolean>>) {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle("${context.getString(R.string.ConversationFragment_delete_for_everyone_title)} - INTERNAL ONLY")
|
||||
.setMessage(R.string.ConversationFragment_delete_for_everyone_body)
|
||||
.setPositiveButton(R.string.ConversationFragment_delete_for_everyone) { _, _ ->
|
||||
handleDeleteForEveryone(context = context, messageRecords = messageRecords, isAdminDelete = true, emitter = emitter)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> emitter.onSuccess(Pair(false, false)) }
|
||||
.setOnCancelListener { emitter.onSuccess(Pair(false, false)) }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun handleDeleteForEveryone(context: Context, messageRecords: Set<MessageRecord>, isAdminDelete: Boolean, emitter: SingleEmitter<Pair<Boolean, Boolean>>) {
|
||||
if (SignalStore.uiHints.hasConfirmedDeleteForEveryoneOnce()) {
|
||||
deleteForEveryone(messageRecords, emitter)
|
||||
deleteForEveryone(messageRecords, isAdminDelete, emitter)
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setMessage(R.string.ConversationFragment_this_message_will_be_deleted_for_everyone_in_the_conversation)
|
||||
.setPositiveButton(R.string.ConversationFragment_delete_for_everyone) { _, _ ->
|
||||
SignalStore.uiHints.markHasConfirmedDeleteForEveryoneOnce()
|
||||
deleteForEveryone(messageRecords, emitter)
|
||||
deleteForEveryone(messageRecords, isAdminDelete, emitter)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> emitter.onSuccess(Pair(false, false)) }
|
||||
.setOnCancelListener { emitter.onSuccess(Pair(false, false)) }
|
||||
@@ -87,10 +102,14 @@ object DeleteDialog {
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteForEveryone(messageRecords: Set<MessageRecord>, emitter: SingleEmitter<Pair<Boolean, Boolean>>) {
|
||||
private fun deleteForEveryone(messageRecords: Set<MessageRecord>, isAdminDelete: Boolean, emitter: SingleEmitter<Pair<Boolean, Boolean>>) {
|
||||
SignalExecutors.BOUNDED.execute {
|
||||
messageRecords.forEach { message ->
|
||||
MessageSender.sendRemoteDelete(message.id)
|
||||
if (isAdminDelete) {
|
||||
MessageSender.sendAdminDelete(message.id)
|
||||
} else {
|
||||
MessageSender.sendRemoteDelete(message.id)
|
||||
}
|
||||
}
|
||||
|
||||
emitter.onSuccess(Pair(true, false))
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
/**
|
||||
@@ -12,8 +13,10 @@ import kotlin.time.Duration.Companion.milliseconds
|
||||
* have strict time limits.
|
||||
*/
|
||||
object MessageConstraintsUtil {
|
||||
private val RECEIVE_THRESHOLD = TimeUnit.DAYS.toMillis(2)
|
||||
private val SEND_THRESHOLD = TimeUnit.DAYS.toMillis(1)
|
||||
private val SEND_THRESHOLD = RemoteConfig.regularDeleteThreshold.milliseconds.inWholeMilliseconds
|
||||
private val RECEIVE_THRESHOLD = SEND_THRESHOLD + 1.days.inWholeMilliseconds
|
||||
private val ADMIN_SEND_THRESHOLD = RemoteConfig.adminDeleteThreshold.milliseconds.inWholeMilliseconds
|
||||
private val ADMIN_RECEIVE_THRESHOLD = ADMIN_SEND_THRESHOLD + 1.days.inWholeMilliseconds
|
||||
|
||||
const val MAX_EDIT_COUNT = 10
|
||||
|
||||
@@ -31,6 +34,14 @@ object MessageConstraintsUtil {
|
||||
((deleteServerTimestamp - messageTimestamp < RECEIVE_THRESHOLD) || (selfIsDeleteSender && targetMessage.isOutgoing))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isValidAdminDeleteReceive(targetMessage: MessageRecord, deleteSender: Recipient, deleteServerTimestamp: Long, groupRecord: GroupRecord): Boolean {
|
||||
val isValidSender = groupRecord.isAdmin(deleteSender)
|
||||
val messageTimestamp = targetMessage.dateSent
|
||||
|
||||
return isValidSender && (deleteServerTimestamp - messageTimestamp < ADMIN_RECEIVE_THRESHOLD)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isValidEditMessageReceive(targetMessage: MessageRecord, editSender: Recipient, editServerTimestamp: Long): Boolean {
|
||||
return isValidRemoteDeleteReceive(targetMessage, editSender.id, editServerTimestamp)
|
||||
@@ -42,6 +53,11 @@ object MessageConstraintsUtil {
|
||||
return targetMessages.all { isValidRemoteDeleteSend(it, currentTime) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isValidAdminDeleteSend(targetMessages: Collection<MessageRecord>, currentTime: Long, isAdmin: Boolean): Boolean {
|
||||
return targetMessages.all { isValidAdminDeleteSend(it, currentTime, isAdmin) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isWithinMaxEdits(targetMessage: MessageRecord): Boolean {
|
||||
return targetMessage.revisionNumber < MAX_EDIT_COUNT
|
||||
@@ -94,6 +110,19 @@ object MessageConstraintsUtil {
|
||||
(currentTime - message.dateSent < SEND_THRESHOLD || message.toRecipient.isSelf)
|
||||
}
|
||||
|
||||
private fun isValidAdminDeleteSend(message: MessageRecord, currentTime: Long, isAdmin: Boolean): Boolean {
|
||||
return RemoteConfig.sendAdminDelete &&
|
||||
isAdmin &&
|
||||
!message.isUpdate &&
|
||||
message.isPush &&
|
||||
(!message.toRecipient.isGroup || message.toRecipient.isActiveGroup) &&
|
||||
!message.isRemoteDelete &&
|
||||
!message.hasGiftBadge() &&
|
||||
!message.isPaymentNotification &&
|
||||
!message.isPaymentTombstone &&
|
||||
(currentTime - message.dateSent < ADMIN_SEND_THRESHOLD)
|
||||
}
|
||||
|
||||
private fun isSelf(recipientId: RecipientId): Boolean {
|
||||
return Recipient.isSelfSet && Recipient.self().id == recipientId
|
||||
}
|
||||
|
||||
@@ -1257,5 +1257,48 @@ object RemoteConfig {
|
||||
hotSwappable = true
|
||||
)
|
||||
|
||||
/**
|
||||
* Whether or not to receive admin delete messages.
|
||||
*/
|
||||
@JvmStatic
|
||||
@get:JvmName("receiveAdminDelete")
|
||||
val receiveAdminDelete: Boolean by remoteBoolean(
|
||||
key = "android.receiveAdminDelete",
|
||||
defaultValue = false,
|
||||
hotSwappable = true
|
||||
)
|
||||
|
||||
/**
|
||||
* Whether or not to send admin delete messages.
|
||||
*/
|
||||
@JvmStatic
|
||||
@get:JvmName("sendAdminDelete")
|
||||
val sendAdminDelete: Boolean by remoteBoolean(
|
||||
key = "android.sendAdminDelete",
|
||||
defaultValue = false,
|
||||
hotSwappable = true
|
||||
)
|
||||
|
||||
/**
|
||||
* Maximum time that passes where a message can still be regularly deleted
|
||||
*/
|
||||
@JvmStatic
|
||||
@get:JvmName("regularDeleteThreshold")
|
||||
val regularDeleteThreshold: Long by remoteLong(
|
||||
key = "android.regularDeleteThreshold",
|
||||
defaultValue = 1.days.inWholeMilliseconds,
|
||||
hotSwappable = true
|
||||
)
|
||||
|
||||
/**
|
||||
* Maximum time that passes where a message can still be deleted by an admin
|
||||
*/
|
||||
@JvmStatic
|
||||
@get:JvmName("adminDeleteThreshold")
|
||||
val adminDeleteThreshold: Long by remoteLong(
|
||||
key = "android.adminDeleteThreshold",
|
||||
defaultValue = 1.days.inWholeMilliseconds,
|
||||
hotSwappable = true
|
||||
)
|
||||
// endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user