mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-23 18:16:15 +02:00
fix mappings not loading & null safety
This commit is contained in:
parent
185fee2027
commit
2fb9bd1f82
@ -3,9 +3,10 @@ package me.rhunk.snapenhance.data.wrapper
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
|
||||
abstract class AbstractWrapper(
|
||||
protected var instance: Any
|
||||
protected var instance: Any?
|
||||
) {
|
||||
fun instance() = instance
|
||||
fun instanceNonNull(): Any = instance!!
|
||||
fun isPresent(): Boolean = instance == null
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return instance.hashCode()
|
||||
@ -15,7 +16,6 @@ abstract class AbstractWrapper(
|
||||
return instance.toString()
|
||||
}
|
||||
|
||||
|
||||
fun <T : Enum<*>> getEnumValue(fieldName: String, defaultValue: T): T {
|
||||
val mContentType = XposedHelpers.getObjectField(instance, fieldName) as Enum<*>
|
||||
return java.lang.Enum.valueOf(defaultValue::class.java, mContentType.name) as T
|
||||
@ -23,7 +23,7 @@ abstract class AbstractWrapper(
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun setEnumValue(fieldName: String, value: Enum<*>) {
|
||||
val type = instance.javaClass.fields.find { it.name == fieldName }?.type as Class<out Enum<*>>
|
||||
val type = instance!!.javaClass.fields.find { it.name == fieldName }?.type as Class<out Enum<*>>
|
||||
XposedHelpers.setObjectField(instance, fieldName, java.lang.Enum.valueOf(type, value.name))
|
||||
}
|
||||
}
|
@ -4,11 +4,11 @@ import me.rhunk.snapenhance.data.MessageState
|
||||
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
|
||||
import me.rhunk.snapenhance.util.getObjectField
|
||||
|
||||
class Message(obj: Any) : AbstractWrapper(obj) {
|
||||
val orderKey get() = instance.getObjectField("mOrderKey") as Long
|
||||
val senderId get() = SnapUUID(instance.getObjectField("mSenderId"))
|
||||
val messageContent get() = MessageContent(instance.getObjectField("mMessageContent"))
|
||||
val messageDescriptor get() = MessageDescriptor(instance.getObjectField("mDescriptor"))
|
||||
val messageMetadata get() = MessageMetadata(instance.getObjectField("mMetadata"))
|
||||
class Message(obj: Any?) : AbstractWrapper(obj) {
|
||||
val orderKey get() = instanceNonNull().getObjectField("mOrderKey") as Long
|
||||
val senderId get() = SnapUUID(instanceNonNull().getObjectField("mSenderId"))
|
||||
val messageContent get() = MessageContent(instanceNonNull().getObjectField("mMessageContent"))
|
||||
val messageDescriptor get() = MessageDescriptor(instanceNonNull().getObjectField("mDescriptor"))
|
||||
val messageMetadata get() = MessageMetadata(instanceNonNull().getObjectField("mMetadata"))
|
||||
val messageState get() = getEnumValue("mState", MessageState.COMMITTED)
|
||||
}
|
@ -5,10 +5,10 @@ import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
|
||||
import me.rhunk.snapenhance.util.getObjectField
|
||||
import me.rhunk.snapenhance.util.setObjectField
|
||||
|
||||
class MessageContent(obj: Any) : AbstractWrapper(obj) {
|
||||
class MessageContent(obj: Any?) : AbstractWrapper(obj) {
|
||||
var content
|
||||
get() = instance.getObjectField("mContent") as ByteArray
|
||||
set(value) = instance.setObjectField("mContent", value)
|
||||
get() = instanceNonNull().getObjectField("mContent") as ByteArray
|
||||
set(value) = instanceNonNull().setObjectField("mContent", value)
|
||||
var contentType
|
||||
get() = getEnumValue("mContentType", ContentType.UNKNOWN)
|
||||
set(value) = setEnumValue("mContentType", value)
|
||||
|
@ -3,7 +3,7 @@ package me.rhunk.snapenhance.data.wrapper.impl
|
||||
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
|
||||
import me.rhunk.snapenhance.util.getObjectField
|
||||
|
||||
class MessageDescriptor(obj: Any) : AbstractWrapper(obj) {
|
||||
val messageId: Long get() = instance.getObjectField("mMessageId") as Long
|
||||
val conversationId: SnapUUID get() = SnapUUID(instance.getObjectField("mConversationId"))
|
||||
class MessageDescriptor(obj: Any?) : AbstractWrapper(obj) {
|
||||
val messageId: Long get() = instanceNonNull().getObjectField("mMessageId") as Long
|
||||
val conversationId: SnapUUID get() = SnapUUID(instanceNonNull().getObjectField("mConversationId")!!)
|
||||
}
|
@ -4,13 +4,13 @@ import me.rhunk.snapenhance.data.PlayableSnapState
|
||||
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
|
||||
import me.rhunk.snapenhance.util.getObjectField
|
||||
|
||||
class MessageMetadata(obj: Any) : AbstractWrapper(obj){
|
||||
val createdAt: Long get() = instance.getObjectField("mCreatedAt") as Long
|
||||
val readAt: Long get() = instance.getObjectField("mReadAt") as Long
|
||||
class MessageMetadata(obj: Any?) : AbstractWrapper(obj){
|
||||
val createdAt: Long get() = instanceNonNull().getObjectField("mCreatedAt") as Long
|
||||
val readAt: Long get() = instanceNonNull().getObjectField("mReadAt") as Long
|
||||
var playableSnapState: PlayableSnapState
|
||||
get() = getEnumValue("mPlayableSnapState", PlayableSnapState.PLAYABLE)
|
||||
set(value) {
|
||||
setEnumValue("mPlayableSnapState", value)
|
||||
}
|
||||
val savedBy: List<SnapUUID> = (instance.getObjectField("mSavedBy") as List<*>).map { SnapUUID(it!!) }
|
||||
val savedBy: List<SnapUUID> = (instanceNonNull().getObjectField("mSavedBy") as List<*>).map { SnapUUID(it!!) }
|
||||
}
|
@ -6,12 +6,10 @@ import me.rhunk.snapenhance.util.getObjectField
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
class SnapUUID(instance: Any) : AbstractWrapper(instance) {
|
||||
class SnapUUID(obj: Any?) : AbstractWrapper(obj) {
|
||||
private val uuidString by lazy { toUUID().toString() }
|
||||
|
||||
val bytes: ByteArray get() {
|
||||
return instance.getObjectField("mId") as ByteArray
|
||||
}
|
||||
val bytes: ByteArray get() = instanceNonNull().getObjectField("mId") as ByteArray
|
||||
|
||||
private fun toUUID(): UUID {
|
||||
val buffer = ByteBuffer.wrap(bytes)
|
||||
|
@ -10,7 +10,7 @@ import javax.crypto.CipherOutputStream
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class EncryptionWrapper(instance: Any) : AbstractWrapper(instance) {
|
||||
class EncryptionWrapper(instance: Any?) : AbstractWrapper(instance) {
|
||||
fun decrypt(data: ByteArray?): ByteArray {
|
||||
return newCipher(Cipher.DECRYPT_MODE).doFinal(data)
|
||||
}
|
||||
@ -30,12 +30,12 @@ class EncryptionWrapper(instance: Any) : AbstractWrapper(instance) {
|
||||
* @return the field
|
||||
*/
|
||||
private fun searchByteArrayField(arrayLength: Int): Field {
|
||||
return instance::class.java.fields.first { f ->
|
||||
return instanceNonNull()::class.java.fields.first { f ->
|
||||
try {
|
||||
if (!f.type.isArray || f.type
|
||||
.componentType != Byte::class.javaPrimitiveType
|
||||
) return@first false
|
||||
return@first (f.get(instance) as ByteArray).size == arrayLength
|
||||
return@first (f.get(instanceNonNull()) as ByteArray).size == arrayLength
|
||||
} catch (e: Exception) {
|
||||
return@first false
|
||||
}
|
||||
|
@ -6,27 +6,27 @@ import me.rhunk.snapenhance.util.getObjectField
|
||||
import java.lang.reflect.Field
|
||||
|
||||
|
||||
class MediaInfo(obj: Any) : AbstractWrapper(obj) {
|
||||
class MediaInfo(obj: Any?) : AbstractWrapper(obj) {
|
||||
val uri: String
|
||||
get() {
|
||||
val firstStringUriField = instance.javaClass.fields.first { f: Field -> f.type == String::class.java }
|
||||
return instance.getObjectField(firstStringUriField.name) as String
|
||||
val firstStringUriField = instanceNonNull().javaClass.fields.first { f: Field -> f.type == String::class.java }
|
||||
return instanceNonNull().getObjectField(firstStringUriField.name) as String
|
||||
}
|
||||
|
||||
init {
|
||||
var mediaInfo: Any = instance
|
||||
if (mediaInfo is List<*>) {
|
||||
if (mediaInfo.size == 0) {
|
||||
throw RuntimeException("MediaInfo is empty")
|
||||
instance?.let {
|
||||
if (it is List<*>) {
|
||||
if (it.size == 0) {
|
||||
throw RuntimeException("MediaInfo is empty")
|
||||
}
|
||||
instance = it[0]!!
|
||||
}
|
||||
mediaInfo = mediaInfo[0]!!
|
||||
}
|
||||
instance = mediaInfo
|
||||
}
|
||||
|
||||
val encryption: EncryptionWrapper?
|
||||
get() {
|
||||
val encryptionAlgorithmField = instance.javaClass.fields.first { f: Field ->
|
||||
val encryptionAlgorithmField = instanceNonNull().javaClass.fields.first { f: Field ->
|
||||
f.type.isInterface && Parcelable::class.java.isAssignableFrom(f.type)
|
||||
}
|
||||
return encryptionAlgorithmField[instance]?.let { EncryptionWrapper(it) }
|
||||
|
@ -3,11 +3,11 @@ package me.rhunk.snapenhance.data.wrapper.impl.media.opera
|
||||
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
|
||||
import me.rhunk.snapenhance.util.ReflectionHelper
|
||||
|
||||
class Layer(obj: Any) : AbstractWrapper(obj) {
|
||||
class Layer(obj: Any?) : AbstractWrapper(obj) {
|
||||
val paramMap: ParamMap
|
||||
get() {
|
||||
val layerControllerField = ReflectionHelper.searchFieldContainsToString(
|
||||
instance::class.java,
|
||||
instanceNonNull()::class.java,
|
||||
instance,
|
||||
"OperaPageModel"
|
||||
)!!
|
||||
|
@ -6,11 +6,11 @@ import me.rhunk.snapenhance.util.ReflectionHelper
|
||||
import java.lang.reflect.Field
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class LayerController(obj: Any) : AbstractWrapper(obj) {
|
||||
class LayerController(obj: Any?) : AbstractWrapper(obj) {
|
||||
val paramMap: ParamMap
|
||||
get() {
|
||||
val paramMapField: Field = ReflectionHelper.searchFieldTypeInSuperClasses(
|
||||
instance::class.java,
|
||||
instanceNonNull()::class.java,
|
||||
ConcurrentHashMap::class.java
|
||||
) ?: throw RuntimeException("Could not find paramMap field")
|
||||
return ParamMap(XposedHelpers.getObjectField(instance, paramMapField.name))
|
||||
|
@ -7,16 +7,16 @@ import java.lang.reflect.Field
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class ParamMap(obj: Any) : AbstractWrapper(obj) {
|
||||
class ParamMap(obj: Any?) : AbstractWrapper(obj) {
|
||||
private val paramMapField: Field by lazy {
|
||||
ReflectionHelper.searchFieldTypeInSuperClasses(
|
||||
instance.javaClass,
|
||||
instanceNonNull().javaClass,
|
||||
ConcurrentHashMap::class.java
|
||||
)!!
|
||||
}
|
||||
|
||||
private val concurrentHashMap: ConcurrentHashMap<Any, Any>
|
||||
get() = instance.getObjectField(paramMapField.name) as ConcurrentHashMap<Any, Any>
|
||||
get() = instanceNonNull().getObjectField(paramMapField.name) as ConcurrentHashMap<Any, Any>
|
||||
|
||||
operator fun get(key: String): Any? {
|
||||
return concurrentHashMap.keys.firstOrNull{ k: Any -> k.toString() == key }?.let { concurrentHashMap[it] }
|
||||
|
@ -44,7 +44,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
|
||||
runCatching {
|
||||
updateMessageMethod.invoke(
|
||||
context.feature(Messaging::class).conversationManager,
|
||||
conversationId.instance(),
|
||||
conversationId.instanceNonNull(),
|
||||
messageId,
|
||||
context.classCache.messageUpdateEnum.enumConstants.first { it.toString() == "SAVE" },
|
||||
callback
|
||||
@ -84,7 +84,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
|
||||
HookStage.BEFORE,
|
||||
{ context.config.bool(ConfigProperty.AUTO_SAVE) && canSave()}
|
||||
) { param ->
|
||||
val conversationId = SnapUUID(param.arg<Any>(0).getObjectField("mConversationId"))
|
||||
val conversationId = SnapUUID(param.arg<Any>(0).getObjectField("mConversationId")!!)
|
||||
val messages = param.arg<List<Any>>(1).map { Message(it) }
|
||||
messages.forEach {
|
||||
if (!canSaveMessage(it)) return@forEach
|
||||
@ -119,7 +119,7 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR
|
||||
val callback = CallbackBuilder(fetchConversationWithMessagesCallbackClass).build()
|
||||
runCatching {
|
||||
fetchConversationWithMessagesPaginatedMethod.invoke(
|
||||
messaging.conversationManager, messaging.lastOpenedConversationUUID!!.instance(),
|
||||
messaging.conversationManager, messaging.lastOpenedConversationUUID!!.instanceNonNull(),
|
||||
Long.MAX_VALUE,
|
||||
3,
|
||||
callback
|
||||
|
@ -95,7 +95,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
||||
}
|
||||
ContentType.SNAP -> {
|
||||
//serialize the message content into a json object
|
||||
val serializedMessageContent = context.gson.toJsonTree(snapMessage.messageContent.instance()).asJsonObject
|
||||
val serializedMessageContent = context.gson.toJsonTree(snapMessage.messageContent.instanceNonNull()).asJsonObject
|
||||
val mediaReferences = serializedMessageContent["mRemoteMediaReferences"]
|
||||
.asJsonArray.map { it.asJsonObject["mMediaReferences"].asJsonArray }
|
||||
.flatten()
|
||||
@ -169,7 +169,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
||||
Logger.xposedLog("Failed to fetch message ${param.arg(0) as Any}")
|
||||
}.build()
|
||||
|
||||
fetchConversationWithMessagesMethod.invoke(conversationManager, SnapUUID.fromString(conversationId).instance(), callback)
|
||||
fetchConversationWithMessagesMethod.invoke(conversationManager, SnapUUID.fromString(conversationId).instanceNonNull(), callback)
|
||||
it.setResult(null)
|
||||
}
|
||||
}
|
||||
|
@ -109,16 +109,17 @@ class MappingManager(private val context: ModContext) : Manager {
|
||||
val classes: MutableList<Class<*>> = ArrayList()
|
||||
|
||||
val classLoader = context.androidContext.classLoader
|
||||
val dexPathList = classLoader.getObjectField("pathList")
|
||||
val dexPathList = classLoader.getObjectField("pathList")!!
|
||||
val dexElements = dexPathList.getObjectField("dexElements") as Array<Any>
|
||||
|
||||
dexElements.forEach { dexElement: Any ->
|
||||
val dexFile = dexElement.getObjectField("dexFile") as DexFile
|
||||
dexFile.entries().toList().forEach fileList@{ className ->
|
||||
//ignore classes without a dot in them
|
||||
if (className.contains(".") && !className.startsWith("com.snap")) return@fileList
|
||||
runCatching {
|
||||
classLoader.loadClass(className)?.let { classes.add(it) }
|
||||
(dexElement.getObjectField("dexFile") as DexFile?)?.apply {
|
||||
entries().toList().forEach fileList@{ className ->
|
||||
//ignore classes without a dot in them
|
||||
if (className.contains(".") && !className.startsWith("com.snap")) return@fileList
|
||||
runCatching {
|
||||
classLoader.loadClass(className)?.let { classes.add(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package me.rhunk.snapenhance.util
|
||||
|
||||
import de.robv.android.xposed.XposedHelpers
|
||||
|
||||
fun Any.getObjectField(fieldName: String): Any {
|
||||
fun Any.getObjectField(fieldName: String): Any? {
|
||||
return XposedHelpers.getObjectField(this, fieldName)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user