diff --git a/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AboutOnClickMethodFingerprint.kt b/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AboutOnClickMethodFingerprint.kt new file mode 100644 index 000000000..0409f293e --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AboutOnClickMethodFingerprint.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.tiktok.misc.settings.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.tiktok.misc.settings.annotations.TikTokSettingsCompatibility + +@Name("about-onclick-method-fingerprint") +@TikTokSettingsCompatibility +@Version("0.0.1") +object AboutOnClickMethodFingerprint : MethodFingerprint( + strings = listOf( + "//setting/about", + "enter_from", + "settings_page", + "enter_settings_about" + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AdPersonalizationActivityFingerprint.kt b/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AdPersonalizationActivityOnCreateFingerprint.kt similarity index 80% rename from src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AdPersonalizationActivityFingerprint.kt rename to src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AdPersonalizationActivityOnCreateFingerprint.kt index 66e58ebac..1e3a98d76 100644 --- a/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AdPersonalizationActivityFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AdPersonalizationActivityOnCreateFingerprint.kt @@ -5,10 +5,10 @@ import app.revanced.patcher.annotation.Version import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patches.tiktok.misc.settings.annotations.TikTokSettingsCompatibility -@Name("ad-personalization-activity-fingerprint") +@Name("ad-personalization-activity-on-create-fingerprint") @TikTokSettingsCompatibility @Version("0.0.1") -object AdPersonalizationActivityFingerprint : MethodFingerprint( +object AdPersonalizationActivityOnCreateFingerprint : MethodFingerprint( customFingerprint = { methodDef -> methodDef.definingClass.endsWith("/AdPersonalizationActivity;") && methodDef.name == "onCreate" diff --git a/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/CopyRightSettingsStringFingerprint.kt b/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/SettingsOnViewCreatedFingerprint.kt similarity index 90% rename from src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/CopyRightSettingsStringFingerprint.kt rename to src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/SettingsOnViewCreatedFingerprint.kt index c1e8650f2..ed55df173 100644 --- a/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/CopyRightSettingsStringFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/SettingsOnViewCreatedFingerprint.kt @@ -8,7 +8,7 @@ import app.revanced.patches.tiktok.misc.settings.annotations.TikTokSettingsCompa @Name("copyright-settings-string-fingerprint") @TikTokSettingsCompatibility @Version("0.0.1") -object CopyRightSettingsStringFingerprint : MethodFingerprint( +object SettingsOnViewCreatedFingerprint : MethodFingerprint( customFingerprint = { methodDef -> methodDef.definingClass.endsWith("/SettingNewVersionFragment;") && methodDef.name == "onViewCreated" diff --git a/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/patch/TikTokSettingsPatch.kt b/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/patch/TikTokSettingsPatch.kt index 5e7122718..0420c5e7a 100644 --- a/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/patch/TikTokSettingsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/patch/TikTokSettingsPatch.kt @@ -5,6 +5,7 @@ import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Version import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.instruction import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchResult @@ -14,85 +15,111 @@ import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.Patch import app.revanced.patches.tiktok.misc.integrations.patch.TikTokIntegrationsPatch import app.revanced.patches.tiktok.misc.settings.annotations.TikTokSettingsCompatibility -import app.revanced.patches.tiktok.misc.settings.fingerprints.AdPersonalizationActivityFingerprint -import app.revanced.patches.tiktok.misc.settings.fingerprints.CopyRightSettingsStringFingerprint +import app.revanced.patches.tiktok.misc.settings.fingerprints.AboutOnClickMethodFingerprint +import app.revanced.patches.tiktok.misc.settings.fingerprints.AdPersonalizationActivityOnCreateFingerprint +import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsOnViewCreatedFingerprint import org.jf.dexlib2.Opcode import org.jf.dexlib2.iface.instruction.OneRegisterInstruction import org.jf.dexlib2.iface.instruction.ReferenceInstruction +import org.jf.dexlib2.iface.instruction.formats.Instruction21c import org.jf.dexlib2.iface.instruction.formats.Instruction35c -import org.jf.dexlib2.iface.reference.MethodReference import org.jf.dexlib2.iface.reference.StringReference +import org.jf.dexlib2.iface.reference.TypeReference @Patch @DependsOn([TikTokIntegrationsPatch::class]) -@Name("tiktok-settings") -@Description("Add settings menu to TikTok.") +@Name("settings") +@Description("Adds settings for ReVanced to TikTok.") @TikTokSettingsCompatibility @Version("0.0.1") class TikTokSettingsPatch : BytecodePatch( listOf( - AdPersonalizationActivityFingerprint, - CopyRightSettingsStringFingerprint + AdPersonalizationActivityOnCreateFingerprint, + SettingsOnViewCreatedFingerprint, + AboutOnClickMethodFingerprint ) ) { override fun execute(context: BytecodeContext): PatchResult { - //Replace string `Copyright Policy` to 'Revanced Settings` in TikTok settings. - val method1 = CopyRightSettingsStringFingerprint.result!!.mutableMethod - val implementation1 = method1.implementation!! - for ((index, instruction) in implementation1.instructions.withIndex()) { - if (instruction.opcode != Opcode.CONST_STRING) continue - val string = ((instruction as ReferenceInstruction).reference as StringReference).string - if (string != "copyright_policy") continue - var targetIndex = index - while (targetIndex >= 0) { - targetIndex-- - val invokeInstruction = implementation1.instructions[targetIndex] - if (invokeInstruction.opcode != Opcode.INVOKE_VIRTUAL) continue - val methodName = ((invokeInstruction as Instruction35c).reference as MethodReference).name - if (methodName != "getString") continue - val resultInstruction = implementation1.instructions[targetIndex + 1] - if (resultInstruction.opcode != Opcode.MOVE_RESULT_OBJECT) continue - val overrideRegister = (resultInstruction as OneRegisterInstruction).registerA - method1.replaceInstruction( - targetIndex + 1, + // Patch Settings UI to add 'Revanced Settings'. + val targetIndexes = findOptionsOnClickIndex() + with(SettingsOnViewCreatedFingerprint.result!!.mutableMethod) { + for (index in targetIndexes) { + if ( + instruction(index).opcode != Opcode.NEW_INSTANCE || + instruction(index - 4).opcode != Opcode.MOVE_RESULT_OBJECT + ) + return PatchResultError("Hardcode offset changed.") + patchOptionNameAndOnClickEvent(index, context) + } + } + // Implement settings screen in `AdPersonalizationActivity` + with(AdPersonalizationActivityOnCreateFingerprint.result!!.mutableMethod) { + for ((index, instruction) in implementation!!.instructions.withIndex()) { + if (instruction.opcode != Opcode.INVOKE_SUPER) continue + val thisRegister = (instruction as Instruction35c).registerC + addInstructions( + index + 1, """ - const-string v$overrideRegister, "Revanced Settings" + invoke-static {v$thisRegister}, Lapp/revanced/tiktok/settingsmenu/SettingsMenu;->initializeSettings(Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;)V + return-void """ ) break } - //Change onClick to start settings activity. - val clickInstruction = implementation1.instructions[index - 1] - if (clickInstruction.opcode != Opcode.INVOKE_DIRECT) - return PatchResultError("Can not find click listener.") - val clickClass = ((clickInstruction as ReferenceInstruction).reference as MethodReference).definingClass - val mutableClickClass = context.findClass(clickClass)!!.mutableClass - val mutableOnClickMethod = mutableClickClass.methods.first { - it.name == "onClick" - } - mutableOnClickMethod.addInstructions( - 0, - """ - invoke-static {}, Lapp/revanced/tiktok/settingsmenu/SettingsMenu;->startSettingsActivity()V - return-void - """ - ) - break - } - //Implement revanced settings screen in `AdPersonalizationActivity` - val method2 = AdPersonalizationActivityFingerprint.result!!.mutableMethod - for ((index, instruction) in method2.implementation!!.instructions.withIndex()) { - if (instruction.opcode != Opcode.INVOKE_SUPER) continue - val thisRegister = (instruction as Instruction35c).registerC - method2.addInstructions( - index + 1, - """ - invoke-static {v$thisRegister}, Lapp/revanced/tiktok/settingsmenu/SettingsMenu;->initializeSettings(Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;)V - return-void - """ - ) - break } return PatchResultSuccess() } + + private fun findOptionsOnClickIndex(): IntArray { + val results = IntArray(2) + var found = 0 + with(SettingsOnViewCreatedFingerprint.result!!.mutableMethod) { + for ((index, instruction) in implementation!!.instructions.withIndex()) { + // Old UI settings option to replace to 'Revanced Settings' + if (instruction.opcode == Opcode.CONST_STRING) { + val string = ((instruction as ReferenceInstruction).reference as StringReference).string + if (string == "copyright_policy") { + results[0] = index - 2 + found++ + } + } + + // New UI settings option to replace to 'Revanced Settings' + if (instruction.opcode == Opcode.NEW_INSTANCE) { + val onClickClass = ((instruction as Instruction21c).reference as TypeReference).type + if (onClickClass == AboutOnClickMethodFingerprint.result!!.mutableMethod.definingClass) { + results[1] = index + found++ + } + } + if (found > 1) break + } + } + return results + } + + private fun patchOptionNameAndOnClickEvent(index: Int, context: BytecodeContext) { + with(SettingsOnViewCreatedFingerprint.result!!.mutableMethod) { + // Patch option name + val overrideRegister = (instruction(index - 4) as OneRegisterInstruction).registerA + replaceInstruction( + index - 4, + """ + const-string v$overrideRegister, "Revanced Settings" + """ + ) + + // Patch option OnClick Event + with(((instruction(index) as ReferenceInstruction).reference as TypeReference).type) { + context.findClass(this)!!.mutableClass.methods.first { it.name == "onClick" } + .addInstructions( + 0, + """ + invoke-static {}, Lapp/revanced/tiktok/settingsmenu/SettingsMenu;->startSettingsActivity()V + return-void + """ + ) + } + } + } } \ No newline at end of file