mirror of
https://github.com/revanced/revanced-patches
synced 2024-08-30 11:05:18 +02:00
feat: downloads
patch (#215)
This commit is contained in:
parent
83f67c3ec6
commit
304fbacab2
@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.youtube.interaction.downloads.annotation
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.27.39", "17.32.35")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class DownloadsCompatibility
|
||||||
|
|
@ -0,0 +1,52 @@
|
|||||||
|
package app.revanced.patches.youtube.interaction.downloads.bytecode.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patches.youtube.interaction.downloads.annotation.DownloadsCompatibility
|
||||||
|
import app.revanced.patches.youtube.interaction.downloads.resource.patch.DownloadsResourcePatch
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.bytecode.patch.PlayerControlsBytecodePatch
|
||||||
|
import app.revanced.patches.youtube.misc.videoid.patch.VideoIdPatch
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("downloads")
|
||||||
|
@DependsOn([DownloadsResourcePatch::class, PlayerControlsBytecodePatch::class, VideoIdPatch::class])
|
||||||
|
@Description("Enables downloading music and videos from YouTube.")
|
||||||
|
@DownloadsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class DownloadsBytecodePatch : BytecodePatch() {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
val integrationsPackage = "app/revanced/integrations"
|
||||||
|
val classDescriptor = "L$integrationsPackage/videoplayer/DownloadButton;"
|
||||||
|
|
||||||
|
/*
|
||||||
|
initialize the control
|
||||||
|
*/
|
||||||
|
|
||||||
|
val initializeDownloadsDescriptor = "$classDescriptor->initializeDownloadButton(Ljava/lang/Object;)V"
|
||||||
|
PlayerControlsBytecodePatch.initializeControl(initializeDownloadsDescriptor)
|
||||||
|
|
||||||
|
/*
|
||||||
|
add code to change the visibility of the control
|
||||||
|
*/
|
||||||
|
|
||||||
|
val changeVisibilityDescriptor = "$classDescriptor->changeVisibility(Z)V"
|
||||||
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall(changeVisibilityDescriptor)
|
||||||
|
|
||||||
|
/*
|
||||||
|
add code to change to update the video id
|
||||||
|
*/
|
||||||
|
|
||||||
|
val setVideoIdDescriptor =
|
||||||
|
"L$integrationsPackage/patches/downloads/DownloadsPatch;->setVideoId(Ljava/lang/String;)V"
|
||||||
|
VideoIdPatch.injectCall(setVideoIdDescriptor)
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package app.revanced.patches.youtube.interaction.downloads.resource.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||||
|
import app.revanced.patches.youtube.interaction.downloads.annotation.DownloadsCompatibility
|
||||||
|
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.resource.patch.BottomControlsResourcePatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
||||||
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
|
||||||
|
import app.revanced.util.resources.ResourceUtils
|
||||||
|
import app.revanced.util.resources.ResourceUtils.Settings.mergeStrings
|
||||||
|
import app.revanced.util.resources.ResourceUtils.copyResources
|
||||||
|
|
||||||
|
@Name("downloads-resource-patch")
|
||||||
|
@DependsOn([BottomControlsResourcePatch::class, FixLocaleConfigErrorPatch::class, SettingsPatch::class])
|
||||||
|
@Description("Makes necessary changes to resources for the download button.")
|
||||||
|
@DownloadsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class DownloadsResourcePatch : ResourcePatch() {
|
||||||
|
override fun execute(data: ResourceData): PatchResult {
|
||||||
|
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference(
|
||||||
|
"revanced_downloads",
|
||||||
|
StringResource("revanced_downloads_enabled_title", "Show download button"),
|
||||||
|
true,
|
||||||
|
StringResource("revanced_downloads_enabled_summary_on", "Download button is visible"),
|
||||||
|
StringResource("revanced_downloads_enabled_summary_off", "Download button is hidden")
|
||||||
|
))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy strings
|
||||||
|
*/
|
||||||
|
|
||||||
|
data.mergeStrings("downloads/host/values/strings.xml")
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy resources
|
||||||
|
*/
|
||||||
|
|
||||||
|
data.copyResources("downloads", ResourceUtils.ResourceGroup("drawable", "revanced_yt_download_button.xml"))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add download button node
|
||||||
|
*/
|
||||||
|
|
||||||
|
BottomControlsResourcePatch.addControls("downloads/host/layout/${BottomControlsResourcePatch.TARGET_RESOURCE_NAME}")
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package app.revanced.patches.youtube.interaction.swipecontrols.patch.resource
|
package app.revanced.patches.youtube.interaction.swipecontrols.patch.resource
|
||||||
|
|
||||||
import app.revanced.extensions.injectResources
|
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.impl.ResourceData
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
@ -11,6 +10,8 @@ import app.revanced.patcher.patch.impl.ResourcePatch
|
|||||||
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility
|
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility
|
||||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.*
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.*
|
||||||
|
import app.revanced.util.resources.ResourceUtils
|
||||||
|
import app.revanced.util.resources.ResourceUtils.copyResources
|
||||||
|
|
||||||
@Name("swipe-controls-resource-patch")
|
@Name("swipe-controls-resource-patch")
|
||||||
@DependsOn([SettingsPatch::class])
|
@DependsOn([SettingsPatch::class])
|
||||||
@ -93,16 +94,15 @@ class SwipeControlsResourcePatch : ResourcePatch() {
|
|||||||
|
|
||||||
val resourcesDir = "swipecontrols"
|
val resourcesDir = "swipecontrols"
|
||||||
|
|
||||||
data.injectResources(
|
data.copyResources(
|
||||||
this.javaClass.classLoader,
|
"swipecontrols",
|
||||||
resourcesDir,
|
ResourceUtils.ResourceGroup(
|
||||||
"drawable",
|
"drawable",
|
||||||
listOf(
|
"ic_sc_brightness_auto.xml",
|
||||||
"ic_sc_brightness_auto",
|
"ic_sc_brightness_manual.xml",
|
||||||
"ic_sc_brightness_manual",
|
"ic_sc_volume_mute.xml",
|
||||||
"ic_sc_volume_mute",
|
"ic_sc_volume_normal.xml"
|
||||||
"ic_sc_volume_normal"
|
)
|
||||||
).map { "$it.xml" }
|
|
||||||
)
|
)
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatc
|
|||||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
|
||||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
||||||
import app.revanced.util.resources.ResourceUtils.iterateXmlNodeChildren
|
import app.revanced.util.resources.ResourceUtils.Settings.mergeStrings
|
||||||
|
|
||||||
@DependsOn([FixLocaleConfigErrorPatch::class, SettingsPatch::class])
|
@DependsOn([FixLocaleConfigErrorPatch::class, SettingsPatch::class])
|
||||||
@Name("return-youtube-dislike-resource-patch")
|
@Name("return-youtube-dislike-resource-patch")
|
||||||
@ -35,12 +35,7 @@ class ReturnYouTubeDislikeResourcePatch : ResourcePatch() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
// merge strings
|
// merge strings
|
||||||
data.iterateXmlNodeChildren("returnyoutubedislike/host/values/strings.xml", "resources") {
|
data.mergeStrings("returnyoutubedislike/host/values/strings.xml")
|
||||||
// TODO: figure out why this is needed
|
|
||||||
if (!it.hasAttributes()) return@iterateXmlNodeChildren
|
|
||||||
val attributes = it.attributes
|
|
||||||
SettingsPatch.addString(attributes.getNamedItem("name")!!.nodeValue!!, it.textContent!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints
|
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Name
|
|
||||||
import app.revanced.patcher.annotation.Version
|
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
|
||||||
|
|
||||||
@Name("show-player-controls-fingerprint")
|
|
||||||
@MatchingMethod(
|
|
||||||
"LYouTubeControlsOverlay;", "ac"
|
|
||||||
)
|
|
||||||
@DirectPatternScanMethod
|
|
||||||
@SponsorBlockCompatibility
|
|
||||||
@Version("0.0.1")
|
|
||||||
object ShowPlayerControlsFingerprint : MethodFingerprint(
|
|
||||||
"V", null, listOf("Z","Z"), null, null
|
|
||||||
)
|
|
@ -23,6 +23,7 @@ import app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints.*
|
|||||||
import app.revanced.patches.youtube.layout.sponsorblock.resource.patch.SponsorBlockResourcePatch
|
import app.revanced.patches.youtube.layout.sponsorblock.resource.patch.SponsorBlockResourcePatch
|
||||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch
|
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.bytecode.patch.PlayerControlsBytecodePatch
|
||||||
import app.revanced.patches.youtube.misc.videoid.patch.VideoIdPatch
|
import app.revanced.patches.youtube.misc.videoid.patch.VideoIdPatch
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
@ -38,7 +39,7 @@ import org.jf.dexlib2.util.MethodUtil
|
|||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@DependsOn(
|
@DependsOn(
|
||||||
dependencies = [IntegrationsPatch::class, ResourceIdMappingProviderResourcePatch::class, SponsorBlockResourcePatch::class, VideoIdPatch::class]
|
dependencies = [PlayerControlsBytecodePatch::class, IntegrationsPatch::class, ResourceIdMappingProviderResourcePatch::class, SponsorBlockResourcePatch::class, VideoIdPatch::class]
|
||||||
)
|
)
|
||||||
@Name("sponsorblock")
|
@Name("sponsorblock")
|
||||||
@Description("Integrate SponsorBlock.")
|
@Description("Integrate SponsorBlock.")
|
||||||
@ -171,8 +172,7 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
|||||||
/*
|
/*
|
||||||
Voting & Shield button
|
Voting & Shield button
|
||||||
*/
|
*/
|
||||||
ShowPlayerControlsFingerprint.resolve(data, data.classes.find { it.type.endsWith("YouTubeControlsOverlay;") }!!)
|
val controlsMethodResult = PlayerControlsBytecodePatch.showPlayerControlsFingerprintResult
|
||||||
val controlsMethodResult = ShowPlayerControlsFingerprint.result!!
|
|
||||||
|
|
||||||
val controlsLayoutStubResourceId =
|
val controlsLayoutStubResourceId =
|
||||||
ResourceIdMappingProviderResourcePatch.resourceMappings.single { it.type == "id" && it.name == "controls_layout_stub" }.id
|
ResourceIdMappingProviderResourcePatch.resourceMappings.single { it.type == "id" && it.name == "controls_layout_stub" }.id
|
||||||
@ -217,12 +217,8 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// change visibility of the buttons
|
// change visibility of the buttons
|
||||||
controlsMethodResult.mutableMethod.addInstructions(
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall("Lapp/revanced/integrations/sponsorblock/ShieldButton;->changeVisibility(Z)V")
|
||||||
0, """
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall("Lapp/revanced/integrations/sponsorblock/VotingButton;->changeVisibility(Z)V")
|
||||||
invoke-static {p1}, Lapp/revanced/integrations/sponsorblock/ShieldButton;->changeVisibility(Z)V
|
|
||||||
invoke-static {p1}, Lapp/revanced/integrations/sponsorblock/VotingButton;->changeVisibility(Z)V
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
|
|
||||||
// set SegmentHelperLayout.context to the player layout instance
|
// set SegmentHelperLayout.context to the player layout instance
|
||||||
val instanceRegister = 0
|
val instanceRegister = 0
|
||||||
|
@ -13,9 +13,9 @@ import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
|||||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
|
||||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
||||||
import app.revanced.util.resources.ResourceUtils
|
import app.revanced.util.resources.ResourceUtils
|
||||||
|
import app.revanced.util.resources.ResourceUtils.Settings.mergeStrings
|
||||||
import app.revanced.util.resources.ResourceUtils.copyResources
|
import app.revanced.util.resources.ResourceUtils.copyResources
|
||||||
import app.revanced.util.resources.ResourceUtils.copyXmlNode
|
import app.revanced.util.resources.ResourceUtils.copyXmlNode
|
||||||
import app.revanced.util.resources.ResourceUtils.iterateXmlNodeChildren
|
|
||||||
|
|
||||||
@Name("sponsorblock-resource-patch")
|
@Name("sponsorblock-resource-patch")
|
||||||
@SponsorBlockCompatibility
|
@SponsorBlockCompatibility
|
||||||
@ -40,20 +40,7 @@ class SponsorBlockResourcePatch : ResourcePatch() {
|
|||||||
/*
|
/*
|
||||||
merge SponsorBlock strings to main strings
|
merge SponsorBlock strings to main strings
|
||||||
*/
|
*/
|
||||||
data.iterateXmlNodeChildren("sponsorblock/host/values/strings.xml", "resources") {
|
data.mergeStrings("sponsorblock/host/values/strings.xml")
|
||||||
// TODO: figure out why this is needed
|
|
||||||
if (!it.hasAttributes()) return@iterateXmlNodeChildren
|
|
||||||
|
|
||||||
val attributes = it.attributes
|
|
||||||
val key = attributes.getNamedItem("name")!!.nodeValue!!
|
|
||||||
val value = it.textContent!!
|
|
||||||
|
|
||||||
// all strings of SponsorBlock which have this attribute have the attribute value false,
|
|
||||||
// hence a null check suffices
|
|
||||||
val formatted = attributes.getNamedItem("formatted") == null
|
|
||||||
|
|
||||||
SettingsPatch.addString(key, value, formatted)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
merge SponsorBlock drawables to main drawables
|
merge SponsorBlock drawables to main drawables
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.annotation
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
|
||||||
|
@Compatibility(
|
||||||
|
[Package(
|
||||||
|
"com.google.android.youtube", arrayOf("17.27.39", "17.32.35")
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
internal annotation class PlayerControlsCompatibility
|
@ -0,0 +1,83 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.bytecode.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstruction
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprintResult
|
||||||
|
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
|
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.annotation.PlayerControlsCompatibility
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.BottomControlsInflateFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerControlsVisibilityFingerprint
|
||||||
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
@Name("player-controls-bytecode-patch")
|
||||||
|
@DependsOn([ResourceIdMappingProviderResourcePatch::class])
|
||||||
|
@Description("Manages the code for the player controls of the YouTube player.")
|
||||||
|
@PlayerControlsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class PlayerControlsBytecodePatch : BytecodePatch(
|
||||||
|
listOf(PlayerControlsVisibilityFingerprint)
|
||||||
|
) {
|
||||||
|
override fun execute(data: BytecodeData): PatchResult {
|
||||||
|
showPlayerControlsFingerprintResult = PlayerControlsVisibilityFingerprint.result!!
|
||||||
|
|
||||||
|
bottomUiContainerResourceId = ResourceIdMappingProviderResourcePatch
|
||||||
|
.resourceMappings
|
||||||
|
.single { it.type == "id" && it.name == "bottom_ui_container_stub" }.id
|
||||||
|
|
||||||
|
// TODO: another solution is required, this is hacky
|
||||||
|
listOf(BottomControlsInflateFingerprint).resolve(data, data.classes)
|
||||||
|
inflateFingerprintResult = BottomControlsInflateFingerprint.result!!
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
var bottomUiContainerResourceId: Long = 0
|
||||||
|
|
||||||
|
lateinit var showPlayerControlsFingerprintResult: MethodFingerprintResult
|
||||||
|
|
||||||
|
private var inflateFingerprintResult: MethodFingerprintResult? = null
|
||||||
|
set(fingerprint) {
|
||||||
|
field = fingerprint!!.also {
|
||||||
|
moveToRegisterInstructionIndex = it.patternScanResult!!.endIndex
|
||||||
|
viewRegister =
|
||||||
|
(it.mutableMethod.implementation!!.instructions[moveToRegisterInstructionIndex] as OneRegisterInstruction).registerA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var moveToRegisterInstructionIndex: Int = 0
|
||||||
|
private var viewRegister: Int = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects the code to change the visibility of controls.
|
||||||
|
* @param descriptor The descriptor of the method which should be called.
|
||||||
|
*/
|
||||||
|
fun injectVisibilityCheckCall(descriptor: String) {
|
||||||
|
showPlayerControlsFingerprintResult.mutableMethod.addInstruction(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static {p1}, $descriptor
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects the code to initialize the controls.
|
||||||
|
* @param descriptor The descriptor of the method which should be calleed.
|
||||||
|
*/
|
||||||
|
fun initializeControl(descriptor: String) {
|
||||||
|
inflateFingerprintResult!!.mutableMethod.addInstruction(
|
||||||
|
moveToRegisterInstructionIndex + 1,
|
||||||
|
"invoke-static {v$viewRegister}, $descriptor"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.annotation.PlayerControlsCompatibility
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.bytecode.patch.PlayerControlsBytecodePatch
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
||||||
|
|
||||||
|
@Name("bottom-controls-inflate-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"Lknf;", "a"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@PlayerControlsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object BottomControlsInflateFingerprint : MethodFingerprint(
|
||||||
|
null, null, null, listOf(
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT
|
||||||
|
), null,
|
||||||
|
{ methodDef ->
|
||||||
|
methodDef.implementation?.instructions?.any { instruction ->
|
||||||
|
(instruction as? WideLiteralInstruction)?.wideLiteral == PlayerControlsBytecodePatch.bottomUiContainerResourceId
|
||||||
|
} == true
|
||||||
|
}
|
||||||
|
)
|
@ -0,0 +1,21 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.annotation.PlayerControlsCompatibility
|
||||||
|
|
||||||
|
@Name("player-controls-visibility-fingerprint")
|
||||||
|
@MatchingMethod(
|
||||||
|
"LYouTubeControlsOverlay;", "ag"
|
||||||
|
)
|
||||||
|
@DirectPatternScanMethod
|
||||||
|
@PlayerControlsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
object PlayerControlsVisibilityFingerprint : MethodFingerprint(
|
||||||
|
"V", null, listOf("Z", "Z"), null, null, { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("YouTubeControlsOverlay;")
|
||||||
|
}
|
||||||
|
)
|
@ -0,0 +1,86 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.playercontrols.resource.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.impl.DomFileEditor
|
||||||
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||||
|
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
||||||
|
import app.revanced.patches.youtube.misc.playercontrols.annotation.PlayerControlsCompatibility
|
||||||
|
import java.io.Closeable
|
||||||
|
|
||||||
|
@Name("bottom-controls-resource-patch")
|
||||||
|
@DependsOn([FixLocaleConfigErrorPatch::class])
|
||||||
|
@Description("Manages the resources for the bottom controls of the YouTube player.")
|
||||||
|
@PlayerControlsCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class BottomControlsResourcePatch : ResourcePatch(), Closeable {
|
||||||
|
override fun execute(data: ResourceData): PatchResult {
|
||||||
|
resourceData = data
|
||||||
|
targetXmlEditor = data.xmlEditor[TARGET_RESOURCE]
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal const val TARGET_RESOURCE_NAME = "youtube_controls_bottom_ui_container.xml"
|
||||||
|
private const val TARGET_RESOURCE = "res/layout/$TARGET_RESOURCE_NAME"
|
||||||
|
|
||||||
|
private lateinit var resourceData: ResourceData
|
||||||
|
private lateinit var targetXmlEditor: DomFileEditor
|
||||||
|
|
||||||
|
// The element to which to add the new elements to
|
||||||
|
private var lastLeftOf = "fullscreen_button"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add new controls to the bottom of the YouTube player.
|
||||||
|
* @param hostYouTubeControlsBottomUiResourceName The hosting resource name containing the elements.
|
||||||
|
*/
|
||||||
|
internal fun addControls(hostYouTubeControlsBottomUiResourceName: String) {
|
||||||
|
val sourceXmlEditor =
|
||||||
|
resourceData.xmlEditor[this::class.java.classLoader.getResourceAsStream(
|
||||||
|
hostYouTubeControlsBottomUiResourceName
|
||||||
|
)!!]
|
||||||
|
|
||||||
|
val targetElement =
|
||||||
|
"android.support.constraint.ConstraintLayout"
|
||||||
|
|
||||||
|
val hostElements = sourceXmlEditor.file.getElementsByTagName(targetElement).item(0).childNodes
|
||||||
|
|
||||||
|
val destinationResourceFile = this.targetXmlEditor.file
|
||||||
|
val destinationElement =
|
||||||
|
destinationResourceFile.getElementsByTagName(targetElement).item(0)
|
||||||
|
|
||||||
|
for (index in 1 until hostElements.length) {
|
||||||
|
val element = hostElements.item(index).cloneNode(true)
|
||||||
|
|
||||||
|
// if the element has no attributes theres no point to adding it to the destination
|
||||||
|
if (!element.hasAttributes()) continue
|
||||||
|
|
||||||
|
// set the elements lastLeftOf attribute to the lastLeftOf value
|
||||||
|
val namespace = "@+id"
|
||||||
|
element.attributes.getNamedItem("yt:layout_constraintRight_toLeftOf").nodeValue =
|
||||||
|
"$namespace/$lastLeftOf"
|
||||||
|
|
||||||
|
// set lastLeftOf attribute to the the current element
|
||||||
|
val nameSpaceLength = 4
|
||||||
|
lastLeftOf = element.attributes.getNamedItem("android:id").nodeValue.substring(nameSpaceLength)
|
||||||
|
|
||||||
|
// copy the element
|
||||||
|
destinationResourceFile.adoptNode(element)
|
||||||
|
destinationElement.appendChild(element)
|
||||||
|
}
|
||||||
|
sourceXmlEditor.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
targetXmlEditor.close()
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,38 @@ package app.revanced.util.resources
|
|||||||
|
|
||||||
import app.revanced.patcher.data.impl.DomFileEditor
|
import app.revanced.patcher.data.impl.DomFileEditor
|
||||||
import app.revanced.patcher.data.impl.ResourceData
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
|
|
||||||
internal object ResourceUtils {
|
internal object ResourceUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings related utilities
|
||||||
|
*/
|
||||||
|
internal object Settings {
|
||||||
|
/**
|
||||||
|
* Merge strings. This handles [StringResource]s automatically.
|
||||||
|
* @param host The hosting xml resource. Needs to be a valid strings.xml resource.
|
||||||
|
*/
|
||||||
|
internal fun ResourceData.mergeStrings(host: String) {
|
||||||
|
this.iterateXmlNodeChildren(host, "resources") {
|
||||||
|
// TODO: figure out why this is needed
|
||||||
|
if (!it.hasAttributes()) return@iterateXmlNodeChildren
|
||||||
|
|
||||||
|
val attributes = it.attributes
|
||||||
|
val key = attributes.getNamedItem("name")!!.nodeValue!!
|
||||||
|
val value = it.textContent!!
|
||||||
|
|
||||||
|
val formatted = attributes.getNamedItem("formatted") == null
|
||||||
|
|
||||||
|
SettingsPatch.addString(key, value, formatted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy resources from the current class loader to the resource directory.
|
* Copy resources from the current class loader to the resource directory.
|
||||||
* @param sourceResourceDirectory The source resource directory name.
|
* @param sourceResourceDirectory The source resource directory name.
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:tint="?attr/ytTextPrimary" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#ff000000" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:yt="http://schemas.android.com/apk/res-auto" android:id="@+id/youtube_controls_bottom_ui_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layoutDirection="ltr">
|
||||||
|
<com.google.android.libraries.youtube.common.ui.TouchImageView android:id="@+id/download_button" android:paddingLeft="12dp" android:paddingTop="22dp" android:paddingRight="12dp" android:paddingBottom="16dp" android:longClickable="false" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/revanced_yt_download_button" android:scaleType="center" yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" style="@style/YouTubePlayerButton"/>
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
5
src/main/resources/downloads/host/values/strings.xml
Normal file
5
src/main/resources/downloads/host/values/strings.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="powertube_not_installed_warning">PowerTube is not installed</string>
|
||||||
|
<string name="powertube_not_installed_notice">Please install PowerTube from https://github.com/razar-dev/PowerTube.</string>
|
||||||
|
</resources>
|
Loading…
Reference in New Issue
Block a user