diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 51ca3bf685..ee3944df47 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1355,8 +1355,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // Method to add an emoji to a queue and remove it a short while later - this is used as a // rate-limiting mechanism and is called from the `sendEmojiReaction` method, below. - - fun canPerformEmojiReaction(timestamp: Long): Boolean { + private fun canPerformEmojiReaction(timestamp: Long): Boolean { // If the emoji reaction queue is full.. if (emojiRateLimiterQueue.size >= EMOJI_REACTIONS_ALLOWED_PER_MINUTE) { // ..grab the timestamp of the oldest emoji reaction. @@ -1421,15 +1420,24 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // Send it reactionMessage.reaction = Reaction.from(originalMessage.timestamp, originalAuthor.serialize(), emoji, true) - if (recipient.isCommunityRecipient) { + // If this is a community then we need to send it to the server + if (recipient.isCommunityRecipient) { val messageServerId = lokiMessageDb.getServerID(originalMessage.id, !originalMessage.isMms) ?: return Log.w(TAG, "Failed to find message server ID when adding emoji reaction") - viewModel.openGroup?.let { - OpenGroupApi.addReaction(it.room, it.server, messageServerId, emoji) - } + viewModel.openGroup?.let { OpenGroupApi.addReaction(it.room, it.server, messageServerId, emoji) } } else { + + // Send a notification regarding the emoji reaction, but ONLY if this is a contact (i.e., a 1-on-1 conversation), we + // do NOT send notifications for emoji reactions to closed groups or communities! + if (recipient.isContactRecipient) { + Log.w("ACL", "Sending notification for emoji reaction") + //ApplicationContext.getInstance(this@ConversationActivityV2).messageNotifier.updateNotification(this@ConversationActivityV2, messageRecord.threadId, true) + ApplicationContext.getInstance(this@ConversationActivityV2).messageNotifier.updateNotification(this@ConversationActivityV2, true, 0) + } + + Log.w("ACL", "reactionMessage is: " + reactionMessage.text) MessageSender.send(reactionMessage, recipient.address) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt index cbf53e6a8b..cdc75f4170 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt @@ -456,6 +456,7 @@ class DefaultMessageNotifier : MessageNotifier { Log.i(TAG, "Posted notification. $notification") } + // Note: The only use of this method is from `updateNotification`, above. private fun constructNotificationState(context: Context, cursor: Cursor): NotificationState { val notificationState = NotificationState() val reader = get(context).mmsSmsDatabase().readerFor(cursor) @@ -510,19 +511,19 @@ class DefaultMessageNotifier : MessageNotifier { val contact = (record as MmsMessageRecord).sharedContacts[0] body = ContactUtil.getStringSummary(context, contact) - // If this is a notification about a multimedia message which contains no text but DOES contain a slide deck with at least one slide.. + // If this is a notification about a multimedia message which contains no text but DOES contain a slide deck with at least one slide.. } else if (record.isMms && TextUtils.isEmpty(body) && !(record as MmsMessageRecord).slideDeck.slides.isEmpty()) { slideDeck = (record as MediaMmsMessageRecord).slideDeck body = SpanUtil.italic(slideDeck.body) - // If this is a notification about a multimedia message, but it's not ITSELF a multimedia notification AND it contains a slide deck with at least one slide.. + // If this is a notification about a multimedia message, but it's not ITSELF a multimedia notification AND it contains a slide deck with at least one slide.. } else if (record.isMms && !record.isMmsNotification && !(record as MmsMessageRecord).slideDeck.slides.isEmpty()) { slideDeck = (record as MediaMmsMessageRecord).slideDeck val message = slideDeck.body + ": " + record.body val italicLength = message.length - body.length body = SpanUtil.italic(message, italicLength) - // If this is a notification about an invitation to a community.. + // If this is a notification about an invitation to a community.. } else if (record.isOpenGroupInvitation) { body = SpanUtil.italic(context.getString(R.string.communityInvitation)) } @@ -533,7 +534,11 @@ class DefaultMessageNotifier : MessageNotifier { blindedPublicKey = generateBlindedId(threadId, context) cache[threadId] = blindedPublicKey } + + // Only proceed with notifications if the thread is not muted if (threadRecipients == null || !threadRecipients.isMuted) { + + // If this notification is regarding a mention.. if (threadRecipients != null && threadRecipients.notifyType == RecipientDatabase.NOTIFY_TYPE_MENTIONS) { // check if mentioned here var isQuoteMentioned = false @@ -547,9 +552,14 @@ class DefaultMessageNotifier : MessageNotifier { if (body.toString().contains("@$userPublicKey") || body.toString().contains("@$blindedPublicKey") || isQuoteMentioned) { notificationState.addNotification(NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, slideDeck)) } + // ..if this notification is regarding a thread.. } else if (threadRecipients != null && threadRecipients.notifyType == RecipientDatabase.NOTIFY_TYPE_NONE) { + Log.w("ACL", "Hit the notify type NONE block.") // do nothing, no notifications } else { + // If this notification is not a mention or thread notification then it must be RecipientDatabase.NOTIFY_TYPE_ALL because those are the only three enums. + // Note: This is the block that hits for 1-on-1 message notifications + Log.w("ACL", "Hit the notify type ALL block") notificationState.addNotification(NotificationItem(id, mms, recipient, conversationRecipient, threadRecipients, threadId, body, timestamp, slideDeck)) } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt index 59755cf3c7..a0c2e26bfa 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt @@ -132,8 +132,10 @@ object MessageReceiver { if (isBlocked(sender!!)) { throw Error.SenderBlocked } + // Parse the proto val proto = SignalServiceProtos.Content.parseFrom(PushTransportDetails.getStrippedPaddingMessageBody(plaintext)) + // Parse the message val message: Message = ReadReceipt.fromProto(proto) ?: TypingIndicator.fromProto(proto) ?: @@ -155,10 +157,12 @@ object MessageReceiver { if (!message.isSelfSendValid) throw Error.SelfSend message.isSenderSelf = true } + // Guard against control messages in open groups if (isOpenGroupMessage && message !is VisibleMessage) { throw Error.InvalidMessage } + // Finish parsing message.sender = sender message.recipient = userPublicKey @@ -166,18 +170,21 @@ object MessageReceiver { message.receivedTimestamp = if (envelope.hasServerTimestamp()) envelope.serverTimestamp else SnodeAPI.nowWithOffset message.groupPublicKey = groupPublicKey message.openGroupServerMessageID = openGroupServerID + // Validate var isValid = message.isValid() if (message is VisibleMessage && !isValid && proto.dataMessage.attachmentsCount != 0) { isValid = true } if (!isValid) { throw Error.InvalidMessage } + // If the message failed to process the first time around we retry it later (if the error is retryable). In this case the timestamp // will already be in the database but we don't want to treat the message as a duplicate. The isRetry flag is a simple workaround // for this issue. if (groupPublicKey != null && groupPublicKey !in (currentClosedGroups ?: emptySet())) { throw Error.NoGroupThread } + if ((message is ClosedGroupControlMessage && message.kind is ClosedGroupControlMessage.Kind.New) || message is SharedConfigurationMessage) { // Allow duplicates in this case to avoid the following situation: // • The app performed a background poll or received a push notification @@ -189,6 +196,7 @@ object MessageReceiver { if (storage.isDuplicateMessage(envelope.timestamp)) { throw Error.DuplicateMessage } storage.addReceivedMessageTimestamp(envelope.timestamp) } + // Return return Pair(message, proto) } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index 525d60e821..524c7f5add 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -286,6 +286,9 @@ fun MessageReceiver.handleVisibleMessage( runThreadUpdate: Boolean, runProfileUpdate: Boolean ): Long? { + + Is this where emoji reacts come in? If so we need to spawn a notification here! + val storage = MessagingModuleConfiguration.shared.storage val context = MessagingModuleConfiguration.shared.context message.takeIf { it.isSenderSelf }?.sentTimestamp?.let { MessagingModuleConfiguration.shared.lastSentTimestampCache.submitTimestamp(threadId, it) }