mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-29 21:10:20 +02:00
feat(message_logger): deleted indicator
This commit is contained in:
parent
302ea7e83b
commit
fc838fed7a
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ class TestMappings {
|
||||
CompositeConfigurationProviderMapper::class,
|
||||
ScoreUpdateMapper::class,
|
||||
FriendRelationshipChangerMapper::class,
|
||||
ViewBinderMapper::class
|
||||
)
|
||||
|
||||
val gson = GsonBuilder().setPrettyPrinting().create()
|
||||
|
Loading…
x
Reference in New Issue
Block a user