mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-12 05:07:46 +02:00
feat(core): mark snaps as seen
This commit is contained in:
@ -5,18 +5,20 @@ import me.rhunk.snapenhance.core.event.events.impl.OnSnapInteractionEvent
|
||||
import me.rhunk.snapenhance.core.features.Feature
|
||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
|
||||
import me.rhunk.snapenhance.core.util.EvictingMap
|
||||
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.hook.hookConstructor
|
||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
||||
import me.rhunk.snapenhance.core.util.ktx.getObjectFieldOrNull
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.ConversationManager
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.Message
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
|
||||
|
||||
class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_ASYNC or FeatureLoadParams.INIT_SYNC) {
|
||||
var conversationManager: ConversationManager? = null
|
||||
private set
|
||||
|
||||
|
||||
var openedConversationUUID: SnapUUID? = null
|
||||
private set
|
||||
var lastFetchConversationUserUUID: SnapUUID? = null
|
||||
@ -27,8 +29,10 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
|
||||
var lastFocusedMessageId: Long = -1
|
||||
private set
|
||||
|
||||
private val feedCachedSnapMessages = EvictingMap<String, List<Long>>(100)
|
||||
|
||||
override fun init() {
|
||||
Hooker.hookConstructor(context.classCache.conversationManager, HookStage.BEFORE) { param ->
|
||||
context.classCache.conversationManager.hookConstructor(HookStage.BEFORE) { param ->
|
||||
conversationManager = ConversationManager(context, param.thisObject())
|
||||
context.messagingBridge.triggerSessionStart()
|
||||
context.mainActivity?.takeIf { it.intent.getBooleanExtra(ReceiversConfig.MESSAGING_PREVIEW_EXTRA,false) }?.run {
|
||||
@ -37,6 +41,8 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
|
||||
}
|
||||
}
|
||||
|
||||
fun getFeedCachedMessageIds(conversationId: String) = feedCachedSnapMessages[conversationId]
|
||||
|
||||
override fun onActivityCreate() {
|
||||
context.mappings.getMappedObjectNullable("FriendsFeedEventDispatcher").let { it as? Map<*, *> }?.let { mappings ->
|
||||
findClass(mappings["class"].toString()).hook("onItemLongPress", HookStage.BEFORE) { param ->
|
||||
@ -51,6 +57,19 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
|
||||
}
|
||||
}
|
||||
|
||||
val myUserId = context.database.myUserId
|
||||
|
||||
context.classCache.feedEntry.hookConstructor(HookStage.AFTER) { param ->
|
||||
val instance = param.thisObject<Any>()
|
||||
val interactionInfo = instance.getObjectFieldOrNull("mInteractionInfo") ?: return@hookConstructor
|
||||
val messages = (interactionInfo.getObjectFieldOrNull("mMessages") as? List<*>)?.map { Message(it) } ?: return@hookConstructor
|
||||
val conversationId = SnapUUID(instance.getObjectFieldOrNull("mConversationId") ?: return@hookConstructor).toString()
|
||||
|
||||
feedCachedSnapMessages[conversationId] = messages.filter { msg ->
|
||||
msg.messageMetadata?.seenBy?.none { it.toString() == myUserId } == true
|
||||
}.sortedBy { it.orderKey }.mapNotNull { it.messageDescriptor?.messageId }
|
||||
}
|
||||
|
||||
context.mappings.getMappedClass("callbacks", "GetOneOnOneConversationIdsCallback").hook("onSuccess", HookStage.BEFORE) { param ->
|
||||
val userIdToConversation = (param.arg<ArrayList<*>>(0))
|
||||
.takeIf { it.isNotEmpty() }
|
||||
@ -96,12 +115,12 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
|
||||
lastFocusedMessageId = event.messageId
|
||||
}
|
||||
|
||||
Hooker.hook(context.classCache.conversationManager, "fetchMessage", HookStage.BEFORE) { param ->
|
||||
context.classCache.conversationManager.hook("fetchMessage", HookStage.BEFORE) { param ->
|
||||
lastFetchConversationUserUUID = SnapUUID((param.arg(0) as Any))
|
||||
lastFocusedMessageId = param.arg(1)
|
||||
}
|
||||
|
||||
Hooker.hook(context.classCache.conversationManager, "sendTypingNotification", HookStage.BEFORE, {
|
||||
context.classCache.conversationManager.hook("sendTypingNotification", HookStage.BEFORE, {
|
||||
hideTypingNotification || stealthMode.canUseRule(openedConversationUUID.toString())
|
||||
}) {
|
||||
it.setResult(null)
|
||||
|
@ -9,9 +9,15 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Switch
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.common.data.ContentType
|
||||
import me.rhunk.snapenhance.common.data.FriendLinkType
|
||||
import me.rhunk.snapenhance.common.data.MessageUpdate
|
||||
import me.rhunk.snapenhance.common.database.impl.ConversationMessage
|
||||
import me.rhunk.snapenhance.common.database.impl.FriendInfo
|
||||
import me.rhunk.snapenhance.common.database.impl.UserConversationLink
|
||||
@ -29,6 +35,9 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.random.Random
|
||||
|
||||
class FriendFeedInfoMenu : AbstractMenu() {
|
||||
private fun getImageDrawable(url: String): Drawable {
|
||||
@ -101,6 +110,44 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun markAsSeen(conversationId: String) {
|
||||
val messaging = context.feature(Messaging::class)
|
||||
val messageIds = messaging.getFeedCachedMessageIds(conversationId)?.takeIf { it.isNotEmpty() } ?: run {
|
||||
context.shortToast("No recent snaps found")
|
||||
return
|
||||
}
|
||||
|
||||
var job: Job? = null
|
||||
val dialog = ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity)
|
||||
.setTitle("Processing...")
|
||||
.setView(ProgressBar(context.mainActivity).apply {
|
||||
setPadding(10, 10, 10, 10)
|
||||
})
|
||||
.setOnDismissListener { job?.cancel() }
|
||||
.show()
|
||||
|
||||
context.coroutineScope.launch(Dispatchers.IO) {
|
||||
messageIds.forEach { messageId ->
|
||||
suspendCoroutine { continuation ->
|
||||
messaging.conversationManager?.updateMessage(conversationId, messageId, MessageUpdate.READ) {
|
||||
continuation.resume(Unit)
|
||||
if (it != null && it != "DUPLICATEREQUEST") {
|
||||
context.log.error("Error marking message as read $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(Random.nextLong(20, 60))
|
||||
context.runOnUiThread {
|
||||
dialog.setTitle("Processing... (${messageIds.indexOf(messageId) + 1}/${messageIds.size})")
|
||||
}
|
||||
}
|
||||
}.also { job = it }.invokeOnCompletion {
|
||||
context.runOnUiThread {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPreview(userId: String?, conversationId: String) {
|
||||
//query message
|
||||
val messageLogger = context.feature(MessageLogger::class)
|
||||
@ -253,5 +300,13 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
||||
{ ruleFeature.setState(conversationId, it) }
|
||||
)
|
||||
}
|
||||
|
||||
if (friendFeedMenuOptions.contains("mark_as_seen")) {
|
||||
viewConsumer(Button(view.context).apply {
|
||||
text = modContext.translation["friend_menu_option.mark_as_seen"]
|
||||
applyTheme(view.width, hasRadius = true)
|
||||
setOnClickListener { markAsSeen(conversationId) }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user