mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-09 19:03:55 +01:00
Compare commits
11 Commits
v4.0.0-dev
...
v4.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
002f11a854 | ||
|
|
861e9a399e | ||
|
|
795aee13e4 | ||
|
|
09f29e6119 | ||
|
|
784aa2f246 | ||
|
|
4ddd5a0b8f | ||
|
|
1482e7e1e8 | ||
|
|
806e94481c | ||
|
|
edcb6eac6d | ||
|
|
4dced95d54 | ||
|
|
62c9085096 |
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,3 +1,31 @@
|
|||||||
|
# [4.0.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.7...v4.0.0-dev.8) (2024-01-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Shorten setting titles to fit on screen ([#2579](https://github.com/ReVanced/revanced-patches/issues/2579)) ([b2a5dd3](https://github.com/ReVanced/revanced-patches/commit/b2a5dd3efc39ae8a42159858b9c00b5b2f8655a4))
|
||||||
|
|
||||||
|
# [4.0.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.6...v4.0.0-dev.7) (2024-01-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Use new integrations patch path ([51e2f3b](https://github.com/ReVanced/revanced-patches/commit/51e2f3b476b49460e2f3fc2b5f302a3a72d7963f))
|
||||||
|
|
||||||
|
# [4.0.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.5...v4.0.0-dev.6) (2024-01-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Tiktok - Playback speed:** Remember playback speed ([#2506](https://github.com/ReVanced/revanced-patches/issues/2506)) ([d2970e5](https://github.com/ReVanced/revanced-patches/commit/d2970e54fbbd7e4b1ae1d354ae2d5c4bbe9336b0))
|
||||||
|
|
||||||
|
# [4.0.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.4...v4.0.0-dev.5) (2024-01-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Change header:** Improve patch descriptions ([#2581](https://github.com/ReVanced/revanced-patches/issues/2581)) ([43a5677](https://github.com/ReVanced/revanced-patches/commit/43a5677397380f14a049ae95532fd5096b94c938))
|
||||||
|
|
||||||
# [4.0.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.3...v4.0.0-dev.4) (2024-01-09)
|
# [4.0.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.0.0-dev.3...v4.0.0-dev.4) (2024-01-09)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching = true
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 4.0.0-dev.4
|
version = 4.0.0-dev.8
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[versions]
|
[versions]
|
||||||
revanced-patcher = "19.1.0"
|
revanced-patcher = "19.2.0"
|
||||||
smali = "3.0.3"
|
smali = "3.0.3"
|
||||||
guava = "33.0.0-jre"
|
guava = "33.0.0-jre"
|
||||||
gson = "2.10.1"
|
gson = "2.10.1"
|
||||||
|
|||||||
5045
package-lock.json
generated
5045
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
|||||||
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
"@saithodev/semantic-release-backmerge": "^4.0.1",
|
||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
"gradle-semantic-release-plugin": "^1.8.0",
|
"gradle-semantic-release-plugin": "^1.9.0",
|
||||||
"semantic-release": "^22.0.12"
|
"semantic-release": "^22.0.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -10,7 +10,6 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
|||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
|
abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
|
||||||
|
|
||||||
abstract fun filterMap(
|
abstract fun filterMap(
|
||||||
classDef: ClassDef,
|
classDef: ClassDef,
|
||||||
method: Method,
|
method: Method,
|
||||||
|
|||||||
@@ -1,49 +1,82 @@
|
|||||||
package app.revanced.patches.tiktok.interaction.speed
|
package app.revanced.patches.tiktok.interaction.speed
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patches.tiktok.interaction.speed.fingerprints.GetSpeedFingerprint
|
||||||
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SpeedControlParentFingerprint
|
import app.revanced.patches.tiktok.interaction.speed.fingerprints.OnRenderFirstFrameFingerprint
|
||||||
|
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SetSpeedFingerprint
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstruction
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Playback speed",
|
name = "Playback speed",
|
||||||
description = "Enables the playback speed option for all videos.",
|
description = "Enables the playback speed option for all videos and " +
|
||||||
|
"retains the speed configurations in between videos.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.ss.android.ugc.trill", ["32.5.3"]),
|
CompatiblePackage("com.ss.android.ugc.trill", ["32.5.3"]),
|
||||||
CompatiblePackage("com.zhiliaoapp.musically", ["32.5.3"])
|
CompatiblePackage("com.zhiliaoapp.musically", ["32.5.3"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object PlaybackSpeedPatch : BytecodePatch(setOf(SpeedControlParentFingerprint)) {
|
object PlaybackSpeedPatch : BytecodePatch(
|
||||||
|
setOf(
|
||||||
|
GetSpeedFingerprint,
|
||||||
|
OnRenderFirstFrameFingerprint,
|
||||||
|
SetSpeedFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
SpeedControlParentFingerprint.result?.mutableMethod?.apply {
|
SetSpeedFingerprint.result?.let { onVideoSwiped ->
|
||||||
val targetMethodCallIndex = indexOfFirstInstruction {
|
// Remember the playback speed of the current video.
|
||||||
if (opcode == Opcode.INVOKE_STATIC) {
|
GetSpeedFingerprint.result?.mutableMethod?.apply {
|
||||||
val paramsTypes = ((this as Instruction35c).reference as MethodReference).parameterTypes
|
val injectIndex = indexOfFirstInstruction { getReference<MethodReference>()?.returnType == "F" } + 2
|
||||||
paramsTypes.size == 1 && paramsTypes[0].contains("/Aweme;")
|
val register = getInstruction<Instruction11x>(injectIndex - 1).registerA
|
||||||
} else false
|
|
||||||
}
|
|
||||||
|
|
||||||
val isSpeedEnableMethod = context
|
addInstruction(
|
||||||
.toMethodWalker(this)
|
injectIndex,
|
||||||
.nextMethod(targetMethodCallIndex, true)
|
"invoke-static { v$register }," +
|
||||||
.getMethod() as MutableMethod
|
" Lapp/revanced/integrations/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V"
|
||||||
|
)
|
||||||
|
} ?: throw GetSpeedFingerprint.exception
|
||||||
|
|
||||||
isSpeedEnableMethod.addInstructions(
|
// By default, the playback speed will reset to 1.0 at the start of each video.
|
||||||
|
// Instead, override it with the desired playback speed.
|
||||||
|
OnRenderFirstFrameFingerprint.result?.mutableMethod?.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
const/4 v0, 0x1
|
# Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method.
|
||||||
return v0
|
const/4 v0, 0x1
|
||||||
|
invoke-virtual {p0, v0}, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getEnterFrom(Z)Ljava/lang/String;
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
# Model of current video retrieved using getCurrentAweme method.
|
||||||
|
invoke-virtual {p0}, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getCurrentAweme()Lcom/ss/android/ugc/aweme/feed/model/Aweme;
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
# Desired playback speed retrieved using getPlaybackSpeed method.
|
||||||
|
invoke-static {}, Lapp/revanced/integrations/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F
|
||||||
|
move-result-object v2
|
||||||
|
invoke-static { v0, v1, v2 }, ${onVideoSwiped.method}
|
||||||
|
"""
|
||||||
|
) ?: throw OnRenderFirstFrameFingerprint.exception
|
||||||
|
|
||||||
|
// Force enable the playback speed option for all videos.
|
||||||
|
onVideoSwiped.mutableClass.methods.find { method -> method.returnType == "Z" }?.addInstructions(
|
||||||
|
0,
|
||||||
"""
|
"""
|
||||||
)
|
const/4 v0, 0x1
|
||||||
} ?: throw SpeedControlParentFingerprint.exception
|
return v0
|
||||||
|
"""
|
||||||
|
) ?: throw PatchException("Failed to force enable the playback speed option.")
|
||||||
|
} ?: throw SetSpeedFingerprint.exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
|
||||||
|
internal object GetSpeedFingerprint : MethodFingerprint(
|
||||||
|
customFingerprint = { methodDef, _ ->
|
||||||
|
methodDef.definingClass.endsWith("/BaseListFragmentPanel;") && methodDef.name == "onFeedSpeedSelectedEvent"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
|
||||||
|
internal object OnRenderFirstFrameFingerprint : MethodFingerprint(
|
||||||
|
customFingerprint = { methodDef, _ ->
|
||||||
|
methodDef.definingClass.endsWith("/BaseListFragmentPanel;") && methodDef.name == "onRenderFirstFrame"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object SetSpeedFingerprint : MethodFingerprint(
|
||||||
|
returnType = "V",
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
parameters = listOf(
|
||||||
|
"Ljava/lang/String;",
|
||||||
|
"Lcom/ss/android/ugc/aweme/feed/model/Aweme;",
|
||||||
|
"F"
|
||||||
|
),
|
||||||
|
strings = listOf("enterFrom")
|
||||||
|
)
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package app.revanced.patches.tiktok.interaction.speed.fingerprints
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
|
|
||||||
internal object SpeedControlParentFingerprint : MethodFingerprint(
|
|
||||||
strings = listOf(
|
|
||||||
"onStopTrackingTouch, hasTouchMove=",
|
|
||||||
", isCurVideoPaused: ",
|
|
||||||
"already_shown_edge_speed_guide"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -12,7 +12,7 @@ import java.io.File
|
|||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Change header",
|
name = "Change header",
|
||||||
description = "Change the in app header. Defaults to the ReVanced header.",
|
description = "Applies a custom header in the top left corner within the app. Defaults to the ReVanced header.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.google.android.youtube")
|
CompatiblePackage("com.google.android.youtube")
|
||||||
],
|
],
|
||||||
@@ -48,8 +48,8 @@ object ChangeHeaderPatch : ResourcePatch() {
|
|||||||
),
|
),
|
||||||
title = "Header",
|
title = "Header",
|
||||||
description = """
|
description = """
|
||||||
The header to use in top bar or the path to a custom header.
|
Either a header name or a path to a custom header folder to use in the top bar.
|
||||||
The path to a folder containing one or more of the following folders matching the DPI of your device:
|
The path to a folder must contain one or more of the following folders matching the DPI of your device:
|
||||||
|
|
||||||
${targetResourceDirectoryNames.joinToString("\n") { "- $it" }}
|
${targetResourceDirectoryNames.joinToString("\n") { "- $it" }}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
|
|||||||
"revanced_hide_search_result_recommendations",
|
"revanced_hide_search_result_recommendations",
|
||||||
StringResource(
|
StringResource(
|
||||||
"revanced_hide_search_result_recommendations_title",
|
"revanced_hide_search_result_recommendations_title",
|
||||||
"Hide search result recommendations (e.g People also watched)"
|
"Hide \\\'People also watched\\\' recommendations"
|
||||||
),
|
),
|
||||||
StringResource(
|
StringResource(
|
||||||
"revanced_hide_search_result_recommendations_summary_on",
|
"revanced_hide_search_result_recommendations_summary_on",
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package app.revanced.patches.youtube.layout.returnyoutubedislike
|
package app.revanced.patches.youtube.layout.returnyoutubedislike
|
||||||
|
|
||||||
import app.revanced.util.exception
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import app.revanced.util.indexOfFirstInstruction
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
@@ -11,19 +8,39 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.*
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.ConversionContextFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.DislikeFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.DislikesOldLayoutTextViewFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.LikeFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RemoveLikeFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberMeasureAnimatedTextFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberMeasureStaticLabelFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberMeasureTextParentFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberSetterFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.RollingNumberTextViewFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.ShortsTextViewFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.TextComponentConstructorFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.TextComponentDataFingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.TextComponentLookupFingerprint
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
||||||
import app.revanced.patches.youtube.shared.fingerprints.RollingNumberTextViewAnimationUpdateFingerprint
|
import app.revanced.patches.youtube.shared.fingerprints.RollingNumberTextViewAnimationUpdateFingerprint
|
||||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||||
|
import app.revanced.util.exception
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
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.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
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.instruction.TwoRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Return YouTube Dislike",
|
name = "Return YouTube Dislike",
|
||||||
@@ -38,6 +55,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube", [
|
"com.google.android.youtube", [
|
||||||
|
"18.38.44",
|
||||||
"18.43.45",
|
"18.43.45",
|
||||||
"18.44.41",
|
"18.44.41",
|
||||||
"18.45.41",
|
"18.45.41",
|
||||||
@@ -49,7 +67,9 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object ReturnYouTubeDislikePatch : BytecodePatch(
|
object ReturnYouTubeDislikePatch : BytecodePatch(
|
||||||
setOf(
|
setOf(
|
||||||
|
ConversionContextFingerprint,
|
||||||
TextComponentConstructorFingerprint,
|
TextComponentConstructorFingerprint,
|
||||||
|
TextComponentDataFingerprint,
|
||||||
ShortsTextViewFingerprint,
|
ShortsTextViewFingerprint,
|
||||||
DislikesOldLayoutTextViewFingerprint,
|
DislikesOldLayoutTextViewFingerprint,
|
||||||
LikeFingerprint,
|
LikeFingerprint,
|
||||||
@@ -67,6 +87,8 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
|
|||||||
private const val FILTER_CLASS_DESCRIPTOR =
|
private const val FILTER_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/integrations/youtube/patches/components/ReturnYouTubeDislikeFilterPatch;"
|
"Lapp/revanced/integrations/youtube/patches/components/ReturnYouTubeDislikeFilterPatch;"
|
||||||
|
|
||||||
|
private fun MethodFingerprint.resultOrThrow() = result ?: throw exception
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// region Inject newVideoLoaded event handler to update dislikes when a new video is loaded.
|
// region Inject newVideoLoaded event handler to update dislikes when a new video is loaded.
|
||||||
|
|
||||||
@@ -97,64 +119,132 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Hook creation of Spans and the cached lookup of them.
|
// region Hook code for creation and cached lookup of text Spans.
|
||||||
|
|
||||||
// Alternatively the hook can be made at the creation of Spans in TextComponentSpec,
|
// Alternatively the hook can be made at the creation of Spans in TextComponentSpec,
|
||||||
// And it works in all situations except it fails to update the Span when the user dislikes,
|
// And it works in all situations except it fails to update the Span when the user dislikes,
|
||||||
// since the underlying (likes only) text did not change.
|
// since the underlying (likes only) text did not change.
|
||||||
// This hook handles all situations, as it's where the created Spans are stored and later reused.
|
// This hook handles all situations, as it's where the created Spans are stored and later reused.
|
||||||
TextComponentContextFingerprint.also {
|
TextComponentConstructorFingerprint.result?.let { textConstructorResult ->
|
||||||
if (!it.resolve(context, TextComponentConstructorFingerprint.result!!.classDef))
|
// Find the field name of the conversion context.
|
||||||
throw it.exception
|
val conversionContextClassType = ConversionContextFingerprint.resultOrThrow().classDef.type
|
||||||
}.result?.also { result ->
|
val conversionContextField = textConstructorResult.classDef.fields.find {
|
||||||
if (!TextComponentAtomicReferenceFingerprint.resolve(context, result.method, result.classDef))
|
it.type == conversionContextClassType
|
||||||
throw TextComponentAtomicReferenceFingerprint.exception
|
} ?: throw PatchException("Could not find conversion context field")
|
||||||
}?.let { textComponentContextFingerprintResult ->
|
|
||||||
val conversionContextIndex = textComponentContextFingerprintResult
|
|
||||||
.scanResult.patternScanResult!!.endIndex
|
|
||||||
val atomicReferenceStartIndex = TextComponentAtomicReferenceFingerprint.result!!
|
|
||||||
.scanResult.patternScanResult!!.startIndex
|
|
||||||
|
|
||||||
val insertIndex = atomicReferenceStartIndex + 9
|
TextComponentLookupFingerprint.resolve(context, textConstructorResult.classDef)
|
||||||
|
TextComponentLookupFingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
// Find the instruction for creating the text data object.
|
||||||
|
val textDataClassType = TextComponentDataFingerprint.resultOrThrow().classDef.type
|
||||||
|
val insertIndex = indexOfFirstInstruction {
|
||||||
|
opcode == Opcode.NEW_INSTANCE &&
|
||||||
|
getReference<TypeReference>()?.type == textDataClassType
|
||||||
|
}
|
||||||
|
if (insertIndex < 0) throw PatchException("Could not find data creation instruction")
|
||||||
|
val tempRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||||
|
|
||||||
textComponentContextFingerprintResult.mutableMethod.apply {
|
// Find the instruction that sets the span to an instance field.
|
||||||
// Get the conversion context obfuscated field name
|
// The instruction is only a few lines after the creation of the instance.
|
||||||
val conversionContextFieldReference =
|
// The method has multiple iput-object instructions using a CharSequence,
|
||||||
getInstruction<ReferenceInstruction>(conversionContextIndex).reference
|
// so verify the found instruction is in the expected location.
|
||||||
|
val putFieldInstruction = implementation!!.instructions
|
||||||
|
.subList(insertIndex, insertIndex + 20)
|
||||||
|
.find {
|
||||||
|
it.opcode == Opcode.IPUT_OBJECT &&
|
||||||
|
it.getReference<FieldReference>()?.type == "Ljava/lang/CharSequence;"
|
||||||
|
} ?: throw PatchException("Could not find put object instruction")
|
||||||
|
val charSequenceRegister = (putFieldInstruction as TwoRegisterInstruction).registerA
|
||||||
|
|
||||||
// Free register to hold the conversion context
|
|
||||||
val freeRegister =
|
|
||||||
getInstruction<TwoRegisterInstruction>(atomicReferenceStartIndex).registerB
|
|
||||||
|
|
||||||
val atomicReferenceRegister =
|
|
||||||
getInstruction<FiveRegisterInstruction>(atomicReferenceStartIndex + 6).registerC
|
|
||||||
|
|
||||||
// Instruction that is replaced, and also has the CharacterSequence register.
|
|
||||||
val moveCharSequenceInstruction = getInstruction<TwoRegisterInstruction>(insertIndex)
|
|
||||||
val charSequenceSourceRegister = moveCharSequenceInstruction.registerB
|
|
||||||
val charSequenceTargetRegister = moveCharSequenceInstruction.registerA
|
|
||||||
|
|
||||||
// Move the current instance to the free register, and get the conversion context from it.
|
|
||||||
// Must replace the instruction to preserve the control flow label.
|
|
||||||
replaceInstruction(insertIndex, "move-object/from16 v$freeRegister, p0")
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
insertIndex + 1,
|
insertIndex,
|
||||||
"""
|
|
||||||
# Move context to free register
|
|
||||||
iget-object v$freeRegister, v$freeRegister, $conversionContextFieldReference
|
|
||||||
invoke-static {v$freeRegister, v$atomicReferenceRegister, v$charSequenceSourceRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/util/concurrent/atomic/AtomicReference;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
|
||||||
move-result-object v$freeRegister
|
|
||||||
# Replace the original instruction
|
|
||||||
move-object v${charSequenceTargetRegister}, v${freeRegister}
|
|
||||||
"""
|
"""
|
||||||
|
# Copy conversion context
|
||||||
|
move-object/from16 v$tempRegister, p0
|
||||||
|
iget-object v$tempRegister, v$tempRegister, $conversionContextField
|
||||||
|
invoke-static {v$tempRegister, v$charSequenceRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||||
|
move-result-object v$charSequenceRegister
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: throw TextComponentContextFingerprint.exception
|
} ?: throw TextComponentConstructorFingerprint.exception
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region Hook for non-litho Short videos.
|
||||||
|
|
||||||
|
ShortsTextViewFingerprint.result?.let {
|
||||||
|
it.mutableMethod.apply {
|
||||||
|
val patternResult = it.scanResult.patternScanResult!!
|
||||||
|
|
||||||
|
// If the field is true, the TextView is for a dislike button.
|
||||||
|
val isDisLikesBooleanReference = getInstruction<ReferenceInstruction>(patternResult.endIndex).reference
|
||||||
|
|
||||||
|
val textViewFieldReference = // Like/Dislike button TextView field
|
||||||
|
getInstruction<ReferenceInstruction>(patternResult.endIndex - 1).reference
|
||||||
|
|
||||||
|
// Check if the hooked TextView object is that of the dislike button.
|
||||||
|
// If RYD is disabled, or the TextView object is not that of the dislike button, the execution flow is not interrupted.
|
||||||
|
// Otherwise, the TextView object is modified, and the execution flow is interrupted to prevent it from being changed afterward.
|
||||||
|
val insertIndex = patternResult.startIndex + 6
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
insertIndex,
|
||||||
|
"""
|
||||||
|
# Check, if the TextView is for a dislike button
|
||||||
|
iget-boolean v0, p0, $isDisLikesBooleanReference
|
||||||
|
if-eqz v0, :is_like
|
||||||
|
|
||||||
|
# Hook the TextView, if it is for the dislike button
|
||||||
|
iget-object v0, p0, $textViewFieldReference
|
||||||
|
invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->setShortsDislikes(Landroid/view/View;)Z
|
||||||
|
move-result v0
|
||||||
|
if-eqz v0, :ryd_disabled
|
||||||
|
return-void
|
||||||
|
|
||||||
|
:is_like
|
||||||
|
:ryd_disabled
|
||||||
|
nop
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: throw ShortsTextViewFingerprint.exception
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Hook for litho Shorts
|
||||||
|
|
||||||
|
// Filter that parses the video id from the UI
|
||||||
|
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)
|
||||||
|
|
||||||
|
// Player response video id is needed to search for the video ids in Shorts litho components.
|
||||||
|
VideoIdPatch.hookPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Hook old UI layout dislikes, for the older app spoofs used with spoof-app-version.
|
||||||
|
|
||||||
|
DislikesOldLayoutTextViewFingerprint.result?.let {
|
||||||
|
it.mutableMethod.apply {
|
||||||
|
val startIndex = it.scanResult.patternScanResult!!.startIndex
|
||||||
|
|
||||||
|
val resourceIdentifierRegister = getInstruction<OneRegisterInstruction>(startIndex).registerA
|
||||||
|
val textViewRegister = getInstruction<OneRegisterInstruction>(startIndex + 4).registerA
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
startIndex + 4,
|
||||||
|
"invoke-static {v$resourceIdentifierRegister, v$textViewRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setOldUILayoutDislikes(ILandroid/widget/TextView;)V"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: throw DislikesOldLayoutTextViewFingerprint.exception
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
|
||||||
// region Hook rolling numbers.
|
// region Hook rolling numbers.
|
||||||
|
|
||||||
|
// Do this last to allow patching old unsupported versions (if the user really wants),
|
||||||
|
// On older unsupported version this will fail to resolve and throw an exception,
|
||||||
|
// but everything will still work correctly anyways.
|
||||||
|
|
||||||
RollingNumberSetterFingerprint.result?.let {
|
RollingNumberSetterFingerprint.result?.let {
|
||||||
val dislikesIndex = it.scanResult.patternScanResult!!.endIndex
|
val dislikesIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
|
|
||||||
@@ -164,7 +254,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
|
|||||||
val charSequenceInstanceRegister =
|
val charSequenceInstanceRegister =
|
||||||
getInstruction<OneRegisterInstruction>(0).registerA
|
getInstruction<OneRegisterInstruction>(0).registerA
|
||||||
val charSequenceFieldReference =
|
val charSequenceFieldReference =
|
||||||
getInstruction<ReferenceInstruction>(dislikesIndex).reference.toString()
|
getInstruction<ReferenceInstruction>(dislikesIndex).reference
|
||||||
|
|
||||||
val registerCount = implementation!!.registerCount
|
val registerCount = implementation!!.registerCount
|
||||||
|
|
||||||
@@ -268,73 +358,6 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Hook for non-litho Short videos.
|
|
||||||
|
|
||||||
ShortsTextViewFingerprint.result?.let {
|
|
||||||
it.mutableMethod.apply {
|
|
||||||
val patternResult = it.scanResult.patternScanResult!!
|
|
||||||
|
|
||||||
// If the field is true, the TextView is for a dislike button.
|
|
||||||
val isDisLikesBooleanReference = getInstruction<ReferenceInstruction>(patternResult.endIndex).reference
|
|
||||||
|
|
||||||
val textViewFieldReference = // Like/Dislike button TextView field
|
|
||||||
getInstruction<ReferenceInstruction>(patternResult.endIndex - 1).reference
|
|
||||||
|
|
||||||
// Check if the hooked TextView object is that of the dislike button.
|
|
||||||
// If RYD is disabled, or the TextView object is not that of the dislike button, the execution flow is not interrupted.
|
|
||||||
// Otherwise, the TextView object is modified, and the execution flow is interrupted to prevent it from being changed afterward.
|
|
||||||
val insertIndex = patternResult.startIndex + 6
|
|
||||||
addInstructionsWithLabels(
|
|
||||||
insertIndex,
|
|
||||||
"""
|
|
||||||
# Check, if the TextView is for a dislike button
|
|
||||||
iget-boolean v0, p0, $isDisLikesBooleanReference
|
|
||||||
if-eqz v0, :is_like
|
|
||||||
|
|
||||||
# Hook the TextView, if it is for the dislike button
|
|
||||||
iget-object v0, p0, $textViewFieldReference
|
|
||||||
invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->setShortsDislikes(Landroid/view/View;)Z
|
|
||||||
move-result v0
|
|
||||||
if-eqz v0, :ryd_disabled
|
|
||||||
return-void
|
|
||||||
|
|
||||||
:is_like
|
|
||||||
:ryd_disabled
|
|
||||||
nop
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} ?: throw ShortsTextViewFingerprint.exception
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Hook for litho Shorts
|
|
||||||
|
|
||||||
// Filter that parses the video id from the UI
|
|
||||||
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)
|
|
||||||
|
|
||||||
// Player response video id is needed to search for the video ids in Shorts litho components.
|
|
||||||
VideoIdPatch.hookPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Hook old UI layout dislikes, for the older app spoofs used with spoof-app-version.
|
|
||||||
|
|
||||||
DislikesOldLayoutTextViewFingerprint.result?.let {
|
|
||||||
it.mutableMethod.apply {
|
|
||||||
val startIndex = it.scanResult.patternScanResult!!.startIndex
|
|
||||||
|
|
||||||
val resourceIdentifierRegister = getInstruction<OneRegisterInstruction>(startIndex).registerA
|
|
||||||
val textViewRegister = getInstruction<OneRegisterInstruction>(startIndex + 4).registerA
|
|
||||||
|
|
||||||
addInstruction(
|
|
||||||
startIndex + 4,
|
|
||||||
"invoke-static {v$resourceIdentifierRegister, v$textViewRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setOldUILayoutDislikes(ILandroid/widget/TextView;)V"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} ?: throw DislikesOldLayoutTextViewFingerprint.exception
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MethodFingerprint.toPatch(voteKind: Vote) = VotePatch(this, voteKind)
|
private fun MethodFingerprint.toPatch(voteKind: Vote) = VotePatch(this, voteKind)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
|
||||||
|
internal object ConversionContextFingerprint : MethodFingerprint(
|
||||||
|
returnType = "Ljava/lang/String;",
|
||||||
|
parameters = listOf(),
|
||||||
|
strings = listOf(
|
||||||
|
", widthConstraint=",
|
||||||
|
", heightConstraint=",
|
||||||
|
", templateLoggerFactory=",
|
||||||
|
", rootDisposableContainer=",
|
||||||
|
// 18.37.36 and after this String is: ConversionContext{containerInternal=
|
||||||
|
// and before it is: ConversionContext{container=
|
||||||
|
// Use a partial string to match both.
|
||||||
|
"ConversionContext{container"
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.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 against the same method that [TextComponentContextFingerprint] resolves to.
|
|
||||||
*/
|
|
||||||
internal object TextComponentAtomicReferenceFingerprint : MethodFingerprint(
|
|
||||||
returnType = "L",
|
|
||||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
|
||||||
parameters = listOf("L"),
|
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.MOVE_OBJECT, // Register B is free register
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
null,
|
|
||||||
Opcode.INVOKE_VIRTUAL, // Register C is atomic reference
|
|
||||||
Opcode.MOVE_RESULT_OBJECT, // Register A is char sequence
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.MOVE_OBJECT, // Replace this instruction with patch code
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.GOTO
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||||
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.layout.returnyoutubedislike.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 against the same class that [TextComponentConstructorFingerprint] resolves to.
|
|
||||||
*/
|
|
||||||
internal object TextComponentContextFingerprint : MethodFingerprint(
|
|
||||||
returnType = "L",
|
|
||||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
|
||||||
parameters = listOf("L"),
|
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
|
||||||
Opcode.INVOKE_STATIC_RANGE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.IGET_OBJECT, // conversion context field name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object TextComponentDataFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
parameters = listOf("L", "L"),
|
||||||
|
strings = listOf("text"),
|
||||||
|
customFingerprint = { _, classDef ->
|
||||||
|
val fields = classDef.fields
|
||||||
|
fields.find { it.type == "Ljava/util/BitSet;" } != null &&
|
||||||
|
fields.find { it.type == "[Ljava/lang/String;" } != null
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves against the same class that [TextComponentConstructorFingerprint] resolves to.
|
||||||
|
*/
|
||||||
|
internal object TextComponentLookupFingerprint : MethodFingerprint(
|
||||||
|
returnType = "L",
|
||||||
|
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||||
|
parameters = listOf("L"),
|
||||||
|
strings = listOf("…")
|
||||||
|
)
|
||||||
@@ -43,7 +43,7 @@ object AnnouncementsPatch : BytecodePatch(
|
|||||||
"revanced_announcements",
|
"revanced_announcements",
|
||||||
StringResource(
|
StringResource(
|
||||||
"revanced_announcements_title",
|
"revanced_announcements_title",
|
||||||
"Show announcements from ReVanced"
|
"Show ReVanced announcements"
|
||||||
),
|
),
|
||||||
StringResource(
|
StringResource(
|
||||||
"revanced_announcements_summary_on",
|
"revanced_announcements_summary_on",
|
||||||
|
|||||||
Reference in New Issue
Block a user