mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-12 13:17:46 +02:00
feat(YouTube): Premium heading
patches has been integrated into Toolbar components
patch
This commit is contained in:
@ -11,7 +11,10 @@ import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPat
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.shared.voicesearch.VoiceSearchUtils.patchXml
|
||||
import app.revanced.patches.youtube.general.toolbar.fingerprints.AttributeResolverFingerprint
|
||||
import app.revanced.patches.youtube.general.toolbar.fingerprints.CreateSearchSuggestionsFingerprint
|
||||
import app.revanced.patches.youtube.general.toolbar.fingerprints.DrawerContentViewConstructorFingerprint
|
||||
import app.revanced.patches.youtube.general.toolbar.fingerprints.DrawerContentViewFingerprint
|
||||
import app.revanced.patches.youtube.general.toolbar.fingerprints.SearchBarFingerprint
|
||||
import app.revanced.patches.youtube.general.toolbar.fingerprints.SearchBarParentFingerprint
|
||||
import app.revanced.patches.youtube.general.toolbar.fingerprints.SearchResultFingerprint
|
||||
@ -24,9 +27,13 @@ import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACK
|
||||
import app.revanced.patches.youtube.utils.integrations.Constants.GENERAL_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.VoiceSearch
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.YtPremiumWordMarkHeader
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.YtWordMarkHeader
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch.contexts
|
||||
import app.revanced.patches.youtube.utils.toolbar.ToolBarHookPatch
|
||||
import app.revanced.util.findMutableMethodOf
|
||||
import app.revanced.util.getTargetIndex
|
||||
import app.revanced.util.getTargetIndexWithMethodReferenceName
|
||||
import app.revanced.util.getTargetIndexWithReference
|
||||
import app.revanced.util.getTargetIndexWithReferenceReversed
|
||||
@ -35,10 +42,13 @@ import app.revanced.util.getWideLiteralInstructionIndex
|
||||
import app.revanced.util.literalInstructionBooleanHook
|
||||
import app.revanced.util.patch.BaseBytecodePatch
|
||||
import app.revanced.util.resultOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
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.instruction.formats.Instruction31i
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
@Suppress("DEPRECATION", "unused")
|
||||
object ToolBarComponentsPatch : BaseBytecodePatch(
|
||||
@ -52,7 +62,9 @@ object ToolBarComponentsPatch : BaseBytecodePatch(
|
||||
),
|
||||
compatiblePackages = COMPATIBLE_PACKAGE,
|
||||
fingerprints = setOf(
|
||||
AttributeResolverFingerprint,
|
||||
CreateSearchSuggestionsFingerprint,
|
||||
DrawerContentViewConstructorFingerprint,
|
||||
SearchBarParentFingerprint,
|
||||
SearchResultFingerprint,
|
||||
SetActionBarRingoFingerprint,
|
||||
@ -72,10 +84,67 @@ object ToolBarComponentsPatch : BaseBytecodePatch(
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
|
||||
// region patch for change header
|
||||
|
||||
// Invoke YouTube's header attribute into integrations.
|
||||
replaceHeaderAttributeId(context)
|
||||
|
||||
// YouTube's headers have the form of AttributeSet, which is decoded from YouTube's built-in classes.
|
||||
val attributeResolverMethod = AttributeResolverFingerprint.resultOrThrow().mutableMethod
|
||||
val attributeResolverMethodCall = attributeResolverMethod.definingClass + "->" + attributeResolverMethod.name + "(Landroid/content/Context;I)Landroid/graphics/drawable/Drawable;"
|
||||
|
||||
context.findClass(GENERAL_CLASS_DESCRIPTOR)!!.mutableClass.methods.single { method ->
|
||||
method.name == "getHeaderDrawable"
|
||||
}.addInstructions(
|
||||
0, """
|
||||
invoke-static {p0, p1}, $attributeResolverMethodCall
|
||||
move-result-object p0
|
||||
return-object p0
|
||||
"""
|
||||
)
|
||||
|
||||
// The sidebar's header is lithoView. Add a listener to change it.
|
||||
DrawerContentViewFingerprint.resolve(
|
||||
context,
|
||||
DrawerContentViewConstructorFingerprint.resultOrThrow().classDef
|
||||
)
|
||||
DrawerContentViewFingerprint.resultOrThrow().let {
|
||||
it.mutableMethod.apply {
|
||||
val insertIndex = getTargetIndexWithMethodReferenceName("addView")
|
||||
val insertRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
|
||||
|
||||
addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static {v$insertRegister}, $GENERAL_CLASS_DESCRIPTOR->setDrawerNavigationHeader(Landroid/view/View;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Override the header in the search bar.
|
||||
val setActionBarRingoMutableClass = SetActionBarRingoFingerprint.resultOrThrow().mutableClass
|
||||
setActionBarRingoMutableClass.methods.first {
|
||||
method -> MethodUtil.isConstructor(method)
|
||||
}.apply {
|
||||
val insertIndex = getTargetIndex(Opcode.IPUT_BOOLEAN)
|
||||
val insertRegister = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
insertIndex + 1,
|
||||
"const/4 v$insertRegister, 0x0"
|
||||
)
|
||||
addInstructions(
|
||||
insertIndex, """
|
||||
invoke-static {}, $GENERAL_CLASS_DESCRIPTOR->overridePremiumHeader()Z
|
||||
move-result v$insertRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region patch for enable wide search bar
|
||||
|
||||
val parentClassDef = SetActionBarRingoFingerprint.resultOrThrow().classDef
|
||||
YouActionBarFingerprint.resolve(context, parentClassDef)
|
||||
YouActionBarFingerprint.resolve(context, setActionBarRingoMutableClass)
|
||||
|
||||
SetWordMarkHeaderFingerprint.resultOrThrow().let {
|
||||
val walkerMethod = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex + 1)
|
||||
@ -243,6 +312,36 @@ object ToolBarComponentsPatch : BaseBytecodePatch(
|
||||
SettingsPatch.updatePatchStatus(this)
|
||||
}
|
||||
|
||||
private fun replaceHeaderAttributeId(context: BytecodeContext) {
|
||||
val headerAttributeIdArray = arrayOf(YtPremiumWordMarkHeader, YtWordMarkHeader)
|
||||
|
||||
context.classes.forEach { classDef ->
|
||||
classDef.methods.forEach { method ->
|
||||
method.implementation.apply {
|
||||
this?.instructions?.forEachIndexed { index, instruction ->
|
||||
if (instruction.opcode != Opcode.CONST)
|
||||
return@forEachIndexed
|
||||
if (headerAttributeIdArray.indexOf((instruction as Instruction31i).wideLiteral) < 0)
|
||||
return@forEachIndexed
|
||||
|
||||
(instructions.elementAt(index)).apply {
|
||||
val register = (this as OneRegisterInstruction).registerA
|
||||
context.proxy(classDef)
|
||||
.mutableClass
|
||||
.findMutableMethodOf(method)
|
||||
.addInstructions(
|
||||
index + 1, """
|
||||
invoke-static {}, $GENERAL_CLASS_DESCRIPTOR->getHeaderAttributeId()I
|
||||
move-result v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableMethod.injectSearchBarHook(
|
||||
insertIndex: Int,
|
||||
descriptor: String
|
||||
|
@ -0,0 +1,12 @@
|
||||
package app.revanced.patches.youtube.general.toolbar.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object AttributeResolverFingerprint : MethodFingerprint(
|
||||
returnType = "Landroid/graphics/drawable/Drawable;",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
parameters = listOf("Landroid/content/Context;", "I"),
|
||||
strings = listOf("Type of attribute is not a reference to a drawable (attr = %d, value = %s)")
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package app.revanced.patches.youtube.general.toolbar.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.DrawerContentView
|
||||
import app.revanced.util.fingerprint.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object DrawerContentViewConstructorFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
literalSupplier = { DrawerContentView }
|
||||
)
|
@ -0,0 +1,16 @@
|
||||
package app.revanced.patches.youtube.general.toolbar.fingerprints
|
||||
|
||||
import app.revanced.util.fingerprint.MethodReferenceNameFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object DrawerContentViewFingerprint : MethodReferenceNameFingerprint(
|
||||
returnType = "V",
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
),
|
||||
reference = { "addView" }
|
||||
)
|
@ -1,69 +0,0 @@
|
||||
package app.revanced.patches.youtube.layout.header
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
|
||||
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusHeader
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
import app.revanced.util.patch.BaseResourcePatch
|
||||
import kotlin.io.path.copyTo
|
||||
|
||||
@Suppress("DEPRECATION", "unused")
|
||||
object PremiumHeadingPatch : BaseResourcePatch(
|
||||
name = "Premium heading",
|
||||
description = "Show or hide the premium heading.",
|
||||
dependencies = setOf(SettingsPatch::class),
|
||||
compatiblePackages = COMPATIBLE_PACKAGE,
|
||||
use = false
|
||||
) {
|
||||
private const val DEFAULT_HEADING_RES = "yt_wordmark_header"
|
||||
private const val PREMIUM_HEADING_RES = "yt_premium_wordmark_header"
|
||||
|
||||
private val UsePremiumHeading by booleanPatchOption(
|
||||
key = "UsePremiumHeading",
|
||||
default = true,
|
||||
title = "Use premium heading",
|
||||
description = "Whether to use the premium heading.",
|
||||
required = true
|
||||
)
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
val resDirectory = context["res"]
|
||||
|
||||
val (original, replacement) = if (UsePremiumHeading == true)
|
||||
PREMIUM_HEADING_RES to DEFAULT_HEADING_RES
|
||||
else
|
||||
DEFAULT_HEADING_RES to PREMIUM_HEADING_RES
|
||||
|
||||
val variants = arrayOf("light", "dark")
|
||||
|
||||
arrayOf(
|
||||
"xxxhdpi",
|
||||
"xxhdpi",
|
||||
"xhdpi",
|
||||
"hdpi",
|
||||
"mdpi"
|
||||
).mapNotNull { dpi ->
|
||||
resDirectory.resolve("drawable-$dpi").takeIf { it.exists() }?.toPath()
|
||||
}.also {
|
||||
if (it.isEmpty())
|
||||
throw PatchException("The drawable folder can not be found. Therefore, the patch can not be applied.")
|
||||
}.forEach { path ->
|
||||
|
||||
variants.forEach { mode ->
|
||||
val fromPath = path.resolve("${original}_$mode.png")
|
||||
val toPath = path.resolve("${replacement}_$mode.png")
|
||||
|
||||
fromPath.copyTo(toPath, true)
|
||||
}
|
||||
}
|
||||
|
||||
val header = if (UsePremiumHeading == true)
|
||||
"Premium"
|
||||
else
|
||||
"Default"
|
||||
|
||||
context.updatePatchStatusHeader(header)
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.shared.mapping.ResourceMappingPatch
|
||||
import app.revanced.patches.shared.mapping.ResourceMappingPatch.getId
|
||||
import app.revanced.patches.shared.mapping.ResourceType.ATTR
|
||||
import app.revanced.patches.shared.mapping.ResourceType.COLOR
|
||||
import app.revanced.patches.shared.mapping.ResourceType.DIMEN
|
||||
import app.revanced.patches.shared.mapping.ResourceType.DRAWABLE
|
||||
@ -37,6 +38,7 @@ object SharedResourceIdPatch : ResourcePatch() {
|
||||
var ControlsLayoutStub = -1L
|
||||
var DarkSplashAnimation = -1L
|
||||
var DonationCompanion = -1L
|
||||
var DrawerContentView = -1L
|
||||
var DrawerResults = -1L
|
||||
var EasySeekEduContainer = -1L
|
||||
var EditSettingsAction = -1L
|
||||
@ -87,6 +89,8 @@ object SharedResourceIdPatch : ResourcePatch() {
|
||||
var VideoQualityBottomSheet = -1L
|
||||
var VoiceSearch = -1L
|
||||
var YouTubeControlsOverlaySubtitleButton = -1L
|
||||
var YtPremiumWordMarkHeader = -1L
|
||||
var YtWordMarkHeader = -1L
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
|
||||
@ -111,6 +115,7 @@ object SharedResourceIdPatch : ResourcePatch() {
|
||||
ControlsLayoutStub = getId(ID, "controls_layout_stub")
|
||||
DarkSplashAnimation = getId(ID, "dark_splash_animation")
|
||||
DonationCompanion = getId(LAYOUT, "donation_companion")
|
||||
DrawerContentView = getId(ID, "drawer_content_view")
|
||||
DrawerResults = getId(ID, "drawer_results")
|
||||
EasySeekEduContainer = getId(ID, "easy_seek_edu_container")
|
||||
EditSettingsAction = getId(STRING, "edit_settings_action")
|
||||
@ -163,6 +168,8 @@ object SharedResourceIdPatch : ResourcePatch() {
|
||||
VideoQualityBottomSheet = getId(LAYOUT, "video_quality_bottom_sheet_list_fragment_title")
|
||||
VoiceSearch = getId(ID, "voice_search")
|
||||
YouTubeControlsOverlaySubtitleButton = getId(LAYOUT, "youtube_controls_overlay_subtitle_button")
|
||||
YtPremiumWordMarkHeader = getId(ATTR, "ytPremiumWordmarkHeader")
|
||||
YtWordMarkHeader = getId(ATTR, "ytWordmarkHeader")
|
||||
|
||||
}
|
||||
}
|
@ -71,10 +71,6 @@ object ResourceUtils {
|
||||
updatePatchStatusSettings(patchTitle, "@string/revanced_patches_included")
|
||||
}
|
||||
|
||||
fun ResourceContext.updatePatchStatusHeader(headerName: String) {
|
||||
updatePatchStatusSettings("Header", headerName)
|
||||
}
|
||||
|
||||
fun ResourceContext.updatePatchStatusIcon(iconName: String) {
|
||||
updatePatchStatusSettings("Icon", "@string/revanced_icon_$iconName")
|
||||
}
|
||||
|
Reference in New Issue
Block a user