mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-15 23:33:57 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e36d72c5c | ||
|
|
6197246e8e | ||
|
|
15a044cae1 | ||
|
|
ede60b994c | ||
|
|
c15cc431d6 | ||
|
|
fb006f87ab | ||
|
|
28206fd804 | ||
|
|
e425e75152 | ||
|
|
69d6c55942 | ||
|
|
f2b29351b4 | ||
|
|
db0b468e71 | ||
|
|
b75e759348 | ||
|
|
83b41322c4 | ||
|
|
8d8878aa08 | ||
|
|
d628674cdd | ||
|
|
23b182ccbb | ||
|
|
4a784ecd6e | ||
|
|
0165b53c6d | ||
|
|
17a0e16087 |
62
CHANGELOG.md
62
CHANGELOG.md
@@ -1,3 +1,65 @@
|
||||
## [2.43.2](https://github.com/revanced/revanced-patches/compare/v2.43.1...v2.43.2) (2022-08-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* package name for Vanced ([#390](https://github.com/revanced/revanced-patches/issues/390)) ([85206b9](https://github.com/revanced/revanced-patches/commit/85206b99e1399d00e5eaebded8aadc5fb982aaa4))
|
||||
|
||||
## [2.43.1](https://github.com/revanced/revanced-patches/compare/v2.43.0...v2.43.1) (2022-08-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `Patch` annotation for `client-spoof` patch ([6a8af47](https://github.com/revanced/revanced-patches/commit/6a8af47f2dfb319a53db9b5f9deb0392f10f4185))
|
||||
|
||||
# [2.43.0](https://github.com/revanced/revanced-patches/compare/v2.42.1...v2.43.0) (2022-08-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* `client-spoof` patch ([5512c07](https://github.com/revanced/revanced-patches/commit/5512c072fa4b047849dbea0d2d382dd85e3a0827))
|
||||
|
||||
## [2.42.1](https://github.com/revanced/revanced-patches/compare/v2.42.0...v2.42.1) (2022-08-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* changed default value for autorepeat setting ([#386](https://github.com/revanced/revanced-patches/issues/386)) ([d43add7](https://github.com/revanced/revanced-patches/commit/d43add7c21f0d9f45830476704985755e37d33ef))
|
||||
|
||||
# [2.42.0](https://github.com/revanced/revanced-patches/compare/v2.41.0...v2.42.0) (2022-08-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* default values for settings ([834c4ad](https://github.com/revanced/revanced-patches/commit/834c4add71570d36b645246621ba24da3869d613))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* setting for downloader package name ([965d05c](https://github.com/revanced/revanced-patches/commit/965d05cfa55d7a51f64a11f0219e2867568ba852))
|
||||
* v17.29.34 compatibility for `downloads` patch ([#374](https://github.com/revanced/revanced-patches/issues/374)) ([d81f1af](https://github.com/revanced/revanced-patches/commit/d81f1af327e0d7471f410811af46da34ddfb1bb4))
|
||||
|
||||
# [2.41.0](https://github.com/revanced/revanced-patches/compare/v2.40.2...v2.41.0) (2022-08-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* `downloads` patch ([#215](https://github.com/revanced/revanced-patches/issues/215)) ([304fbac](https://github.com/revanced/revanced-patches/commit/304fbacab22538fb0945f564e061783b47120120))
|
||||
|
||||
## [2.40.2](https://github.com/revanced/revanced-patches/compare/v2.40.1...v2.40.2) (2022-08-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correct title for `tablet-miniplayer` setting switch ([6af6c02](https://github.com/revanced/revanced-patches/commit/6af6c02154d170153093bd846dfcb7a6205dd6fa))
|
||||
|
||||
## [2.40.1](https://github.com/revanced/revanced-patches/compare/v2.40.0...v2.40.1) (2022-08-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add missing switch for `tablet-mini-player` patch ([244a1b2](https://github.com/revanced/revanced-patches/commit/244a1b2cb9f77272dc62287a4a34a487b0289295))
|
||||
|
||||
# [2.40.0](https://github.com/revanced/revanced-patches/compare/v2.39.1...v2.40.0) (2022-08-22)
|
||||
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ Official patches by ReVanced
|
||||
| 💊 Patch | 📜 Description | 🏹 Target Version |
|
||||
|:--------:|:--------------:|:-----------------:|
|
||||
| `swipe-controls` | Adds volume and brightness swipe controls. | 17.32.35 |
|
||||
| `downloads` | Enables downloading music and videos from YouTube. | 17.32.35 |
|
||||
| `seekbar-tapping` | Enables tap-to-seek on the seekbar of the video player. | 17.32.35 |
|
||||
| `amoled` | Enables pure black theme. | 17.32.35 |
|
||||
| `disable-create-button` | Hides the create button in the navigation bar. | 17.32.35 |
|
||||
@@ -83,9 +84,10 @@ Official patches by ReVanced
|
||||
| `enable-wide-searchbar` | Replaces the search icon with a wide search bar. This will hide the YouTube logo when active. | 17.32.35 |
|
||||
| `tablet-mini-player` | Enables the tablet mini player layout. | 17.32.35 |
|
||||
| `minimized-playback` | Enables minimized and background playback. | 17.32.35 |
|
||||
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
|
||||
| `custom-video-buffer` | Lets you change the buffers of videos. | 17.32.35 |
|
||||
| `always-autorepeat` | Always repeats the playing video again. | 17.32.35 |
|
||||
| `microg-support` | Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG | 17.32.35 |
|
||||
| `microg-support` | Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG. | 17.32.35 |
|
||||
| `settings` | Adds settings for ReVanced to YouTube. | all |
|
||||
| `enable-debugging` | Enables app debugging by patching the manifest file. | all |
|
||||
| `custom-playback-speed` | Adds more video playback speed options. | 17.32.35 |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
kotlin.code.style = official
|
||||
version = 2.40.0
|
||||
version = 2.43.2
|
||||
|
||||
@@ -73,11 +73,11 @@ class GeneralBytecodeAdsPatch : BytecodePatch() {
|
||||
override fun execute(data: BytecodeData): PatchResult {
|
||||
SettingsPatch.PreferenceScreen.ADS.addPreferences(
|
||||
SwitchPreference(
|
||||
"revanced_home_ads_enabled",
|
||||
StringResource("revanced_home_ads_enabled_title", "Hide home ads"),
|
||||
"revanced_home_ads_removal",
|
||||
StringResource("revanced_home_ads_removal_title", "Remove home ads"),
|
||||
true,
|
||||
StringResource("revanced_home_ads_enabled_summary_on", "Home ads are shown"),
|
||||
StringResource("revanced_home_ads_enabled_summary_off", "Home ads are hidden")
|
||||
StringResource("revanced_home_ads_removal_summary_on", "Home ads are hidden"),
|
||||
StringResource("revanced_home_ads_removal_summary_off", "Home ads are shown")
|
||||
),
|
||||
SwitchPreference(
|
||||
"revanced_adremover_ad_removal",
|
||||
|
||||
@@ -33,11 +33,11 @@ class VideoAdsPatch : BytecodePatch(
|
||||
override fun execute(data: BytecodeData): PatchResult {
|
||||
SettingsPatch.PreferenceScreen.ADS.addPreferences(
|
||||
SwitchPreference(
|
||||
"revanced_video_ads_enabled",
|
||||
StringResource("revanced_video_ads_enabled_title", "Hide video ads"),
|
||||
"revanced_video_ads_removal",
|
||||
StringResource("revanced_video_ads_removal_title", "Hide video ads"),
|
||||
true,
|
||||
StringResource("revanced_video_ads_enabled_summary_on", "Video ads are hidden"),
|
||||
StringResource("revanced_video_ads_enabled_summary_off", "Video ads are shown")
|
||||
StringResource("revanced_video_ads_removal_summary_on", "Video ads are hidden"),
|
||||
StringResource("revanced_video_ads_removal_summary_off", "Video ads are shown")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -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.29.34", "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,72 @@
|
||||
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.*
|
||||
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.INTERACTIONS.addPreferences(
|
||||
PreferenceScreen(
|
||||
"revanced_downloads",
|
||||
StringResource("revanced_downloads_title", "Download settings"),
|
||||
listOf(
|
||||
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")
|
||||
),
|
||||
TextPreference(
|
||||
"revanced_downloads_package_name",
|
||||
StringResource("revanced_downloads_package_name_title", "Downloader package name"),
|
||||
InputType.STRING,
|
||||
"org.schabi.newpipe" /* NewPipe */,
|
||||
StringResource("revanced_downloads_package_name_summary", "Package name of the downloader app such as NewPipe\\'s or PowerTube\\'s")
|
||||
)
|
||||
),
|
||||
StringResource("revanced_downloads_summary", "Settings related to downloads")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
import app.revanced.extensions.injectResources
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
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.misc.settings.bytecode.patch.SettingsPatch
|
||||
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")
|
||||
@DependsOn([SettingsPatch::class])
|
||||
@@ -93,16 +94,15 @@ class SwipeControlsResourcePatch : ResourcePatch() {
|
||||
|
||||
val resourcesDir = "swipecontrols"
|
||||
|
||||
data.injectResources(
|
||||
this.javaClass.classLoader,
|
||||
resourcesDir,
|
||||
"drawable",
|
||||
listOf(
|
||||
"ic_sc_brightness_auto",
|
||||
"ic_sc_brightness_manual",
|
||||
"ic_sc_volume_mute",
|
||||
"ic_sc_volume_normal"
|
||||
).map { "$it.xml" }
|
||||
data.copyResources(
|
||||
"swipecontrols",
|
||||
ResourceUtils.ResourceGroup(
|
||||
"drawable",
|
||||
"ic_sc_brightness_auto.xml",
|
||||
"ic_sc_brightness_manual.xml",
|
||||
"ic_sc_volume_mute.xml",
|
||||
"ic_sc_volume_normal.xml"
|
||||
)
|
||||
)
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ class CreateButtonRemoverPatch : BytecodePatch(
|
||||
"revanced_create_button_enabled",
|
||||
StringResource("revanced_create_button_enabled_title", "Show create button"),
|
||||
false,
|
||||
StringResource("revanced_create_button_summary_on", "Create button is shown."),
|
||||
StringResource("revanced_create_button_summary_off", "Create button is hidden.")
|
||||
StringResource("revanced_create_button_summary_on", "Create button is shown"),
|
||||
StringResource("revanced_create_button_summary_off", "Create button is hidden")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -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.framework.components.impl.Preference
|
||||
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])
|
||||
@Name("return-youtube-dislike-resource-patch")
|
||||
@@ -35,12 +35,7 @@ class ReturnYouTubeDislikeResourcePatch : ResourcePatch() {
|
||||
)
|
||||
)
|
||||
// merge strings
|
||||
data.iterateXmlNodeChildren("returnyoutubedislike/host/values/strings.xml", "resources") {
|
||||
// TODO: figure out why this is needed
|
||||
if (!it.hasAttributes()) return@iterateXmlNodeChildren
|
||||
val attributes = it.attributes
|
||||
SettingsPatch.addString(attributes.getNamedItem("name")!!.nodeValue!!, it.textContent!!)
|
||||
}
|
||||
data.mergeStrings("returnyoutubedislike/host/values/strings.xml")
|
||||
|
||||
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.misc.integrations.patch.IntegrationsPatch
|
||||
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 org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
@@ -38,7 +39,7 @@ import org.jf.dexlib2.util.MethodUtil
|
||||
|
||||
@Patch
|
||||
@DependsOn(
|
||||
dependencies = [IntegrationsPatch::class, ResourceIdMappingProviderResourcePatch::class, SponsorBlockResourcePatch::class, VideoIdPatch::class]
|
||||
dependencies = [PlayerControlsBytecodePatch::class, IntegrationsPatch::class, ResourceIdMappingProviderResourcePatch::class, SponsorBlockResourcePatch::class, VideoIdPatch::class]
|
||||
)
|
||||
@Name("sponsorblock")
|
||||
@Description("Integrate SponsorBlock.")
|
||||
@@ -171,8 +172,7 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
||||
/*
|
||||
Voting & Shield button
|
||||
*/
|
||||
ShowPlayerControlsFingerprint.resolve(data, data.classes.find { it.type.endsWith("YouTubeControlsOverlay;") }!!)
|
||||
val controlsMethodResult = ShowPlayerControlsFingerprint.result!!
|
||||
val controlsMethodResult = PlayerControlsBytecodePatch.showPlayerControlsFingerprintResult
|
||||
|
||||
val controlsLayoutStubResourceId =
|
||||
ResourceIdMappingProviderResourcePatch.resourceMappings.single { it.type == "id" && it.name == "controls_layout_stub" }.id
|
||||
@@ -217,12 +217,8 @@ class SponsorBlockBytecodePatch : BytecodePatch(
|
||||
}
|
||||
|
||||
// change visibility of the buttons
|
||||
controlsMethodResult.mutableMethod.addInstructions(
|
||||
0, """
|
||||
invoke-static {p1}, Lapp/revanced/integrations/sponsorblock/ShieldButton;->changeVisibility(Z)V
|
||||
invoke-static {p1}, Lapp/revanced/integrations/sponsorblock/VotingButton;->changeVisibility(Z)V
|
||||
""".trimIndent()
|
||||
)
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("Lapp/revanced/integrations/sponsorblock/ShieldButton;->changeVisibility(Z)V")
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("Lapp/revanced/integrations/sponsorblock/VotingButton;->changeVisibility(Z)V")
|
||||
|
||||
// set SegmentHelperLayout.context to the player layout instance
|
||||
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.StringResource
|
||||
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.copyXmlNode
|
||||
import app.revanced.util.resources.ResourceUtils.iterateXmlNodeChildren
|
||||
|
||||
@Name("sponsorblock-resource-patch")
|
||||
@SponsorBlockCompatibility
|
||||
@@ -40,20 +40,7 @@ class SponsorBlockResourcePatch : ResourcePatch() {
|
||||
/*
|
||||
merge SponsorBlock strings to main strings
|
||||
*/
|
||||
data.iterateXmlNodeChildren("sponsorblock/host/values/strings.xml", "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!!
|
||||
|
||||
// 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)
|
||||
}
|
||||
data.mergeStrings("sponsorblock/host/values/strings.xml")
|
||||
|
||||
/*
|
||||
merge SponsorBlock drawables to main drawables
|
||||
|
||||
@@ -19,10 +19,13 @@ import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPla
|
||||
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideNoContextFingerprint
|
||||
import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerResponseModelSizeCheckFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
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 org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Patch
|
||||
@DependsOn([IntegrationsPatch::class])
|
||||
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])
|
||||
@Name("tablet-mini-player")
|
||||
@Description("Enables the tablet mini player layout.")
|
||||
@TabletMiniPlayerCompatibility
|
||||
@@ -34,6 +37,14 @@ class TabletMiniPlayerPatch : BytecodePatch(
|
||||
)
|
||||
) {
|
||||
override fun execute(data: BytecodeData): PatchResult {
|
||||
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference(
|
||||
"revanced_tablet_miniplayer",
|
||||
StringResource("revanced_tablet_miniplayer_title", "Enable the tablet Mini-player"),
|
||||
false,
|
||||
StringResource("revanced_tablet_miniplayer_summary_on", "Tablet Mini-player is enabled"),
|
||||
StringResource("revanced_tablet_miniplayer_summary_off", "Tablet Mini-player is disabled")
|
||||
))
|
||||
|
||||
// first resolve the fingerprints via the parent fingerprint
|
||||
val miniPlayerClass = MiniPlayerDimensionsCalculatorFingerprint.result!!.classDef
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class AutoRepeatPatch : BytecodePatch(
|
||||
SwitchPreference(
|
||||
"revanced_pref_auto_repeat",
|
||||
StringResource("revanced_auto_repeat_enabled_title", "Enable auto-repeat"),
|
||||
true,
|
||||
false,
|
||||
StringResource("revanced_auto_repeat_summary_on", "Auto-repeat is enabled"),
|
||||
StringResource("revanced_auto_repeat_summary_off", "Auto-repeat is disabled")
|
||||
)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.youtube.misc.clientspoof.annotations
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[
|
||||
Package("com.google.android.youtube", arrayOf()),
|
||||
Package("com.vanced.android.youtube", arrayOf())
|
||||
]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class ClientSpoofCompatibility
|
||||
@@ -0,0 +1,20 @@
|
||||
package app.revanced.patches.youtube.misc.clientspoof.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.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.misc.clientspoof.annotations.ClientSpoofCompatibility
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("user-agent-header-builder-fingerprint")
|
||||
@ClientSpoofCompatibility
|
||||
@DirectPatternScanMethod
|
||||
@Version("0.0.1")
|
||||
object UserAgentHeaderBuilderFingerprint : MethodFingerprint(
|
||||
null,
|
||||
null,
|
||||
listOf("L", "L", "L"),
|
||||
listOf(Opcode.MOVE_RESULT_OBJECT, Opcode.INVOKE_VIRTUAL),
|
||||
listOf("(Linux; U; Android "),
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
package app.revanced.patches.youtube.misc.clientspoof.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.addInstructions
|
||||
import app.revanced.patcher.extensions.instruction
|
||||
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.misc.clientspoof.annotations.ClientSpoofCompatibility
|
||||
import app.revanced.patches.youtube.misc.clientspoof.fingerprints.UserAgentHeaderBuilderFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
|
||||
@Patch
|
||||
@Name("client-spoof")
|
||||
@Description("Spoofs the YouTube or Vanced client to prevent playback issues.")
|
||||
@DependsOn([IntegrationsPatch::class])
|
||||
@ClientSpoofCompatibility
|
||||
@Version("0.0.1")
|
||||
class ClientSpoofPatch : BytecodePatch(
|
||||
listOf(UserAgentHeaderBuilderFingerprint)
|
||||
) {
|
||||
override fun execute(data: BytecodeData): PatchResult {
|
||||
val result = UserAgentHeaderBuilderFingerprint.result!!
|
||||
val method = result.mutableMethod
|
||||
|
||||
val insertIndex = result.patternScanResult!!.endIndex
|
||||
val packageNameRegister = (method.instruction(insertIndex) as FiveRegisterInstruction).registerD
|
||||
|
||||
val originalPackageName = "com.google.android.youtube"
|
||||
method.addInstructions(insertIndex, "const-string v$packageNameRegister, \"$originalPackageName\"")
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patches.youtube.layout.castbutton.patch.HideCastButtonPatch
|
||||
import app.revanced.patches.youtube.misc.clientspoof.patch.ClientSpoofPatch
|
||||
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
|
||||
import app.revanced.patches.youtube.misc.microg.fingerprints.*
|
||||
import app.revanced.patches.youtube.misc.microg.patch.resource.MicroGResourcePatch
|
||||
@@ -32,10 +33,11 @@ import org.jf.dexlib2.immutable.reference.ImmutableStringReference
|
||||
[
|
||||
MicroGResourcePatch::class,
|
||||
HideCastButtonPatch::class,
|
||||
ClientSpoofPatch::class
|
||||
]
|
||||
)
|
||||
@Name("microg-support")
|
||||
@Description("Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG")
|
||||
@Description("Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG.")
|
||||
@MicroGPatchCompatibility
|
||||
@Version("0.0.1")
|
||||
class MicroGBytecodePatch : BytecodePatch(
|
||||
|
||||
@@ -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.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 java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
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.
|
||||
* @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>
|
||||
4
src/main/resources/downloads/host/values/strings.xml
Normal file
4
src/main/resources/downloads/host/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="downloader_not_installed_warning">is not installed</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user