diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fingerprints/RollingNumberTextViewAnimationUpdateFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fingerprints/RollingNumberTextViewAnimationUpdateFingerprint.kt new file mode 100644 index 000000000..0f619f53f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fingerprints/RollingNumberTextViewAnimationUpdateFingerprint.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.youtube.utils.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +object RollingNumberTextViewAnimationUpdateFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Landroid/graphics/Bitmap;"), + opcodes = listOf( + Opcode.NEW_INSTANCE, // bitmap ImageSpan + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_4, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.INT_TO_FLOAT, + Opcode.INVOKE_VIRTUAL, // set textview padding using bitmap width + ), + customFingerprint = { _, classDef -> + classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/ReturnYouTubeDislikeRollingNumberPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/ReturnYouTubeDislikeRollingNumberPatch.kt index b7c7ccee3..7afe6435c 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/ReturnYouTubeDislikeRollingNumberPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/ReturnYouTubeDislikeRollingNumberPatch.kt @@ -4,9 +4,14 @@ import app.revanced.extensions.exception import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.youtube.utils.fingerprints.RollingNumberTextViewAnimationUpdateFingerprint +import app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints.RollingNumberMeasureAnimatedTextFingerprint +import app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints.RollingNumberMeasureStaticLabelFingerprint +import app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints.RollingNumberMeasureTextParentFingerprint import app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints.RollingNumberSetterFingerprint import app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints.RollingNumberTextViewFingerprint import app.revanced.patches.youtube.utils.settings.SettingsPatch @@ -16,6 +21,7 @@ import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.Reference @@ -23,11 +29,11 @@ import com.android.tools.smali.dexlib2.iface.reference.Reference object ReturnYouTubeDislikeRollingNumberPatch : BytecodePatch( setOf( RollingNumberSetterFingerprint, - RollingNumberTextViewFingerprint + RollingNumberMeasureTextParentFingerprint, + RollingNumberTextViewFingerprint, + RollingNumberTextViewAnimationUpdateFingerprint ) ) { - private const val CONVERSION_CONTEXT_PARAMETER = 2 - private const val INTEGRATIONS_RYD_CLASS_DESCRIPTOR = "$UTILS_PATH/ReturnYouTubeDislikePatch;" @@ -37,9 +43,6 @@ object ReturnYouTubeDislikeRollingNumberPatch : BytecodePatch( * * In order to maintain compatibility with YouTube v18.40.34 or previous versions, * This patch is applied only to the version after YouTube v18.41.39 - * - * Resolves following issue: - * https://github.com/revanced/revanced-patches/issues/2904 */ if (SettingsPatch.upward1841) { @@ -84,6 +87,55 @@ object ReturnYouTubeDislikeRollingNumberPatch : BytecodePatch( } } ?: throw RollingNumberSetterFingerprint.exception + RollingNumberMeasureTextParentFingerprint.result?.let { parentResult -> + // Rolling Number text views use the measured width of the raw string for layout. + // Modify the measure text calculation to include the left drawable separator if needed. + RollingNumberMeasureAnimatedTextFingerprint.also { + it.resolve( + context, + parentResult.classDef + ) + }.result?.let { + it.mutableMethod.apply { + val returnInstructionIndex = it.scanResult.patternScanResult!!.endIndex + val measuredTextWidthRegister = + getInstruction(returnInstructionIndex).registerA + + replaceInstruction( // Replace instruction to preserve control flow label. + returnInstructionIndex, + "invoke-static {p1, v$measuredTextWidthRegister}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F" + ) + addInstructions( + returnInstructionIndex + 1, """ + move-result v$measuredTextWidthRegister + return v$measuredTextWidthRegister + """ + ) + } + } ?: throw RollingNumberMeasureAnimatedTextFingerprint.exception + + // Additional text measurement method. Used if YouTube decides not to animate the likes count + // and sometimes used for initial video load. + RollingNumberMeasureStaticLabelFingerprint.also { + it.resolve( + context, + parentResult.classDef + ) + }.result?.let { + it.mutableMethod.apply { + val measureTextIndex = it.scanResult.patternScanResult!!.startIndex + 1 + val freeRegister = getInstruction(0).registerA + + addInstructions( + measureTextIndex + 1, """ + move-result v$freeRegister + invoke-static {p1, v$freeRegister}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F + """ + ) + } + } ?: throw RollingNumberMeasureStaticLabelFingerprint.exception + } ?: throw RollingNumberMeasureTextParentFingerprint.exception + // The rolling number Span is missing styling since it's initially set as a String. // Modify the UI text view and use the styled like/dislike Span. RollingNumberTextViewFingerprint.result?.let { diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureAnimatedTextFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureAnimatedTextFingerprint.kt new file mode 100644 index 000000000..36b0d2c17 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureAnimatedTextFingerprint.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +/** + * Resolves to class found in [RollingNumberMeasureTextParentFingerprint]. + */ +object RollingNumberMeasureAnimatedTextFingerprint : MethodFingerprint( + returnType = "F", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Ljava/lang/String;"), + opcodes = listOf( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.ADD_FLOAT_2ADDR, + Opcode.ADD_INT_LIT8, + Opcode.GOTO, + Opcode.RETURN + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureStaticLabelFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureStaticLabelFingerprint.kt new file mode 100644 index 000000000..d1a8af72b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureStaticLabelFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +/** + * Resolves to class found in [RollingNumberMeasureTextParentFingerprint]. + */ +object RollingNumberMeasureStaticLabelFingerprint : MethodFingerprint( + returnType = "F", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Ljava/lang/String;"), + opcodes = listOf( + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.RETURN + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureTextParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureTextParentFingerprint.kt new file mode 100644 index 000000000..18c2652c6 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureTextParentFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +object RollingNumberMeasureTextParentFingerprint : MethodFingerprint( + returnType = "Ljava/lang/String;", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf(), + strings = listOf("RollingNumberFontProperties{paint=") +) \ No newline at end of file