From d93a90c74d643482f9ef0ce8f0df94cfba29ad95 Mon Sep 17 00:00:00 2001 From: auth <64337177+authorisation@users.noreply.github.com> Date: Fri, 4 Aug 2023 16:53:30 +0200 Subject: [PATCH] refactor: config override (#169) * refactor: config override * perf: mapper * revert(hooker): inline method - hookAll methods * feat: no friend score delay --------- Co-authored-by: rhunk <101876869+rhunk@users.noreply.github.com> --- app/src/main/assets/lang/en_US.json | 4 + .../snapenhance/config/ConfigProperty.kt | 7 +- .../features/impl/ConfigEnumKeys.kt | 113 ------------------ .../features/impl/ConfigurationOverride.kt | 71 +++++++++++ .../impl/experiments/NoFriendScoreDelay.kt | 21 ++++ .../features/impl/tweaks/CameraTweaks.kt | 10 -- .../me/rhunk/snapenhance/hook/Hooker.kt | 80 +++++++------ .../manager/impl/FeatureManager.kt | 6 +- .../manager/impl/MappingManager.kt | 9 +- ...osedHelperMacros.kt => XposedHelperExt.kt} | 0 .../CompositeConfigurationProviderMapper.kt | 55 +++++++++ .../snapmapper/impl/DefaultMediaItemMapper.kt | 1 + .../me/rhunk/snapmapper/impl/EnumMapper.kt | 50 +------- .../impl/FriendsFeedEventDispatcherMapper.kt | 1 + .../impl/MediaQualityLevelProviderMapper.kt | 1 + .../impl/PlatformAnalyticsCreatorMapper.kt | 1 + .../snapmapper/impl/ScCameraSettingsMapper.kt | 1 + .../snapmapper/impl/ScoreUpdateMapper.kt | 25 ++++ .../snapmapper/impl/StoryBoostStateMapper.kt | 1 + .../snapenhance/mapper/tests/TestMappings.kt | 4 +- 20 files changed, 248 insertions(+), 213 deletions(-) delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigurationOverride.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/NoFriendScoreDelay.kt rename app/src/main/kotlin/me/rhunk/snapenhance/util/{XposedHelperMacros.kt => XposedHelperExt.kt} (100%) create mode 100644 mapper/src/main/kotlin/me/rhunk/snapmapper/impl/CompositeConfigurationProviderMapper.kt create mode 100644 mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ScoreUpdateMapper.kt diff --git a/app/src/main/assets/lang/en_US.json b/app/src/main/assets/lang/en_US.json index b3010e56..256e8ae3 100644 --- a/app/src/main/assets/lang/en_US.json +++ b/app/src/main/assets/lang/en_US.json @@ -223,6 +223,10 @@ "name": "Unlimited Multi Snap", "description": "Allows you to take an unlimited amount of multi snaps" }, + "no_friend_score_delay": { + "name": "No Friend Score Delay", + "description": "Removes the delay in displaying friends scores. It will be updated immediately when you open a profile page" + }, "device_spoof": { "name": "Spoof Device Values", "description": "Spoofs the devices values" diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt b/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt index 19b3cd75..027eb757 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt @@ -386,7 +386,12 @@ enum class ConfigProperty( ConfigCategory.EXPERIMENTAL_DEBUGGING, ConfigStateValue(false) ), - + NO_FRIEND_SCORE_DELAY( + "no_friend_score_delay", + ConfigCategory.EXPERIMENTAL_DEBUGGING, + ConfigStateValue(false) + ), + //DEVICE SPOOFER DEVICE_SPOOF( "device_spoof", diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt deleted file mode 100644 index eb04b21e..00000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt +++ /dev/null @@ -1,113 +0,0 @@ -package me.rhunk.snapenhance.features.impl - -import android.annotation.SuppressLint -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.features.Feature -import me.rhunk.snapenhance.features.FeatureLoadParams -import me.rhunk.snapenhance.hook.HookStage -import me.rhunk.snapenhance.hook.hook -import me.rhunk.snapenhance.util.getObjectField -import me.rhunk.snapenhance.util.setObjectField -import java.lang.reflect.Field -import java.lang.reflect.Modifier -import java.lang.reflect.Type - -class ConfigEnumKeys : Feature("Config enum keys", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { - - data class HookEnumContext( - val key: String, - val type: Type?, - val value: Any?, - val set: (Any) -> Unit - ) - - companion object { - fun hookAllEnums(enumClass: Class<*>, callback: HookEnumContext.() -> Unit) { - //Enum(String, int, ?) - //or Enum(?) - val enumDataClass = enumClass.constructors[0].parameterTypes.first { clazz: Class<*> -> clazz != String::class.java && !clazz.isPrimitive } - - //get the field which contains the enum data class - val enumDataField = enumClass.declaredFields.first { field: Field -> field.type == enumDataClass } - - val typeField = enumDataClass.declaredFields.first { field: Field -> field.type == Type::class.java } - - //get the field value of the enum data class (the first field of the class with the desc Object) - val objectDataField = enumDataField.type.fields.first { field: Field -> - field.type == Any::class.java && Modifier.isPublic( - field.modifiers - ) && Modifier.isFinal(field.modifiers) - } - - enumClass.enumConstants.forEach { enum -> - enumDataField.get(enum)?.let { enumData -> - val key = enum.toString() - val type = typeField.get(enumData) as Type? - val value = enumData.getObjectField(objectDataField.name) - val set = { newValue: Any -> - enumData.setObjectField(objectDataField.name, newValue) - } - callback(HookEnumContext(key, type, value, set)) - } - } - } - } - - @SuppressLint("PrivateApi") - override fun onActivityCreate() { - if (context.config.bool(ConfigProperty.NEW_MAP_UI)) { - hookAllEnums(context.mappings.getMappedClass("enums", "PLUS")) { - if (key == "REDUCE_MY_PROFILE_UI_COMPLEXITY") set(true) - } - } - - hookAllEnums(context.mappings.getMappedClass("enums", "ARROYO")) { - if (key == "ENABLE_LONG_SNAP_SENDING") { - if (context.config.bool(ConfigProperty.DISABLE_SNAP_SPLITTING)) set(true) - } - } - - if (context.config.bool(ConfigProperty.STREAK_EXPIRATION_INFO)) { - hookAllEnums(context.mappings.getMappedClass("enums", "FRIENDS_FEED")) { - if (key == "STREAK_EXPIRATION_INFO") set(true) - } - } - - if (context.config.bool(ConfigProperty.BLOCK_ADS)) { - hookAllEnums(context.mappings.getMappedClass("enums", "SNAPADS")) { - if (key == "BYPASS_AD_FEATURE_GATE") { - set(true) - } - if (key == "CUSTOM_AD_SERVER_URL" || key == "CUSTOM_AD_INIT_SERVER_URL" || key == "CUSTOM_AD_TRACKER_URL") { - set("http://127.0.0.1") - } - } - } - - context.config.state(ConfigProperty.STORY_VIEWER_OVERRIDE).let { state -> - if (state == "OFF") return@let - - hookAllEnums(context.mappings.getMappedClass("enums", "DISCOVER_FEED")) { - if (key == "DF_ENABLE_SHOWS_PAGE_CONTROLS" && state == "DISCOVER_PLAYBACK_SEEKBAR") { - set(true) - } - if (key == "DF_VOPERA_FOR_STORIES" && state == "VERTICAL_STORY_VIEWER") { - set(true) - } - } - } - - ConfigProperty.ENABLE_APP_APPEARANCE.valueContainer.addPropertyChangeListener { - context.softRestartApp(true) - } - - val sharedPreferencesImpl = context.androidContext.classLoader.loadClass("android.app.SharedPreferencesImpl") - - sharedPreferencesImpl.methods.first { it.name == "getBoolean" }.hook(HookStage.BEFORE) { param -> - when (param.arg(0)) { - "SIG_APP_APPEARANCE_SETTING" -> if (context.config.bool(ConfigProperty.ENABLE_APP_APPEARANCE)) param.setResult(true) - "SPOTLIGHT_5TH_TAB_ENABLED" -> if (context.config.bool(ConfigProperty.DISABLE_SPOTLIGHT)) param.setResult(false) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigurationOverride.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigurationOverride.kt new file mode 100644 index 00000000..c7cb60aa --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigurationOverride.kt @@ -0,0 +1,71 @@ +package me.rhunk.snapenhance.features.impl + +import de.robv.android.xposed.XposedHelpers +import me.rhunk.snapenhance.config.ConfigProperty +import me.rhunk.snapenhance.features.Feature +import me.rhunk.snapenhance.features.FeatureLoadParams +import me.rhunk.snapenhance.hook.HookStage +import me.rhunk.snapenhance.hook.hook +import me.rhunk.snapenhance.util.setObjectField + +class ConfigurationOverride : Feature("Configuration Override", loadParams = FeatureLoadParams.INIT_SYNC) { + override fun init() { + val propertyOverrides = mutableMapOf Boolean), Any>>() + + fun overrideProperty(key: String, filter: () -> Boolean, value: Any) { + propertyOverrides[key] = Pair(filter, value) + } + + overrideProperty("STREAK_EXPIRATION_INFO", { context.config.bool(ConfigProperty.STREAK_EXPIRATION_INFO) }, true) + + overrideProperty("FORCE_CAMERA_HIGHEST_FPS", { context.config.bool(ConfigProperty.FORCE_HIGHEST_FRAME_RATE) }, true) + overrideProperty("MEDIA_RECORDER_MAX_QUALITY_LEVEL", { context.config.bool(ConfigProperty.FORCE_CAMERA_SOURCE_ENCODING) }, true) + overrideProperty("REDUCE_MY_PROFILE_UI_COMPLEXITY", { context.config.bool(ConfigProperty.NEW_MAP_UI) }, true) + overrideProperty("ENABLE_LONG_SNAP_SENDING", { context.config.bool(ConfigProperty.DISABLE_SNAP_SPLITTING) }, true) + overrideProperty("BYPASS_AD_FEATURE_GATE", { context.config.bool(ConfigProperty.BLOCK_ADS) }, true) + + context.config.state(ConfigProperty.STORY_VIEWER_OVERRIDE).let { state -> + overrideProperty("DF_ENABLE_SHOWS_PAGE_CONTROLS", { state == "DISCOVER_PLAYBACK_SEEKBAR" }, true) + overrideProperty("DF_VOPERA_FOR_STORIES", { state == "VERTICAL_STORY_VIEWER" }, true) + } + + overrideProperty("SIG_APP_APPEARANCE_SETTING", { context.config.bool(ConfigProperty.ENABLE_APP_APPEARANCE) }, true) + overrideProperty("SPOTLIGHT_5TH_TAB_ENABLED", { context.config.bool(ConfigProperty.DISABLE_SPOTLIGHT) }, false) + + arrayOf("CUSTOM_AD_TRACKER_URL", "CUSTOM_AD_INIT_SERVER_URL", "CUSTOM_AD_SERVER_URL").forEach { + overrideProperty(it, { context.config.bool(ConfigProperty.BLOCK_ADS) }, "http://127.0.0.1") + } + + val compositeConfigurationProviderMappings = context.mappings.getMappedMap("CompositeConfigurationProvider") + val enumMappings = compositeConfigurationProviderMappings["enum"] as Map<*, *> + + findClass(compositeConfigurationProviderMappings["class"].toString()).hook( + compositeConfigurationProviderMappings["observeProperty"].toString(), + HookStage.BEFORE + ) { param -> + val enumData = param.arg(0) + val key = enumData.toString() + val setValue: (Any?) -> Unit = { value -> + val valueHolder = XposedHelpers.callMethod(enumData, enumMappings["getValue"].toString()) + valueHolder.setObjectField(enumMappings["defaultValueField"].toString(), value) + } + + propertyOverrides[key]?.let { (filter, value) -> + if (!filter()) return@let + setValue(value) + } + } + + findClass(compositeConfigurationProviderMappings["class"].toString()).hook( + compositeConfigurationProviderMappings["getProperty"].toString(), + HookStage.AFTER + ) { param -> + val propertyKey = param.arg(0).toString() + + propertyOverrides[propertyKey]?.let { (filter, value) -> + if (!filter()) return@let + param.setResult(value) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/NoFriendScoreDelay.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/NoFriendScoreDelay.kt new file mode 100644 index 00000000..283d65af --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/NoFriendScoreDelay.kt @@ -0,0 +1,21 @@ +package me.rhunk.snapenhance.features.impl.experiments + +import me.rhunk.snapenhance.config.ConfigProperty +import me.rhunk.snapenhance.features.Feature +import me.rhunk.snapenhance.features.FeatureLoadParams +import me.rhunk.snapenhance.hook.HookStage +import me.rhunk.snapenhance.hook.hookConstructor +import java.lang.reflect.Constructor + +class NoFriendScoreDelay : Feature("NoFriendScoreDelay", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { + override fun onActivityCreate() { + if (!context.config.bool(ConfigProperty.NO_FRIEND_SCORE_DELAY)) return + val scoreUpdateClass = context.mappings.getMappedClass("ScoreUpdate") + + scoreUpdateClass.hookConstructor(HookStage.BEFORE) { param -> + val constructor = param.method() as Constructor<*> + if (constructor.parameterTypes.size < 3 || constructor.parameterTypes[3] != java.util.Collection::class.java) return@hookConstructor + param.setArg(2, 0L) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt index 3f1a1bf6..19dee9fd 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt @@ -9,7 +9,6 @@ import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.wrapper.impl.ScSize import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams -import me.rhunk.snapenhance.features.impl.ConfigEnumKeys import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.hook import me.rhunk.snapenhance.hook.hookConstructor @@ -38,15 +37,6 @@ class CameraTweaks : Feature("Camera Tweaks", loadParams = FeatureLoadParams.ACT } } - ConfigEnumKeys.hookAllEnums(context.mappings.getMappedClass("enums", "CAMERA")) { - if (key == "FORCE_CAMERA_HIGHEST_FPS" && context.config.bool(ConfigProperty.FORCE_HIGHEST_FRAME_RATE)) { - set(true) - } - if (key == "MEDIA_RECORDER_MAX_QUALITY_LEVEL" && context.config.bool(ConfigProperty.FORCE_CAMERA_SOURCE_ENCODING)) { - value!!.javaClass.enumConstants?.let { enumData -> set(enumData.filter { it.toString() == "LEVEL_MAX" }) } - } - } - val previewResolutionConfig = parseResolution(context.config.state(ConfigProperty.OVERRIDE_PREVIEW_RESOLUTION)) val captureResolutionConfig = parseResolution(context.config.state(ConfigProperty.OVERRIDE_PICTURE_RESOLUTION)) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt b/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt index b3ee2a7a..052d4e0e 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt @@ -3,6 +3,7 @@ package me.rhunk.snapenhance.hook import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XposedBridge import java.lang.reflect.Member +import java.lang.reflect.Method object Hooker { inline fun newMethodHook( @@ -21,29 +22,14 @@ object Hooker { } } - inline fun hook( - clazz: Class<*>, - methodName: String, - stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit - ): Set = hook(clazz, methodName, stage, { true }, consumer) - inline fun hook( clazz: Class<*>, methodName: String, stage: HookStage, crossinline filter: (HookAdapter) -> Boolean, - crossinline consumer: (HookAdapter) -> Unit + noinline consumer: (HookAdapter) -> Unit ): Set = XposedBridge.hookAllMethods(clazz, methodName, newMethodHook(stage, consumer, filter)) - inline fun hook( - member: Member, - stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit - ): XC_MethodHook.Unhook { - return hook(member, stage, { true }, consumer) - } - inline fun hook( member: Member, stage: HookStage, @@ -53,20 +39,34 @@ object Hooker { return XposedBridge.hookMethod(member, newMethodHook(stage, consumer, filter)) } + fun hook( + clazz: Class<*>, + methodName: String, + stage: HookStage, + consumer: (HookAdapter) -> Unit + ): Set = hook(clazz, methodName, stage, { true }, consumer) - inline fun hookConstructor( + fun hook( + member: Member, + stage: HookStage, + consumer: (HookAdapter) -> Unit + ): XC_MethodHook.Unhook { + return hook(member, stage, { true }, consumer) + } + + fun hookConstructor( clazz: Class<*>, stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + consumer: (HookAdapter) -> Unit ) { XposedBridge.hookAllConstructors(clazz, newMethodHook(stage, consumer)) } - inline fun hookConstructor( + fun hookConstructor( clazz: Class<*>, stage: HookStage, - crossinline filter: ((HookAdapter) -> Boolean), - crossinline consumer: (HookAdapter) -> Unit + filter: ((HookAdapter) -> Boolean), + consumer: (HookAdapter) -> Unit ) { XposedBridge.hookAllConstructors(clazz, newMethodHook(stage, consumer, filter)) } @@ -117,37 +117,43 @@ object Hooker { } } -inline fun Class<*>.hookConstructor( +fun Class<*>.hookConstructor( stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + consumer: (HookAdapter) -> Unit ) = Hooker.hookConstructor(this, stage, consumer) -inline fun Class<*>.hookConstructor( +fun Class<*>.hookConstructor( stage: HookStage, - crossinline filter: ((HookAdapter) -> Boolean), - crossinline consumer: (HookAdapter) -> Unit + filter: ((HookAdapter) -> Boolean), + consumer: (HookAdapter) -> Unit ) = Hooker.hookConstructor(this, stage, filter, consumer) -inline fun Class<*>.hook( +fun Class<*>.hook( methodName: String, stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + consumer: (HookAdapter) -> Unit ): Set = Hooker.hook(this, methodName, stage, consumer) -inline fun Class<*>.hook( +fun Class<*>.hook( methodName: String, stage: HookStage, - crossinline filter: (HookAdapter) -> Boolean, - crossinline consumer: (HookAdapter) -> Unit + filter: (HookAdapter) -> Boolean, + consumer: (HookAdapter) -> Unit ): Set = Hooker.hook(this, methodName, stage, filter, consumer) -inline fun Member.hook( +fun Member.hook( stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + consumer: (HookAdapter) -> Unit ): XC_MethodHook.Unhook = Hooker.hook(this, stage, consumer) -inline fun Member.hook( +fun Member.hook( stage: HookStage, - crossinline filter: ((HookAdapter) -> Boolean), - crossinline consumer: (HookAdapter) -> Unit -): XC_MethodHook.Unhook = Hooker.hook(this, stage, filter, consumer) \ No newline at end of file + filter: ((HookAdapter) -> Boolean), + consumer: (HookAdapter) -> Unit +): XC_MethodHook.Unhook = Hooker.hook(this, stage, filter, consumer) + +fun Array.hookAll(stage: HookStage, param: (HookAdapter) -> Unit) { + filter { it.declaringClass != Object::class.java }.forEach { + it.hook(stage, param) + } +} diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt index 258bb1b9..66219c6d 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt @@ -5,7 +5,7 @@ import me.rhunk.snapenhance.ModContext import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.features.impl.AutoUpdater -import me.rhunk.snapenhance.features.impl.ConfigEnumKeys +import me.rhunk.snapenhance.features.impl.ConfigurationOverride import me.rhunk.snapenhance.features.impl.Messaging import me.rhunk.snapenhance.features.impl.downloader.AntiAutoDownload import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader @@ -14,6 +14,7 @@ import me.rhunk.snapenhance.features.impl.experiments.AppPasscode import me.rhunk.snapenhance.features.impl.experiments.DeviceSpooferHook import me.rhunk.snapenhance.features.impl.experiments.InfiniteStoryBoost import me.rhunk.snapenhance.features.impl.experiments.MeoPasscodeBypass +import me.rhunk.snapenhance.features.impl.experiments.NoFriendScoreDelay import me.rhunk.snapenhance.features.impl.experiments.UnlimitedMultiSnap import me.rhunk.snapenhance.features.impl.privacy.DisableMetrics import me.rhunk.snapenhance.features.impl.privacy.PreventMessageSending @@ -74,7 +75,7 @@ class FeatureManager(private val context: ModContext) : Manager { register(Notifications::class) register(AutoSave::class) register(UITweaks::class) - register(ConfigEnumKeys::class) + register(ConfigurationOverride::class) register(AntiAutoDownload::class) register(GalleryMediaSendOverride::class) register(AntiAutoSave::class) @@ -93,6 +94,7 @@ class FeatureManager(private val context: ModContext) : Manager { register(DeviceSpooferHook::class) register(StartupPageOverride::class) register(GooglePlayServicesDialogs::class) + register(NoFriendScoreDelay::class) initializeFeatures() } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt index 86d8ad65..d0e751d7 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.manager.impl -import android.app.AlertDialog import com.google.gson.JsonElement import com.google.gson.JsonParser import me.rhunk.snapenhance.Constants @@ -12,6 +11,7 @@ import me.rhunk.snapenhance.ui.ViewAppearanceHelper 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.FriendsFeedEventDispatcherMapper @@ -20,6 +20,7 @@ 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 java.nio.charset.StandardCharsets import java.util.concurrent.ConcurrentHashMap @@ -38,7 +39,9 @@ class MappingManager(private val context: ModContext) : Manager { PlusSubscriptionMapper::class, ScCameraSettingsMapper::class, StoryBoostStateMapper::class, - FriendsFeedEventDispatcherMapper::class + FriendsFeedEventDispatcherMapper::class, + CompositeConfigurationProviderMapper::class, + ScoreUpdateMapper::class ) private val mappings = ConcurrentHashMap() @@ -95,7 +98,7 @@ class MappingManager(private val context: ModContext) : Manager { statusDialogBuilder.show() } } - } + } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt b/app/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperExt.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperExt.kt diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/CompositeConfigurationProviderMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/CompositeConfigurationProviderMapper.kt new file mode 100644 index 00000000..f667d4d3 --- /dev/null +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/CompositeConfigurationProviderMapper.kt @@ -0,0 +1,55 @@ +package me.rhunk.snapmapper.impl + +import me.rhunk.snapmapper.AbstractClassMapper +import me.rhunk.snapmapper.MapperContext +import me.rhunk.snapmapper.ext.findConstString +import me.rhunk.snapmapper.ext.getClassName +import me.rhunk.snapmapper.ext.hasStaticConstructorString +import me.rhunk.snapmapper.ext.isEnum +import java.lang.reflect.Modifier + +class CompositeConfigurationProviderMapper : AbstractClassMapper() { + override fun run(context: MapperContext) { + for (classDef in context.classes) { + val constructor = classDef.methods.firstOrNull { it.name == "" } ?: continue + if (constructor.parameterTypes.size == 0 || constructor.parameterTypes[0] != "Ljava/util/List;") continue + if (constructor.implementation?.findConstString("CompositeConfigurationProvider") != true) continue + + val getPropertyMethod = classDef.methods.first { method -> + method.parameterTypes.size > 1 && + method.returnType == "Ljava/lang/Object;" && + context.getClass(method.parameterTypes[0])?.interfaces?.contains("Ljava/io/Serializable;") == true && + context.getClass(method.parameterTypes[1])?.let { it.isEnum() && it.hasStaticConstructorString("BOOLEAN") } == true + } + + val configEnumInterface = context.getClass(getPropertyMethod.parameterTypes[0])!! + val enumType = context.getClass(getPropertyMethod.parameterTypes[1])!! + + val observePropertyMethod = classDef.methods.first { + it.parameterTypes.size > 2 && + it.parameterTypes[0] == configEnumInterface.type && + it.parameterTypes[1] == "Ljava/lang/String;" && + it.parameterTypes[2] == enumType.type + } + + val enumGetDefaultValueMethod = configEnumInterface.methods.first { context.getClass(it.returnType)?.interfaces?.contains("Ljava/io/Serializable;") == true } + val defaultValueField = context.getClass(enumGetDefaultValueMethod.returnType)!!.fields.first { + Modifier.isFinal(it.accessFlags) && + Modifier.isPublic(it.accessFlags) && + it.type == "Ljava/lang/Object;" + } + + context.addMapping("CompositeConfigurationProvider", + "class" to classDef.getClassName(), + "observeProperty" to observePropertyMethod.name, + "getProperty" to getPropertyMethod.name, + "enum" to mapOf( + "class" to configEnumInterface.getClassName(), + "getValue" to enumGetDefaultValueMethod.name, + "defaultValueField" to defaultValueField.name + ) + ) + return + } + } +} \ No newline at end of file diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/DefaultMediaItemMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/DefaultMediaItemMapper.kt index 84320e05..60a7c8bb 100644 --- a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/DefaultMediaItemMapper.kt +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/DefaultMediaItemMapper.kt @@ -16,6 +16,7 @@ class DefaultMediaItemMapper : AbstractClassMapper() { if (constructorParameters.size < 6 || constructorParameters[5] != "J") continue context.addMapping("DefaultMediaItem", clazz.type.replace("L", "").replace(";", "")) + return } } } \ No newline at end of file diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/EnumMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/EnumMapper.kt index 41e3e478..f9eb0624 100644 --- a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/EnumMapper.kt +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/EnumMapper.kt @@ -14,59 +14,17 @@ import org.jf.dexlib2.iface.reference.StringReference class EnumMapper : AbstractClassMapper() { override fun run(context: MapperContext) { - var enumQualityLevel : String? = null - val enums = mutableListOf>() + lateinit var enumQualityLevel : String for (enumClass in context.classes) { if (!enumClass.isEnum()) continue - if (enumQualityLevel == null && enumClass.hasStaticConstructorString("LEVEL_MAX")) { + if (enumClass.hasStaticConstructorString("LEVEL_MAX")) { enumQualityLevel = enumClass.getClassName() - } - - if (enumClass.interfaces.isEmpty()) continue - - //check if it's a config enum - val serializableInterfaceClass = context.getClass(enumClass.interfaces.first()) ?: continue - if (serializableInterfaceClass.methods.none {it.name == "getName" && it.returnType == "Ljava/lang/String;" }) continue - - //find the method which returns the enum name - val getEnumMethod = enumClass.virtualMethods.firstOrNull { context.getClass(it.returnType)?.isEnum() == true } ?: continue - - //search for constant field instruction sget-object - - fun getFirstFieldReference21c(opcode: Opcode, method: Method) = method.implementation!!.instructions.firstOrNull { - it.opcode == opcode && it is Instruction21c - }.let { it as? Instruction21c }?.let { - it.reference as? FieldReference - } - - val fieldReference = getFirstFieldReference21c(Opcode.SGET_OBJECT, getEnumMethod) ?: - getFirstFieldReference21c(Opcode.SGET_OBJECT,enumClass.directMethods.first { it.name == "" }) ?: continue - - //search field name in the class - val enumClassListEnum = context.getClass(fieldReference.definingClass) ?: continue - - enumClassListEnum.getStaticConstructor()?.let { constructor -> - var lastEnumClassName = "" - constructor.implementation!!.instructions.forEach { - if (it.opcode == Opcode.CONST_STRING) { - lastEnumClassName = ((it as Instruction21c).reference as StringReference).string - return@forEach - } - - if (it.opcode == Opcode.SPUT_OBJECT && it is Instruction21c) { - val field = it.reference as? FieldReference ?: return@forEach - if (field.name != fieldReference.name || field.type != fieldReference.type) return@forEach - - enums.add(lastEnumClassName to enumClass.getClassName()) - } - } + break; } } - context.addMapping("EnumQualityLevel", enumQualityLevel!!) - - context.addMapping("enums", *enums.sortedBy { it.first }.toTypedArray()) + context.addMapping("EnumQualityLevel", enumQualityLevel) } } \ No newline at end of file diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/FriendsFeedEventDispatcherMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/FriendsFeedEventDispatcherMapper.kt index ca5cf4ea..8131a9ce 100644 --- a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/FriendsFeedEventDispatcherMapper.kt +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/FriendsFeedEventDispatcherMapper.kt @@ -22,6 +22,7 @@ class FriendsFeedEventDispatcherMapper : AbstractClassMapper() { "class" to clazz.getClassName(), "viewModelField" to viewModelField ) + return } } } \ No newline at end of file diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/MediaQualityLevelProviderMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/MediaQualityLevelProviderMapper.kt index 19ab42b6..cd2c7f7c 100644 --- a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/MediaQualityLevelProviderMapper.kt +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/MediaQualityLevelProviderMapper.kt @@ -18,6 +18,7 @@ class MediaQualityLevelProviderMapper : AbstractClassMapper(EnumMapper::class) { "class" to clazz.type.replace("L", "").replace(";", ""), "method" to it.name ) + return } } } diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/PlatformAnalyticsCreatorMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/PlatformAnalyticsCreatorMapper.kt index ee340f5d..e55795f1 100644 --- a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/PlatformAnalyticsCreatorMapper.kt +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/PlatformAnalyticsCreatorMapper.kt @@ -18,6 +18,7 @@ class PlatformAnalyticsCreatorMapper : AbstractClassMapper() { if (firstParameterClass.getStaticConstructor()?.implementation?.findConstString("IN_APP_NOTIFICATION") != true) continue context.addMapping("PlatformAnalyticsCreator", clazz.type.replace("L", "").replace(";", "")) + return } } } \ No newline at end of file diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ScCameraSettingsMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ScCameraSettingsMapper.kt index d473a4df..5a34d1e3 100644 --- a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ScCameraSettingsMapper.kt +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ScCameraSettingsMapper.kt @@ -15,6 +15,7 @@ class ScCameraSettingsMapper : AbstractClassMapper() { if (!firstParameter.isEnum() || firstParameter.getStaticConstructor()?.implementation?.findConstString("CONTINUOUS_PICTURE") != true) continue context.addMapping("ScCameraSettings", clazz.type.replace("L", "").replace(";", "")) + return } } } \ No newline at end of file diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ScoreUpdateMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ScoreUpdateMapper.kt new file mode 100644 index 00000000..a91fb8b0 --- /dev/null +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/ScoreUpdateMapper.kt @@ -0,0 +1,25 @@ +package me.rhunk.snapmapper.impl + +import me.rhunk.snapmapper.AbstractClassMapper +import me.rhunk.snapmapper.MapperContext +import me.rhunk.snapmapper.ext.findConstString +import me.rhunk.snapmapper.ext.getClassName + +class ScoreUpdateMapper : AbstractClassMapper() { + override fun run(context: MapperContext) { + for (classDef in context.classes) { + classDef.methods.firstOrNull { + it.name == "" && + it.parameterTypes.size > 4 && + it.parameterTypes[1] == "Ljava/lang/Long;" && + it.parameterTypes[3] == "Ljava/util/Collection;" + } ?: continue + if (classDef.methods.firstOrNull { + it.name == "toString" + }?.implementation?.findConstString("Friend.sq:selectFriendUserScoresNeedToUpdate") != true) continue + + context.addMapping("ScoreUpdate", classDef.getClassName()) + return + } + } +} \ No newline at end of file diff --git a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/StoryBoostStateMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/StoryBoostStateMapper.kt index b6ee421f..a01ee2e8 100644 --- a/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/StoryBoostStateMapper.kt +++ b/mapper/src/main/kotlin/me/rhunk/snapmapper/impl/StoryBoostStateMapper.kt @@ -18,6 +18,7 @@ class StoryBoostStateMapper : AbstractClassMapper() { if (storyBoostEnumClass.getStaticConstructor()?.implementation?.findConstString("NeedSubscriptionCannotSubscribe") != true) continue context.addMapping("StoryBoostStateClass", clazz.type.replace("L", "").replace(";", "")) + return } } } \ 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 index 604c6566..5da213fd 100644 --- a/mapper/src/test/kotlin/me/rhunk/snapenhance/mapper/tests/TestMappings.kt +++ b/mapper/src/test/kotlin/me/rhunk/snapenhance/mapper/tests/TestMappings.kt @@ -21,7 +21,9 @@ class TestMappings { PlusSubscriptionMapper::class, ScCameraSettingsMapper::class, StoryBoostStateMapper::class, - FriendsFeedEventDispatcherMapper::class + FriendsFeedEventDispatcherMapper::class, + CompositeConfigurationProviderMapper::class, + ScoreUpdateMapper::class, ) val gson = GsonBuilder().setPrettyPrinting().create()