feat(YouTube/Shorts components): add Double-tap animation settings

This commit is contained in:
inotia00
2024-06-27 23:47:32 +09:00
parent df97e5340a
commit 17dc2fdde6
16 changed files with 194 additions and 68 deletions

View File

@ -1,33 +0,0 @@
package app.revanced.patches.youtube.layout.animated
import app.revanced.patcher.data.ResourceContext
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.patch.BaseResourcePatch
@Suppress("unused")
object AnimatedButtonBackgroundPatch : BaseResourcePatch(
name = "Hide animated button background",
description = "Removes, at compile time, the background of the animated pause and play buttons in the Shorts player.",
dependencies = setOf(SettingsPatch::class),
compatiblePackages = COMPATIBLE_PACKAGE,
use = false
) {
override fun execute(context: ResourceContext) {
/**
* Copy json
*/
context.copyResources(
"youtube/shorts/animated",
ResourceGroup(
"raw",
"pause_tap_feedback.json",
"play_tap_feedback.json"
)
)
SettingsPatch.updatePatchStatus(this)
}
}

View File

@ -1,33 +0,0 @@
package app.revanced.patches.youtube.layout.animated
import app.revanced.patcher.data.ResourceContext
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.patch.BaseResourcePatch
@Suppress("unused")
object AnimatedLikePatch : BaseResourcePatch(
name = "Hide double tap to like animations",
description = "Removes, at compile time, the like animations that appear when double-tapping in the Shorts player.",
dependencies = setOf(SettingsPatch::class),
compatiblePackages = COMPATIBLE_PACKAGE,
use = false
) {
override fun execute(context: ResourceContext) {
/**
* Copy json
*/
context.copyResources(
"youtube/shorts/animated",
ResourceGroup(
"raw",
"like_tap_feedback.json"
)
)
SettingsPatch.updatePatchStatus(this)
}
}

View File

@ -0,0 +1,77 @@
package app.revanced.patches.youtube.shorts.components
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.shorts.components.fingerprints.ReelFeedbackFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.SHORTS_PATH
import app.revanced.patches.youtube.utils.lottie.LottieAnimationViewHookPatch
import app.revanced.patches.youtube.utils.lottie.fingerprints.SetAnimationFingerprint.LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelFeedbackLike
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelFeedbackPause
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelFeedbackPlay
import app.revanced.patches.youtube.utils.settings.SettingsPatch.contexts
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.getWideLiteralInstructionIndex
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
@Patch(dependencies = [LottieAnimationViewHookPatch::class])
object ShortsAnimationPatch : BytecodePatch(
setOf(ReelFeedbackFingerprint)
) {
private const val INTEGRATION_CLASS_DESCRIPTOR =
"$SHORTS_PATH/AnimationFeedbackPatch;"
override fun execute(context: BytecodeContext) {
ReelFeedbackFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
mapOf(
ReelFeedbackLike to "setShortsLikeFeedback",
ReelFeedbackPause to "setShortsPauseFeedback",
ReelFeedbackPlay to "setShortsPlayFeedback",
).forEach { (literal, methodName) ->
val literalIndex = getWideLiteralInstructionIndex(literal)
val viewIndex = indexOfFirstInstructionOrThrow(literalIndex) {
opcode == Opcode.CHECK_CAST
&& (this as? ReferenceInstruction)?.reference?.toString() == LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR
}
val viewRegister = getInstruction<OneRegisterInstruction>(viewIndex).registerA
val methodCall = "invoke-static {v$viewRegister}, " +
INTEGRATION_CLASS_DESCRIPTOR +
"->" +
methodName +
"($LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR)V"
addInstruction(
viewIndex + 1,
methodCall
)
}
}
}
/**
* Copy json
*/
contexts.copyResources(
"youtube/shorts/feedback",
ResourceGroup(
"raw",
"like_tap_feedback_cairo.json",
"like_tap_feedback_heart.json",
"like_tap_feedback_heart_tint.json",
"like_tap_feedback_hidden.json",
"pause_tap_feedback_hidden.json",
"play_tap_feedback_hidden.json"
)
)
}
}

View File

@ -57,6 +57,7 @@ object ShortsComponentPatch : BaseBytecodePatch(
PlayerTypeHookPatch::class,
SettingsPatch::class,
SharedResourceIdPatch::class,
ShortsAnimationPatch::class,
ShortsNavigationBarPatch::class,
ShortsToolBarPatch::class,
VideoInformationPatch::class

View File

@ -0,0 +1,16 @@
package app.revanced.patches.youtube.shorts.components.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelFeedbackLike
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelFeedbackPause
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ReelFeedbackPlay
import app.revanced.util.containsWideLiteralInstructionIndex
internal object ReelFeedbackFingerprint : MethodFingerprint(
returnType = "V",
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionIndex(ReelFeedbackLike)
&& methodDef.containsWideLiteralInstructionIndex(ReelFeedbackPause)
&& methodDef.containsWideLiteralInstructionIndex(ReelFeedbackPlay)
},
)

View File

@ -0,0 +1,40 @@
package app.revanced.patches.youtube.utils.lottie
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
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.integrations.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.lottie.fingerprints.SetAnimationFingerprint
import app.revanced.patches.youtube.utils.lottie.fingerprints.SetAnimationFingerprint.LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR
import app.revanced.util.resultOrThrow
@Patch(
description = "Hook YouTube to use LottieAnimationView.setAnimation in the integration.",
)
object LottieAnimationViewHookPatch : BytecodePatch(
setOf(SetAnimationFingerprint)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"$UTILS_PATH/LottieAnimationViewPatch;"
override fun execute(context: BytecodeContext) {
val setAnimationMethodName = SetAnimationFingerprint.resultOrThrow().mutableMethod.name
val setAnimationCall = "invoke-virtual {p0, p1}, " +
LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR +
"->" +
setAnimationMethodName +
"(I)V"
context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)
?.mutableClass
?.methods
?.first { method -> method.name == "setAnimation" }
?.addInstruction(
0,
setAnimationCall
)?: throw PatchException("Could not find setAnimation method")
}
}

View File

@ -0,0 +1,24 @@
package app.revanced.patches.youtube.utils.lottie.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.lottie.fingerprints.SetAnimationFingerprint.LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object SetAnimationFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("I"),
opcodes = listOf(
Opcode.IF_EQZ,
Opcode.NEW_INSTANCE,
Opcode.NEW_INSTANCE,
),
customFingerprint = { methodDef, _ ->
methodDef.definingClass == LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR
}
) {
const val LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR =
"Lcom/airbnb/lottie/LottieAnimationView;"
}