diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index ed14b2fa4c..83390906e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -300,6 +300,9 @@ class ConversationSettingsFragment : DSLSettingsFragment( recipient = state.recipient, onDisableProfileSharingClick = { viewModel.disableProfileSharing() + }, + onDeleteSessionClick = { + viewModel.deleteSession() } ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt index 44eb19b304..742f00f311 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt @@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientUtil import org.thoughtcrime.securesms.util.FeatureFlags import org.whispersystems.libsignal.util.guava.Optional +import org.whispersystems.libsignal.util.guava.Preconditions import java.io.IOException private val TAG = Log.tag(ConversationSettingsRepository::class.java) @@ -185,12 +186,22 @@ class ConversationSettingsRepository( } } - fun disableProfileSharing(recipientId: RecipientId) { + fun disableProfileSharingForInternalUser(recipientId: RecipientId) { + Preconditions.checkArgument(FeatureFlags.internalUser(), "Internal users only!"); + SignalExecutors.BOUNDED.execute { DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipientId, false) } } + fun deleteSessionForInternalUser(recipientId: RecipientId) { + Preconditions.checkArgument(FeatureFlags.internalUser(), "Internal users only!"); + + SignalExecutors.BOUNDED.execute { + DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId) + } + } + @WorkerThread fun isMessageRequestAccepted(recipient: Recipient): Boolean { return RecipientUtil.isMessageRequestAccepted(context, recipient) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt index 059aefedd0..17b90b6d3d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt @@ -116,6 +116,8 @@ sealed class ConversationSettingsViewModel( open fun disableProfileSharing(): Unit = error("This ViewModel does not support this interaction") + open fun deleteSession(): Unit = error("This ViewModel does not support this interaction") + open fun initiateGroupUpgrade(): Unit = error("This ViewModel does not support this interaction") private class RecipientSettingsViewModel( @@ -237,7 +239,11 @@ sealed class ConversationSettingsViewModel( } override fun disableProfileSharing() { - repository.disableProfileSharing(recipientId) + repository.disableProfileSharingForInternalUser(recipientId) + } + + override fun deleteSession() { + repository.deleteSessionForInternalUser(recipientId) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/InternalPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/InternalPreference.kt index 1c139dc30f..9396595969 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/InternalPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/InternalPreference.kt @@ -19,7 +19,8 @@ object InternalPreference { class Model( private val recipient: Recipient, - val onDisableProfileSharingClick: () -> Unit + val onDisableProfileSharingClick: () -> Unit, + val onDeleteSessionClick: () -> Unit ) : PreferenceModel() { val body: String get() { @@ -58,10 +59,12 @@ object InternalPreference { private val body: TextView = itemView.findViewById(R.id.internal_preference_body) private val disableProfileSharing: View = itemView.findViewById(R.id.internal_disable_profile_sharing) + private val deleteSession: View = itemView.findViewById(R.id.internal_delete_session) override fun bind(model: Model) { body.text = model.body disableProfileSharing.setOnClickListener { model.onDisableProfileSharingClick() } + deleteSession.setOnClickListener { model.onDeleteSessionClick() } } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java index 99004c497e..4be2b23aee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java @@ -131,7 +131,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns public abstract List setEntireThreadRead(long threadId); public abstract List setMessagesReadSince(long threadId, long timestamp); public abstract List setAllMessagesRead(); - public abstract Pair updateBundleMessageBody(long messageId, String body); + public abstract InsertResult updateBundleMessageBody(long messageId, String body); public abstract @NonNull List getViewedIncomingMessages(long threadId); public abstract @Nullable MarkedMessageInfo setIncomingMessageViewed(long messageId); public abstract @NonNull List setIncomingMessagesViewed(@NonNull List messageIds); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt index 7d021ca5b8..326d5d5d7a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogDatabase.kt @@ -142,12 +142,12 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC ) } - fun insertIfPossible(recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, relatedMessageId: Long, isRelatedMessageMms: Boolean) { + fun insertIfPossible(recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, messageId: MessageId) { if (!FeatureFlags.senderKey()) return if (sendMessageResult.isSuccess && sendMessageResult.success.content.isPresent) { val recipientDevice = listOf(RecipientDevice(recipientId, sendMessageResult.success.devices)) - insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, listOf(MessageId(relatedMessageId, isRelatedMessageMms))) + insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, listOf(messageId)) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index b76f607b9d..bed7f9de6c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -380,7 +380,7 @@ public class MmsDatabase extends MessageDatabase { } @Override - public Pair updateBundleMessageBody(long messageId, String body) { + public InsertResult updateBundleMessageBody(long messageId, String body) { throw new UnsupportedOperationException(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 8c20e32b0f..73e2fba7a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -664,7 +664,7 @@ public class SmsDatabase extends MessageDatabase { } @Override - public Pair updateBundleMessageBody(long messageId, String body) { + public InsertResult updateBundleMessageBody(long messageId, String body) { long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT; return updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, type); } @@ -684,7 +684,7 @@ public class SmsDatabase extends MessageDatabase { return Collections.emptyList(); } - private Pair updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) { + private InsertResult updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " + @@ -697,7 +697,7 @@ public class SmsDatabase extends MessageDatabase { notifyConversationListeners(threadId); notifyConversationListListeners(); - return new Pair<>(messageId, threadId); + return new InsertResult(messageId, threadId); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageId.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageId.kt index 4186c9d973..c72f82b37f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageId.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageId.kt @@ -7,4 +7,16 @@ package org.thoughtcrime.securesms.database.model data class MessageId( val id: Long, @get:JvmName("isMms") val mms: Boolean -) +) { + fun serialize(): String { + return "$id|$mms" + } + + companion object { + @JvmStatic + fun deserialize(serialized: String): MessageId { + val parts: List = serialized.split("|") + return MessageId(parts[0].toLong(), parts[1].toBoolean()) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java index 8663cc6f8f..a6b44aa98d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.database.MessageDatabase; import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; +import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; @@ -224,11 +225,11 @@ public class PushMediaSendJob extends PushSendJob { SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess); SendMessageResult result = messageSender.sendSyncMessage(syncMessage, syncAccess); - DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, messageId, true); + DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true)); return syncAccess.isPresent(); } else { SendMessageResult result = messageSender.sendDataMessage(address, UnidentifiedAccessUtil.getAccessFor(context, messageRecipient), ContentHint.RESENDABLE, mediaMessage); - DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, messageId, true); + DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true)); return result.getSuccess().isUnidentified(); } } catch (UnregisteredUserException e) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index 700df41028..3dac31c1c8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.database.MessageDatabase; import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; +import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobmanager.Data; @@ -176,11 +177,11 @@ public class PushTextSendJob extends PushSendJob { SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess); SendMessageResult result = messageSender.sendSyncMessage(syncMessage, syncAccess); - DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, messageId, false); + DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false)); return syncAccess.isPresent(); } else { SendMessageResult result = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, textSecureMessage); - DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, messageId, false); + DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false)); return result.getSuccess().isUnidentified(); } } catch (UnregisteredUserException e) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java index 1a4c0e3445..02e0efa4e9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java @@ -2,9 +2,12 @@ package org.thoughtcrime.securesms.jobs; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; @@ -15,7 +18,9 @@ import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.whispersystems.signalservice.api.SignalServiceMessageSender; +import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; +import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; @@ -29,17 +34,21 @@ public class SendDeliveryReceiptJob extends BaseJob { public static final String KEY = "SendDeliveryReceiptJob"; - private static final String KEY_RECIPIENT = "recipient"; - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_TIMESTAMP = "timestamp"; + private static final String KEY_RECIPIENT = "recipient"; + private static final String KEY_MESSAGE_SENT_TIMESTAMP = "message_id"; + private static final String KEY_TIMESTAMP = "timestamp"; + private static final String KEY_MESSAGE_ID = "message_db_id"; private static final String TAG = Log.tag(SendReadReceiptJob.class); - private RecipientId recipientId; - private long messageId; - private long timestamp; + private final RecipientId recipientId; + private final long messageSentTimestamp; + private final long timestamp; - public SendDeliveryReceiptJob(@NonNull RecipientId recipientId, long messageId) { + @Nullable + private final MessageId messageId; + + public SendDeliveryReceiptJob(@NonNull RecipientId recipientId, long messageSentTimestamp, @NonNull MessageId messageId) { this(new Job.Parameters.Builder() .addConstraint(NetworkConstraint.KEY) .setLifespan(TimeUnit.DAYS.toMillis(1)) @@ -47,28 +56,36 @@ public class SendDeliveryReceiptJob extends BaseJob { .setQueue(recipientId.toQueueKey()) .build(), recipientId, + messageSentTimestamp, messageId, System.currentTimeMillis()); } private SendDeliveryReceiptJob(@NonNull Job.Parameters parameters, @NonNull RecipientId recipientId, - long messageId, + long messageSentTimestamp, + @Nullable MessageId messageId, long timestamp) { super(parameters); - this.recipientId = recipientId; - this.messageId = messageId; - this.timestamp = timestamp; + this.recipientId = recipientId; + this.messageSentTimestamp = messageSentTimestamp; + this.messageId = messageId; + this.timestamp = timestamp; } @Override public @NonNull Data serialize() { - return new Data.Builder().putString(KEY_RECIPIENT, recipientId.serialize()) - .putLong(KEY_MESSAGE_ID, messageId) - .putLong(KEY_TIMESTAMP, timestamp) - .build(); + Data.Builder builder = new Data.Builder().putString(KEY_RECIPIENT, recipientId.serialize()) + .putLong(KEY_MESSAGE_SENT_TIMESTAMP, messageSentTimestamp) + .putLong(KEY_TIMESTAMP, timestamp); + + if (messageId != null) { + builder.putString(KEY_MESSAGE_ID, messageId.serialize()); + } + + return builder.build(); } @Override @@ -86,12 +103,16 @@ public class SendDeliveryReceiptJob extends BaseJob { Recipient recipient = Recipient.resolved(recipientId); SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient); SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, - Collections.singletonList(messageId), + Collections.singletonList(messageSentTimestamp), timestamp); - messageSender.sendReceipt(remoteAddress, - UnidentifiedAccessUtil.getAccessFor(context, recipient), - receiptMessage); + SendMessageResult result = messageSender.sendReceipt(remoteAddress, + UnidentifiedAccessUtil.getAccessFor(context, recipient), + receiptMessage); + + if (messageId != null) { + DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageId); + } } @Override @@ -109,9 +130,16 @@ public class SendDeliveryReceiptJob extends BaseJob { public static final class Factory implements Job.Factory { @Override public @NonNull SendDeliveryReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { + MessageId messageId = null; + + if (data.hasString(KEY_MESSAGE_ID)) { + messageId = MessageId.deserialize(data.getString(KEY_MESSAGE_ID)); + } + return new SendDeliveryReceiptJob(parameters, RecipientId.from(data.getString(KEY_RECIPIENT)), - data.getLong(KEY_MESSAGE_ID), + data.getLong(KEY_MESSAGE_SENT_TIMESTAMP), + messageId, data.getLong(KEY_TIMESTAMP)); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index b5263d1886..f261ff876c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -253,15 +253,17 @@ public final class MessageContentProcessor { } } + MessageId messageId = null; + if (isInvalidMessage(message)) handleInvalidMessage(content.getSender(), content.getSenderDevice(), groupId, content.getTimestamp(), smsMessageId); - else if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId); + else if (message.isEndSession()) messageId = handleEndSessionMessage(content, smsMessageId); else if (message.isGroupV1Update()) handleGroupV1Message(content, message, smsMessageId, groupId.get().requireV1(), receivedTime); - else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId, groupId, receivedTime); - else if (message.getReaction().isPresent()) handleReaction(content, message); - else if (message.getRemoteDelete().isPresent()) handleRemoteDelete(content, message); + else if (message.isExpirationUpdate()) messageId = handleExpirationUpdate(content, message, smsMessageId, groupId, receivedTime); + else if (message.getReaction().isPresent()) messageId = handleReaction(content, message); + else if (message.getRemoteDelete().isPresent()) messageId = handleRemoteDelete(content, message); else if (message.getPayment().isPresent()) handlePayment(content, message); - else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId, receivedTime); - else if (message.getBody().isPresent()) handleTextMessage(content, message, smsMessageId, groupId, receivedTime); + else if (isMediaMessage) messageId = handleMediaMessage(content, message, smsMessageId, receivedTime); + else if (message.getBody().isPresent()) messageId = handleTextMessage(content, message, smsMessageId, groupId, receivedTime); else if (Build.VERSION.SDK_INT > 19 && message.getGroupCallUpdate().isPresent()) handleGroupCallUpdateMessage(content, message, groupId); if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) { @@ -272,9 +274,9 @@ public final class MessageContentProcessor { handleProfileKey(content, message.getProfileKey().get()); } - if (content.isNeedsReceipt()) { - handleNeedsDeliveryReceipt(content, message); - } else { + if (content.isNeedsReceipt() && messageId != null) { + handleNeedsDeliveryReceipt(content, message, messageId); + } else if (!content.isNeedsReceipt()) { Recipient sender = getMessageDestination(content, message); if (RecipientUtil.shouldHaveProfileKey(context, sender)) { @@ -643,8 +645,8 @@ public final class MessageContentProcessor { GroupCallPeekJob.enqueue(groupRecipientId); } - private void handleEndSessionMessage(@NonNull SignalServiceContent content, - @NonNull Optional smsMessageId) + private @Nullable MessageId handleEndSessionMessage(@NonNull SignalServiceContent content, + @NonNull Optional smsMessageId) { MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalHighTrustPush(context, content.getSender()).getId(), @@ -658,25 +660,27 @@ public final class MessageContentProcessor { content.isNeedsReceipt(), content.getServerUuid()); - Long threadId; + Optional insertResult; if (!smsMessageId.isPresent()) { IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage); - Optional insertResult = smsDatabase.insertMessageInbox(incomingEndSessionMessage); - if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); - else threadId = null; + insertResult = smsDatabase.insertMessageInbox(incomingEndSessionMessage); } else { smsDatabase.markAsEndSession(smsMessageId.get()); - threadId = smsDatabase.getThreadIdForMessage(smsMessageId.get()); + insertResult = Optional.of(new InsertResult(smsMessageId.get(), smsDatabase.getThreadIdForMessage(smsMessageId.get()))); } - if (threadId != null) { + if (insertResult.isPresent()) { SessionStore sessionStore = new TextSecureSessionStore(context); sessionStore.deleteAllSessions(content.getSender().getIdentifier()); SecurityEvent.broadcastSecurityUpdateEvent(context); - ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId); + ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId()); + + return new MessageId(insertResult.get().getMessageId(), true); + } else { + return null; } } @@ -742,16 +746,16 @@ public final class MessageContentProcessor { } } - private void handleExpirationUpdate(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message, - @NonNull Optional smsMessageId, - @NonNull Optional groupId, - long receivedTime) + private @Nullable MessageId handleExpirationUpdate(@NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message, + @NonNull Optional smsMessageId, + @NonNull Optional groupId, + long receivedTime) throws StorageFailedException, BadGroupIdException { if (groupId.isPresent() && groupId.get().isV2()) { warn(String.valueOf(content.getTimestamp()), "Expiration update received for GV2. Ignoring."); - return; + return null; } int expiresInSeconds = message.getExpiresInSeconds(); @@ -760,7 +764,7 @@ public final class MessageContentProcessor { if (recipient.getExpireMessages() == expiresInSeconds) { log(String.valueOf(content.getTimestamp()), "No change in message expiry for group. Ignoring."); - return; + return null; } try { @@ -785,24 +789,30 @@ public final class MessageContentProcessor { Optional.absent(), content.getServerUuid()); - database.insertSecureDecryptedMessageInbox(mediaMessage, -1); + Optional insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1); DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient.getId(), expiresInSeconds); if (smsMessageId.isPresent()) { DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); } + + if (insertResult.isPresent()) { + return new MessageId(insertResult.get().getMessageId(), true); + } } catch (MmsException e) { throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice()); } + + return null; } - private void handleReaction(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { + private @Nullable MessageId handleReaction(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { SignalServiceDataMessage.Reaction reaction = message.getReaction().get(); if (!EmojiUtil.isEmoji(reaction.getEmoji())) { Log.w(TAG, "Reaction text is not a valid emoji! Ignoring the message."); - return; + return null; } Recipient reactionAuthor = Recipient.externalHighTrustPush(context, content.getSender()); @@ -812,31 +822,31 @@ public final class MessageContentProcessor { if (targetMessage == null) { warn(String.valueOf(content.getTimestamp()), "[handleReaction] Could not find matching message! Putting it in the early message cache. timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); ApplicationDependencies.getEarlyMessageCache().store(targetAuthor.getId(), reaction.getTargetSentTimestamp(), content); - return; + return null; } if (targetMessage.isRemoteDelete()) { warn(String.valueOf(content.getTimestamp()), "[handleReaction] Found a matching message, but it's flagged as remotely deleted. timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); - return; + return null; } ThreadRecord targetThread = DatabaseFactory.getThreadDatabase(context).getThreadRecord(targetMessage.getThreadId()); if (targetThread == null) { warn(String.valueOf(content.getTimestamp()), "[handleReaction] Could not find a thread for the message! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); - return; + return null; } Recipient threadRecipient = targetThread.getRecipient().resolve(); if (threadRecipient.isGroup() && !threadRecipient.getParticipants().contains(reactionAuthor)) { warn(String.valueOf(content.getTimestamp()), "[handleReaction] Reaction author is not in the group! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); - return; + return null; } if (!threadRecipient.isGroup() && !reactionAuthor.equals(threadRecipient) && !reactionAuthor.isSelf()) { warn(String.valueOf(content.getTimestamp()), "[handleReaction] Reaction author is not a part of the 1:1 thread! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); - return; + return null; } MessageDatabase db = targetMessage.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context); @@ -849,9 +859,11 @@ public final class MessageContentProcessor { db.addReaction(targetMessage.getId(), reactionRecord); ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false); } + + return new MessageId(targetMessage.getId(), targetMessage.isMms()); } - private void handleRemoteDelete(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { + private @Nullable MessageId handleRemoteDelete(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { SignalServiceDataMessage.RemoteDelete delete = message.getRemoteDelete().get(); Recipient sender = Recipient.externalHighTrustPush(context, content.getSender()); @@ -861,12 +873,15 @@ public final class MessageContentProcessor { MessageDatabase db = targetMessage.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context); db.markAsRemoteDelete(targetMessage.getId()); ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false); + return new MessageId(targetMessage.getId(), targetMessage.isMms()); } else if (targetMessage == null) { warn(String.valueOf(content.getTimestamp()), "[handleRemoteDelete] Could not find matching message! timestamp: " + delete.getTargetSentTimestamp() + " author: " + sender.getId()); ApplicationDependencies.getEarlyMessageCache().store(sender.getId(), delete.getTargetSentTimestamp(), content); + return null; } else { warn(String.valueOf(content.getTimestamp()), String.format(Locale.ENGLISH, "[handleRemoteDelete] Invalid remote delete! deleteTime: %d, targetTime: %d, deleteAuthor: %s, targetAuthor: %s", content.getServerReceivedTimestamp(), targetMessage.getServerTimestamp(), sender.getId(), targetMessage.getRecipient().getId())); + return null; } } @@ -1192,10 +1207,10 @@ public final class MessageContentProcessor { messageNotifier.updateNotification(context); } - private void handleMediaMessage(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message, - @NonNull Optional smsMessageId, - long receivedTime) + private @Nullable MessageId handleMediaMessage(@NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message, + @NonNull Optional smsMessageId, + long receivedTime) throws StorageFailedException, BadGroupIdException { notifyTypingStoppedFromIncomingMessage(getMessageDestination(content, message), content.getSender(), content.getSenderDevice()); @@ -1263,6 +1278,10 @@ public final class MessageContentProcessor { if (message.isViewOnce()) { ApplicationDependencies.getViewOnceMessageManager().scheduleIfNecessary(); } + + return new MessageId(insertResult.get().getMessageId(), true); + } else { + return null; } } @@ -1416,11 +1435,11 @@ public final class MessageContentProcessor { receiptDatabase.setUnidentified(unidentifiedStatus, messageId); } - private void handleTextMessage(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message, - @NonNull Optional smsMessageId, - @NonNull Optional groupId, - long receivedTime) + private @Nullable MessageId handleTextMessage(@NonNull SignalServiceContent content, + @NonNull SignalServiceDataMessage message, + @NonNull Optional smsMessageId, + @NonNull Optional groupId, + long receivedTime) throws StorageFailedException, BadGroupIdException { MessageDatabase database = DatabaseFactory.getSmsDatabase(context); @@ -1431,10 +1450,10 @@ public final class MessageContentProcessor { handleExpirationUpdate(content, message, Optional.absent(), groupId, receivedTime); } - Long threadId; + Optional insertResult; if (smsMessageId.isPresent() && !message.getGroupContext().isPresent()) { - threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second(); + insertResult = Optional.of(database.updateBundleMessageBody(smsMessageId.get(), body)); } else { notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice()); @@ -1450,16 +1469,16 @@ public final class MessageContentProcessor { content.getServerUuid()); textMessage = new IncomingEncryptedMessage(textMessage, body); - Optional insertResult = database.insertMessageInbox(textMessage); - - if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); - else threadId = null; + insertResult = database.insertMessageInbox(textMessage); if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get()); } - if (threadId != null) { - ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId); + if (insertResult.isPresent()) { + ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId()); + return new MessageId(insertResult.get().getMessageId(), false); + } else { + return null; } } @@ -1638,9 +1657,10 @@ public final class MessageContentProcessor { } private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content, - @NonNull SignalServiceDataMessage message) + @NonNull SignalServiceDataMessage message, + @NonNull MessageId messageId) { - ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(RecipientId.fromHighTrust(content.getSender()), message.getTimestamp())); + ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(RecipientId.fromHighTrust(content.getSender()), message.getTimestamp(), messageId)); } private void handleViewedReceipt(@NonNull SignalServiceContent content, diff --git a/app/src/main/res/layout/conversation_settings_internal_preference.xml b/app/src/main/res/layout/conversation_settings_internal_preference.xml index 10fe8c61a0..909af3eac1 100644 --- a/app/src/main/res/layout/conversation_settings_internal_preference.xml +++ b/app/src/main/res/layout/conversation_settings_internal_preference.xml @@ -23,4 +23,12 @@ android:layout_gravity="center_horizontal" android:text="@string/preferences__internal_disable_profile_sharing" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c9f41a0c6d..040a32efa2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2453,6 +2453,7 @@ Delete all dynamic shortcuts Click to delete all dynamic shortcuts Disable Profile Sharing + Delete Session Sender Key Clear all state Click to delete all sender key state