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:
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)
}
}