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