commit d0ff3c35eff2ca0867f78baf039e42d4c927da5c
parent 37daae3799c45cd6ce0cd8f715e63ad3771c67ab
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Tue, 12 Dec 2023 23:37:02 +0100

feat(core/events): conversation updated event

Diffstat:
Acore/src/main/kotlin/me/rhunk/snapenhance/core/event/events/impl/ConversationUpdateEvent.kt | 11+++++++++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/AutoSave.kt | 62+++++++++++++++++++-------------------------------------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Messaging.kt | 21++++++++++++++++-----
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt | 3+--
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/tweaks/PreventMessageListAutoScroll.kt | 20++++++++++----------
5 files changed, 57 insertions(+), 60 deletions(-)

diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/events/impl/ConversationUpdateEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/events/impl/ConversationUpdateEvent.kt @@ -0,0 +1,10 @@ +package me.rhunk.snapenhance.core.event.events.impl + +import me.rhunk.snapenhance.core.event.events.AbstractHookEvent +import me.rhunk.snapenhance.core.wrapper.impl.Message + +class ConversationUpdateEvent( + val conversationId: String, + val conversation: Any?, + val messages: List<Message> +) : AbstractHookEvent()+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/AutoSave.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/AutoSave.kt @@ -3,13 +3,13 @@ package me.rhunk.snapenhance.core.features.impl.messaging import me.rhunk.snapenhance.common.data.MessageState import me.rhunk.snapenhance.common.data.MessageUpdate import me.rhunk.snapenhance.common.data.MessagingRuleType +import me.rhunk.snapenhance.core.event.events.impl.ConversationUpdateEvent import me.rhunk.snapenhance.core.features.FeatureLoadParams import me.rhunk.snapenhance.core.features.MessagingRuleFeature import me.rhunk.snapenhance.core.features.impl.spying.MessageLogger import me.rhunk.snapenhance.core.features.impl.spying.StealthMode -import me.rhunk.snapenhance.core.logger.CoreLogger import me.rhunk.snapenhance.core.util.hook.HookStage -import me.rhunk.snapenhance.core.util.hook.Hooker +import me.rhunk.snapenhance.core.util.hook.hook import me.rhunk.snapenhance.core.util.ktx.getObjectField import me.rhunk.snapenhance.core.wrapper.impl.Message import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID @@ -19,20 +19,18 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE, private val asyncSaveExecutorService = Executors.newSingleThreadExecutor() private val messageLogger by lazy { context.feature(MessageLogger::class) } - private val messaging by lazy { context.feature(Messaging::class) } private val autoSaveFilter by lazy { context.config.messaging.autoSaveMessagesInConversations.get() } - fun saveMessage(conversationId: SnapUUID, message: Message) { + fun saveMessage(conversationId: String, message: Message) { val messageId = message.messageDescriptor!!.messageId!! - if (messageLogger.takeIf { it.isEnabled }?.isMessageDeleted(conversationId.toString(), messageId) == true) return - if (message.messageState != MessageState.COMMITTED) return + if (messageLogger.takeIf { it.isEnabled }?.isMessageDeleted(conversationId, messageId) == true) return runCatching { context.feature(Messaging::class).conversationManager?.updateMessage( - conversationId.toString(), + conversationId, messageId, MessageUpdate.SAVE ) { @@ -49,6 +47,8 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE, } fun canSaveMessage(message: Message, headless: Boolean = false): Boolean { + if (message.messageState != MessageState.COMMITTED) return false + if (!headless && (context.mainActivity == null || context.isMainActivityPaused)) return false if (message.messageMetadata!!.savedBy!!.any { uuid -> uuid.toString() == context.database.myUserId }) return false val contentType = message.messageContent!!.contentType.toString() @@ -69,9 +69,8 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE, } override fun asyncOnActivityCreate() { - //called when enter in a conversation (or when a message is sent) - Hooker.hook( - context.mappings.getMappedClass("callbacks", "FetchConversationWithMessagesCallback"), + // called when enter in a conversation + context.mappings.getMappedClass("callbacks", "FetchConversationWithMessagesCallback").hook( "onFetchConversationWithMessagesComplete", HookStage.BEFORE, { autoSaveFilter.isNotEmpty() } @@ -83,45 +82,22 @@ class AutoSave : MessagingRuleFeature("Auto Save", MessagingRuleType.AUTO_SAVE, messages.forEach { if (!canSaveMessage(it)) return@forEach asyncSaveExecutorService.submit { - saveMessage(conversationId, it) + saveMessage(conversationId.toString(), it) } } } - //called when a message is received - Hooker.hook( - context.mappings.getMappedClass("callbacks", "FetchMessageCallback"), - "onFetchMessageComplete", - HookStage.BEFORE, + context.event.subscribe( + ConversationUpdateEvent::class, { autoSaveFilter.isNotEmpty() } - ) { param -> - val message = Message(param.arg(0)) - val conversationId = message.messageDescriptor!!.conversationId!! - if (!canSaveInConversation(conversationId.toString())) return@hook - if (!canSaveMessage(message)) return@hook + ) { event -> + if (!canSaveInConversation(event.conversationId)) return@subscribe - asyncSaveExecutorService.submit { - saveMessage(conversationId, message) - } - } - - Hooker.hook( - context.mappings.getMappedClass("callbacks", "SendMessageCallback"), - "onSuccess", - HookStage.BEFORE, - { autoSaveFilter.isNotEmpty() } - ) { - val conversationUUID = messaging.openedConversationUUID ?: return@hook - runCatching { - messaging.conversationManager?.fetchConversationWithMessagesPaginated( - conversationUUID.toString(), - Long.MAX_VALUE, - 10, - onSuccess = {}, - onError = {} - ) - }.onFailure { - CoreLogger.xposedLog("failed to save message", it) + event.messages.forEach { message -> + if (!canSaveMessage(message)) return@forEach + asyncSaveExecutorService.submit { + saveMessage(event.conversationId, message) + } } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Messaging.kt @@ -1,6 +1,7 @@ package me.rhunk.snapenhance.core.features.impl.messaging import me.rhunk.snapenhance.common.ReceiversConfig +import me.rhunk.snapenhance.core.event.events.impl.ConversationUpdateEvent import me.rhunk.snapenhance.core.event.events.impl.OnSnapInteractionEvent import me.rhunk.snapenhance.core.features.Feature import me.rhunk.snapenhance.core.features.FeatureLoadParams @@ -41,6 +42,21 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C finishAndRemoveTask() } } + + context.mappings.getMappedClass("callbacks", "ConversationManagerDelegate").apply { + hookConstructor(HookStage.AFTER) { param -> + conversationManagerDelegate = param.thisObject() + } + hook("onConversationUpdated", HookStage.BEFORE) { param -> + context.event.post(ConversationUpdateEvent( + conversationId = SnapUUID(param.arg(0)).toString(), + conversation = param.argNullable(1), + messages = param.arg<ArrayList<*>>(2).map { Message(it) }, + ).apply { adapter = param }) { + param.setArg(2, messages.map { it.instanceNonNull() }.toCollection(ArrayList())) + } + } + } } fun getFeedCachedMessageIds(conversationId: String) = feedCachedSnapMessages[conversationId] @@ -83,11 +99,6 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C } } - context.mappings.getMappedClass("callbacks", "ConversationManagerDelegate").apply { - hookConstructor(HookStage.AFTER) { param -> - conversationManagerDelegate = param.thisObject() - } - } context.classCache.feedEntry.hookConstructor(HookStage.AFTER) { param -> val instance = param.thisObject<Any>() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt @@ -37,7 +37,6 @@ import me.rhunk.snapenhance.core.util.ktx.setObjectField import me.rhunk.snapenhance.core.util.media.PreviewUtils import me.rhunk.snapenhance.core.wrapper.impl.Message import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID -import me.rhunk.snapenhance.core.wrapper.impl.toSnapUUID import kotlin.coroutines.suspendCoroutine class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.INIT_SYNC) { @@ -245,7 +244,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN messages.reversed().forEach { message -> if (!autoSave.canSaveMessage(message, headless = true)) return@forEach context.coroutineScope.launch(coroutineDispatcher) { - autoSave.saveMessage(conversationId.toSnapUUID(), message) + autoSave.saveMessage(conversationId, message) } } }, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/tweaks/PreventMessageListAutoScroll.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/tweaks/PreventMessageListAutoScroll.kt @@ -2,11 +2,11 @@ package me.rhunk.snapenhance.core.features.impl.tweaks import android.view.View import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent +import me.rhunk.snapenhance.core.event.events.impl.ConversationUpdateEvent import me.rhunk.snapenhance.core.features.Feature import me.rhunk.snapenhance.core.features.FeatureLoadParams import me.rhunk.snapenhance.core.util.hook.HookStage import me.rhunk.snapenhance.core.util.hook.hook -import me.rhunk.snapenhance.core.wrapper.impl.Message import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID class PreventMessageListAutoScroll : Feature("PreventMessageListAutoScroll", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { @@ -18,28 +18,28 @@ class PreventMessageListAutoScroll : Feature("PreventMessageListAutoScroll", loa override fun onActivityCreate() { if (!context.config.userInterface.preventMessageListAutoScroll.get()) return - context.mappings.getMappedClass("callbacks", "ConversationManagerDelegate").hook("onConversationUpdated", HookStage.BEFORE) { param -> - val updatedMessage = param.arg<ArrayList<*>>(2).map { Message(it) }.firstOrNull() ?: return@hook - if (openedConversationId != updatedMessage.messageDescriptor?.conversationId.toString()) return@hook + context.event.subscribe(ConversationUpdateEvent::class) { event -> + val updatedMessage = event.messages.firstOrNull() ?: return@subscribe + if (openedConversationId != updatedMessage.messageDescriptor?.conversationId.toString()) return@subscribe // cancel if the message is already in focus - if (focusedMessages.entries.any { entry -> entry.value == updatedMessage.messageDescriptor?.messageId && entry.key.isAttachedToWindow }) return@hook + if (focusedMessages.entries.any { entry -> entry.value == updatedMessage.messageDescriptor?.messageId && entry.key.isAttachedToWindow }) return@subscribe val conversationLastMessages = context.database.getMessagesFromConversationId( openedConversationId.toString(), 4 - ) ?: return@hook + ) ?: return@subscribe if (conversationLastMessages.none { - focusedMessages.entries.any { entry -> entry.value == it.clientMessageId.toLong() && entry.key.isAttachedToWindow } - }) { + focusedMessages.entries.any { entry -> entry.value == it.clientMessageId.toLong() && entry.key.isAttachedToWindow } + }) { synchronized(delayedMessageUpdates) { if (firstFocusedMessageId == null) firstFocusedMessageId = conversationLastMessages.lastOrNull()?.clientMessageId?.toLong() delayedMessageUpdates.add { - param.invokeOriginal() + event.adapter.invokeOriginal() } } - param.setResult(null) + event.adapter.setResult(null) } }