commit fc838fed7ab0b889d92e5d9b43863319df973fec
parent 302ea7e83bbc1bad48b1988846779339b07ae996
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Sat, 23 Sep 2023 00:49:24 +0200
feat(message_logger): deleted indicator
Diffstat:
5 files changed, 98 insertions(+), 20 deletions(-)
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/wrapper/MappingsWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/bridge/wrapper/MappingsWrapper.kt
@@ -9,20 +9,7 @@ import me.rhunk.snapenhance.core.Logger
import me.rhunk.snapenhance.core.bridge.FileLoaderWrapper
import me.rhunk.snapenhance.core.bridge.types.BridgeFileType
import me.rhunk.snapmapper.Mapper
-import me.rhunk.snapmapper.impl.BCryptClassMapper
-import me.rhunk.snapmapper.impl.CallbackMapper
-import me.rhunk.snapmapper.impl.CompositeConfigurationProviderMapper
-import me.rhunk.snapmapper.impl.DefaultMediaItemMapper
-import me.rhunk.snapmapper.impl.EnumMapper
-import me.rhunk.snapmapper.impl.FriendRelationshipChangerMapper
-import me.rhunk.snapmapper.impl.FriendsFeedEventDispatcherMapper
-import me.rhunk.snapmapper.impl.MediaQualityLevelProviderMapper
-import me.rhunk.snapmapper.impl.OperaPageViewControllerMapper
-import me.rhunk.snapmapper.impl.PlatformAnalyticsCreatorMapper
-import me.rhunk.snapmapper.impl.PlusSubscriptionMapper
-import me.rhunk.snapmapper.impl.ScCameraSettingsMapper
-import me.rhunk.snapmapper.impl.ScoreUpdateMapper
-import me.rhunk.snapmapper.impl.StoryBoostStateMapper
+import me.rhunk.snapmapper.impl.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.system.measureTimeMillis
@@ -44,6 +31,7 @@ class MappingsWrapper : FileLoaderWrapper(BridgeFileType.MAPPINGS, "{}".toByteAr
CompositeConfigurationProviderMapper::class,
ScoreUpdateMapper::class,
FriendRelationshipChangerMapper::class,
+ ViewBinderMapper::class,
)
}
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt
@@ -1,6 +1,8 @@
package me.rhunk.snapenhance.features.impl.spying
+import android.graphics.drawable.ColorDrawable
import android.os.DeadObjectException
+import android.view.View
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import me.rhunk.snapenhance.data.ContentType
@@ -10,6 +12,8 @@ import me.rhunk.snapenhance.features.Feature
import me.rhunk.snapenhance.features.FeatureLoadParams
import me.rhunk.snapenhance.hook.HookStage
import me.rhunk.snapenhance.hook.Hooker
+import me.rhunk.snapenhance.hook.hook
+import me.rhunk.snapenhance.hook.hookConstructor
import java.util.concurrent.Executors
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
@@ -23,6 +27,7 @@ private fun Any.longHashCode(): Long {
class MessageLogger : Feature("MessageLogger",
loadParams = FeatureLoadParams.INIT_SYNC or
+ FeatureLoadParams.ACTIVITY_CREATE_SYNC or
FeatureLoadParams.ACTIVITY_CREATE_ASYNC
) {
companion object {
@@ -30,6 +35,8 @@ class MessageLogger : Feature("MessageLogger",
const val PREFETCH_FEED_COUNT = 20
}
+ private val isEnabled get() = context.config.messaging.messageLogger.get()
+
private val threadPool = Executors.newFixedThreadPool(10)
//two level of cache to avoid querying the database
@@ -57,13 +64,16 @@ class MessageLogger : Feature("MessageLogger",
private fun computeMessageIdentifier(conversationId: String, orderKey: Long) = (orderKey.toString() + conversationId).longHashCode()
private fun getServerMessageIdentifier(conversationId: String, clientMessageId: Long): Long? {
- val serverMessageId = context.database.getConversationMessageFromId(clientMessageId)?.serverMessageId?.toLong() ?: return null
+ val serverMessageId = context.database.getConversationMessageFromId(clientMessageId)?.serverMessageId?.toLong() ?: return run {
+ context.log.error("Failed to get server message id for $conversationId $clientMessageId")
+ null
+ }
return computeMessageIdentifier(conversationId, serverMessageId)
}
@OptIn(ExperimentalTime::class)
override fun asyncOnActivityCreate() {
- if (!context.database.hasArroyo()) {
+ if (!isEnabled || !context.database.hasArroyo()) {
return
}
@@ -125,20 +135,60 @@ class MessageLogger : Feature("MessageLogger",
}
}
- //set the message state to PREPARING for visibility
+ /*//set the message state to PREPARING for visibility
with(message.messageContent.contentType) {
if (this != ContentType.SNAP && this != ContentType.EXTERNAL_MEDIA) {
message.messageState = MessageState.PREPARING
}
- }
+ }*/
deletedMessageCache[serverIdentifier] = deletedMessageObject
}
override fun init() {
- val messageLogger by context.config.messaging.messageLogger
- Hooker.hookConstructor(context.classCache.message, HookStage.AFTER, { messageLogger }) { param ->
+ Hooker.hookConstructor(context.classCache.message, HookStage.AFTER, { isEnabled }) { param ->
processSnapMessage(param.thisObject())
}
}
+
+ override fun onActivityCreate() {
+ if (!isEnabled) return
+
+ val viewBinderMappings = context.mappings.getMappedMap("ViewBinder")
+ val cachedHooks = mutableListOf<String>()
+
+ fun cacheHook(clazz: Class<*>, block: Class<*>.() -> Unit) {
+ if (!cachedHooks.contains(clazz.name)) {
+ clazz.block()
+ cachedHooks.add(clazz.name)
+ }
+ }
+
+ findClass(viewBinderMappings["class"].toString()).hookConstructor(HookStage.AFTER) { methodParam ->
+ cacheHook(
+ methodParam.thisObject<Any>()::class.java
+ ) {
+ hook(viewBinderMappings["bindMethod"].toString(), HookStage.BEFORE) bindViewMethod@{ param ->
+ val instance = param.thisObject<Any>()
+ val model1 = param.arg<Any>(0).toString().also {
+ if (!it.startsWith("ChatViewModel")) return@bindViewMethod
+ }
+
+ val messageId = model1.substringAfter("messageId=").substringBefore(",").split(":").let {
+ it[0] to it[2]
+ }
+
+ getServerMessageIdentifier(messageId.first, messageId.second.toLong())?.let { serverMessageId ->
+ if (!deletedMessageCache.contains(serverMessageId)) return@bindViewMethod
+ } ?: return@bindViewMethod
+
+ val view = instance::class.java.methods.first {
+ it.name == viewBinderMappings["getViewMethod"].toString()
+ }.invoke(instance) as? View ?: return@bindViewMethod
+
+ view.foreground = ColorDrawable(0x1E90313e) // red with alpha
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/ext/DexClassDef.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/ext/DexClassDef.kt
@@ -5,6 +5,7 @@ import org.jf.dexlib2.iface.ClassDef
fun ClassDef.isEnum(): Boolean = accessFlags and AccessFlags.ENUM.value != 0
fun ClassDef.isAbstract(): Boolean = accessFlags and AccessFlags.ABSTRACT.value != 0
+fun ClassDef.isInterface(): Boolean = accessFlags and AccessFlags.INTERFACE.value != 0
fun ClassDef.isFinal(): Boolean = accessFlags and AccessFlags.FINAL.value != 0
fun ClassDef.hasStaticConstructorString(string: String): Boolean = methods.any {
diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ViewBinderMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ViewBinderMapper.kt
@@ -0,0 +1,37 @@
+package me.rhunk.snapmapper.impl
+
+import me.rhunk.snapmapper.AbstractClassMapper
+import me.rhunk.snapmapper.MapperContext
+import me.rhunk.snapmapper.ext.getClassName
+import me.rhunk.snapmapper.ext.isAbstract
+import me.rhunk.snapmapper.ext.isInterface
+import java.lang.reflect.Modifier
+
+class ViewBinderMapper : AbstractClassMapper() {
+ override fun run(context: MapperContext) {
+ for (clazz in context.classes) {
+ if (!clazz.isAbstract() || clazz.isInterface()) continue
+
+ val getViewMethod = clazz.methods.firstOrNull { it.returnType == "Landroid/view/View;" && it.parameterTypes.size == 0 } ?: continue
+
+ // update view
+ clazz.methods.filter {
+ Modifier.isAbstract(it.accessFlags) && it.parameterTypes.size == 1 && it.parameterTypes[0] == "Landroid/view/View;" && it.returnType == "V"
+ }.also {
+ if (it.size != 1) return@also
+ }.firstOrNull() ?: continue
+
+ val bindMethod = clazz.methods.filter {
+ Modifier.isAbstract(it.accessFlags) && it.parameterTypes.size == 2 && it.parameterTypes[0] == it.parameterTypes[1] && it.returnType == "V"
+ }.also {
+ if (it.size != 1) return@also
+ }.firstOrNull() ?: continue
+
+ context.addMapping("ViewBinder",
+ "class" to clazz.getClassName(),
+ "bindMethod" to bindMethod.name,
+ "getViewMethod" to getViewMethod.name
+ )
+ }
+ }
+}+
\ No newline at end of file
diff --git a/mapper/src/test/kotlin/me/rhunk/snapenhance/mapper/tests/TestMappings.kt b/mapper/src/test/kotlin/me/rhunk/snapenhance/mapper/tests/TestMappings.kt
@@ -25,6 +25,7 @@ class TestMappings {
CompositeConfigurationProviderMapper::class,
ScoreUpdateMapper::class,
FriendRelationshipChangerMapper::class,
+ ViewBinderMapper::class
)
val gson = GsonBuilder().setPrettyPrinting().create()