mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Add additional delete sync support.
This commit is contained in:
committed by
Greyson Parrelli
parent
d22d18da47
commit
c80ccd70ec
@@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms
|
||||
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.whispersystems.signalservice.api.account.AccountAttributes
|
||||
|
||||
object AppCapabilities {
|
||||
@@ -17,7 +18,8 @@ object AppCapabilities {
|
||||
stories = true,
|
||||
giftBadges = true,
|
||||
pni = true,
|
||||
paymentActivation = true
|
||||
paymentActivation = true,
|
||||
deleteSync = RemoteConfig.deleteSyncEnabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
|
||||
/**
|
||||
@@ -48,7 +48,7 @@ class DeleteSyncEducationDialog : ComposeBottomSheetDialogFragment() {
|
||||
fun shouldShow(): Boolean {
|
||||
return TextSecurePreferences.isMultiDevice(AppDependencies.application) &&
|
||||
!SignalStore.uiHints().hasSeenDeleteSyncEducationSheet &&
|
||||
RemoteConfig.deleteSyncEnabled
|
||||
Recipient.self().deleteSyncCapability.isSupported
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -66,7 +66,7 @@ import org.thoughtcrime.securesms.database.MediaTable
|
||||
import org.thoughtcrime.securesms.keyvalue.KeepMessagesDuration
|
||||
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity
|
||||
import org.thoughtcrime.securesms.preferences.widgets.StorageGraphView
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
@@ -138,7 +138,7 @@ class ManageStorageSettingsFragment : ComposeFragment() {
|
||||
dialog("confirm-delete-chat-history") {
|
||||
Dialogs.SimpleAlertDialog(
|
||||
title = stringResource(id = R.string.preferences_storage__delete_message_history),
|
||||
body = if (TextSecurePreferences.isMultiDevice(LocalContext.current) && RemoteConfig.deleteSyncEnabled) {
|
||||
body = if (TextSecurePreferences.isMultiDevice(LocalContext.current) && Recipient.self().deleteSyncCapability.isSupported) {
|
||||
stringResource(id = R.string.preferences_storage__this_will_delete_all_message_history_and_media_from_your_device_linked_device)
|
||||
} else {
|
||||
stringResource(id = R.string.preferences_storage__this_will_delete_all_message_history_and_media_from_your_device)
|
||||
@@ -154,7 +154,7 @@ class ManageStorageSettingsFragment : ComposeFragment() {
|
||||
dialog("double-confirm-delete-chat-history", dialogProperties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) {
|
||||
Dialogs.SimpleAlertDialog(
|
||||
title = stringResource(id = R.string.preferences_storage__are_you_sure_you_want_to_delete_all_message_history),
|
||||
body = if (TextSecurePreferences.isMultiDevice(LocalContext.current) && RemoteConfig.deleteSyncEnabled) {
|
||||
body = if (TextSecurePreferences.isMultiDevice(LocalContext.current) && Recipient.self().deleteSyncCapability.isSupported) {
|
||||
stringResource(id = R.string.preferences_storage__all_message_history_will_be_permanently_removed_this_action_cannot_be_undone_linked_device)
|
||||
} else {
|
||||
stringResource(id = R.string.preferences_storage__all_message_history_will_be_permanently_removed_this_action_cannot_be_undone)
|
||||
|
||||
@@ -341,7 +341,9 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
|
||||
|
||||
return if (capabilities != null) {
|
||||
TextUtils.concat(
|
||||
colorize("PaymentActivation", capabilities.paymentActivation)
|
||||
colorize("PaymentActivation", capabilities.paymentActivation),
|
||||
", ",
|
||||
colorize("DeleteSync", capabilities.deleteSync)
|
||||
)
|
||||
} else {
|
||||
"Recipient not found!"
|
||||
|
||||
@@ -2408,7 +2408,7 @@ class ConversationFragment :
|
||||
disposables += DeleteDialog.show(
|
||||
context = requireContext(),
|
||||
messageRecords = records,
|
||||
message = if (TextSecurePreferences.isMultiDevice(requireContext()) && RemoteConfig.deleteSyncEnabled) {
|
||||
message = if (TextSecurePreferences.isMultiDevice(requireContext()) && Recipient.self().deleteSyncCapability.isSupported) {
|
||||
resources.getQuantityString(R.plurals.ConversationFragment_delete_on_linked_warning, records.size)
|
||||
} else {
|
||||
null
|
||||
|
||||
@@ -1202,7 +1202,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
alert.setTitle(context.getResources().getQuantityString(R.plurals.ConversationListFragment_delete_selected_conversations,
|
||||
conversationsCount, conversationsCount));
|
||||
|
||||
if (TextSecurePreferences.isMultiDevice(context) && RemoteConfig.deleteSyncEnabled()) {
|
||||
if (TextSecurePreferences.isMultiDevice(context) && Recipient.self().getDeleteSyncCapability().isSupported()) {
|
||||
alert.setMessage(context.getResources().getQuantityString(R.plurals.ConversationListFragment_this_will_permanently_delete_all_n_selected_conversations_linked_device,
|
||||
conversationsCount, conversationsCount));
|
||||
} else {
|
||||
@@ -1230,7 +1230,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
SignalDatabase.threads().deleteConversations(selectedConversations);
|
||||
SignalDatabase.threads().deleteConversations(selectedConversations, true);
|
||||
AppDependencies.getMessageNotifier().updateNotification(requireActivity());
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -462,8 +462,11 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
* - Not an encryption message
|
||||
* - Not a report spam message
|
||||
* - Not a message rqeuest accepted message
|
||||
* - Not be a story
|
||||
* - Have a valid sent timestamp
|
||||
* - Be a normal message or direct (1:1) story reply
|
||||
*
|
||||
* Changes should be reflected in [MmsMessageRecord.canDeleteSync].
|
||||
*/
|
||||
private const val IS_ADDRESSABLE_CLAUSE = """
|
||||
(($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_SENT_TYPE} OR ($TYPE & ${MessageTypes.BASE_TYPE_MASK}) = ${MessageTypes.BASE_INBOX_TYPE}) AND
|
||||
@@ -472,7 +475,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
($TYPE & ${MessageTypes.KEY_EXCHANGE_MASK}) = 0 AND
|
||||
($TYPE & ${MessageTypes.ENCRYPTION_MASK}) = 0 AND
|
||||
($TYPE & ${MessageTypes.SPECIAL_TYPES_MASK}) != ${MessageTypes.SPECIAL_TYPE_REPORTED_SPAM} AND
|
||||
($TYPE & ${MessageTypes.SPECIAL_TYPES_MASK}) != ${MessageTypes.SPECIAL_TYPE_MESSAGE_REQUEST_ACCEPTED} AND
|
||||
($TYPE & ${MessageTypes.SPECIAL_TYPES_MASK}) != ${MessageTypes.SPECIAL_TYPE_MESSAGE_REQUEST_ACCEPTED} AND
|
||||
$STORY_TYPE = 0 AND
|
||||
$DATE_SENT > 0 AND
|
||||
$PARENT_STORY_ID <= 0
|
||||
"""
|
||||
@@ -3277,7 +3281,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
threads.setLastScrolled(threadId, 0)
|
||||
|
||||
val threadDeleted = if (updateThread) {
|
||||
threads.update(threadId, false)
|
||||
threads.update(threadId, unarchive = false, syncThreadDelete = false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -3332,11 +3336,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteThread(threadId: Long) {
|
||||
Log.d(TAG, "deleteThread($threadId)")
|
||||
deleteThreads(setOf(threadId))
|
||||
}
|
||||
|
||||
private fun getSerializedSharedContacts(insertedAttachmentIds: Map<Attachment, AttachmentId>, contacts: List<Contact>): String? {
|
||||
if (contacts.isEmpty()) {
|
||||
return null
|
||||
@@ -3434,27 +3433,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
return ids
|
||||
}
|
||||
|
||||
private fun deleteThreads(threadIds: Set<Long>) {
|
||||
Log.d(TAG, "deleteThreads(count: ${threadIds.size})")
|
||||
|
||||
writableDatabase.withinTransaction { db ->
|
||||
SqlUtil.buildCollectionQuery(THREAD_ID, threadIds).forEach { query ->
|
||||
db.select(ID, THREAD_ID)
|
||||
.from(TABLE_NAME)
|
||||
.where(query.where, query.whereArgs)
|
||||
.run()
|
||||
.forEach { cursor ->
|
||||
deleteMessage(cursor.requireLong(ID), cursor.requireLong(THREAD_ID), notify = false, updateThread = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyConversationListeners(threadIds)
|
||||
notifyStickerListeners()
|
||||
notifyStickerPackListeners()
|
||||
OptimizeMessageSearchIndexJob.enqueue()
|
||||
}
|
||||
|
||||
fun deleteMessagesInThreadBeforeDate(threadId: Long, date: Long, inclusive: Boolean): Int {
|
||||
val condition = if (inclusive) "<=" else "<"
|
||||
|
||||
|
||||
@@ -410,6 +410,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
fun maskCapabilitiesToLong(capabilities: SignalServiceProfile.Capabilities): Long {
|
||||
var value: Long = 0
|
||||
value = Bitmask.update(value, Capabilities.PAYMENT_ACTIVATION, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isPaymentActivation).serialize().toLong())
|
||||
value = Bitmask.update(value, Capabilities.DELETE_SYNC, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isDeleteSync).serialize().toLong())
|
||||
return value
|
||||
}
|
||||
}
|
||||
@@ -4577,6 +4578,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
// const val GIFT_BADGES = 6
|
||||
// const val PNP = 7
|
||||
const val PAYMENT_ACTIVATION = 8
|
||||
const val DELETE_SYNC = 9
|
||||
}
|
||||
|
||||
enum class VibrateState(val id: Int) {
|
||||
|
||||
@@ -175,7 +175,8 @@ object RecipientTableCursorUtil {
|
||||
val capabilities = cursor.requireLong(RecipientTable.CAPABILITIES)
|
||||
return RecipientRecord.Capabilities(
|
||||
rawBits = capabilities,
|
||||
paymentActivation = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.PAYMENT_ACTIVATION, Capabilities.BIT_LENGTH).toInt())
|
||||
paymentActivation = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.PAYMENT_ACTIVATION, Capabilities.BIT_LENGTH).toInt()),
|
||||
deleteSync = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.DELETE_SYNC, Capabilities.BIT_LENGTH).toInt())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ import org.thoughtcrime.securesms.util.ConversationUtil
|
||||
import org.thoughtcrime.securesms.util.JsonUtils
|
||||
import org.thoughtcrime.securesms.util.JsonUtils.SaneJSONObject
|
||||
import org.thoughtcrime.securesms.util.LRUCache
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.isScheduled
|
||||
import org.whispersystems.signalservice.api.storage.SignalAccountRecord
|
||||
@@ -326,7 +325,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
return
|
||||
}
|
||||
|
||||
val syncThreadTrimDeletes = SignalStore.settings().shouldSyncThreadTrimDeletes() && RemoteConfig.deleteSyncEnabled
|
||||
val syncThreadTrimDeletes = SignalStore.settings().shouldSyncThreadTrimDeletes() && Recipient.self().deleteSyncCapability.isSupported
|
||||
val threadTrimsToSync = mutableListOf<Pair<Long, Set<MessageRecord>>>()
|
||||
|
||||
readableDatabase
|
||||
@@ -1119,57 +1118,34 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
if (containsAddressable || isEmpty) {
|
||||
false
|
||||
} else {
|
||||
deleteConversation(threadId, syncThreadDeletes = false)
|
||||
deleteConversation(threadId, syncThreadDelete = false)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun deleteConversation(threadId: Long, syncThreadDeletes: Boolean = true) {
|
||||
val recipientIdForThreadId = getRecipientIdForThreadId(threadId)
|
||||
|
||||
var addressableMessages: Set<MessageRecord> = emptySet()
|
||||
writableDatabase.withinTransaction { db ->
|
||||
if (syncThreadDeletes && RemoteConfig.deleteSyncEnabled) {
|
||||
addressableMessages = messages.getMostRecentAddressableMessages(threadId)
|
||||
}
|
||||
|
||||
messages.deleteThread(threadId)
|
||||
drafts.clearDrafts(threadId)
|
||||
db.deactivateThread(threadId)
|
||||
synchronized(threadIdCache) {
|
||||
threadIdCache.remove(recipientIdForThreadId)
|
||||
}
|
||||
}
|
||||
|
||||
if (syncThreadDeletes) {
|
||||
MultiDeviceDeleteSendSyncJob.enqueueThreadDeletes(listOf(threadId to addressableMessages), isFullDelete = true)
|
||||
}
|
||||
|
||||
notifyConversationListListeners()
|
||||
notifyConversationListeners(threadId)
|
||||
AppDependencies.databaseObserver.notifyConversationDeleteListeners(threadId)
|
||||
ConversationUtil.clearShortcuts(context, setOf(recipientIdForThreadId))
|
||||
fun deleteConversation(threadId: Long, syncThreadDelete: Boolean = true) {
|
||||
deleteConversations(setOf(threadId), syncThreadDelete)
|
||||
}
|
||||
|
||||
fun deleteConversations(selectedConversations: Set<Long>) {
|
||||
fun deleteConversations(selectedConversations: Set<Long>, syncThreadDeletes: Boolean = true) {
|
||||
val recipientIds = getRecipientIdsForThreadIds(selectedConversations)
|
||||
|
||||
val addressableMessages = mutableListOf<Pair<Long, Set<MessageRecord>>>()
|
||||
|
||||
val queries: List<SqlUtil.Query> = SqlUtil.buildCollectionQuery(ID, selectedConversations)
|
||||
writableDatabase.withinTransaction { db ->
|
||||
for (query in queries) {
|
||||
db.deactivateThread(query)
|
||||
}
|
||||
|
||||
if (RemoteConfig.deleteSyncEnabled) {
|
||||
if (syncThreadDeletes && Recipient.self().deleteSyncCapability.isSupported) {
|
||||
for (threadId in selectedConversations) {
|
||||
addressableMessages += threadId to messages.getMostRecentAddressableMessages(threadId)
|
||||
}
|
||||
}
|
||||
|
||||
for (query in queries) {
|
||||
db.deactivateThread(query)
|
||||
}
|
||||
|
||||
messages.deleteAbandonedMessages()
|
||||
attachments.trimAllAbandonedAttachments()
|
||||
groupReceipts.deleteAbandonedRows()
|
||||
@@ -1183,12 +1159,19 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
}
|
||||
}
|
||||
|
||||
MultiDeviceDeleteSendSyncJob.enqueueThreadDeletes(addressableMessages, isFullDelete = true)
|
||||
if (syncThreadDeletes) {
|
||||
MultiDeviceDeleteSendSyncJob.enqueueThreadDeletes(addressableMessages, isFullDelete = true)
|
||||
}
|
||||
|
||||
notifyConversationListListeners()
|
||||
notifyConversationListeners(selectedConversations)
|
||||
notifyStickerListeners()
|
||||
notifyStickerPackListeners()
|
||||
AppDependencies.databaseObserver.notifyConversationDeleteListeners(selectedConversations)
|
||||
|
||||
ConversationUtil.clearShortcuts(context, recipientIds)
|
||||
|
||||
OptimizeMessageSearchIndexJob.enqueue()
|
||||
}
|
||||
|
||||
@SuppressLint("DiscouragedApi")
|
||||
@@ -1485,12 +1468,13 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
.run()
|
||||
}
|
||||
|
||||
fun update(threadId: Long, unarchive: Boolean): Boolean {
|
||||
fun update(threadId: Long, unarchive: Boolean, syncThreadDelete: Boolean = true): Boolean {
|
||||
return update(
|
||||
threadId = threadId,
|
||||
unarchive = unarchive,
|
||||
allowDeletion = true,
|
||||
notifyListeners = true
|
||||
notifyListeners = true,
|
||||
syncThreadDelete = syncThreadDelete
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1499,16 +1483,18 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
threadId = threadId,
|
||||
unarchive = unarchive,
|
||||
allowDeletion = true,
|
||||
notifyListeners = false
|
||||
notifyListeners = false,
|
||||
syncThreadDelete = true
|
||||
)
|
||||
}
|
||||
|
||||
fun update(threadId: Long, unarchive: Boolean, allowDeletion: Boolean): Boolean {
|
||||
fun update(threadId: Long, unarchive: Boolean, allowDeletion: Boolean, syncThreadDelete: Boolean = true): Boolean {
|
||||
return update(
|
||||
threadId = threadId,
|
||||
unarchive = unarchive,
|
||||
allowDeletion = allowDeletion,
|
||||
notifyListeners = true
|
||||
notifyListeners = true,
|
||||
syncThreadDelete = syncThreadDelete
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1543,7 +1529,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
stopwatch?.split("thread-update")
|
||||
}
|
||||
|
||||
private fun update(threadId: Long, unarchive: Boolean, allowDeletion: Boolean, notifyListeners: Boolean): Boolean {
|
||||
private fun update(threadId: Long, unarchive: Boolean, allowDeletion: Boolean, notifyListeners: Boolean, syncThreadDelete: Boolean): Boolean {
|
||||
if (threadId == -1L) {
|
||||
Log.d(TAG, "Skipping update for threadId -1")
|
||||
return false
|
||||
@@ -1558,7 +1544,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
if (!meaningfulMessages) {
|
||||
if (shouldDelete) {
|
||||
Log.d(TAG, "Deleting thread $threadId because it has no meaningful messages.")
|
||||
deleteConversation(threadId)
|
||||
deleteConversation(threadId, syncThreadDelete = syncThreadDelete)
|
||||
return@withinTransaction true
|
||||
} else if (!isPinned) {
|
||||
return@withinTransaction false
|
||||
|
||||
@@ -823,6 +823,13 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||
return revisionNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* A message that can be correctly identified and delete sync'd across devices.
|
||||
*/
|
||||
public boolean canDeleteSync() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final class InviteAddState {
|
||||
|
||||
private final boolean invited;
|
||||
|
||||
@@ -308,6 +308,19 @@ public class MmsMessageRecord extends MessageRecord {
|
||||
return latestRevisionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDeleteSync() {
|
||||
return (isSent() || MessageTypes.isInboxType(type)) &&
|
||||
(isSecure() || isPush()) &&
|
||||
(type & MessageTypes.GROUP_MASK) == 0 &&
|
||||
(type & MessageTypes.KEY_EXCHANGE_MASK) == 0 &&
|
||||
!isReportedSpam() &&
|
||||
!isMessageRequestAccepted() &&
|
||||
storyType == StoryType.NONE &&
|
||||
getDateSent() > 0 &&
|
||||
(parentStoryId == null || parentStoryId.isDirectReply());
|
||||
}
|
||||
|
||||
public @NonNull MmsMessageRecord withReactions(@NonNull List<ReactionRecord> reactions) {
|
||||
return new MmsMessageRecord(getId(), getFromRecipient(), getFromDeviceId(), getToRecipient(), getDateSent(), getDateReceived(), getServerTimestamp(), hasDeliveryReceipt(), getThreadId(), getBody(), getSlideDeck(),
|
||||
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
|
||||
|
||||
@@ -119,12 +119,14 @@ data class RecipientRecord(
|
||||
|
||||
data class Capabilities(
|
||||
val rawBits: Long,
|
||||
val paymentActivation: Recipient.Capability
|
||||
val paymentActivation: Recipient.Capability,
|
||||
val deleteSync: Recipient.Capability
|
||||
) {
|
||||
companion object {
|
||||
@JvmField
|
||||
val UNKNOWN = Capabilities(
|
||||
0,
|
||||
Recipient.Capability.UNKNOWN,
|
||||
Recipient.Capability.UNKNOWN
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1240,7 +1240,7 @@ final class GroupManagerV2 {
|
||||
try {
|
||||
long messageId = SignalDatabase.messages().insertMessageOutbox(outgoingMessage, threadId, false, null);
|
||||
SignalDatabase.messages().markAsSent(messageId, true);
|
||||
SignalDatabase.threads().update(threadId, true);
|
||||
SignalDatabase.threads().update(threadId, true, true);
|
||||
} catch (MmsException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,7 @@ import org.thoughtcrime.securesms.jobs.protos.DeleteSyncJobData.AddressableMessa
|
||||
import org.thoughtcrime.securesms.jobs.protos.DeleteSyncJobData.ThreadDelete
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.pad
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.Recipient.Companion.self
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
@@ -58,13 +56,18 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
return
|
||||
}
|
||||
|
||||
if (!RemoteConfig.deleteSyncEnabled) {
|
||||
if (!Recipient.self().deleteSyncCapability.isSupported) {
|
||||
Log.i(TAG, "Delete sync support not enabled.")
|
||||
return
|
||||
}
|
||||
|
||||
messageRecords.chunked(CHUNK_SIZE).forEach { chunk ->
|
||||
AppDependencies.jobManager.add(createMessageDeletes(chunk))
|
||||
val deletes = createMessageDeletes(chunk)
|
||||
if (deletes.isNotEmpty()) {
|
||||
AppDependencies.jobManager.add(MultiDeviceDeleteSendSyncJob(messages = deletes))
|
||||
} else {
|
||||
Log.i(TAG, "No valid message deletes to sync")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,20 +77,29 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
return
|
||||
}
|
||||
|
||||
if (!RemoteConfig.deleteSyncEnabled) {
|
||||
if (!Recipient.self().deleteSyncCapability.isSupported) {
|
||||
Log.i(TAG, "Delete sync support not enabled.")
|
||||
return
|
||||
}
|
||||
|
||||
threads.chunked(THREAD_CHUNK_SIZE).forEach { chunk ->
|
||||
AppDependencies.jobManager.add(createThreadDeletes(chunk, isFullDelete))
|
||||
val threadDeletes = createThreadDeletes(chunk, isFullDelete)
|
||||
if (threadDeletes.isNotEmpty()) {
|
||||
AppDependencies.jobManager.add(
|
||||
MultiDeviceDeleteSendSyncJob(
|
||||
threads = threadDeletes.filter { it.messages.isNotEmpty() },
|
||||
localOnlyThreads = threadDeletes.filter { it.messages.isEmpty() }
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Log.i(TAG, "No valid thread deletes to sync")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@VisibleForTesting
|
||||
fun createMessageDeletes(messageRecords: Collection<MessageRecord>): MultiDeviceDeleteSendSyncJob {
|
||||
val deletes = messageRecords.mapNotNull { message ->
|
||||
private fun createMessageDeletes(messageRecords: Collection<MessageRecord>): List<AddressableMessage> {
|
||||
return messageRecords.mapNotNull { message ->
|
||||
val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(message.threadId)
|
||||
if (threadRecipient == null) {
|
||||
Log.w(TAG, "Unable to find thread recipient for message: ${message.id} thread: ${message.threadId}")
|
||||
@@ -95,6 +107,8 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
} else if (threadRecipient.isReleaseNotes) {
|
||||
Log.w(TAG, "Syncing release channel deletes are not currently supported")
|
||||
null
|
||||
} else if (threadRecipient.isDistributionList || !message.canDeleteSync()) {
|
||||
null
|
||||
} else {
|
||||
AddressableMessage(
|
||||
threadRecipientId = threadRecipient.id.toLong(),
|
||||
@@ -103,14 +117,11 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return MultiDeviceDeleteSendSyncJob(messages = deletes)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@VisibleForTesting
|
||||
fun createThreadDeletes(threads: List<Pair<Long, Set<MessageRecord>>>, isFullDelete: Boolean): MultiDeviceDeleteSendSyncJob {
|
||||
val threadDeletes: List<ThreadDelete> = threads.mapNotNull { (threadId, messages) ->
|
||||
private fun createThreadDeletes(threads: List<Pair<Long, Set<MessageRecord>>>, isFullDelete: Boolean): List<ThreadDelete> {
|
||||
return threads.mapNotNull { (threadId, messages) ->
|
||||
val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(threadId)
|
||||
if (threadRecipient == null) {
|
||||
Log.w(TAG, "Unable to find thread recipient for thread: $threadId")
|
||||
@@ -118,6 +129,8 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
} else if (threadRecipient.isReleaseNotes) {
|
||||
Log.w(TAG, "Syncing release channel delete is not currently supported")
|
||||
null
|
||||
} else if (threadRecipient.isDistributionList) {
|
||||
null
|
||||
} else {
|
||||
ThreadDelete(
|
||||
threadRecipientId = threadRecipient.id.toLong(),
|
||||
@@ -131,11 +144,6 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return MultiDeviceDeleteSendSyncJob(
|
||||
threads = threadDeletes.filter { it.messages.isNotEmpty() },
|
||||
localOnlyThreads = threadDeletes.filter { it.messages.isEmpty() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +165,7 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
override fun getFactoryKey(): String = KEY
|
||||
|
||||
override fun run(): Result {
|
||||
if (!self().isRegistered) {
|
||||
if (!Recipient.self().isRegistered) {
|
||||
Log.w(TAG, "Not registered")
|
||||
return Result.failure()
|
||||
}
|
||||
@@ -250,6 +258,7 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
val syncMessageContent = deleteForMeContent(deleteForMe)
|
||||
|
||||
return try {
|
||||
Log.d(TAG, "Sending delete sync messageDeletes=${deleteForMe.messageDeletes.size} conversationDeletes=${deleteForMe.conversationDeletes.size} localOnlyConversationDeletes=${deleteForMe.localOnlyConversationDeletes.size}")
|
||||
AppDependencies.signalServiceMessageSender.sendSyncMessage(syncMessageContent, true, Optional.empty()).isSuccess
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "Unable to send message delete sync", e)
|
||||
@@ -271,7 +280,8 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
private fun Recipient.toDeleteSyncConversationId(): DeleteForMe.ConversationIdentifier? {
|
||||
return when {
|
||||
isGroup -> DeleteForMe.ConversationIdentifier(threadGroupId = requireGroupId().decodedId.toByteString())
|
||||
hasAci -> DeleteForMe.ConversationIdentifier(threadAci = requireAci().toString())
|
||||
hasAci -> DeleteForMe.ConversationIdentifier(threadServiceId = requireAci().toString())
|
||||
hasPni -> DeleteForMe.ConversationIdentifier(threadServiceId = requirePni().toString())
|
||||
hasE164 -> DeleteForMe.ConversationIdentifier(threadE164 = requireE164())
|
||||
else -> null
|
||||
}
|
||||
@@ -279,19 +289,19 @@ class MultiDeviceDeleteSendSyncJob private constructor(
|
||||
|
||||
private fun AddressableMessage.toDeleteSyncMessage(): DeleteForMe.AddressableMessage? {
|
||||
val author: Recipient = Recipient.resolved(RecipientId.from(authorRecipientId))
|
||||
val authorAci: String? = author.aci.orNull()?.toString()
|
||||
val authorE164: String? = if (authorAci == null) {
|
||||
val authorServiceId: String? = author.aci.orNull()?.toString() ?: author.pni.orNull()?.toString()
|
||||
val authorE164: String? = if (authorServiceId == null) {
|
||||
author.e164.orNull()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return if (authorAci == null && authorE164 == null) {
|
||||
Log.w(TAG, "Unable to send sync message without aci and e164 recipient: ${author.id}")
|
||||
return if (authorServiceId == null && authorE164 == null) {
|
||||
Log.w(TAG, "Unable to send sync message without serviceId or e164 recipient: ${author.id}")
|
||||
null
|
||||
} else {
|
||||
DeleteForMe.AddressableMessage(
|
||||
authorAci = authorAci,
|
||||
authorServiceId = authorServiceId,
|
||||
authorE164 = authorE164,
|
||||
sentTimestamp = sentTimestamp
|
||||
)
|
||||
|
||||
@@ -55,7 +55,7 @@ public final class ThreadUpdateJob extends BaseJob {
|
||||
|
||||
@Override
|
||||
protected void onRun() throws Exception {
|
||||
SignalDatabase.threads().update(threadId, true);
|
||||
SignalDatabase.threads().update(threadId, true, true);
|
||||
if (!AppDependencies.getIncomingMessageObserver().getDecryptionDrained()) {
|
||||
ThreadUtil.sleep(DEBOUNCE_INTERVAL_WITH_BACKLOG);
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadTable;
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
|
||||
import org.thoughtcrime.securesms.keyvalue.KeepMessagesDuration;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
public class TrimThreadJob extends BaseJob {
|
||||
|
||||
@@ -78,7 +78,7 @@ public class TrimThreadJob extends BaseJob {
|
||||
long trimBeforeDate = keepMessagesDuration != KeepMessagesDuration.FOREVER ? System.currentTimeMillis() - keepMessagesDuration.getDuration()
|
||||
: ThreadTable.NO_TRIM_BEFORE_DATE_SET;
|
||||
|
||||
SignalDatabase.threads().trimThread(threadId, SignalStore.settings().shouldSyncThreadTrimDeletes() && RemoteConfig.deleteSyncEnabled(), trimLength, trimBeforeDate, false);
|
||||
SignalDatabase.threads().trimThread(threadId, SignalStore.settings().shouldSyncThreadTrimDeletes() && Recipient.self().getDeleteSyncCapability().isSupported(), trimLength, trimBeforeDate, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,8 +16,8 @@ import org.thoughtcrime.securesms.database.MediaTable;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceDeleteSendSyncJob;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.StorageUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
@@ -65,7 +65,7 @@ final class MediaActions {
|
||||
recordCount);
|
||||
|
||||
String confirmMessage;
|
||||
if (TextSecurePreferences.isMultiDevice(context) && RemoteConfig.deleteSyncEnabled()) {
|
||||
if (TextSecurePreferences.isMultiDevice(context) && Recipient.self().getDeleteSyncCapability().isSupported()) {
|
||||
confirmMessage = res.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_message_linked_device,
|
||||
recordCount,
|
||||
recordCount);
|
||||
@@ -98,7 +98,7 @@ final class MediaActions {
|
||||
}
|
||||
}
|
||||
|
||||
if (RemoteConfig.deleteSyncEnabled() && Util.hasItems(deletedMessageRecords)) {
|
||||
if (Recipient.self().getDeleteSyncCapability().isSupported() && Util.hasItems(deletedMessageRecords)) {
|
||||
MultiDeviceDeleteSendSyncJob.enqueueMessageDeletes(deletedMessageRecords);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceDeleteSendSyncJob
|
||||
import org.thoughtcrime.securesms.longmessage.resolveBody
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
|
||||
/**
|
||||
* Repository for accessing the attachments in the encrypted database.
|
||||
@@ -85,7 +85,7 @@ class MediaPreviewRepository {
|
||||
fun localDelete(attachment: DatabaseAttachment): Completable {
|
||||
return Completable.fromRunnable {
|
||||
val deletedMessageRecord = AttachmentUtil.deleteAttachment(attachment)
|
||||
if (deletedMessageRecord != null && RemoteConfig.deleteSyncEnabled) {
|
||||
if (deletedMessageRecord != null && Recipient.self().deleteSyncCapability.isSupported) {
|
||||
MultiDeviceDeleteSendSyncJob.enqueueMessageDeletes(setOf(deletedMessageRecord))
|
||||
}
|
||||
}.subscribeOn(Schedulers.io())
|
||||
|
||||
@@ -70,7 +70,6 @@ import org.thoughtcrime.securesms.util.Debouncer
|
||||
import org.thoughtcrime.securesms.util.FullscreenHelper
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.util.MessageConstraintsUtil
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask
|
||||
import org.thoughtcrime.securesms.util.SpanUtil
|
||||
import org.thoughtcrime.securesms.util.StorageUtil
|
||||
@@ -601,7 +600,7 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
|
||||
MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setIcon(R.drawable.symbol_error_triangle_fill_24)
|
||||
setTitle(R.string.MediaPreviewActivity_media_delete_confirmation_title)
|
||||
setMessage(if (TextSecurePreferences.isMultiDevice(requireContext()) && RemoteConfig.deleteSyncEnabled) R.string.MediaPreviewActivity_media_delete_confirmation_message_linked_device else R.string.MediaPreviewActivity_media_delete_confirmation_message)
|
||||
setMessage(if (TextSecurePreferences.isMultiDevice(requireContext()) && Recipient.self().deleteSyncCapability.isSupported) R.string.MediaPreviewActivity_media_delete_confirmation_message_linked_device else R.string.MediaPreviewActivity_media_delete_confirmation_message)
|
||||
setCancelable(true)
|
||||
setNegativeButton(android.R.string.cancel, null)
|
||||
setPositiveButton(R.string.ConversationFragment_delete_for_me) { _, _ ->
|
||||
|
||||
@@ -274,7 +274,7 @@ public final class MessageRequestRepository {
|
||||
}
|
||||
|
||||
ThreadTable threadTable = SignalDatabase.threads();
|
||||
threadTable.deleteConversation(threadId);
|
||||
threadTable.deleteConversation(threadId, false);
|
||||
|
||||
onMessageRequestDeleted.run();
|
||||
});
|
||||
|
||||
@@ -1103,7 +1103,7 @@ object SyncMessageProcessor {
|
||||
MessageRequestResponse.Type.DELETE -> {
|
||||
SignalDatabase.recipients.setProfileSharing(recipient.id, false)
|
||||
if (threadId > 0) {
|
||||
SignalDatabase.threads.deleteConversation(threadId)
|
||||
SignalDatabase.threads.deleteConversation(threadId, syncThreadDelete = false)
|
||||
}
|
||||
}
|
||||
MessageRequestResponse.Type.BLOCK -> {
|
||||
@@ -1114,7 +1114,7 @@ object SyncMessageProcessor {
|
||||
SignalDatabase.recipients.setBlocked(recipient.id, true)
|
||||
SignalDatabase.recipients.setProfileSharing(recipient.id, false)
|
||||
if (threadId > 0) {
|
||||
SignalDatabase.threads.deleteConversation(threadId)
|
||||
SignalDatabase.threads.deleteConversation(threadId, syncThreadDelete = false)
|
||||
}
|
||||
}
|
||||
MessageRequestResponse.Type.SPAM -> {
|
||||
@@ -1475,11 +1475,6 @@ object SyncMessageProcessor {
|
||||
}
|
||||
|
||||
private fun handleSynchronizeDeleteForMe(context: Context, deleteForMe: SyncMessage.DeleteForMe, envelopeTimestamp: Long, earlyMessageCacheEntry: EarlyMessageCacheEntry?) {
|
||||
if (!RemoteConfig.deleteSyncEnabled) {
|
||||
warn(envelopeTimestamp, "Delete for me sync message dropped as support not enabled")
|
||||
return
|
||||
}
|
||||
|
||||
log(envelopeTimestamp, "Synchronize delete message messageDeletes=${deleteForMe.messageDeletes.size} conversationDeletes=${deleteForMe.conversationDeletes.size} localOnlyConversationDeletes=${deleteForMe.localOnlyConversationDeletes.size}")
|
||||
|
||||
if (deleteForMe.messageDeletes.isNotEmpty()) {
|
||||
@@ -1586,8 +1581,8 @@ object SyncMessageProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
threadAci != null -> {
|
||||
ServiceId.parseOrNull(threadAci)?.let {
|
||||
threadServiceId != null -> {
|
||||
ServiceId.parseOrNull(threadServiceId)?.let {
|
||||
SignalDatabase.recipients.getOrInsertFromServiceId(it)
|
||||
}
|
||||
}
|
||||
@@ -1601,8 +1596,8 @@ object SyncMessageProcessor {
|
||||
}
|
||||
|
||||
private fun SyncMessage.DeleteForMe.AddressableMessage.toSyncMessageId(envelopeTimestamp: Long): MessageTable.SyncMessageId? {
|
||||
return if (this.sentTimestamp != null && (this.authorAci != null || this.authorE164 != null)) {
|
||||
val serviceId = ServiceId.parseOrNull(this.authorAci)
|
||||
return if (this.sentTimestamp != null && (this.authorServiceId != null || this.authorE164 != null)) {
|
||||
val serviceId = ServiceId.parseOrNull(this.authorServiceId)
|
||||
val id = if (serviceId != null) {
|
||||
SignalDatabase.recipients.getOrInsertFromServiceId(serviceId)
|
||||
} else {
|
||||
|
||||
@@ -87,7 +87,7 @@ class ReviewCardRepository {
|
||||
ThreadTable threadTable = SignalDatabase.threads();
|
||||
long threadId = Objects.requireNonNull(threadTable.getThreadIdFor(recipientId));
|
||||
|
||||
threadTable.deleteConversation(threadId);
|
||||
threadTable.deleteConversation(threadId, false);
|
||||
onActionCompleteListener.run();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -317,6 +317,9 @@ class Recipient(
|
||||
/** The user's payment capability. */
|
||||
val paymentActivationCapability: Capability = capabilities.paymentActivation
|
||||
|
||||
/** The user's payment capability. */
|
||||
val deleteSyncCapability: Capability = capabilities.deleteSync
|
||||
|
||||
/** The state around whether we can send sealed sender to this user. */
|
||||
val unidentifiedAccessMode: UnidentifiedAccessMode = if (pni.isPresent && pni == serviceId) {
|
||||
UnidentifiedAccessMode.DISABLED
|
||||
|
||||
@@ -210,7 +210,7 @@ public class MessageSender {
|
||||
onMessageSent();
|
||||
|
||||
for (long threadId : threads) {
|
||||
threadTable.update(threadId, true);
|
||||
threadTable.update(threadId, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ public class MessageSender {
|
||||
|
||||
sendMessageInternal(context, recipient, sendType, messageId, Collections.emptyList(), message.getScheduledDate() > 0);
|
||||
onMessageSent();
|
||||
threadTable.update(allocatedThreadId, true);
|
||||
threadTable.update(allocatedThreadId, true, true);
|
||||
|
||||
return allocatedThreadId;
|
||||
} catch (MmsException e) {
|
||||
@@ -279,7 +279,7 @@ public class MessageSender {
|
||||
|
||||
sendMessageInternal(context, recipient, SendType.SIGNAL, messageId, jobIds, false);
|
||||
onMessageSent();
|
||||
threadTable.update(allocatedThreadId, true);
|
||||
threadTable.update(allocatedThreadId, true, true);
|
||||
|
||||
return allocatedThreadId;
|
||||
} catch (MmsException e) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceDeleteSendSyncJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask
|
||||
|
||||
@@ -45,13 +46,24 @@ object DeleteDialog {
|
||||
if (forceRemoteDelete) {
|
||||
builder.setPositiveButton(R.string.ConversationFragment_delete_for_everyone) { _, _ -> deleteForEveryone(messageRecords, emitter) }
|
||||
} else {
|
||||
builder.setPositiveButton(if (isNoteToSelfDelete) R.string.ConversationFragment_delete_on_this_device else R.string.ConversationFragment_delete_for_me) { _, _ ->
|
||||
val deleteSyncEnabled = Recipient.self().deleteSyncCapability.isSupported
|
||||
|
||||
val positiveButton = if (isNoteToSelfDelete) {
|
||||
if (deleteSyncEnabled) R.string.ConversationFragment_delete else R.string.ConversationFragment_delete_on_this_device
|
||||
} else {
|
||||
R.string.ConversationFragment_delete_for_me
|
||||
}
|
||||
|
||||
builder.setPositiveButton(positiveButton) { _, _ ->
|
||||
DeleteProgressDialogAsyncTask(context, messageRecords) {
|
||||
emitter.onSuccess(Pair(true, it))
|
||||
}.executeOnExecutor(SignalExecutors.BOUNDED)
|
||||
}
|
||||
|
||||
if (MessageConstraintsUtil.isValidRemoteDeleteSend(messageRecords, System.currentTimeMillis()) && (!isNoteToSelfDelete || TextSecurePreferences.isMultiDevice(context))) {
|
||||
val canDeleteForEveryone = MessageConstraintsUtil.isValidRemoteDeleteSend(messageRecords, System.currentTimeMillis()) && !isNoteToSelfDelete
|
||||
val canDeleteForEveryoneInNoteToSelf = isNoteToSelfDelete && TextSecurePreferences.isMultiDevice(context) && !deleteSyncEnabled
|
||||
|
||||
if (canDeleteForEveryone || canDeleteForEveryoneInNoteToSelf) {
|
||||
builder.setNeutralButton(if (isNoteToSelfDelete) R.string.ConversationFragment_delete_everywhere else R.string.ConversationFragment_delete_for_everyone) { _, _ -> handleDeleteForEveryone(context, messageRecords, emitter) }
|
||||
}
|
||||
}
|
||||
@@ -109,7 +121,7 @@ object DeleteDialog {
|
||||
}
|
||||
}
|
||||
|
||||
if (RemoteConfig.deleteSyncEnabled) {
|
||||
if (Recipient.self().deleteSyncCapability.isSupported) {
|
||||
MultiDeviceDeleteSendSyncJob.enqueueMessageDeletes(messageRecords)
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public final class IdentityUtil {
|
||||
} catch (MmsException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
SignalDatabase.threads().update(threadId, true);
|
||||
SignalDatabase.threads().update(threadId, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ public final class IdentityUtil {
|
||||
} catch (MmsException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
SignalDatabase.threads().update(threadId, true);
|
||||
SignalDatabase.threads().update(threadId, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import org.signal.core.util.mebiBytes
|
||||
import org.thoughtcrime.securesms.BuildConfig
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
|
||||
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob
|
||||
import org.thoughtcrime.securesms.jobs.RemoteConfigRefreshJob
|
||||
import org.thoughtcrime.securesms.jobs.Svr3MirrorJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
@@ -1070,12 +1072,13 @@ object RemoteConfig {
|
||||
)
|
||||
|
||||
/** Whether or not to delete syncing is enabled. */
|
||||
@JvmStatic
|
||||
@get:JvmName("deleteSyncEnabled")
|
||||
val deleteSyncEnabled: Boolean by remoteBoolean(
|
||||
key = "android.deleteSyncSendReceive",
|
||||
key = "android.deleteSyncEnabled",
|
||||
defaultValue = false,
|
||||
hotSwappable = true
|
||||
hotSwappable = true,
|
||||
onChangeListener = {
|
||||
AppDependencies.jobManager.startChain(RefreshAttributesJob()).then(RefreshOwnProfileJob()).enqueue()
|
||||
}
|
||||
)
|
||||
|
||||
/** Which phase we're in for the SVR3 migration */
|
||||
|
||||
Reference in New Issue
Block a user