mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-03 16:04:30 +02:00
feat(core/events): conversation updated event
This commit is contained in:
parent
37daae3799
commit
d0ff3c35ef
@ -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()
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>()
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user