From 96b5aede482f7a69d6df17864a2e17568b0da880 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 21 Oct 2024 02:53:09 -0400 Subject: [PATCH 01/65] feat(YouTube): Add `Shorts autoplay` patch (#3794) --- api/revanced-patches.api | 6 + .../HideShortsComponentsResourcePatch.kt | 79 +++++++----- .../shortsautoplay/ShortsAutoplayPatch.kt | 119 ++++++++++++++++++ .../ReelEnumConstructorFingerprint.kt | 18 +++ .../ReelPlaybackRepeatFingerprint.kt | 9 ++ .../misc/playservice/VersionCheckPatch.kt | 2 + .../youtube/misc/settings/SettingsPatch.kt | 1 - .../resources/addresources/values/strings.xml | 10 ++ 8 files changed, 209 insertions(+), 35 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelEnumConstructorFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelPlaybackRepeatFingerprint.kt diff --git a/api/revanced-patches.api b/api/revanced-patches.api index ed2f78f371..152dc8346f 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -1832,6 +1832,12 @@ public final class app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbar public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch : app/revanced/patcher/patch/BytecodePatch { public static final field INSTANCE Lapp/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch; public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt index 7c1f10ccc4..c548cf2f18 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt @@ -5,6 +5,8 @@ import app.revanced.patcher.patch.ResourcePatch import app.revanced.patcher.patch.annotation.Patch import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsAppShortcut import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsWidget @@ -34,40 +36,49 @@ object HideShortsComponentsResourcePatch : ResourcePatch() { SwitchPreference("revanced_hide_shorts_subscriptions"), SwitchPreference("revanced_hide_shorts_search"), - // Shorts player components. - // Ideally each group should be ordered similar to how they appear in the UI - // since this Setting menu currently uses the ordering used here. - - // Vertical row of buttons on right side of the screen. - SwitchPreference("revanced_hide_shorts_like_fountain"), - SwitchPreference("revanced_hide_shorts_like_button"), - SwitchPreference("revanced_hide_shorts_dislike_button"), - SwitchPreference("revanced_hide_shorts_comments_button"), - SwitchPreference("revanced_hide_shorts_share_button"), - SwitchPreference("revanced_hide_shorts_remix_button"), - SwitchPreference("revanced_hide_shorts_sound_button"), - - // Everything else. - SwitchPreference("revanced_hide_shorts_join_button"), - SwitchPreference("revanced_hide_shorts_subscribe_button"), - SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"), - SwitchPreference("revanced_hide_shorts_save_sound_button"), - SwitchPreference("revanced_hide_shorts_use_template_button"), - SwitchPreference("revanced_hide_shorts_upcoming_button"), - SwitchPreference("revanced_hide_shorts_green_screen_button"), - SwitchPreference("revanced_hide_shorts_hashtag_button"), - SwitchPreference("revanced_hide_shorts_shop_button"), - SwitchPreference("revanced_hide_shorts_tagged_products"), - SwitchPreference("revanced_hide_shorts_stickers"), - SwitchPreference("revanced_hide_shorts_search_suggestions"), - SwitchPreference("revanced_hide_shorts_super_thanks_button"), - SwitchPreference("revanced_hide_shorts_location_label"), - SwitchPreference("revanced_hide_shorts_channel_bar"), - SwitchPreference("revanced_hide_shorts_info_panel"), - SwitchPreference("revanced_hide_shorts_full_video_link_label"), - SwitchPreference("revanced_hide_shorts_video_title"), - SwitchPreference("revanced_hide_shorts_sound_metadata_label"), - SwitchPreference("revanced_hide_shorts_navigation_bar"), + PreferenceScreen( + key = "revanced_shorts_player_screen", + sorting = Sorting.UNSORTED, + preferences = setOf( + // Shorts player components. + // Ideally each group should be ordered similar to how they appear in the UI + + // Vertical row of buttons on right side of the screen. + SwitchPreference("revanced_hide_shorts_like_fountain"), + SwitchPreference("revanced_hide_shorts_like_button"), + SwitchPreference("revanced_hide_shorts_dislike_button"), + SwitchPreference("revanced_hide_shorts_comments_button"), + SwitchPreference("revanced_hide_shorts_share_button"), + SwitchPreference("revanced_hide_shorts_remix_button"), + SwitchPreference("revanced_hide_shorts_sound_button"), + + // Upper and middle area of the player. + SwitchPreference("revanced_hide_shorts_join_button"), + SwitchPreference("revanced_hide_shorts_subscribe_button"), + SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"), + + // Suggested actions. + SwitchPreference("revanced_hide_shorts_save_sound_button"), + SwitchPreference("revanced_hide_shorts_use_template_button"), + SwitchPreference("revanced_hide_shorts_upcoming_button"), + SwitchPreference("revanced_hide_shorts_green_screen_button"), + SwitchPreference("revanced_hide_shorts_hashtag_button"), + SwitchPreference("revanced_hide_shorts_shop_button"), + SwitchPreference("revanced_hide_shorts_tagged_products"), + SwitchPreference("revanced_hide_shorts_search_suggestions"), + SwitchPreference("revanced_hide_shorts_super_thanks_button"), + SwitchPreference("revanced_hide_shorts_stickers"), + + // Bottom of the screen. + SwitchPreference("revanced_hide_shorts_location_label"), + SwitchPreference("revanced_hide_shorts_channel_bar"), + SwitchPreference("revanced_hide_shorts_info_panel"), + SwitchPreference("revanced_hide_shorts_full_video_link_label"), + SwitchPreference("revanced_hide_shorts_video_title"), + SwitchPreference("revanced_hide_shorts_sound_metadata_label"), + SwitchPreference("revanced_hide_shorts_navigation_bar"), + ) + ) ) if (hideShortsAppShortcut == true) { diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt new file mode 100644 index 0000000000..c8be358972 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt @@ -0,0 +1,119 @@ +package app.revanced.patches.youtube.layout.shortsautoplay + +import app.revanced.patcher.data.BytecodeContext +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.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.all.misc.resources.AddResourcesPatch +import app.revanced.patches.all.misc.resources.AddResourcesPatch.invoke +import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.layout.shortsautoplay.fingerprints.ReelEnumConstructorFingerprint +import app.revanced.patches.youtube.layout.shortsautoplay.fingerprints.ReelPlaybackRepeatFingerprint +import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch +import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.patches.youtube.shared.fingerprints.MainActivityOnCreateFingerprint +import app.revanced.util.findOpcodeIndicesReversed +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.resultOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Patch( + name = "Shorts autoplay", + description = "Adds options to automatically play the next Short.", + dependencies = [ + IntegrationsPatch::class, + SettingsPatch::class, + ResourceMappingPatch::class, + VersionCheckPatch::class, + ], + compatiblePackages = [ + CompatiblePackage( + "com.google.android.youtube", + [ + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ], + ), + ], +) +@Suppress("unused") +object ShortsAutoplayPatch : BytecodePatch( + setOf( + MainActivityOnCreateFingerprint, + ReelEnumConstructorFingerprint, + ReelPlaybackRepeatFingerprint + ) +) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = + "Lapp/revanced/integrations/youtube/patches/ShortsAutoplayPatch;" + + override fun execute(context: BytecodeContext) { + AddResourcesPatch(this::class) + + SettingsPatch.PreferenceScreen.SHORTS.addPreferences( + SwitchPreference("revanced_shorts_autoplay") + ) + + if (VersionCheckPatch.is_19_34_or_greater) { + SettingsPatch.PreferenceScreen.SHORTS.addPreferences( + SwitchPreference("revanced_shorts_autoplay_background") + ) + } + + // Main activity is used to check if app is in pip mode. + MainActivityOnCreateFingerprint.resultOrThrow().mutableMethod.addInstructions( + 0, + "invoke-static/range { p0 .. p0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->" + + "setMainActivity(Landroid/app/Activity;)V", + ) + + val reelEnumClass: String + + ReelEnumConstructorFingerprint.resultOrThrow().let { + reelEnumClass = it.classDef.type + + it.mutableMethod.apply { + val insertIndex = it.scanResult.patternScanResult!!.startIndex + + addInstructions( + insertIndex, + """ + # Pass the first enum value to integrations. + # Any enum value of this type will work. + sget-object v0, $reelEnumClass->a:$reelEnumClass + invoke-static { v0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->setYTShortsRepeatEnum(Ljava/lang/Enum;)V + """ + ) + } + } + + ReelPlaybackRepeatFingerprint.resultOrThrow().mutableMethod.apply { + // The behavior enums are looked up from an ordinal value to an enum type. + findOpcodeIndicesReversed { + val reference = getReference() + reference?.definingClass == reelEnumClass + && reference.parameterTypes.firstOrNull() == "I" + && reference.returnType == reelEnumClass + }.forEach { index -> + val register = getInstruction(index + 1).registerA + + addInstructions( + index + 2, + """ + invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->changeShortsRepeatBehavior(Ljava/lang/Enum;)Ljava/lang/Enum; + move-result-object v$register + """ + ) + } + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelEnumConstructorFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelEnumConstructorFingerprint.kt new file mode 100644 index 0000000000..b9bc8bfed2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelEnumConstructorFingerprint.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.youtube.layout.shortsautoplay.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 +import kotlin.collections.listOf + +internal object ReelEnumConstructorFingerprint : MethodFingerprint( + accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR, + opcodes = listOf(Opcode.RETURN_VOID), + strings = listOf( + "REEL_LOOP_BEHAVIOR_UNKNOWN", + "REEL_LOOP_BEHAVIOR_SINGLE_PLAY", + "REEL_LOOP_BEHAVIOR_REPEAT", + "REEL_LOOP_BEHAVIOR_END_SCREEN" + ) +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelPlaybackRepeatFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelPlaybackRepeatFingerprint.kt new file mode 100644 index 0000000000..793516be97 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelPlaybackRepeatFingerprint.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.youtube.layout.shortsautoplay.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +internal object ReelPlaybackRepeatFingerprint : MethodFingerprint( + parameters = listOf("L"), + returnType = "V", + strings = listOf("YoutubePlayerState is in throwing an Error.") +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt index 5ef04463c6..4a704ca541 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt @@ -20,6 +20,7 @@ internal object VersionCheckPatch : ResourcePatch() { var is_19_29_or_greater by Delegates.notNull() var is_19_32_or_greater by Delegates.notNull() var is_19_33_or_greater by Delegates.notNull() + var is_19_34_or_greater by Delegates.notNull() var is_19_36_or_greater by Delegates.notNull() var is_19_41_or_greater by Delegates.notNull() @@ -46,6 +47,7 @@ internal object VersionCheckPatch : ResourcePatch() { is_19_29_or_greater = 243005000 <= playStoreServicesVersion is_19_32_or_greater = 243199000 <= playStoreServicesVersion is_19_33_or_greater = 243405000 <= playStoreServicesVersion + is_19_34_or_greater = 243499000 <= playStoreServicesVersion is_19_36_or_greater = 243705000 <= playStoreServicesVersion is_19_41_or_greater = 244305000 <= playStoreServicesVersion } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt index ade624eaf9..2cc3142ff9 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt @@ -151,7 +151,6 @@ object SettingsPatch : val SHORTS = Screen( key = "revanced_settings_screen_06_shorts", summaryKey = null, - sorting = Sorting.UNSORTED, ) // Don't sort, because title sorting scatters the custom color preferences. val SEEKBAR = Screen( diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index 712d8d2fdf..d08ae90ea1 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -602,6 +602,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Thumbnail seekbar is shown + Shorts player + Hide or show components in the Shorts player Hide Shorts in home feed Shorts in home feed are hidden @@ -997,6 +999,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts player will not resume on app startup Shorts player will resume on app startup + + Autoplay Shorts + Shorts will autoplay + Shorts will repeat + Autoplay Shorts background play + Shorts background play will autoplay + Shorts background play will repeat + Enable tablet layout Tablet layout is enabled From 1ed677f7b8ba561b2bb173dcaf5d6123c22179c4 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 21 Oct 2024 02:54:06 -0400 Subject: [PATCH 02/65] fix(YouTube - Hide layout components): Move hide chips settings to Feed menu --- .../youtube/layout/hide/general/HideLayoutComponentsPatch.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index 3930d17716..202bb19c4e 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -108,6 +108,8 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_artist_cards"), SwitchPreference("revanced_hide_community_posts"), SwitchPreference("revanced_hide_compact_banner"), + SwitchPreference("revanced_hide_chips_shelf"), + SwitchPreference("revanced_hide_expandable_chip"), SwitchPreference("revanced_hide_feed_survey"), SwitchPreference("revanced_hide_for_you_shelf"), SwitchPreference("revanced_hide_horizontal_shelves"), @@ -137,8 +139,6 @@ object HideLayoutComponentsPatch : BytecodePatch( ) SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences( - SwitchPreference("revanced_hide_chips_shelf"), - SwitchPreference("revanced_hide_expandable_chip"), SwitchPreference("revanced_hide_gray_separator"), PreferenceScreen( key = "revanced_custom_filter_screen", From e5f38494466dada1c7374052e88946c4b5f6a9a2 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 21 Oct 2024 06:56:34 +0000 Subject: [PATCH 03/65] chore: Release v4.18.0-dev.1 [skip ci] # [4.18.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.17.0...v4.18.0-dev.1) (2024-10-21) ### Bug Fixes * **YouTube - Hide layout components:** Move hide chips settings to Feed menu ([1ed677f](https://github.com/ReVanced/revanced-patches/commit/1ed677f7b8ba561b2bb173dcaf5d6123c22179c4)) ### Features * **YouTube:** Add `Shorts autoplay` patch ([#3794](https://github.com/ReVanced/revanced-patches/issues/3794)) ([96b5aed](https://github.com/ReVanced/revanced-patches/commit/96b5aede482f7a69d6df17864a2e17568b0da880)) --- CHANGELOG.md | 12 ++++++++++++ gradle.properties | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a841566e6e..b53dea1508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# [4.18.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.17.0...v4.18.0-dev.1) (2024-10-21) + + +### Bug Fixes + +* **YouTube - Hide layout components:** Move hide chips settings to Feed menu ([1ed677f](https://github.com/ReVanced/revanced-patches/commit/1ed677f7b8ba561b2bb173dcaf5d6123c22179c4)) + + +### Features + +* **YouTube:** Add `Shorts autoplay` patch ([#3794](https://github.com/ReVanced/revanced-patches/issues/3794)) ([96b5aed](https://github.com/ReVanced/revanced-patches/commit/96b5aede482f7a69d6df17864a2e17568b0da880)) + # [4.17.0](https://github.com/ReVanced/revanced-patches/compare/v4.16.0...v4.17.0) (2024-10-20) diff --git a/gradle.properties b/gradle.properties index aac016168c..4285ecf136 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 4.17.0 +version = 4.18.0-dev.1 From b54592cf9c5d859e1af2f02e8e6aaad7d47ab760 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 23 Oct 2024 01:03:33 +0400 Subject: [PATCH 04/65] fix(Twitter - Change link sharing domain): Support latest app version (#3786) --- .../misc/links/ChangeLinkSharingDomainPatch.kt | 16 ++++++++-------- .../ChangeLinkSharingDomainResourcePatch.kt | 17 +++++++++++++++++ .../LinkResourceGetterFingerprint.kt | 7 ++++--- 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainResourcePatch.kt diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt index 06597dd81d..c165d41a24 100644 --- a/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt @@ -12,15 +12,16 @@ import app.revanced.patches.twitter.misc.links.fingerprints.LinkBuilderFingerpri import app.revanced.patches.twitter.misc.links.fingerprints.LinkResourceGetterFingerprint import app.revanced.patches.twitter.misc.links.fingerprints.LinkSharingDomainFingerprint import app.revanced.util.exception -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c -import com.android.tools.smali.dexlib2.iface.reference.StringReference @Patch( name = "Change link sharing domain", description = "Replaces the domain name of Twitter links when sharing them.", + dependencies = [ + ChangeLinkSharingDomainResourcePatch::class, + ], compatiblePackages = [CompatiblePackage("com.twitter.android")], ) @Suppress("unused") @@ -75,12 +76,11 @@ object ChangeLinkSharingDomainPatch : BytecodePatch( // Used in the Share via... dialog. LinkResourceGetterFingerprint.result?.mutableMethod?.apply { - val constWithParameterName = indexOfFirstInstructionOrThrow { - getReference()?.string?.contains("id.toString()") == true - } + val templateIdConstIndex = + indexOfFirstWideLiteralInstructionValueOrThrow(ChangeLinkSharingDomainResourcePatch.tweetShareLinkTemplateId) - // Format the link with the new domain name register (2 instructions above the const-string). - val formatLinkCallIndex = constWithParameterName - 2 + // Format the link with the new domain name register (1 instruction below the const). + val formatLinkCallIndex = templateIdConstIndex + 1 val formatLinkCall = getInstruction(formatLinkCallIndex) // Replace the original method call with the new method call. diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainResourcePatch.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainResourcePatch.kt new file mode 100644 index 0000000000..52845a48e0 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainResourcePatch.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.twitter.misc.links + +import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.ResourcePatch +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch + +@Patch( + dependencies = [ResourceMappingPatch::class], +) +internal object ChangeLinkSharingDomainResourcePatch : ResourcePatch() { + internal var tweetShareLinkTemplateId: Long = -1 + + override fun execute(context: ResourceContext) { + tweetShareLinkTemplateId = ResourceMappingPatch["string", "tweet_share_link"] + } +} diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkResourceGetterFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkResourceGetterFingerprint.kt index a84181f62c..343c9dce3d 100644 --- a/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkResourceGetterFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkResourceGetterFingerprint.kt @@ -1,12 +1,13 @@ package app.revanced.patches.twitter.misc.links.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patches.twitter.misc.links.ChangeLinkSharingDomainResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags // Gets Resource string for share link view available by pressing "Share via" button. -internal object LinkResourceGetterFingerprint : MethodFingerprint( +internal object LinkResourceGetterFingerprint : LiteralValueFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, parameters = listOf("Landroid/content/res/Resources;"), - strings = listOf("res.getString(R.string.t…lUsername, id.toString())"), + literalSupplier = { ChangeLinkSharingDomainResourcePatch.tweetShareLinkTemplateId } ) \ No newline at end of file From 77568428a77c33ea4e05bb9b1864476c28a73a8e Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 22 Oct 2024 21:05:36 +0000 Subject: [PATCH 05/65] chore: Release v4.18.0-dev.2 [skip ci] # [4.18.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.1...v4.18.0-dev.2) (2024-10-22) ### Bug Fixes * **Twitter - Change link sharing domain:** Support latest app version ([#3786](https://github.com/ReVanced/revanced-patches/issues/3786)) ([b54592c](https://github.com/ReVanced/revanced-patches/commit/b54592cf9c5d859e1af2f02e8e6aaad7d47ab760)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b53dea1508..c8fcfe41bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [4.18.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.1...v4.18.0-dev.2) (2024-10-22) + + +### Bug Fixes + +* **Twitter - Change link sharing domain:** Support latest app version ([#3786](https://github.com/ReVanced/revanced-patches/issues/3786)) ([b54592c](https://github.com/ReVanced/revanced-patches/commit/b54592cf9c5d859e1af2f02e8e6aaad7d47ab760)) + # [4.18.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.17.0...v4.18.0-dev.1) (2024-10-21) diff --git a/gradle.properties b/gradle.properties index 4285ecf136..24f7bbffd3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 4.18.0-dev.1 +version = 4.18.0-dev.2 From bbcb57a32dfc8f031886f98b1b9701285105c579 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:29:23 -0400 Subject: [PATCH 06/65] feat(YouTube): Merge multiple layout patches into `Hide Layout Components` (#3799) --- .../layout/hide/albumcards/AlbumCardsPatch.kt | 53 +----- .../albumcards/AlbumCardsResourcePatch.kt | 30 --- .../layout/hide/comments/CommentsPatch.kt | 60 +----- .../crowdfundingbox/CrowdfundingBoxPatch.kt | 48 +---- .../CrowdfundingBoxResourcePatch.kt | 33 ---- .../hide/filterbar/HideFilterBarPatch.kt | 88 +-------- .../filterbar/HideFilterBarResourcePatch.kt | 36 ---- .../HideFloatingMicrophoneButtonPatch.kt | 51 +---- ...deFloatingMicrophoneButtonResourcePatch.kt | 30 --- .../hide/general/HideLayoutComponentsPatch.kt | 178 +++++++++++++++--- .../HideLayoutComponentsResourcePatch.kt | 41 +++- .../fingerprints/AlbumCardsFingerprint.kt | 6 +- .../CrowdfundingBoxFingerprint.kt | 6 +- .../FilterBarHeightFingerprint.kt | 6 +- .../RelatedChipCloudFingerprint.kt | 6 +- .../SearchResultsChipBarFingerprint.kt | 6 +- ...ShowFloatingMicrophoneButtonFingerprint.kt | 6 +- .../resources/addresources/values/strings.xml | 92 +++++---- 18 files changed, 283 insertions(+), 493 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonResourcePatch.kt rename src/main/kotlin/app/revanced/patches/youtube/layout/hide/{albumcards => general}/fingerprints/AlbumCardsFingerprint.kt (69%) rename src/main/kotlin/app/revanced/patches/youtube/layout/hide/{crowdfundingbox => general}/fingerprints/CrowdfundingBoxFingerprint.kt (65%) rename src/main/kotlin/app/revanced/patches/youtube/layout/hide/{filterbar => general}/fingerprints/FilterBarHeightFingerprint.kt (67%) rename src/main/kotlin/app/revanced/patches/youtube/layout/hide/{filterbar => general}/fingerprints/RelatedChipCloudFingerprint.kt (65%) rename src/main/kotlin/app/revanced/patches/youtube/layout/hide/{filterbar => general}/fingerprints/SearchResultsChipBarFingerprint.kt (68%) rename src/main/kotlin/app/revanced/patches/youtube/layout/hide/{floatingmicrophone => general}/fingerprints/ShowFloatingMicrophoneButtonFingerprint.kt (64%) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch.kt index 937f2b509d..7d79a66ef6 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch.kt @@ -1,56 +1,13 @@ package app.revanced.patches.youtube.layout.hide.albumcards -import app.revanced.util.exception import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.youtube.layout.hide.albumcards.fingerprints.AlbumCardsFingerprint -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsPatch -@Patch( - name = "Hide album cards", - description = "Adds an option to hide album cards below artist descriptions.", - dependencies = [ - IntegrationsPatch::class, - AlbumCardsResourcePatch::class - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ] - ) - ] -) +@Deprecated("This patch has been merged to HideLayoutComponentsPatch.") @Suppress("unused") object AlbumCardsPatch : BytecodePatch( - setOf(AlbumCardsFingerprint) + dependencies = setOf(HideLayoutComponentsPatch::class), ) { - override fun execute(context: BytecodeContext) { - AlbumCardsFingerprint.result?.let { - it.mutableMethod.apply { - val checkCastAnchorIndex = it.scanResult.patternScanResult!!.endIndex - val insertIndex = checkCastAnchorIndex + 1 - - val albumCardViewRegister = getInstruction(checkCastAnchorIndex).registerA - - addInstruction( - insertIndex, - "invoke-static {v$albumCardViewRegister}, " + - "Lapp/revanced/integrations/youtube/patches/HideAlbumCardsPatch;" + - "->" + - "hideAlbumCard(Landroid/view/View;)V" - ) - } - } ?: throw AlbumCardsFingerprint.exception - } -} + override fun execute(context: BytecodeContext) { } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsResourcePatch.kt deleted file mode 100644 index e8265b68dc..0000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsResourcePatch.kt +++ /dev/null @@ -1,30 +0,0 @@ -package app.revanced.patches.youtube.layout.hide.albumcards - -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.settings.SettingsPatch - -@Patch( - dependencies = [ - SettingsPatch::class, - ResourceMappingPatch::class, - AddResourcesPatch::class, - ], -) -internal object AlbumCardsResourcePatch : ResourcePatch() { - internal var albumCardId: Long = -1 - - override fun execute(context: ResourceContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.FEED.addPreferences( - SwitchPreference("revanced_hide_album_cards"), - ) - - albumCardId = ResourceMappingPatch["layout", "album_card"] - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt index 9c595da663..c9d58438e0 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt @@ -2,58 +2,12 @@ package app.revanced.patches.youtube.layout.hide.comments import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch -import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsPatch -@Patch( - name = "Comments", - description = "Adds options to hide components related to comments.", - dependencies = [ - SettingsPatch::class, - LithoFilterPatch::class, - AddResourcesPatch::class - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ] - ) - ] -) +@Deprecated("This patch has been merged to HideLayoutComponentsPatch.") @Suppress("unused") -object CommentsPatch : ResourcePatch() { - private const val FILTER_CLASS_DESCRIPTOR = - "Lapp/revanced/integrations/youtube/patches/components/CommentsFilter;" - - override fun execute(context: ResourceContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.PLAYER.addPreferences( - PreferenceScreen( - "revanced_comments_screen", - preferences = setOf( - SwitchPreference("revanced_hide_comments_by_members_header"), - SwitchPreference("revanced_hide_comments_section"), - SwitchPreference("revanced_hide_comments_create_a_short_button"), - SwitchPreference("revanced_hide_comments_preview_comment"), - SwitchPreference("revanced_hide_comments_thanks_button"), - SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons") - ), - sorting = PreferenceScreen.Sorting.UNSORTED - ) - ) - - LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR) - } -} +object CommentsPatch : ResourcePatch( + dependencies = setOf(HideLayoutComponentsPatch::class), +) { + override fun execute(context: ResourceContext) { } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch.kt index e186babd7b..be071179c6 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch.kt @@ -1,51 +1,13 @@ package app.revanced.patches.youtube.layout.hide.crowdfundingbox -import app.revanced.util.exception import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.youtube.layout.hide.crowdfundingbox.fingerprints.CrowdfundingBoxFingerprint -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch -import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsPatch -@Patch( - name = "Hide crowdfunding box", - description = "Adds an option to hide the crowdfunding box between the player and video description.", - dependencies = [ - IntegrationsPatch::class, - CrowdfundingBoxResourcePatch::class - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ] - ) - ] -) +@Deprecated("This patch has been merged to HideLayoutComponentsPatch.") @Suppress("unused") object CrowdfundingBoxPatch : BytecodePatch( - setOf(CrowdfundingBoxFingerprint) + dependencies = setOf(HideLayoutComponentsPatch::class), ) { - private const val INTEGRATIONS_METHOD_DESCRIPTOR = - "Lapp/revanced/integrations/youtube/patches/HideCrowdfundingBoxPatch;->hideCrowdfundingBox(Landroid/view/View;)V" - - override fun execute(context: BytecodeContext) { - CrowdfundingBoxFingerprint.result?.let { - it.mutableMethod.apply { - val insertIndex = it.scanResult.patternScanResult!!.endIndex - val objectRegister = getInstruction(insertIndex).registerA - - addInstruction(insertIndex, "invoke-static {v$objectRegister}, $INTEGRATIONS_METHOD_DESCRIPTOR") - } - } ?: throw CrowdfundingBoxFingerprint.exception - } -} + override fun execute(context: BytecodeContext) { } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxResourcePatch.kt deleted file mode 100644 index f1763704c9..0000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxResourcePatch.kt +++ /dev/null @@ -1,33 +0,0 @@ -package app.revanced.patches.youtube.layout.hide.crowdfundingbox - -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.settings.SettingsPatch - -@Patch( - dependencies = [ - SettingsPatch::class, - ResourceMappingPatch::class, - AddResourcesPatch::class, - ], -) -internal object CrowdfundingBoxResourcePatch : ResourcePatch() { - internal var crowdfundingBoxId: Long = -1 - - override fun execute(context: ResourceContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.FEED.addPreferences( - SwitchPreference("revanced_hide_crowdfunding_box"), - ) - - crowdfundingBoxId = ResourceMappingPatch[ - "layout", - "donation_companion", - ] - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch.kt index 7e4ad51230..c084ea9e86 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch.kt @@ -1,90 +1,12 @@ package app.revanced.patches.youtube.layout.hide.filterbar -import app.revanced.util.exception import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.youtube.layout.hide.filterbar.fingerprints.FilterBarHeightFingerprint -import app.revanced.patches.youtube.layout.hide.filterbar.fingerprints.RelatedChipCloudFingerprint -import app.revanced.patches.youtube.layout.hide.filterbar.fingerprints.SearchResultsChipBarFingerprint -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsPatch -@Patch( - name = "Hide filter bar", - description = "Adds options to hide the category bar at the top of video feeds.", - dependencies = [HideFilterBarResourcePatch::class], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ] - ) - ] -) -@Suppress("unused") +@Deprecated("This patch class has been merged into HideLayoutComponentsPatch.") object HideFilterBarPatch : BytecodePatch( - setOf( - RelatedChipCloudFingerprint, - SearchResultsChipBarFingerprint, - FilterBarHeightFingerprint - ) + dependencies = setOf(HideLayoutComponentsPatch::class) ) { - private const val INTEGRATIONS_CLASS_DESCRIPTOR = - "Lapp/revanced/integrations/youtube/patches/HideFilterBarPatch;" - - override fun execute(context: BytecodeContext) { - FilterBarHeightFingerprint.patch { register -> - """ - invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->hideInFeed(I)I - move-result v$register - """ - } - - RelatedChipCloudFingerprint.patch(1) { register -> - "invoke-static { v$register }, " + - "$INTEGRATIONS_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V" - } - - SearchResultsChipBarFingerprint.patch(-1, -2) { register -> - """ - invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->hideInSearch(I)I - move-result v$register - """ - } - } - - /** - * Patch a [MethodFingerprint] with a given [instructions]. - * - * @param RegisterInstruction The type of instruction to get the register from. - * @param insertIndexOffset The offset to add to the end index of the [MethodFingerprint]. - * @param hookRegisterOffset The offset to add to the register of the hook. - * @param instructions The instructions to add with the register as a parameter. - */ - private fun MethodFingerprint.patch( - insertIndexOffset: Int = 0, - hookRegisterOffset: Int = 0, - instructions: (Int) -> String - ) = - result?.let { - it.mutableMethod.apply { - val endIndex = it.scanResult.patternScanResult!!.endIndex - - val insertIndex = endIndex + insertIndexOffset - val register = - getInstruction(endIndex + hookRegisterOffset).registerA - - addInstructions(insertIndex, instructions(register)) - } - } ?: throw exception -} + override fun execute(context: BytecodeContext) {} +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarResourcePatch.kt deleted file mode 100644 index d49df7a5b4..0000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarResourcePatch.kt +++ /dev/null @@ -1,36 +0,0 @@ -package app.revanced.patches.youtube.layout.hide.filterbar - -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch -import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.settings.SettingsPatch - -@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class, AddResourcesPatch::class]) -internal object HideFilterBarResourcePatch : ResourcePatch() { - internal var filterBarHeightId = -1L - internal var relatedChipCloudMarginId = -1L - internal var barContainerHeightId = -1L - - override fun execute(context: ResourceContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.FEED.addPreferences( - PreferenceScreen( - key = "revanced_hide_filter_bar_screen", - preferences = setOf( - SwitchPreference("revanced_hide_filter_bar_feed_in_feed"), - SwitchPreference("revanced_hide_filter_bar_feed_in_search"), - SwitchPreference("revanced_hide_filter_bar_feed_in_related_videos"), - ), - ), - ) - - relatedChipCloudMarginId = ResourceMappingPatch["layout", "related_chip_cloud_reduced_margins"] - filterBarHeightId = ResourceMappingPatch["dimen", "filter_bar_height"] - barContainerHeightId = ResourceMappingPatch["dimen", "bar_container_height"] - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch.kt index 07b8425727..efdbb84fb7 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch.kt @@ -1,54 +1,13 @@ package app.revanced.patches.youtube.layout.hide.floatingmicrophone -import app.revanced.util.exception import app.revanced.patcher.data.BytecodeContext -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.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.youtube.layout.hide.floatingmicrophone.fingerprints.ShowFloatingMicrophoneButtonFingerprint -import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsPatch -@Patch( - name = "Hide floating microphone button", - description = "Adds an option to hide the floating microphone button when searching.", - dependencies = [HideFloatingMicrophoneButtonResourcePatch::class], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ] - ) - ] -) +@Deprecated("This patch has been merged to HideLayoutComponentsPatch.") @Suppress("unused") object HideFloatingMicrophoneButtonPatch : BytecodePatch( - setOf(ShowFloatingMicrophoneButtonFingerprint) + dependencies = setOf(HideLayoutComponentsPatch::class), ) { - private const val INTEGRATIONS_CLASS_DESCRIPTOR = - "Lapp/revanced/integrations/youtube/patches/HideFloatingMicrophoneButtonPatch;" - - override fun execute(context: BytecodeContext) { - ShowFloatingMicrophoneButtonFingerprint.result?.let { result -> - with(result.mutableMethod) { - val insertIndex = result.scanResult.patternScanResult!!.startIndex + 1 - val showButtonRegister = - getInstruction(insertIndex - 1).registerA - - addInstructions( - insertIndex, - """ - invoke-static {v$showButtonRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->hideFloatingMicrophoneButton(Z)Z - move-result v$showButtonRegister - """ - ) - } - } ?: throw ShowFloatingMicrophoneButtonFingerprint.exception - } -} + override fun execute(context: BytecodeContext) { } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonResourcePatch.kt deleted file mode 100644 index 30dd8e84d1..0000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonResourcePatch.kt +++ /dev/null @@ -1,30 +0,0 @@ -package app.revanced.patches.youtube.layout.hide.floatingmicrophone - -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.settings.SettingsPatch - -@Patch( - dependencies = [ - SettingsPatch::class, - ResourceMappingPatch::class, - AddResourcesPatch::class, - ], -) -internal object HideFloatingMicrophoneButtonResourcePatch : ResourcePatch() { - internal var fabButtonId: Long = -1 - - override fun execute(context: ResourceContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences( - SwitchPreference("revanced_hide_floating_microphone_button"), - ) - - fabButtonId = ResourceMappingPatch["id", "fab"] - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index 202bb19c4e..b9e8c8598f 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -7,6 +7,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstructions import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.Patch @@ -14,17 +15,23 @@ import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.shared.misc.settings.preference.* import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting +import app.revanced.patches.youtube.layout.hide.general.fingerprints.AlbumCardsFingerprint +import app.revanced.patches.youtube.layout.hide.general.fingerprints.CrowdfundingBoxFingerprint +import app.revanced.patches.youtube.layout.hide.general.fingerprints.FilterBarHeightFingerprint import app.revanced.patches.youtube.layout.hide.general.fingerprints.HideShowMoreButtonFingerprint import app.revanced.patches.youtube.layout.hide.general.fingerprints.ParseElementFromBufferFingerprint import app.revanced.patches.youtube.layout.hide.general.fingerprints.PlayerOverlayFingerprint +import app.revanced.patches.youtube.layout.hide.general.fingerprints.RelatedChipCloudFingerprint +import app.revanced.patches.youtube.layout.hide.general.fingerprints.SearchResultsChipBarFingerprint +import app.revanced.patches.youtube.layout.hide.general.fingerprints.ShowFloatingMicrophoneButtonFingerprint import app.revanced.patches.youtube.layout.hide.general.fingerprints.ShowWatermarkFingerprint import app.revanced.patches.youtube.layout.hide.general.fingerprints.YoodlesImageViewFingerprint import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.util.alsoResolve import app.revanced.util.findOpcodeIndicesReversed import app.revanced.util.getReference -import app.revanced.util.alsoResolve import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction @@ -61,13 +68,21 @@ object HideLayoutComponentsPatch : BytecodePatch( ParseElementFromBufferFingerprint, PlayerOverlayFingerprint, HideShowMoreButtonFingerprint, + AlbumCardsFingerprint, + CrowdfundingBoxFingerprint, YoodlesImageViewFingerprint, + RelatedChipCloudFingerprint, + SearchResultsChipBarFingerprint, + ShowFloatingMicrophoneButtonFingerprint, + FilterBarHeightFingerprint ), ) { private const val LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/components/LayoutComponentsFilter;" private const val DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME = "Lapp/revanced/integrations/youtube/patches/components/DescriptionComponentsFilter;" + private const val COMMENTS_FILTER_CLASS_NAME = + "Lapp/revanced/integrations/youtube/patches/components/CommentsFilter;" private const val CUSTOM_FILTER_CLASS_NAME = "Lapp/revanced/integrations/youtube/patches/components/CustomFilter;" private const val KEYWORD_FILTER_CLASS_NAME = @@ -77,11 +92,6 @@ object HideLayoutComponentsPatch : BytecodePatch( AddResourcesPatch(this::class) SettingsPatch.PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_hide_channel_bar"), - SwitchPreference("revanced_hide_channel_guidelines"), - SwitchPreference("revanced_hide_channel_member_shelf"), - SwitchPreference("revanced_hide_channel_watermark"), - SwitchPreference("revanced_hide_community_guidelines"), PreferenceScreen( key = "revanced_hide_description_components_screen", preferences = setOf( @@ -93,6 +103,23 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_transcript_section"), ), ), + PreferenceScreen( + "revanced_comments_screen", + preferences = setOf( + SwitchPreference("revanced_hide_comments_by_members_header"), + SwitchPreference("revanced_hide_comments_section"), + SwitchPreference("revanced_hide_comments_create_a_short_button"), + SwitchPreference("revanced_hide_comments_preview_comment"), + SwitchPreference("revanced_hide_comments_thanks_button"), + SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons") + ), + sorting = PreferenceScreen.Sorting.UNSORTED + ), + SwitchPreference("revanced_hide_channel_bar"), + SwitchPreference("revanced_hide_channel_guidelines"), + SwitchPreference("revanced_hide_channel_member_shelf"), + SwitchPreference("revanced_hide_channel_watermark"), + SwitchPreference("revanced_hide_community_guidelines"), SwitchPreference("revanced_hide_emergency_box"), SwitchPreference("revanced_hide_info_panels"), SwitchPreference("revanced_hide_join_membership_button"), @@ -105,12 +132,36 @@ object HideLayoutComponentsPatch : BytecodePatch( ) SettingsPatch.PreferenceScreen.FEED.addPreferences( + PreferenceScreen( + key = "revanced_hide_keyword_content_screen", + sorting = Sorting.UNSORTED, + preferences = setOf( + SwitchPreference("revanced_hide_keyword_content_home"), + SwitchPreference("revanced_hide_keyword_content_subscriptions"), + SwitchPreference("revanced_hide_keyword_content_search"), + TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE), + NonInteractivePreference("revanced_hide_keyword_content_about"), + NonInteractivePreference(key = "revanced_hide_keyword_content_about_whole_words", + tag = "app.revanced.integrations.youtube.settings.preference.HtmlPreference") + ) + ), + PreferenceScreen( + key = "revanced_hide_filter_bar_screen", + preferences = setOf( + SwitchPreference("revanced_hide_filter_bar_feed_in_feed"), + SwitchPreference("revanced_hide_filter_bar_feed_in_search"), + SwitchPreference("revanced_hide_filter_bar_feed_in_related_videos"), + ), + ), + SwitchPreference("revanced_hide_album_cards"), SwitchPreference("revanced_hide_artist_cards"), SwitchPreference("revanced_hide_community_posts"), SwitchPreference("revanced_hide_compact_banner"), + SwitchPreference("revanced_hide_crowdfunding_box"), SwitchPreference("revanced_hide_chips_shelf"), SwitchPreference("revanced_hide_expandable_chip"), SwitchPreference("revanced_hide_feed_survey"), + SwitchPreference("revanced_hide_floating_microphone_button"), SwitchPreference("revanced_hide_for_you_shelf"), SwitchPreference("revanced_hide_horizontal_shelves"), SwitchPreference("revanced_hide_image_shelf"), @@ -123,19 +174,6 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_search_result_shelf_header"), SwitchPreference("revanced_hide_show_more_button"), SwitchPreference("revanced_hide_doodles"), - PreferenceScreen( - key = "revanced_hide_keyword_content_screen", - sorting = Sorting.UNSORTED, - preferences = setOf( - SwitchPreference("revanced_hide_keyword_content_home"), - SwitchPreference("revanced_hide_keyword_content_subscriptions"), - SwitchPreference("revanced_hide_keyword_content_search"), - TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE), - NonInteractivePreference("revanced_hide_keyword_content_about"), - NonInteractivePreference(key = "revanced_hide_keyword_content_about_whole_words", - tag = "app.revanced.integrations.youtube.settings.preference.HtmlPreference") - ) - ) ) SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences( @@ -153,6 +191,7 @@ object HideLayoutComponentsPatch : BytecodePatch( LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR) LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME) + LithoFilterPatch.addFilter(COMMENTS_FILTER_CLASS_NAME) LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME) LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_NAME) @@ -213,8 +252,60 @@ object HideLayoutComponentsPatch : BytecodePatch( val insertIndex = moveRegisterIndex + 1 addInstruction( insertIndex, - "invoke-static { v$viewRegister }, " + - "$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideShowMoreButton(Landroid/view/View;)V", + "invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + + "->hideShowMoreButton(Landroid/view/View;)V", + ) + } + } + + // endregion + + // region crowd funding box + CrowdfundingBoxFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val insertIndex = it.scanResult.patternScanResult!!.endIndex + val objectRegister = getInstruction(insertIndex).registerA + + addInstruction( + insertIndex, + "invoke-static {v$objectRegister}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + + "->hideCrowdfundingBox(Landroid/view/View;)V") + } + } + + // endregion + + // region hide album cards + + AlbumCardsFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val checkCastAnchorIndex = it.scanResult.patternScanResult!!.endIndex + val insertIndex = checkCastAnchorIndex + 1 + val register = getInstruction(checkCastAnchorIndex).registerA + + addInstruction( + insertIndex, + "invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + + "->hideAlbumCard(Landroid/view/View;)V" + ) + } + } + + // endregion + + // region hide floating microphone + + ShowFloatingMicrophoneButtonFingerprint.resultOrThrow().let { result -> + with(result.mutableMethod) { + val startIndex = result.scanResult.patternScanResult!!.startIndex + val register = getInstruction(startIndex).registerA + + addInstructions( + startIndex + 1, + """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideFloatingMicrophoneButton(Z)Z + move-result v$register + """ ) } } @@ -243,5 +334,50 @@ object HideLayoutComponentsPatch : BytecodePatch( } // endregion + + // region hide filter bar + + FilterBarHeightFingerprint.patch { register -> + """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInFeed(I)I + move-result v$register + """ + } + + SearchResultsChipBarFingerprint.patch(-1, -2) { register -> + """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInSearch(I)I + move-result v$register + """ + } + + RelatedChipCloudFingerprint.patch(1) { register -> + "invoke-static { v$register }, " + + "$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V" + } + } + + /** + * Patch a [MethodFingerprint] with a given [instructions]. + * + * @param RegisterInstruction The type of instruction to get the register from. + * @param insertIndexOffset The offset to add to the end index of the [MethodFingerprint]. + * @param hookRegisterOffset The offset to add to the register of the hook. + * @param instructions The instructions to add with the register as a parameter. + */ + private fun MethodFingerprint.patch( + insertIndexOffset: Int = 0, + hookRegisterOffset: Int = 0, + instructions: (Int) -> String + ) = resultOrThrow().let { + it.mutableMethod.apply { + val endIndex = it.scanResult.patternScanResult!!.endIndex + + val insertIndex = endIndex + insertIndexOffset + val register = + getInstruction(endIndex + hookRegisterOffset).registerA + + addInstructions(insertIndex, instructions(register)) + } } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsResourcePatch.kt index 24dcbb3f19..ce32d2b8a0 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsResourcePatch.kt @@ -15,19 +15,56 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch ], ) internal object HideLayoutComponentsResourcePatch : ResourcePatch() { - internal var expandButtonDownId: Long = -1 - + var expandButtonDownId: Long = -1 + var albumCardId: Long = -1 + var crowdfundingBoxId: Long = -1 var youTubeLogo = -1L + var filterBarHeightId = -1L + var relatedChipCloudMarginId = -1L + var barContainerHeightId = -1L + + var fabButtonId: Long = -1 + override fun execute(context: ResourceContext) { expandButtonDownId = ResourceMappingPatch[ "layout", "expand_button_down", ] + albumCardId = ResourceMappingPatch[ + "layout", + "album_card" + ] + + crowdfundingBoxId = ResourceMappingPatch[ + "layout", + "donation_companion", + ] + youTubeLogo = ResourceMappingPatch[ "id", "youtube_logo" ] + + relatedChipCloudMarginId = ResourceMappingPatch[ + "layout", + "related_chip_cloud_reduced_margins" + ] + + filterBarHeightId = ResourceMappingPatch[ + "dimen", + "filter_bar_height" + ] + + barContainerHeightId = ResourceMappingPatch[ + "dimen", + "bar_container_height" + ] + + fabButtonId = ResourceMappingPatch[ + "id", + "fab" + ] } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/fingerprints/AlbumCardsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/AlbumCardsFingerprint.kt similarity index 69% rename from src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/fingerprints/AlbumCardsFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/AlbumCardsFingerprint.kt index 6bbabdf520..7032d1b793 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/fingerprints/AlbumCardsFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/AlbumCardsFingerprint.kt @@ -1,7 +1,7 @@ -package app.revanced.patches.youtube.layout.hide.albumcards.fingerprints +package app.revanced.patches.youtube.layout.hide.general.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.layout.hide.albumcards.AlbumCardsResourcePatch +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -16,5 +16,5 @@ internal object AlbumCardsFingerprint : LiteralValueFingerprint( Opcode.MOVE_RESULT_OBJECT, Opcode.CHECK_CAST, ), - literalSupplier = { AlbumCardsResourcePatch.albumCardId } + literalSupplier = { HideLayoutComponentsResourcePatch.albumCardId } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/fingerprints/CrowdfundingBoxFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/CrowdfundingBoxFingerprint.kt similarity index 65% rename from src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/fingerprints/CrowdfundingBoxFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/CrowdfundingBoxFingerprint.kt index 465b10bfe5..ec40629ee0 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/fingerprints/CrowdfundingBoxFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/CrowdfundingBoxFingerprint.kt @@ -1,7 +1,7 @@ -package app.revanced.patches.youtube.layout.hide.crowdfundingbox.fingerprints +package app.revanced.patches.youtube.layout.hide.general.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.layout.hide.crowdfundingbox.CrowdfundingBoxResourcePatch +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -13,5 +13,5 @@ internal object CrowdfundingBoxFingerprint : LiteralValueFingerprint( Opcode.MOVE_RESULT_OBJECT, Opcode.IPUT_OBJECT, ), - literalSupplier = { CrowdfundingBoxResourcePatch.crowdfundingBoxId } + literalSupplier = { HideLayoutComponentsResourcePatch.crowdfundingBoxId } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/FilterBarHeightFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/FilterBarHeightFingerprint.kt similarity index 67% rename from src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/FilterBarHeightFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/FilterBarHeightFingerprint.kt index b15abf5bb9..c074abd426 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/FilterBarHeightFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/FilterBarHeightFingerprint.kt @@ -1,7 +1,7 @@ -package app.revanced.patches.youtube.layout.hide.filterbar.fingerprints +package app.revanced.patches.youtube.layout.hide.general.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.layout.hide.filterbar.HideFilterBarResourcePatch +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -15,5 +15,5 @@ internal object FilterBarHeightFingerprint : LiteralValueFingerprint( Opcode.MOVE_RESULT, Opcode.IPUT ), - literalSupplier = { HideFilterBarResourcePatch.filterBarHeightId } + literalSupplier = { HideLayoutComponentsResourcePatch.filterBarHeightId } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/RelatedChipCloudFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/RelatedChipCloudFingerprint.kt similarity index 65% rename from src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/RelatedChipCloudFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/RelatedChipCloudFingerprint.kt index d9efa6c49d..2f51b6611f 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/RelatedChipCloudFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/RelatedChipCloudFingerprint.kt @@ -1,7 +1,7 @@ -package app.revanced.patches.youtube.layout.hide.filterbar.fingerprints +package app.revanced.patches.youtube.layout.hide.general.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.layout.hide.filterbar.HideFilterBarResourcePatch +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -14,5 +14,5 @@ internal object RelatedChipCloudFingerprint : LiteralValueFingerprint( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT ), - literalSupplier = { HideFilterBarResourcePatch.relatedChipCloudMarginId } + literalSupplier = { HideLayoutComponentsResourcePatch.relatedChipCloudMarginId } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/SearchResultsChipBarFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/SearchResultsChipBarFingerprint.kt similarity index 68% rename from src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/SearchResultsChipBarFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/SearchResultsChipBarFingerprint.kt index ed1179ba72..27fc800c30 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/fingerprints/SearchResultsChipBarFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/SearchResultsChipBarFingerprint.kt @@ -1,7 +1,7 @@ -package app.revanced.patches.youtube.layout.hide.filterbar.fingerprints +package app.revanced.patches.youtube.layout.hide.general.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.layout.hide.filterbar.HideFilterBarResourcePatch +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -16,5 +16,5 @@ internal object SearchResultsChipBarFingerprint : LiteralValueFingerprint( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT ), - literalSupplier = { HideFilterBarResourcePatch.barContainerHeightId } + literalSupplier = { HideLayoutComponentsResourcePatch.barContainerHeightId } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/fingerprints/ShowFloatingMicrophoneButtonFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/ShowFloatingMicrophoneButtonFingerprint.kt similarity index 64% rename from src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/fingerprints/ShowFloatingMicrophoneButtonFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/ShowFloatingMicrophoneButtonFingerprint.kt index 4abde10606..b9a1a7b3b7 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/fingerprints/ShowFloatingMicrophoneButtonFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/ShowFloatingMicrophoneButtonFingerprint.kt @@ -1,7 +1,7 @@ -package app.revanced.patches.youtube.layout.hide.floatingmicrophone.fingerprints +package app.revanced.patches.youtube.layout.hide.general.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.layout.hide.floatingmicrophone.HideFloatingMicrophoneButtonResourcePatch +import app.revanced.patches.youtube.layout.hide.general.HideLayoutComponentsResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -15,5 +15,5 @@ internal object ShowFloatingMicrophoneButtonFingerprint : LiteralValueFingerprin Opcode.IF_EQZ, Opcode.RETURN_VOID ), - literalSupplier = { HideFloatingMicrophoneButtonResourcePatch.fabButtonId } + literalSupplier = { HideLayoutComponentsResourcePatch.fabButtonId } ) \ No newline at end of file diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index d08ae90ea1..05c06137ac 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Disable like / subscribe button glow Like and subscribe button will not glow when mentioned Like and subscribe button will glow when mentioned + Hide album cards + Album cards are hidden + Album cards are shown + Hide crowdfunding box + Crowdfunding box is hidden + Crowdfunding box is shown + Hide floating microphone button + Microphone button hidden + Microphone button shown Hide gray separator Gray separators are hidden Gray separators are shown @@ -232,6 +241,39 @@ This is because Crowdin requires temporarily flattening this file and removing t Video description Hide or show video description components + Filter bar + Hide or show the filter bar in the feed, search, and related videos + Hide in feed + Hidden in feed + Shown in feed + Hide in search + Hidden in search + Shown in search + Hide in related videos + Hidden in related videos + Shown in related videos + + Comments + Hide or show comments section components + Hide \'Comments by members\' header + \'Comments by members\' header is hidden + \'Comments by members\' header is shown + Hide comments section + Comments section is hidden + Comments section is shown + Hide \'Create a Short\' button + \'Create a Short\' button is hidden + \'Create a Short\' button is shown + Hide preview comment + Preview comment is hidden + Preview comment is shown + Hide thanks button + Thanks button is hidden + Thanks button is shown + Hide timestamp and emoji buttons + Timestamp and emoji buttons are hidden + Timestamp and emoji buttons are shown + Hide YouTube Doodles Search bar Doodles are hidden @@ -523,61 +565,11 @@ This is because Crowdin requires temporarily flattening this file and removing t Buttons are hidden Buttons are shown - - Hide album cards - Album cards are hidden - Album cards are shown - - - Comments - Hide or show comments section components - Hide \'Comments by members\' header - \'Comments by members\' header is hidden - \'Comments by members\' header is shown - Hide comments section - Comments section is hidden - Comments section is shown - Hide \'Create a Short\' button - \'Create a Short\' button is hidden - \'Create a Short\' button is shown - Hide preview comment - Preview comment is hidden - Preview comment is shown - Hide thanks button - Thanks button is hidden - Thanks button is shown - Hide timestamp and emoji buttons - Timestamp and emoji buttons are hidden - Timestamp and emoji buttons are shown - - - Hide crowdfunding box - Crowdfunding box is hidden - Crowdfunding box is shown - Hide end screen cards End screen cards are hidden End screen cards are shown - - Filter bar - Hide or show the filter bar in the feed, search, and related videos - Hide in feed - Hidden in feed - Shown in feed - Hide in search - Hidden in search - Shown in search - Hide in related videos - Hidden in related videos - Shown in related videos - - - Hide floating microphone button - Microphone button hidden - Microphone button shown - Disable ambient mode in fullscreen Ambient mode disabled From 4ba0300590dd988bdcaa0761c4e606c1d7f86ce5 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:31:07 -0400 Subject: [PATCH 07/65] feat(YouTube): Merge multiple player overlay patches into `Hide player overlay buttons` (#3800) Co-authored-by: oSumAtrIX --- api/revanced-patches.api | 6 + .../layout/autocaptions/AutoCaptionsPatch.kt | 2 +- .../autoplay/HideAutoplayButtonPatch.kt | 78 +-------- .../captions/HideCaptionsButtonPatch.kt | 53 +----- .../buttons/cast/HideCastButtonPatch.kt | 47 +----- .../overlay/HidePlayerOverlayButtonsPatch.kt | 151 ++++++++++++++++++ .../HidePlayerOverlayButtonsResourcePatch.kt} | 4 +- .../MediaRouteButtonFingerprint.kt | 10 ++ ...rolsPreviousNextOverlayTouchFingerprint.kt | 8 +- .../player/hide/HidePlayerButtonsPatch.kt | 69 +------- .../youtube/misc/gms/GmsCoreSupportPatch.kt | 4 +- .../SubtitleButtonControllerFingerprint.kt | 2 +- .../resources/addresources/values/strings.xml | 34 ++-- 13 files changed, 209 insertions(+), 259 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt rename src/main/kotlin/app/revanced/patches/youtube/layout/buttons/{player/hide/HidePlayerButtonsResourcePatch.kt => overlay/HidePlayerOverlayButtonsResourcePatch.kt} (84%) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/MediaRouteButtonFingerprint.kt rename src/main/kotlin/app/revanced/patches/youtube/layout/buttons/{player/hide => overlay}/fingerprints/PlayerControlsPreviousNextOverlayTouchFingerprint.kt (63%) rename src/main/kotlin/app/revanced/patches/youtube/{layout/autocaptions => shared}/fingerprints/SubtitleButtonControllerFingerprint.kt (91%) diff --git a/api/revanced-patches.api b/api/revanced-patches.api index 152dc8346f..0b4976b98e 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -1670,6 +1670,12 @@ public final class app/revanced/patches/youtube/layout/buttons/navigation/Naviga public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + public final class app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch : app/revanced/patcher/patch/BytecodePatch { public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch; public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt index f5ea004743..3d82a1e412 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt @@ -9,10 +9,10 @@ import app.revanced.patcher.patch.annotation.Patch import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.layout.autocaptions.fingerprints.StartVideoInformerFingerprint -import app.revanced.patches.youtube.layout.autocaptions.fingerprints.SubtitleButtonControllerFingerprint import app.revanced.patches.youtube.layout.autocaptions.fingerprints.SubtitleTrackFingerprint import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.patches.youtube.shared.fingerprints.SubtitleButtonControllerFingerprint import app.revanced.util.exception diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch.kt index 161a3a2b3c..a322b755d1 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch.kt @@ -1,84 +1,14 @@ package app.revanced.patches.youtube.layout.buttons.autoplay import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch -import app.revanced.patches.youtube.misc.settings.SettingsPatch -import app.revanced.patches.youtube.shared.fingerprints.LayoutConstructorFingerprint -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfIdResourceOrThrow -import app.revanced.util.resultOrThrow -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import app.revanced.patches.youtube.layout.buttons.overlay.HidePlayerOverlayButtonsPatch -@Patch( - name = "Hide autoplay button", - description = "Adds an option to hide the autoplay button in the video player.", - dependencies = [ - IntegrationsPatch::class, - SettingsPatch::class, - ResourceMappingPatch::class, - AddResourcesPatch::class, - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ], - ), - ], -) @Suppress("unused") +@Deprecated("This patch has been merged into HidePlayerOverlayButtonsPatch.") object HideAutoplayButtonPatch : BytecodePatch( - setOf(LayoutConstructorFingerprint), + dependencies = setOf(HidePlayerOverlayButtonsPatch::class), ) { - - private const val INTEGRATIONS_CLASS_DESCRIPTOR = - "Lapp/revanced/integrations/youtube/patches/HideAutoplayButtonPatch;" - override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_hide_autoplay_button"), - ) - - LayoutConstructorFingerprint.resultOrThrow().mutableMethod.apply { - val constIndex = indexOfIdResourceOrThrow("autonav_toggle") - val constRegister = getInstruction(constIndex).registerA - - // Add a conditional branch around the code that inflates and adds the auto repeat button. - val gotoIndex = indexOfFirstInstructionOrThrow(constIndex) { - val parameterTypes = getReference()?.parameterTypes - opcode == Opcode.INVOKE_VIRTUAL && - parameterTypes?.size == 2 && - parameterTypes.first() == "Landroid/view/ViewStub;" - } + 1 - - addInstructionsWithLabels( - constIndex, - """ - invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->hideAutoPlayButton()Z - move-result v$constRegister - if-nez v$constRegister, :hidden - """, - ExternalLabel("hidden", getInstruction(gotoIndex)), - ) - } } -} +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch.kt index bf41baa3be..200f8e5ffa 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch.kt @@ -1,61 +1,14 @@ package app.revanced.patches.youtube.layout.buttons.captions import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.layout.autocaptions.fingerprints.SubtitleButtonControllerFingerprint -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch -import app.revanced.patches.youtube.misc.settings.SettingsPatch -import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patches.youtube.layout.buttons.overlay.HidePlayerOverlayButtonsPatch -@Patch( - name = "Hide captions button", - description = "Adds an option to hide the captions button in the video player.", - dependencies = [ - IntegrationsPatch::class, - SettingsPatch::class, - AddResourcesPatch::class - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ] - ) - ] -) @Suppress("unused") +@Deprecated("This patch has been merged into HidePlayerOverlayButtonsPatch.") object HideCaptionsButtonPatch : BytecodePatch( - setOf(SubtitleButtonControllerFingerprint) + dependencies = setOf(HidePlayerOverlayButtonsPatch::class), ) { override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_hide_captions_button") - ) - - val subtitleButtonControllerMethod = SubtitleButtonControllerFingerprint.result!!.mutableMethod - - // Due to previously applied patches, scanResult index cannot be used in this context - val insertIndex = subtitleButtonControllerMethod.implementation!!.instructions.indexOfFirst { - it.opcode == Opcode.IGET_BOOLEAN - } + 1 - - subtitleButtonControllerMethod.addInstruction( - insertIndex, - """ - invoke-static {v0}, Lapp/revanced/integrations/youtube/patches/HideCaptionsButtonPatch;->hideCaptionsButton(Landroid/widget/ImageView;)V - """ - ) } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch.kt index aaec98701c..7a8aec8d14 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch.kt @@ -1,47 +1,14 @@ package app.revanced.patches.youtube.layout.buttons.cast import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions 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.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch -import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.patches.youtube.layout.buttons.overlay.HidePlayerOverlayButtonsPatch -@Patch( - name = "Hide cast button", - description = "Adds an option to hide the cast button in the video player.", - dependencies = [ - IntegrationsPatch::class, - SettingsPatch::class, - AddResourcesPatch::class, - ], - compatiblePackages = [ - CompatiblePackage("com.google.android.youtube"), - ], -) -object HideCastButtonPatch : BytecodePatch(emptySet()) { +@Suppress("unused") +@Deprecated("This patch has been merged into HidePlayerOverlayButtonsPatch.") +object HideCastButtonPatch : BytecodePatch( + dependencies = setOf(HidePlayerOverlayButtonsPatch::class), +) { override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_hide_cast_button") - ) - - val buttonClass = context.findClass("MediaRouteButton") - ?: throw PatchException("MediaRouteButton class not found.") - - buttonClass.mutableClass.methods.find { it.name == "setVisibility" }?.apply { - addInstructions( - 0, - """ - invoke-static {p1}, Lapp/revanced/integrations/youtube/patches/HideCastButtonPatch;->getCastButtonOverrideV2(I)I - move-result p1 - """, - ) - } ?: throw PatchException("setVisibility method not found.") } -} +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt new file mode 100644 index 0000000000..1cc48c63f1 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt @@ -0,0 +1,151 @@ +package app.revanced.patches.youtube.layout.buttons.overlay + +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.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.AddResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.layout.buttons.overlay.fingerprints.MediaRouteButtonFingerprint +import app.revanced.patches.youtube.layout.buttons.overlay.fingerprints.PlayerControlsPreviousNextOverlayTouchFingerprint +import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.patches.youtube.shared.fingerprints.LayoutConstructorFingerprint +import app.revanced.patches.youtube.shared.fingerprints.SubtitleButtonControllerFingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow +import app.revanced.util.indexOfIdResourceOrThrow +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.reference.MethodReference + +@Patch( + name = "Hide player overlay buttons", + description = "Adds options to hide the player cast, autoplay, caption button and next/ previous buttons.", + dependencies = [ + IntegrationsPatch::class, + SettingsPatch::class, + AddResourcesPatch::class, + HidePlayerOverlayButtonsResourcePatch::class, + ], + compatiblePackages = [ + CompatiblePackage( + "com.google.android.youtube", + [ + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ] + ) + ] +) +@Suppress("unused") +object HidePlayerOverlayButtonsPatch : BytecodePatch( + setOf( + PlayerControlsPreviousNextOverlayTouchFingerprint, + MediaRouteButtonFingerprint, + SubtitleButtonControllerFingerprint, + LayoutConstructorFingerprint + ) +) { + + private const val INTEGRATIONS_CLASS_DESCRIPTOR = + "Lapp/revanced/integrations/youtube/patches/HidePlayerOverlayButtonsPatch;" + + override fun execute(context: BytecodeContext) { + AddResourcesPatch(this::class) + + SettingsPatch.PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_hide_player_previous_next_buttons"), + SwitchPreference("revanced_hide_cast_button"), + SwitchPreference("revanced_hide_captions_button"), + SwitchPreference("revanced_hide_autoplay_button"), + ) + + // region hide player next/previous button + + PlayerControlsPreviousNextOverlayTouchFingerprint.resultOrThrow().mutableMethod.apply { + val resourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow( + HidePlayerOverlayButtonsResourcePatch.playerControlPreviousButtonTouchArea + ) + + val insertIndex = indexOfFirstInstructionOrThrow(resourceIndex) { + opcode == Opcode.INVOKE_STATIC + && getReference()?.parameterTypes?.firstOrNull() == "Landroid/view/View;" + } + + val viewRegister = getInstruction(insertIndex).registerC + + addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR" + + "->hidePreviousNextButtons(Landroid/view/View;)V" + ) + } + + // endregion + + // region hide cast button + + MediaRouteButtonFingerprint.resultOrThrow().mutableMethod.addInstructions( + 0, + """ + invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getCastButtonOverrideV2(I)I + move-result p1 + """ + ) + + // endregion + + // region hide captions button + + SubtitleButtonControllerFingerprint.resultOrThrow().mutableMethod.apply { + // Due to previously applied patches, scanResult index cannot be used in this context + val insertIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1 + + addInstruction( + insertIndex, + "invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->hideCaptionsButton(Landroid/widget/ImageView;)V" + ) + } + + // endregion + + // region hide auto play button + + LayoutConstructorFingerprint.resultOrThrow().mutableMethod.apply { + val constIndex = indexOfIdResourceOrThrow("autonav_toggle") + val constRegister = getInstruction(constIndex).registerA + + // Add a conditional branch around the code that inflates and adds the auto repeat button. + val gotoIndex = indexOfFirstInstructionOrThrow(constIndex) { + val parameterTypes = getReference()?.parameterTypes + opcode == Opcode.INVOKE_VIRTUAL && + parameterTypes?.size == 2 && + parameterTypes.first() == "Landroid/view/ViewStub;" + } + 1 + + addInstructionsWithLabels( + constIndex, + """ + invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->hideAutoPlayButton()Z + move-result v$constRegister + if-nez v$constRegister, :hidden + """, + ExternalLabel("hidden", getInstruction(gotoIndex)), + ) + } + + // endregion + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsResourcePatch.kt similarity index 84% rename from src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsResourcePatch.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsResourcePatch.kt index 7bd23b17a1..4b997d7891 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsResourcePatch.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.youtube.layout.buttons.player.hide +package app.revanced.patches.youtube.layout.buttons.overlay import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.patch.ResourcePatch @@ -6,7 +6,7 @@ import app.revanced.patcher.patch.annotation.Patch import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch @Patch(dependencies = [ResourceMappingPatch::class]) -internal object HidePlayerButtonsResourcePatch : ResourcePatch() { +internal object HidePlayerOverlayButtonsResourcePatch : ResourcePatch() { var playerControlPreviousButtonTouchArea = -1L var playerControlNextButtonTouchArea = -1L diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/MediaRouteButtonFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/MediaRouteButtonFingerprint.kt new file mode 100644 index 0000000000..69e2815452 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/MediaRouteButtonFingerprint.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.youtube.layout.buttons.overlay.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +internal object MediaRouteButtonFingerprint : MethodFingerprint ( + parameters = listOf("I"), + customFingerprint = { methodDef, _ -> + methodDef.definingClass.endsWith("/MediaRouteButton;") && methodDef.name == "setVisibility" + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/fingerprints/PlayerControlsPreviousNextOverlayTouchFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/PlayerControlsPreviousNextOverlayTouchFingerprint.kt similarity index 63% rename from src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/fingerprints/PlayerControlsPreviousNextOverlayTouchFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/PlayerControlsPreviousNextOverlayTouchFingerprint.kt index 131bf47130..46270b516e 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/fingerprints/PlayerControlsPreviousNextOverlayTouchFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/PlayerControlsPreviousNextOverlayTouchFingerprint.kt @@ -1,8 +1,8 @@ -package app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints +package app.revanced.patches.youtube.layout.buttons.overlay.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint -import app.revanced.patches.youtube.layout.buttons.player.hide.HidePlayerButtonsResourcePatch +import app.revanced.patches.youtube.layout.buttons.overlay.HidePlayerOverlayButtonsResourcePatch import app.revanced.util.containsWideLiteralInstructionValue import com.android.tools.smali.dexlib2.AccessFlags @@ -12,9 +12,9 @@ internal object PlayerControlsPreviousNextOverlayTouchFingerprint : MethodFinger strings = listOf("1.0x"), customFingerprint = { methodDef, _ -> methodDef.containsWideLiteralInstructionValue( - HidePlayerButtonsResourcePatch.playerControlPreviousButtonTouchArea + HidePlayerOverlayButtonsResourcePatch.playerControlPreviousButtonTouchArea ) && methodDef.containsWideLiteralInstructionValue( - HidePlayerButtonsResourcePatch.playerControlNextButtonTouchArea + HidePlayerOverlayButtonsResourcePatch.playerControlNextButtonTouchArea ) } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch.kt index 9581f8f8ac..e8707a39b9 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch.kt @@ -1,74 +1,13 @@ package app.revanced.patches.youtube.layout.buttons.player.hide import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints.PlayerControlsPreviousNextOverlayTouchFingerprint -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch -import app.revanced.patches.youtube.misc.settings.SettingsPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow -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.reference.MethodReference +import app.revanced.patches.youtube.layout.buttons.overlay.HidePlayerOverlayButtonsPatch -@Patch( - name = "Hide player buttons", - description = "Adds an option to hide the previous and next buttons in the video player.", - dependencies = [ - IntegrationsPatch::class, - SettingsPatch::class, - AddResourcesPatch::class, - HidePlayerButtonsResourcePatch::class, - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ] - ) - ] -) -@Suppress("unused") +@Deprecated("This patch has been merged into HidePlayerOverlayButtonsPatch.") object HidePlayerButtonsPatch : BytecodePatch( - setOf(PlayerControlsPreviousNextOverlayTouchFingerprint) + dependencies = setOf(HidePlayerOverlayButtonsPatch::class), ) { override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.PLAYER.addPreferences( - SwitchPreference("revanced_hide_player_buttons") - ) - - PlayerControlsPreviousNextOverlayTouchFingerprint.resultOrThrow().mutableMethod.apply { - val resourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow( - HidePlayerButtonsResourcePatch.playerControlPreviousButtonTouchArea - ) - - val insertIndex = indexOfFirstInstructionOrThrow(resourceIndex) { - opcode == Opcode.INVOKE_STATIC - && getReference()?.parameterTypes?.firstOrNull() == "Landroid/view/View;" - } - - val viewRegister = getInstruction(insertIndex).registerC - - addInstruction( - insertIndex, - "invoke-static { v$viewRegister }, Lapp/revanced/integrations/youtube/patches/HidePlayerButtonsPatch;" + - "->hidePreviousNextButtons(Landroid/view/View;)V" - ) - } } -} +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt index 5ecd694f8d..e8f72052cf 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt @@ -2,7 +2,7 @@ package app.revanced.patches.youtube.misc.gms import app.revanced.patches.shared.fingerprints.CastContextFetchFingerprint import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch -import app.revanced.patches.youtube.layout.buttons.cast.HideCastButtonPatch +import app.revanced.patches.youtube.layout.buttons.overlay.HidePlayerOverlayButtonsPatch import app.revanced.patches.youtube.misc.fix.playback.SpoofVideoStreamsPatch import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME @@ -22,7 +22,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch( mainActivityOnCreateFingerprint = MainActivityOnCreateFingerprint, integrationsPatchDependency = IntegrationsPatch::class, dependencies = setOf( - HideCastButtonPatch::class, + HidePlayerOverlayButtonsPatch::class, // Hide non functional cast button. SpoofVideoStreamsPatch::class, ), gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch, diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/fingerprints/SubtitleButtonControllerFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/SubtitleButtonControllerFingerprint.kt similarity index 91% rename from src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/fingerprints/SubtitleButtonControllerFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/SubtitleButtonControllerFingerprint.kt index 6379cb6707..e840e446b3 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/fingerprints/SubtitleButtonControllerFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/SubtitleButtonControllerFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.youtube.layout.autocaptions.fingerprints +package app.revanced.patches.youtube.shared.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index 05c06137ac..5dfc34f8b4 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -472,22 +472,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Save to playlist button is hidden Save to playlist button is shown - - Hide autoplay button - Autoplay button is hidden - Autoplay button is shown - - - - Hide captions button - Captions button is hidden - Captions button is shown - - - Hide cast button - Cast button is hidden - Cast button is shown - Navigation buttons Hide or change buttons in the navigation bar @@ -560,10 +544,20 @@ This is because Crowdin requires temporarily flattening this file and removing t Watch in VR menu is hidden Watch in VR menu is shown - - Hide previous & next video buttons - Buttons are hidden - Buttons are shown + + Hide previous & next video buttons + Buttons are hidden + Buttons are shown + Hide cast button + Cast button is hidden + Cast button is shown + + Hide captions button + Captions button is hidden + Captions button is shown + Hide autoplay button + Autoplay button is hidden + Autoplay button is shown Hide end screen cards From 95ca85a36cfe529ec0faeab49dca01b06c99a2d6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 22 Oct 2024 22:33:09 +0000 Subject: [PATCH 08/65] chore: Release v4.18.0-dev.3 [skip ci] # [4.18.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.2...v4.18.0-dev.3) (2024-10-22) ### Features * **YouTube:** Merge multiple layout patches into `Hide Layout Components` ([#3799](https://github.com/ReVanced/revanced-patches/issues/3799)) ([bbcb57a](https://github.com/ReVanced/revanced-patches/commit/bbcb57a32dfc8f031886f98b1b9701285105c579)) * **YouTube:** Merge multiple player overlay patches into `Hide player overlay buttons` ([#3800](https://github.com/ReVanced/revanced-patches/issues/3800)) ([4ba0300](https://github.com/ReVanced/revanced-patches/commit/4ba0300590dd988bdcaa0761c4e606c1d7f86ce5)) --- CHANGELOG.md | 8 ++++++++ gradle.properties | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8fcfe41bb..a50f20d6e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# [4.18.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.2...v4.18.0-dev.3) (2024-10-22) + + +### Features + +* **YouTube:** Merge multiple layout patches into `Hide Layout Components` ([#3799](https://github.com/ReVanced/revanced-patches/issues/3799)) ([bbcb57a](https://github.com/ReVanced/revanced-patches/commit/bbcb57a32dfc8f031886f98b1b9701285105c579)) +* **YouTube:** Merge multiple player overlay patches into `Hide player overlay buttons` ([#3800](https://github.com/ReVanced/revanced-patches/issues/3800)) ([4ba0300](https://github.com/ReVanced/revanced-patches/commit/4ba0300590dd988bdcaa0761c4e606c1d7f86ce5)) + # [4.18.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.1...v4.18.0-dev.2) (2024-10-22) diff --git a/gradle.properties b/gradle.properties index 24f7bbffd3..0c5922d460 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 4.18.0-dev.2 +version = 4.18.0-dev.3 From a89853cf277e69e22ca9f15df0a4e6811f6f94f4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 20:59:25 -0400 Subject: [PATCH 09/65] chore: Sync translations (#3803) --- .../addresources/values-af-rZA/strings.xml | 22 +-- .../addresources/values-am-rET/strings.xml | 22 +-- .../addresources/values-ar-rSA/strings.xml | 134 +++++++------- .../addresources/values-as-rIN/strings.xml | 22 +-- .../addresources/values-az-rAZ/strings.xml | 172 +++++++++--------- .../addresources/values-be-rBY/strings.xml | 126 ++++++------- .../addresources/values-bg-rBG/strings.xml | 126 ++++++------- .../addresources/values-bn-rBD/strings.xml | 114 +++++------- .../addresources/values-bs-rBA/strings.xml | 22 +-- .../addresources/values-ca-rES/strings.xml | 22 +-- .../addresources/values-cs-rCZ/strings.xml | 126 ++++++------- .../addresources/values-da-rDK/strings.xml | 126 ++++++------- .../addresources/values-de-rDE/strings.xml | 134 +++++++------- .../addresources/values-el-rGR/strings.xml | 136 +++++++------- .../addresources/values-es-rES/strings.xml | 134 +++++++------- .../addresources/values-et-rEE/strings.xml | 22 +-- .../addresources/values-eu-rES/strings.xml | 22 +-- .../addresources/values-fa-rIR/strings.xml | 22 +-- .../addresources/values-fi-rFI/strings.xml | 165 ++++++++--------- .../addresources/values-fil-rPH/strings.xml | 124 ++++++------- .../addresources/values-fr-rFR/strings.xml | 134 +++++++------- .../addresources/values-ga-rIE/strings.xml | 162 ++++++++++------- .../addresources/values-gl-rES/strings.xml | 22 +-- .../addresources/values-gu-rIN/strings.xml | 22 +-- .../addresources/values-hi-rIN/strings.xml | 22 +-- .../addresources/values-hr-rHR/strings.xml | 22 +-- .../addresources/values-hu-rHU/strings.xml | 134 +++++++------- .../addresources/values-hy-rAM/strings.xml | 22 +-- .../addresources/values-in-rID/strings.xml | 172 ++++++++++-------- .../addresources/values-is-rIS/strings.xml | 22 +-- .../addresources/values-it-rIT/strings.xml | 131 +++++++------ .../addresources/values-iw-rIL/strings.xml | 26 +-- .../addresources/values-ja-rJP/strings.xml | 128 ++++++------- .../addresources/values-ka-rGE/strings.xml | 22 +-- .../addresources/values-kk-rKZ/strings.xml | 22 +-- .../addresources/values-km-rKH/strings.xml | 22 +-- .../addresources/values-kn-rIN/strings.xml | 22 +-- .../addresources/values-ko-rKR/strings.xml | 168 +++++++++-------- .../addresources/values-ky-rKG/strings.xml | 22 +-- .../addresources/values-lo-rLA/strings.xml | 22 +-- .../addresources/values-lt-rLT/strings.xml | 22 +-- .../addresources/values-lv-rLV/strings.xml | 22 +-- .../addresources/values-mk-rMK/strings.xml | 22 +-- .../addresources/values-ml-rIN/strings.xml | 22 +-- .../addresources/values-mn-rMN/strings.xml | 22 +-- .../addresources/values-mr-rIN/strings.xml | 22 +-- .../addresources/values-ms-rMY/strings.xml | 22 +-- .../addresources/values-my-rMM/strings.xml | 22 +-- .../addresources/values-nb-rNO/strings.xml | 126 ++++++------- .../addresources/values-ne-rIN/strings.xml | 22 +-- .../addresources/values-nl-rNL/strings.xml | 126 ++++++------- .../addresources/values-or-rIN/strings.xml | 22 +-- .../addresources/values-pa-rIN/strings.xml | 22 +-- .../addresources/values-pl-rPL/strings.xml | 126 ++++++------- .../addresources/values-pt-rBR/strings.xml | 124 ++++++------- .../addresources/values-pt-rPT/strings.xml | 129 ++++++------- .../addresources/values-ro-rRO/strings.xml | 126 ++++++------- .../addresources/values-ru-rRU/strings.xml | 166 ++++++++--------- .../addresources/values-si-rLK/strings.xml | 22 +-- .../addresources/values-sk-rSK/strings.xml | 126 ++++++------- .../addresources/values-sl-rSI/strings.xml | 22 +-- .../addresources/values-sq-rAL/strings.xml | 22 +-- .../addresources/values-sr-rCS/strings.xml | 126 ++++++------- .../addresources/values-sr-rSP/strings.xml | 126 ++++++------- .../addresources/values-sv-rSE/strings.xml | 134 +++++++------- .../addresources/values-sw-rKE/strings.xml | 22 +-- .../addresources/values-ta-rIN/strings.xml | 22 +-- .../addresources/values-te-rIN/strings.xml | 22 +-- .../addresources/values-th-rTH/strings.xml | 22 +-- .../addresources/values-tr-rTR/strings.xml | 126 ++++++------- .../addresources/values-uk-rUA/strings.xml | 134 +++++++------- .../addresources/values-ur-rIN/strings.xml | 22 +-- .../addresources/values-uz-rUZ/strings.xml | 22 +-- .../addresources/values-vi-rVN/strings.xml | 126 ++++++------- .../addresources/values-zh-rCN/strings.xml | 126 ++++++------- .../addresources/values-zh-rTW/strings.xml | 139 +++++++------- .../addresources/values-zu-rZA/strings.xml | 22 +-- 77 files changed, 2385 insertions(+), 3275 deletions(-) diff --git a/src/main/resources/addresources/values-af-rZA/strings.xml b/src/main/resources/addresources/values-af-rZA/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-af-rZA/strings.xml +++ b/src/main/resources/addresources/values-af-rZA/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-am-rET/strings.xml b/src/main/resources/addresources/values-am-rET/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-am-rET/strings.xml +++ b/src/main/resources/addresources/values-am-rET/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-ar-rSA/strings.xml b/src/main/resources/addresources/values-ar-rSA/strings.xml index a396e2bb49..ac85b3a8c8 100644 --- a/src/main/resources/addresources/values-ar-rSA/strings.xml +++ b/src/main/resources/addresources/values-ar-rSA/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t تعطيل توهج زر أعجبني / اشتراك لن يتوهج زر أعجبني واشتراك عند ذكره أعجبني واشتراك سوف يتوهج عند الإشارة + إخفاء بطاقات الألبوم + تم إخفاء بطاقات الألبوم + يتم عرض بطاقات الألبوم + إخفاء مربع التمويل الجماعي + تم إخفاء مربع التمويل الجماعي + يتم عرض مربع التمويل الجماعي + إخفاء زر الميكروفون العائم + تم إخفاء زر الميكروفون + يتم عرض زر الميكروفون إخفاء الفاصل الرمادي تم إخفاء الفواصل الرمادية يتم عرض الفواصل الرمادية @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t يتم عرض قسم النص وصف الفيديو إخفاء أو عرض مكونات وصف الفيديو + شريط التصفية + إخفاء شريط التصفية أو عرضه في الموجز والبحث ومقاطع الفيديو ذات الصلة + إخفاء في الموجز + مخفي في الموجز + يعرض في الموجز + إخفاء في البحث + مخفي في البحث + يعرض في البحث + إخفاء في الفيديوهات ذات الصلة + مخفي في الفيديوهات ذات الصلة + يعرض في الفيديوهات ذات الصلة + التعليقات + إخفاء أو عرض مكونات قسم التعليقات + إخفاء رأس \'تعليقات الأعضاء\' + تم إخفاء رأس \'تعليقات الأعضاء\' + يتم عرض رأس \'تعليقات الأعضاء\' + إخفاء قسم التعليقات + تم إخفاء قسم التعليقات + يتم عرض قسم التعليقات + إخفاء زر \'إنشاء مقطع Short\' + تم إخفاء زر \'إنشاء Short\' + يتم عرض زر \'إنشاء Short\' + إخفاء تعليق المعاينة + تم إخفاء تعليق المعاينة + يتم عرض تعليق المعاينة + إخفاء زر شكرًا + تم إخفاء زر شكرًا + يتم عرض زر شكرًا + إخفاء أزرار الطابع الزمني والرموز التعبيرية + تم إخفاء أزرار الطابع الزمني والرموز التعبيرية + يتم عرض أزرار الطابع الزمني والرموز التعبيرية إخفاء رسومات YouTube تم إخفاء رسومات شريط البحث @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t تم إخفاء زر الحفظ في قائمة التشغيل يتم عرض زر الحفظ في قائمة التشغيل - - إخفاء زر التشغيل التلقائي - تم إخفاء زر التشغيل التلقائي - يتم عرض زر التشغيل التلقائي - - - - إخفاء زر التَرْجَمَة - تم إخفاء زر التَرْجَمَة - يتم عرض زر التَرْجَمَة - - - إخفاء زر البث - تم إخفاء زر البث - يتم عرض زر البث - أزرار التنقل إخفاء أو تغيير الأزرار في شريط التنقل @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t تم إخفاء قائمة المشاهدة في الوضع الافتراضي يتم عرض قائمة المشاهدة في الوضع الافتراضي - - إخفاء أزرار الفيديو السابق & التالي - تم إخفاء الأزرار - يتم عرض الأزرار - - - إخفاء بطاقات الألبوم - تم إخفاء بطاقات الألبوم - يتم عرض بطاقات الألبوم - - - التعليقات - إخفاء أو عرض مكونات قسم التعليقات - إخفاء رأس \'تعليقات الأعضاء\' - تم إخفاء رأس \'تعليقات الأعضاء\' - يتم عرض رأس \'تعليقات الأعضاء\' - إخفاء قسم التعليقات - تم إخفاء قسم التعليقات - يتم عرض قسم التعليقات - إخفاء زر \'إنشاء مقطع Short\' - تم إخفاء زر \'إنشاء Short\' - يتم عرض زر \'إنشاء Short\' - إخفاء تعليق المعاينة - تم إخفاء تعليق المعاينة - يتم عرض تعليق المعاينة - إخفاء زر شكرًا - تم إخفاء زر شكرًا - يتم عرض زر شكرًا - إخفاء أزرار الطابع الزمني والرموز التعبيرية - تم إخفاء أزرار الطابع الزمني والرموز التعبيرية - يتم عرض أزرار الطابع الزمني والرموز التعبيرية - - - إخفاء مربع التمويل الجماعي - تم إخفاء مربع التمويل الجماعي - يتم عرض مربع التمويل الجماعي + + إخفاء أزرار الفيديو السابق & التالي + تم إخفاء الأزرار + يتم عرض الأزرار + إخفاء زر البث + تم إخفاء زر البث + يتم عرض زر البث + + إخفاء زر التَرْجَمَة + تم إخفاء زر التَرْجَمَة + يتم عرض زر التَرْجَمَة + إخفاء زر التشغيل التلقائي + تم إخفاء زر التشغيل التلقائي + يتم عرض زر التشغيل التلقائي إخفاء بطاقات شاشة النهاية تم إخفاء بطاقات شاشة النهاية يتم عرض بطاقات شاشة النهاية - - شريط التصفية - إخفاء شريط التصفية أو عرضه في الموجز والبحث ومقاطع الفيديو ذات الصلة - إخفاء في الموجز - مخفي في الموجز - يعرض في الموجز - إخفاء في البحث - مخفي في البحث - يعرض في البحث - إخفاء في الفيديوهات ذات الصلة - مخفي في الفيديوهات ذات الصلة - يعرض في الفيديوهات ذات الصلة - - - إخفاء زر الميكروفون العائم - تم إخفاء زر الميكروفون - يتم عرض زر الميكروفون - تعطيل وضع الإضاءة السينمائية في ملء الشاشة تم تعطيل وضع الإضاءة السينمائية @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t يتم عرض مصغرة شريط التقدم + مشغل Shorts + إخفاء أو عرض المكونات في مشغل Shorts إخفاء Shorts في موجز الصفحة الرئيسية تم إخفاء Shorts في موجز الصفحة الرئيسية @@ -988,6 +974,14 @@ This is because Crowdin requires temporarily flattening this file and removing t لن يتم استئناف تشغيل مشغل Shorts عند بدء تشغيل التطبيق سيتم استئناف تشغيل مشغل Shorts عند بدء تشغيل التطبيق + + التشغيل التلقائي لفيديوهات Shorts + سيتم تشغيل فيديوهات Shorts تلقائيًا + سيتم تكرار فيديوهات Shorts + تشغيل فيديوهات Shorts تلقائيًا في الخلفية + سيتم تشغيل فيديوهات Shorts تلقائيًا في الخلفية + سيتم تكرار فيديوهات Shorts في الخلفية + تمكين تصميم الجهاز اللوحي تم تمكين تصميم الجهاز اللوحي diff --git a/src/main/resources/addresources/values-as-rIN/strings.xml b/src/main/resources/addresources/values-as-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-as-rIN/strings.xml +++ b/src/main/resources/addresources/values-as-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-az-rAZ/strings.xml b/src/main/resources/addresources/values-az-rAZ/strings.xml index e76bb708de..070d7ee3ee 100644 --- a/src/main/resources/addresources/values-az-rAZ/strings.xml +++ b/src/main/resources/addresources/values-az-rAZ/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Bəyən/abunə ol düymə parıltısın söndür \"Bəyən və abunə ol\" düyməsin klikləyəndə parıldamayacaq \"Bəyən və abunə ol\" düyməsinə klikləyəndə parlayacaq + Albom kartlarını gizlət + Albom kartları gizlidir + Albom kartları göstərilir + İanə qutusunu gizlət + İanə qutusu gizlidir + İanə qutusu göstərilir + Üzən mikrofon düyməsini gizlət + Mikrofon düyməsi gizlidir + Mikrofon düyməsi göstərilir Boz ayırıcını gizlət Boz ayırıcılar gizlidir Boz ayırıcılar göstərilir @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Transkripsiya bölməsi göstərilir Video açıqlaması Video açıqlaması elementlərini gizlət və ya göstər + Filtr çubuğu + Axında, axtarışda və əlaqəli videolardakı filtr çubuğunu gizlət və ya göstər + Axında gizlət + Axında gizlidir + Axında göstərilir + Axtarışda gizlət + Axtarışda gizlidir + Axtarışda görünür + Əlaqəli videolarda gizlət + Əlaqəli videolarda gizlidir + Əlaqəli videolarda görünür + Şərhlər + Şərhlər bölməsi elementlərin gizlət və ya göstər + \'Üzvlərin şərhləri\' başlığını gizlət + \"Üzvlərin şərhləri\" başlığı gizlədilib + \"Üzvlərin şərhləri\" başlığı göstərilir + Şərhlər bölməsini gizlət + Şərhlər bölməsi gizlidir + Şərhlər bölməsi göstərilir + \"Shorts Yarat\" düyməsini gizlət + \"Shorts yarat\" düyməsi gizlidir + \"Shorts yarat\" düyməsi göstərilir + Önbaxış şərhin gizlət + Önbaxış şərhi gizlədilib + Önbaxış şərhi göstərilir + Təşəkkürlər düyməsini gizlət + Təşəkkür düyməsi gizlidir + Təşəkkür düyməsi göstərilir + Vaxt möhürü və emoji düymələrin gizlə + Vaxt möhürü və emoji düymələri gizlədilib + Vaxt möhürü və emoji düymələri göstərilir YouTube Doodle-ları gizlət Axtarış çubuğu Doodle-ları gizlidir @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Pleylistdə saxla düyməsi gizlidir Pleylistdə saxla düyməsi göstərilir - - Avto-oynatma düyməsini gizlət - Avtomatik oynatma düyməsi gizlidir - Avtomatik oynatma düyməsi göstərilir - - - - Titrlər düyməsini gizlət - Titrlər düyməsi gizlidir - Titrlər düyməsi göstərilir - - - Yayımla düyməsini gizlət - Yayım düyməsi gizlidir - Yayım düyməsi göstərilir - Fəaliyyət düymələri Fəaliyyət çubuğundakı düymələri gizlət və ya dəyiş @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t VR menyusunda izləmə gizlidir VR menyusunda izləmə göstərilir - - Əvvəlki/növbəti video düymələrin gizlə - Düymələr gizlidir - Düymələr göstərilir - - - Albom kartlarını gizlət - Albom kartları gizlidir - Albom kartları göstərilir - - - Şərhlər - Şərhlər bölməsi elementlərin gizlət və ya göstər - \'Üzvlərin şərhləri\' başlığını gizlət - \"Üzvlərin şərhləri\" başlığı gizlədilib - \"Üzvlərin şərhləri\" başlığı göstərilir - Şərhlər bölməsini gizlət - Şərhlər bölməsi gizlidir - Şərhlər bölməsi göstərilir - \"Shorts Yarat\" düyməsini gizlət - \"Shorts yarat\" düyməsi gizlidir - \"Shorts yarat\" düyməsi göstərilir - Önbaxış şərhin gizlət - Önbaxış şərhi gizlədilib - Önbaxış şərhi göstərilir - Təşəkkürlər düyməsini gizlət - Təşəkkür düyməsi gizlidir - Təşəkkür düyməsi göstərilir - Vaxt möhürü və emoji düymələrin gizlə - Vaxt möhürü və emoji düymələri gizlədilib - Vaxt möhürü və emoji düymələri göstərilir - - - İanə qutusunu gizlət - İanə qutusu gizlidir - İanə qutusu göstərilir + + Əvvəlki/növbəti video düymələrin gizlə + Düymələr gizlidir + Düymələr göstərilir + Yayımla düyməsini gizlət + Yayım düyməsi gizlidir + Yayım düyməsi göstərilir + + Titrlər düyməsini gizlət + Titrlər düyməsi gizlidir + Titrlər düyməsi göstərilir + Avto-oynatma düyməsini gizlət + Avtomatik oynatma düyməsi gizlidir + Avtomatik oynatma düyməsi göstərilir Son ekran kartlarını gizlət Son ekran kartları gizlidir Son ekran kartları göstərilir - - Filtr çubuğu - Axında, axtarışda və əlaqəli videolardakı filtr çubuğunu gizlət və ya göstər - Axında gizlət - Axında gizlidir - Axında göstərilir - Axtarışda gizlət - Axtarışda gizlidir - Axtarışda görünür - Əlaqəli videolarda gizlət - Əlaqəli videolarda gizlidir - Əlaqəli videolarda görünür - - - Üzən mikrofon düyməsini gizlət - Mikrofon düyməsi gizlidir - Mikrofon düyməsi göstərilir - Tam ekranda ambient rejimin bağla Ambient rejimi qeyri-aktiv edilib @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Miniatür axtarış çubuğu göstərilir + Shorts oynadıcı + Shorts oynadıcıda hissəcikləri gizlət və ya göstər Ev axınında \"Shorts\"u gizlət Ev axınındakı Shorts gizlidir @@ -644,9 +630,9 @@ This is because Crowdin requires temporarily flattening this file and removing t \"Yaşıl ekran\" düyməsini gizlət \"Yaşıl ekran\" düyməsi gizlidir \"Yaşıl ekran\" düyməsi göstərilir - Hashtag düyməsini gizlət - Hashtag düyməsi gizlidir - Hashtag düyməsi göstərilir + Mövzu etiketi düyməsini gizlət + Mövzu etiketi düyməsi gizlidir + Mövzu etiketi düyməsi göstərilir Axtarış təkliflərini gizlət Axtarış təklifləri gizlədilib Axtarış təklifləri göstərilir @@ -968,7 +954,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Başlanğıc səhifəsini tənzimlə İlkin - Kəşf edilən kanallar + Kanallara nəzər yetir Kəşf et Oyun Tarixçə @@ -977,10 +963,10 @@ This is because Crowdin requires temporarily flattening this file and removing t Canlı Filmlər Musiqi - Axtarış + Axtar İdman Abunəliklər - Trenddə olan + Trendlər Sonra izlə @@ -988,6 +974,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Tətbiq açılanda Shorts oynadıcı davam etməyəcək Tətbiq açılanda Shorts oynadıcı davam edəcək + + Shorts-ları avto-oynatma + Shorts-lar avto-oynadılacaq + Shorts-lar təkrarlanacaq + Shorts-ları arxa planda avto-oynat + Shorts-lar arxa planda avto-oynadılacaq + Shorts-lar arxa planda təkrarlanacaq + Planşet tərtibatını aktiv et Planşet tərtibatı aktiv edilib @@ -1005,20 +999,20 @@ This is because Crowdin requires temporarily flattening this file and removing t Müasir 2 Müasir 3 Dairəvi küncləri aktivləşdir - Künclər dövrələnir + Künclər dairəvidir Künclər kvadratdır - Ölçüsünü dəyişmək üçün iki dəfə toxun və çimdiklə - İki dəfə kliklə fəaliyyəti və ölçüsünü dəyişmək üçün çimdiklə, aktivdir\n\n• Kiçik oynadıcı ölçüsün artırmaq üçün iki dəfə toxunun\n• Əsl ölçüsün bərpa etmək üçün təkrar iki dəfə toxun - Ölçüsün dəyişmək üçün cüt kliklə və çimdiklə bağlıdır - Çəkmə və buraxma funksiyasın aktivləşdir - Çəkmə və buraxma aktivdir\n\nMinipleyeri ekranın istənilən küncünə çəkmək olar - Çəkmə və buraxma bağlıdır - Bağlama düyməsin gizlət - Bağlama düyməsi gizlidir - Bağlama düyməsi göstərilir + Ölçüsünü dəyişmək üçün cüt toxunmanı və çimdikləməni aktivləşdir + Ölçüsünü dəyişdirmək üçün cüt toxunma fəaliyyəti və çimdikləmə aktivləşdirildi\n\n• Mini oynadıcı ölçüsün artırmaq üçün cüt toxunun\n• Orijinal ölçünü bərpa etmək üçün təkrar cüt toxun + Ölçüsünü dəyişdirmək üçün cüt toxunma fəaliyyəti və çimdikləmə yoxdur + \"Sürüklə və burax\"ı aktivləşdir + \"Sürüklə və burax\" aktivdir\n\nMini oynadıcı, ekranın istənilən küncünə sürüklənə bilər + \"Sürüklə və burax\" aktiv deyil + \"Bağla\" düyməsini gizlət + \"Bağla\" düyməsi gizlidir + \"Bağla\" düyməsi göstərilir Genişləndir və bağla düymələrini gizlət Düymələr gizlədilib\n\nGenişləndirmək və ya bağlamaq üçün sürüşdür - Genişləndirmə və bağlama düymələri göstərilir + Genişləndir və bağla düymələri göstərilir Alt mətnləri gizlət Alt mətnlər gizlədilir Alt mətnlər göstərilir @@ -1026,8 +1020,8 @@ This is because Crowdin requires temporarily flattening this file and removing t İrəli və geri ötürücülər gizlidir İrəli və geri ötürücülər göstərilir İlkin ölçü - Ekran ölçüsündə, piksellərdə ilkin ölçü - Piksel ölçüsü %1$s və %2$s arası olmalıdır + Piksel olaraq ekranda ilkin ölçü + Piksel ölçüsü, %1$s - %2$s arası olmalıdır Örtük qeyri-şəffaflığı 0-100 arasında qeyri-şəffaflıq dəyəri, burada 0 şəffafdır Kiçik Oynadıcı örtük qeyri-şəffaflığı 0-100 arası olmalıdır diff --git a/src/main/resources/addresources/values-be-rBY/strings.xml b/src/main/resources/addresources/values-be-rBY/strings.xml index df8a93411c..b7d3558613 100644 --- a/src/main/resources/addresources/values-be-rBY/strings.xml +++ b/src/main/resources/addresources/values-be-rBY/strings.xml @@ -96,6 +96,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Адключыць свячэнне кнопкі \"Падабаецца\" / \"Падпісацца\". Кнопка \"Падабаецца\" і \"Падпісацца\" не будуць свяціцца пры згадванні Кнопка \"Падабаецца\" і \"Падпісацца\" будуць свяціцца пры згадванні + Схаваць карты альбома + Карткі альбомаў схаваныя + Паказваюцца альбомныя карткі + Схаваць скрыню краўдфандынгу + Краўдфандынгавая скрыня схавана + Паказана скрыня краўдфандынгу + Схаваць плаваючую кнопку мікрафона + Кнопка мікрафона схавана + Паказана кнопка мікрафона Схаваць шэры раздзяляльнік Шэрыя падзельнікі схаваныя Паказаны шэрыя раздзяляльнікі @@ -220,6 +229,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Паказваецца раздзел стэнаграмы Апісанне відэа Схаваць або паказаць кампаненты апісання відэа + Панэль фільтраў + Схаваць або паказаць панэль фільтраў у стужцы, пошуку і звязаных відэа + Схаваць у карме + Схаваны ў стужцы + Паказваецца ў стужцы + Схавацца ў пошуку + Схаваны ў пошуку + Паказваецца ў пошуку + Схаваць у звязаных відэа + Схавана ў звязаных відэа + Паказана ў звязаных відэа + Каментарыі + Схаваць або паказаць кампаненты раздзела каментарыяў + Схаваць загаловак \"Каментарыі ўдзельнікаў\" + Загаловак \"Каментарыі ўдзельнікаў\" схаваны + Паказаны загаловак \"Каментарыі ўдзельнікаў\" + Схаваць раздзел каментарыяў + Раздзел каментарыяў схаваны + Паказваецца раздзел каментарыяў + Схаваць кнопку \"Стварыць кароткае відэа\" + Кнопка \"Стварыць кароткае відэа\" схавана + Паказана кнопка \"Стварыць кароткае відэа\" + Схаваць каментарый для папярэдняга прагляду + Каментарый перад праглядам схаваны + Паказваецца папярэдні прагляд каментарыя + Схаваць кнопку падзякі + Кнопка падзякі схавана + Паказана кнопка падзякі + Схаваць метку часу і кнопкі эмодзі + Кнопкі меткі часу і эмодзі схаваны + Паказваюцца кнопкі меткі часу і эмодзі Карыстальніцкі фільтр Схавайце кампаненты з дапамогай карыстацкіх фільтраў @@ -403,22 +443,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Кнопка \"Захаваць у спіс прайгравання\" схавана Паказана кнопка \"Захаваць у спіс прайгравання\". - - Схаваць кнопку аўтазапуску - Кнопка аўтазапуску схавана - Паказана кнопка аўтазапуску - - - - Кнопка «Схаваць цітры». - Кнопка субцітраў схавана - Паказана кнопка субцітраў - - - Схаваць кнопку трансляцыі - Кнопка Cast схавана - Паказана кнопка Cast - Кнопкі навігацыі Схаваць або змяніць кнопкі на панэлі навігацыі @@ -491,66 +515,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Меню прагляду ў VR схавана Паказана меню \"Глядзець у VR\". - - Схаваць папярэдні & кнопкі наступнага відэа - Кнопкі схаваныя - Паказваюцца кнопкі - - - Схаваць карты альбома - Карткі альбомаў схаваныя - Паказваюцца альбомныя карткі - - - Каментарыі - Схаваць або паказаць кампаненты раздзела каментарыяў - Схаваць загаловак \"Каментарыі ўдзельнікаў\" - Загаловак \"Каментарыі ўдзельнікаў\" схаваны - Паказаны загаловак \"Каментарыі ўдзельнікаў\" - Схаваць раздзел каментарыяў - Раздзел каментарыяў схаваны - Паказваецца раздзел каментарыяў - Схаваць кнопку \"Стварыць кароткае відэа\" - Кнопка \"Стварыць кароткае відэа\" схавана - Паказана кнопка \"Стварыць кароткае відэа\" - Схаваць каментарый для папярэдняга прагляду - Каментарый перад праглядам схаваны - Паказваецца папярэдні прагляд каментарыя - Схаваць кнопку падзякі - Кнопка падзякі схавана - Паказана кнопка падзякі - Схаваць метку часу і кнопкі эмодзі - Кнопкі меткі часу і эмодзі схаваны - Паказваюцца кнопкі меткі часу і эмодзі - - - Схаваць скрыню краўдфандынгу - Краўдфандынгавая скрыня схавана - Паказана скрыня краўдфандынгу + + Схаваць папярэдні & кнопкі наступнага відэа + Кнопкі схаваныя + Паказваюцца кнопкі + Схаваць кнопку трансляцыі + Кнопка Cast схавана + Паказана кнопка Cast + + Кнопка «Схаваць цітры». + Кнопка субцітраў схавана + Паказана кнопка субцітраў + Схаваць кнопку аўтазапуску + Кнопка аўтазапуску схавана + Паказана кнопка аўтазапуску Схаваць карткі канцавога экрана Карткі канцавога экрана схаваны Паказваюцца карткі канцавога экрана - - Панэль фільтраў - Схаваць або паказаць панэль фільтраў у стужцы, пошуку і звязаных відэа - Схаваць у карме - Схаваны ў стужцы - Паказваецца ў стужцы - Схавацца ў пошуку - Схаваны ў пошуку - Паказваецца ў пошуку - Схаваць у звязаных відэа - Схавана ў звязаных відэа - Паказана ў звязаных відэа - - - Схаваць плаваючую кнопку мікрафона - Кнопка мікрафона схавана - Паказана кнопка мікрафона - Адключыць актыўны рэжым у поўнаэкранным рэжыме Навакольны рэжым адключаны @@ -934,6 +918,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Прайгравальнік Shorts не аднаўляецца пры запуску праграмы Прайгравальнік Shorts адновіцца пры запуску праграмы + + Уключыць макет планшэта Макет планшэта ўключаны diff --git a/src/main/resources/addresources/values-bg-rBG/strings.xml b/src/main/resources/addresources/values-bg-rBG/strings.xml index ec3dd07258..b702cd9279 100644 --- a/src/main/resources/addresources/values-bg-rBG/strings.xml +++ b/src/main/resources/addresources/values-bg-rBG/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Деактивирайте подсветката на бутона Харесвам /Абонамент Бутоните „Харесвам“ и „Абониране“ няма да светят, когато бъдат натиснати Бутоните „Харесвам“ и „Абониране“ ще светят, когато бъдат натиснати + \"Карти на албумите\" + Албумните карти са скрити + Албумните карти се показват + Дарителска кутия + Кутията за дарения е скрита + Кутията за дарения се показва + Плаващ бутон за микрофона + Бутонът на микрофона е скрит + Показан е бутон на микрофона Скриване на сивия разделител Сивите разделители са скрити Сивите разделители са показани @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Разделът за транскрипция е показан Описание на видеото Скриване или показване на компонентите за описание на видеоклиповете + Лента с филтри + Скриване или показване на лентата с категории в емисията, резултатите от търсенето и свързаните видеоклипове + Скриване на горната лента с категории в емисията + Скрита + Показва се + Филтъри на търсене + Панелът с филтъри на търсене е скрит + Панелът с филтъри на търсене се показва + Скриване в сродни видеоклипове + Скриване в сродни видеоклипове + Показано в сродни видеоклипове + Коментари + Скриване или показване на секцията за коментари + Скриване на „Коментари, направени от членове“ + „Коментари от членове“ са скрити + „Коментари от членове“ се показват + Скриване на секцията с коментари + Секцията с коментари е скрита + Секцията с коментари се показва + Бутон за създаване на Shorts + Бутон за създаване на Shorts е скрит + Бутон за създаване на Shorts се показва + Преглед на коментари + Прегледа на коментари е скрит + Прегледа на коментари се показва + Бутон за благодарност + Бутона за благодарност е скрит + Бутона за благодарност се показва + Бутони в лентата на прогреса и емотикони + Бутоните са скрити + Бутоните се показват YouTube Doodles Doodles в лентата за търсене са скрити @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Бутонът за Запазване в плейлиста е скрит Бутонът за Запазване в плейлиста се показва - - Бутона за авт. изпълнение - Бутона за авт. изпълнение е скрит - Бутона за авт. изпълнение се показва - - - - Бутона за Субтитри - Бутона за субтити е скрит - Бутона за субтити се показва - - - Бутон за предаване на Тв - Бутонът за предаване е скрит - Бутонът за предаване се показва - Бутони за навигация Скриване или промяна на бутоните в лентата за навигация @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Менюто за гледане в VR е скрито Менюто за гледане в VR се показва - - Бутони за Предишно & Следващо видео - Бутоните са скрити - Бутоните се показват - - - \"Карти на албумите\" - Албумните карти са скрити - Албумните карти се показват - - - Коментари - Скриване или показване на секцията за коментари - Скриване на „Коментари, направени от членове“ - „Коментари от членове“ са скрити - „Коментари от членове“ се показват - Скриване на секцията с коментари - Секцията с коментари е скрита - Секцията с коментари се показва - Бутон за създаване на Shorts - Бутон за създаване на Shorts е скрит - Бутон за създаване на Shorts се показва - Преглед на коментари - Прегледа на коментари е скрит - Прегледа на коментари се показва - Бутон за благодарност - Бутона за благодарност е скрит - Бутона за благодарност се показва - Бутони в лентата на прогреса и емотикони - Бутоните са скрити - Бутоните се показват - - - Дарителска кутия - Кутията за дарения е скрита - Кутията за дарения се показва + + Бутони за Предишно & Следващо видео + Бутоните са скрити + Бутоните се показват + Бутон за предаване на Тв + Бутонът за предаване е скрит + Бутонът за предаване се показва + + Бутона за Субтитри + Бутона за субтити е скрит + Бутона за субтити се показва + Бутона за авт. изпълнение + Бутона за авт. изпълнение е скрит + Бутона за авт. изпълнение се показва Скриване на препоръките в края Препоръките в края са скрити Препоръките в края се показват - - Лента с филтри - Скриване или показване на лентата с категории в емисията, резултатите от търсенето и свързаните видеоклипове - Скриване на горната лента с категории в емисията - Скрита - Показва се - Филтъри на търсене - Панелът с филтъри на търсене е скрит - Панелът с филтъри на търсене се показва - Скриване в сродни видеоклипове - Скриване в сродни видеоклипове - Показано в сродни видеоклипове - - - Плаващ бутон за микрофона - Бутонът на микрофона е скрит - Показан е бутон на микрофона - Деактивирайте подсветка около видеото на цял екран Подсветката в режим на цял екран е деактивирана @@ -977,6 +961,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts плейъра при стартиране на приложението е скрит Shorts плейъра при стартиране на приложението се показва + + Включи режим за таблет Режим за таблет е вкл. diff --git a/src/main/resources/addresources/values-bn-rBD/strings.xml b/src/main/resources/addresources/values-bn-rBD/strings.xml index 96a0208f15..3592157942 100644 --- a/src/main/resources/addresources/values-bn-rBD/strings.xml +++ b/src/main/resources/addresources/values-bn-rBD/strings.xml @@ -105,6 +105,15 @@ This is because Crowdin requires temporarily flattening this file and removing t পছন্দ / সদস্যতা বোতামের উজ্জ্বলতা নিষ্ক্রিয় করুন পছন্দ এবং সদস্যতা বোতাম যখন উল্লেখ করা হবে উজ্জ্বলতা দিবে না পছন্দ এবং সদস্যতা বোতাম যখন উল্লেখ করা হবে উজ্জ্বলতা দিবে + অ্যালবাম কার্ড লুকান + অ্যালবাম কার্ড লুকিয়ে রয়েছে + অ্যালবাম কার্ড প্রদর্শিত হয়েছে + গণ-অর্থায়ন বাক্স লুকান + গণ-অর্থায়ন বাক্স লুকিয়ে রয়েছে + গণ-অর্থায়ন বাক্স প্রদর্শিত হয়েছে + ভাসমান মাইক্রোফোন বোতাম লুকান + মাইক্রোফোন বোতাম লুকিয়ে রয়েছে + মাইক্রোফোন বোতাম প্রদর্শিত হয়েছে ধূসর বিভাজক লুকান ধূসর বিভাজক লুকিয়ে রয়েছে ধূসর বিভাজক প্রদর্শিত হয়েছে @@ -222,6 +231,37 @@ This is because Crowdin requires temporarily flattening this file and removing t ট্রান্সস্ক্রিপ্ট বিভাগ প্রদর্শিত হয়েছে ভিডিওর বিবরণ ভিডিও বিবরণ এর উপাদান লুকান বা প্রদর্শন করুন + ফিল্টার বার + ফিড, অনুসন্ধান এবং সম্পর্কিত ভিডিওতে ফিল্টার বার লুকান বা প্রদর্শন করুন + ফিডে লুকান + ফিডে লুকিয়ে রয়েছে + ফিডে প্রদর্শিত হয়েছে + অনুসন্ধানে লুকান + অনুসন্ধানে লুকিয়ে রয়েছে + অনুসন্ধানে প্রদর্শিত হয়েছে + সম্পর্কিত ভিডিওতে লুকান + সম্পর্কিত ভিডিওতে লুকিয়ে রয়েছে + সম্পর্কিত ভিডিওতে প্রদর্শিত হয়েছে + মন্তব্য + মন্তব্য বিভাগের উপাদানগুলি লুকান বা দেখান৷ + \'মেম্বারদের মন্তব্য\' হেডার লুকান + \'মেম্বারদের মন্তব্য\' হেডার লুকিয়ে রয়েছে + \'মেম্বারদের মন্তব্য\' হেডার প্রদর্শিত হয়েছে + মন্তব্য বিভাগ লুকান + মন্তব্য বিভাগ লুকিয়ে রয়েছে + মন্তব্য বিভাগ প্রদর্শিত হয়েছে + \'Short তৈরি করুন\' বোতাম লুকান + \'Short তৈরি করুন\' বোতাম লুকিয়ে রয়েছে + \'Short তৈরি করুন\' বোতাম প্রদর্শিত হয়েছে + মন্তব্যের পূর্বরূপ লুকান + মন্তব্যের পূর্বরূপ লুকিয়ে রয়েছে + মন্তব্যের পূর্বরূপ প্রদর্শিত হয়েছে + ধন্যবাদ বোতাম লুকান + ধন্যবাদ বোতাম লুকিয়ে রয়েছে + ধন্যবাদ বোতাম প্রদর্শিত হয়েছে + টাইমস্ট্যাম্প ও ইমোজি বোতাম লুকান + টাইমস্ট্যাম্প ও ইমোজি বোতাম লুকিয়ে রয়েছে + টাইমস্ট্যাম্প ও ইমোজি বোতাম প্রদর্শিত হয়েছে কাস্টম ফিল্টার কাস্টম ফিল্টার ব্যবহার করে বিভিন্ন উপাদান লুকান @@ -382,16 +422,6 @@ This is because Crowdin requires temporarily flattening this file and removing t ক্লিপ বোতাম প্রদর্শিত হয়েছে - - - - - - - কাস্ট বাটন লুকান - কাস্ট বাটন লুকিয়ে রয়েছে - কাস্ট বাটন প্রদর্শিত হয়েছে - নেভিগেশন বোতাম নেভিগেশন বারে বোতাম লুকান বা পরিবর্তন করুন @@ -464,66 +494,20 @@ This is because Crowdin requires temporarily flattening this file and removing t ভিআর মেনুতে দেখুন লুকানো আছে ভিআর মেনুতে দেখুন দেখানো হয়েছে - - পূর্ববর্তী লুকান & পরবর্তী ভিডিও বোতাম - বোতাম লুকানো হয় - বোতাম দেখানো হয় - - - অ্যালবাম কার্ড লুকান - অ্যালবাম কার্ড লুকিয়ে রয়েছে - অ্যালবাম কার্ড প্রদর্শিত হয়েছে - - - মন্তব্য - মন্তব্য বিভাগের উপাদানগুলি লুকান বা দেখান৷ - \'মেম্বারদের মন্তব্য\' হেডার লুকান - \'মেম্বারদের মন্তব্য\' হেডার লুকিয়ে রয়েছে - \'মেম্বারদের মন্তব্য\' হেডার প্রদর্শিত হয়েছে - মন্তব্য বিভাগ লুকান - মন্তব্য বিভাগ লুকিয়ে রয়েছে - মন্তব্য বিভাগ প্রদর্শিত হয়েছে - \'Short তৈরি করুন\' বোতাম লুকান - \'Short তৈরি করুন\' বোতাম লুকিয়ে রয়েছে - \'Short তৈরি করুন\' বোতাম প্রদর্শিত হয়েছে - মন্তব্যের পূর্বরূপ লুকান - মন্তব্যের পূর্বরূপ লুকিয়ে রয়েছে - মন্তব্যের পূর্বরূপ প্রদর্শিত হয়েছে - ধন্যবাদ বোতাম লুকান - ধন্যবাদ বোতাম লুকিয়ে রয়েছে - ধন্যবাদ বোতাম প্রদর্শিত হয়েছে - টাইমস্ট্যাম্প ও ইমোজি বোতাম লুকান - টাইমস্ট্যাম্প ও ইমোজি বোতাম লুকিয়ে রয়েছে - টাইমস্ট্যাম্প ও ইমোজি বোতাম প্রদর্শিত হয়েছে - - - গণ-অর্থায়ন বাক্স লুকান - গণ-অর্থায়ন বাক্স লুকিয়ে রয়েছে - গণ-অর্থায়ন বাক্স প্রদর্শিত হয়েছে + + পূর্ববর্তী লুকান & পরবর্তী ভিডিও বোতাম + বোতাম লুকানো হয় + বোতাম দেখানো হয় + কাস্ট বাটন লুকান + কাস্ট বাটন লুকিয়ে রয়েছে + কাস্ট বাটন প্রদর্শিত হয়েছে + শেষ স্ক্রীন কার্ড লুকান শেষ স্ক্রীন কার্ড লুকিয়ে রয়েছে শেষ স্ক্রীন কার্ড প্রদর্শিত হয়েছে - - ফিল্টার বার - ফিড, অনুসন্ধান এবং সম্পর্কিত ভিডিওতে ফিল্টার বার লুকান বা প্রদর্শন করুন - ফিডে লুকান - ফিডে লুকিয়ে রয়েছে - ফিডে প্রদর্শিত হয়েছে - অনুসন্ধানে লুকান - অনুসন্ধানে লুকিয়ে রয়েছে - অনুসন্ধানে প্রদর্শিত হয়েছে - সম্পর্কিত ভিডিওতে লুকান - সম্পর্কিত ভিডিওতে লুকিয়ে রয়েছে - সম্পর্কিত ভিডিওতে প্রদর্শিত হয়েছে - - - ভাসমান মাইক্রোফোন বোতাম লুকান - মাইক্রোফোন বোতাম লুকিয়ে রয়েছে - মাইক্রোফোন বোতাম প্রদর্শিত হয়েছে - পূর্ণ স্ক্রীনে অ্যাম্বিয়েন্ট মোড নিষ্ক্রিয় করুন অ্যাম্বিয়েন্ট মোড নিষ্ক্রিয় করা হয়েছে @@ -913,6 +897,8 @@ This is because Crowdin requires temporarily flattening this file and removing t অ্যাপের শুরুতে Shorts প্লেয়ার আবার চলবে না অ্যাপের শুরুতে Shorts প্লেয়ার আবার চলবে + + ট্যাবলেট লেআউট সক্রিয় করুন ট্যাবলেট লেআউট সক্রিয় হয়েছে diff --git a/src/main/resources/addresources/values-bs-rBA/strings.xml b/src/main/resources/addresources/values-bs-rBA/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-bs-rBA/strings.xml +++ b/src/main/resources/addresources/values-bs-rBA/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-ca-rES/strings.xml b/src/main/resources/addresources/values-ca-rES/strings.xml index 4620263fed..b3292130b9 100644 --- a/src/main/resources/addresources/values-ca-rES/strings.xml +++ b/src/main/resources/addresources/values-ca-rES/strings.xml @@ -97,13 +97,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -124,20 +117,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -191,6 +175,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-cs-rCZ/strings.xml b/src/main/resources/addresources/values-cs-rCZ/strings.xml index d965ed90fd..40c099d7a1 100644 --- a/src/main/resources/addresources/values-cs-rCZ/strings.xml +++ b/src/main/resources/addresources/values-cs-rCZ/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Zakázat záři tlačítka jako / odebírat Tlačítko se mi líbí a odebírá, pokud je zmíněno Tlačítko se mi líbí a odebírá, když je zmíněno + Skrýt alba + Album karty jsou skryty + Alba karty jsou zobrazeny + Skrýt box crowdfunding + Crowdfunding box je skrytý + Zobrazí se Crowdfunding box + Skrýt plovoucí tlačítko mikrofonu + Tlačítko mikrofonu skryté + Zobrazené tlačítko mikrofonu Skrýt šedý oddělovač Šedé oddělovače jsou skryty Šedé oddělovače jsou viditelné @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Je zobrazena sekce přepisu Popis videa Skrýt nebo zobrazit komponenty popisu videa + Filtrovat lištu + Skrýt nebo zobrazit filtrovací lištu ve kanálu, vyhledávání a souvisejících videích + Skrýt v kanálu + Skryté v krmivu + Zobrazeno v kanálu + Skrýt ve vyhledávání + Skryté ve vyhledávání + Zobrazeno ve vyhledávání + Skrýt v souvisejících videích + Skrytá v souvisejících videích + Zobrazeno v souvisejících videích + Komentáře + Skrýt nebo zobrazit komponenty sekce komentářů + Skrýt \'Komentáře podle záhlaví členů + Komentáře členů jsou skryté + \'Komentáře členů\' jsou zobrazeny + Skrýt sekci komentáře + Sekce komentářů je skrytá + Část Komentáře je zobrazena + Skrýt tlačítko \'Vytvořit krátký\' + Tlačítko \"Vytvořit krátký\" je skryté + Zobrazí se tlačítko \'Vytvořit krátké\' + Skrýt náhled komentáře + Náhled komentáře je skrytý + Náhled komentáře je zobrazen + Skrýt poděkování + Tlačítko poděkování je skryté + Tlačítko poděkování je zobrazeno + Skrýt časové razítko a tlačítka emoji + Časové razítko a tlačítka emoji jsou skrytá + Časové razítko a tlačítka emoji jsou zobrazena Skrýt YouTube Dveody Vyhledávací lišty jsou skryté Doodles @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Tlačítko Uložit do playlistu je skryté Je zobrazeno tlačítko Uložit do playlistu - - Skrýt tlačítko automatického přehrávání - Tlačítko automatického přehrávání je skryté - Tlačítko automatického přehrávání je zobrazeno - - - - Skrýt tlačítko titulků - Tlačítko titulek je skryté - Tlačítko s titulky je zobrazeno - - - Skrýt tlačítko pro přehrávání - Tlačítko vysílání je skryté - Tlačítko vysílání je viditelné - Navigation buttons Skrýt nebo změnit tlačítka v navigačním panelu @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Sledování v nabídce VR je skryté Zobrazeno sledování v nabídce VR - - Skrýt předchozí & další video tlačítka - Tlačítka jsou skryta - Tlačítka jsou zobrazena - - - Skrýt alba - Album karty jsou skryty - Alba karty jsou zobrazeny - - - Komentáře - Skrýt nebo zobrazit komponenty sekce komentářů - Skrýt \'Komentáře podle záhlaví členů - Komentáře členů jsou skryté - \'Komentáře členů\' jsou zobrazeny - Skrýt sekci komentáře - Sekce komentářů je skrytá - Část Komentáře je zobrazena - Skrýt tlačítko \'Vytvořit krátký\' - Tlačítko \"Vytvořit krátký\" je skryté - Zobrazí se tlačítko \'Vytvořit krátké\' - Skrýt náhled komentáře - Náhled komentáře je skrytý - Náhled komentáře je zobrazen - Skrýt poděkování - Tlačítko poděkování je skryté - Tlačítko poděkování je zobrazeno - Skrýt časové razítko a tlačítka emoji - Časové razítko a tlačítka emoji jsou skrytá - Časové razítko a tlačítka emoji jsou zobrazena - - - Skrýt box crowdfunding - Crowdfunding box je skrytý - Zobrazí se Crowdfunding box + + Skrýt předchozí & další video tlačítka + Tlačítka jsou skryta + Tlačítka jsou zobrazena + Skrýt tlačítko pro přehrávání + Tlačítko vysílání je skryté + Tlačítko vysílání je viditelné + + Skrýt tlačítko titulků + Tlačítko titulek je skryté + Tlačítko s titulky je zobrazeno + Skrýt tlačítko automatického přehrávání + Tlačítko automatického přehrávání je skryté + Tlačítko automatického přehrávání je zobrazeno Skrýt karty na konci obrazovky Karty na konci obrazovky jsou skryty Karty na konci obrazovky jsou zobrazeny - - Filtrovat lištu - Skrýt nebo zobrazit filtrovací lištu ve kanálu, vyhledávání a souvisejících videích - Skrýt v kanálu - Skryté v krmivu - Zobrazeno v kanálu - Skrýt ve vyhledávání - Skryté ve vyhledávání - Zobrazeno ve vyhledávání - Skrýt v souvisejících videích - Skrytá v souvisejících videích - Zobrazeno v souvisejících videích - - - Skrýt plovoucí tlačítko mikrofonu - Tlačítko mikrofonu skryté - Zobrazené tlačítko mikrofonu - Zakázat ambientní režim v režimu celé obrazovky Ambientní režim zakázán @@ -989,6 +973,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Krátký přehrávač nebude pokračovat při spuštění aplikace Krátký přehrávač bude pokračovat při spuštění aplikace + + Povolit rozložení tabletu Rozložení tabletu je povoleno diff --git a/src/main/resources/addresources/values-da-rDK/strings.xml b/src/main/resources/addresources/values-da-rDK/strings.xml index 2fd0fbf069..9f04fa7339 100644 --- a/src/main/resources/addresources/values-da-rDK/strings.xml +++ b/src/main/resources/addresources/values-da-rDK/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktivér som / abonnér knap glow Ligesom og abonnér knap vil ikke gløde når nævnt Ligesom og abonnér knappen vil gløde, når nævnt + Skjul albumkort + Albumkort er skjult + Albumkort vises + Skjul crowdfunding boks + Crowdfunding boks er skjult + Crowdfunding boks er vist + Skjul flydende mikrofon knap + Mikrofon knap skjult + Mikrofon knap vist Skjul grå separator Grå separatorer er skjult Grå separatorer er vist @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Afsnittet er vist Video beskrivelse Skjul eller vis komponenter til videobeskrivelse + Filtrer bjælke + Skjul eller vis filterbjælken i feedet, søg og relaterede videoer + Skjul i feed + Skjult i feed + Vist i feed + Skjul i søgning + Skjult i søgning + Vist i søgning + Skjul i relaterede videoer + Skjult i relaterede videoer + Vist i relaterede videoer + Kommentarer + Skjul eller vis kommentarer sektion komponenter + Skjul \'Kommentarer fra medlemmer\' header + \'Kommentarer fra medlemmer\' overskrift er skjult + \'Kommentarer fra medlemmer\' overskrift vises + Skjul kommentarsektion + Kommentarer sektion er skjult + Kommentarer sektion er vist + Skjul knappen \'Opret en kort\' + \'Opret en kort\'-knap er skjult + \'Opret en kort\'-knap vises + Skjul forhåndsvisning kommentar + Forhåndsvisning kommentar er skjult + Forhåndsvis kommentar er vist + Skjul tasteknap + Tak knappen er skjult + Tak knappen er vist + Skjul tidsstempel og emoji knapper + Tidsstempel og emoji knapper er skjult + Tidsstempel og emoji knapper vises Skjul YouTube-Doudler Søgebjælke Doudler er skjult @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Gem i afspilningslisteknappen er skjult Gem i afspilningslisteknappen vises - - Skjul autoplay knap - Automatisk spil-knap er skjult - Automatisk afspilningsknap vises - - - - Skjul billedtekst knap - Undertekster knappen er skjult - Underskriftsknappen vises - - - Skjul cast-knap - Cast-knappen er skjult - Cast knap er vist - Navigation buttons Skjul eller skift knapper i navigationsbjælken @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Se i VR-menuen er skjult Se i VR-menuen vises - - Skjul forrige & næste video knapper - Knapper er skjult - Knapper vises - - - Skjul albumkort - Albumkort er skjult - Albumkort vises - - - Kommentarer - Skjul eller vis kommentarer sektion komponenter - Skjul \'Kommentarer fra medlemmer\' header - \'Kommentarer fra medlemmer\' overskrift er skjult - \'Kommentarer fra medlemmer\' overskrift vises - Skjul kommentarsektion - Kommentarer sektion er skjult - Kommentarer sektion er vist - Skjul knappen \'Opret en kort\' - \'Opret en kort\'-knap er skjult - \'Opret en kort\'-knap vises - Skjul forhåndsvisning kommentar - Forhåndsvisning kommentar er skjult - Forhåndsvis kommentar er vist - Skjul tasteknap - Tak knappen er skjult - Tak knappen er vist - Skjul tidsstempel og emoji knapper - Tidsstempel og emoji knapper er skjult - Tidsstempel og emoji knapper vises - - - Skjul crowdfunding boks - Crowdfunding boks er skjult - Crowdfunding boks er vist + + Skjul forrige & næste video knapper + Knapper er skjult + Knapper vises + Skjul cast-knap + Cast-knappen er skjult + Cast knap er vist + + Skjul billedtekst knap + Undertekster knappen er skjult + Underskriftsknappen vises + Skjul autoplay knap + Automatisk spil-knap er skjult + Automatisk afspilningsknap vises Skjul slutskærmkort End screen cards are hidden Kort til slutskærm vises - - Filtrer bjælke - Skjul eller vis filterbjælken i feedet, søg og relaterede videoer - Skjul i feed - Skjult i feed - Vist i feed - Skjul i søgning - Skjult i søgning - Vist i søgning - Skjul i relaterede videoer - Skjult i relaterede videoer - Vist i relaterede videoer - - - Skjul flydende mikrofon knap - Mikrofon knap skjult - Mikrofon knap vist - Deaktivér omgivende tilstand i fuld skærm Omgivelsestilstand deaktiveret @@ -988,6 +972,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Kortspilleren vil ikke genoptage ved app-opstart Kortspilleren vil genoptage ved app-opstart + + Aktivér tabletlayout Tablet layout er aktiveret diff --git a/src/main/resources/addresources/values-de-rDE/strings.xml b/src/main/resources/addresources/values-de-rDE/strings.xml index 0981d82784..4ae6950774 100644 --- a/src/main/resources/addresources/values-de-rDE/strings.xml +++ b/src/main/resources/addresources/values-de-rDE/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktiviere das Like / Abonnieren Button aufleuchten \"Gefällt mir\" und \"Abonnieren\"-Button wird nicht aufleuchten, wenn er erwähnt wird \"Gefällt mir\" und \"Abonnieren\"-Button wird aufleuchten, wenn er erwähnt wird + Albumkarten ausblenden + Albumkarten sind ausgeblendet + Albumkarten werden angezeigt + Crowdfunding Box ausblenden + Crowdfunding-Box ist ausgeblendet + Crowdfunding-Box wird angezeigt + Schwebende Mikrofon-Taste ausblenden + Mikrofon-Button ausgeblendet + Mikrofon-Taste angezeigt Verstecke graue Separatoren Graue Separatoren sind ausgeblendet Graue Trennzeichen werden angezeigt @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Sektion Transkripte wird angezeigt Videobeschreibung Komponenten der Videobeschreibung ausblenden oder anzeigen + Filterleiste + Verstecke oder zeige die Filterleiste im Feed, in der Suche und verwandten Videos + Im Feed ausblenden + Versteckt im Feed + Im Feed angezeigt + In der Suche ausblenden + Versteckt in der Suche + In der Suche angezeigt + In verwandten Videos ausblenden + Versteckt in verwandten Videos + In verwandten Videos angezeigt + Kommentare + Komponenten der Kommentar-Sektion ausblenden oder anzeigen + \'Kommentare von Mitglieder\' im Kopfbereich ausblenden + \'Kommentare von Mitglieder\' Header ist ausgeblendet + \'Kommentare von Mitgliedern\' wird angezeigt + Kommentarbereich ausblenden + Kommentarbereich ist ausgeblendet + Kommentarbereich wird angezeigt + \'Verknüpfung erstellen\'-Button ausblenden + \'Verknüpfung erstellen\' Button ist ausgeblendet + Schaltfläche \"Verknüpfung erstellen\" wird angezeigt + Vorschaukommentar ausblenden + Vorschaukommentar ist ausgeblendet + Vorschau des Kommentars wird angezeigt + Dankeschön ausblenden + Dankeschön-Taste ist ausgeblendet + Dankeschön Button wird angezeigt + Zeitstempel und Emoji-Tasten ausblenden + Zeitstempel und Emoji-Tasten sind ausgeblendet + Zeitstempel und Emoji-Tasten werden angezeigt YouTube Doodles ausblenden Suchleiste Doodles sind versteckt @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t In Wiedergabeliste speichern Button ist ausgeblendet In Wiedergabeliste speichern wird angezeigt - - Verstecke Autoplay-Schaltfläche - Autoplay Button ist ausgeblendet - Autoplay Button wird angezeigt - - - - Beschriftungstaste ausblenden - Beschriftungs-Button ist ausgeblendet - Schaltfläche für Untertitel wird angezeigt - - - Cast-Button ausblenden - Der Cast button ist ausgeblendet - Der Cast button wird angezeigt - Navigation buttons Verstecke oder ändere Schaltflächen in der Navigationsleiste @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Im VR-Menü beobachten ist ausgeblendet Im VR-Menü beobachten wird angezeigt - - Vorherige & Nächste Video-Tasten ausblenden - Buttons sind ausgeblendet - Tasten werden angezeigt - - - Albumkarten ausblenden - Albumkarten sind ausgeblendet - Albumkarten werden angezeigt - - - Kommentare - Komponenten der Kommentar-Sektion ausblenden oder anzeigen - \'Kommentare von Mitglieder\' im Kopfbereich ausblenden - \'Kommentare von Mitglieder\' Header ist ausgeblendet - \'Kommentare von Mitgliedern\' wird angezeigt - Kommentarbereich ausblenden - Kommentarbereich ist ausgeblendet - Kommentarbereich wird angezeigt - \'Verknüpfung erstellen\'-Button ausblenden - \'Verknüpfung erstellen\' Button ist ausgeblendet - Schaltfläche \"Verknüpfung erstellen\" wird angezeigt - Vorschaukommentar ausblenden - Vorschaukommentar ist ausgeblendet - Vorschau des Kommentars wird angezeigt - Dankeschön ausblenden - Dankeschön-Taste ist ausgeblendet - Dankeschön Button wird angezeigt - Zeitstempel und Emoji-Tasten ausblenden - Zeitstempel und Emoji-Tasten sind ausgeblendet - Zeitstempel und Emoji-Tasten werden angezeigt - - - Crowdfunding Box ausblenden - Crowdfunding-Box ist ausgeblendet - Crowdfunding-Box wird angezeigt + + Vorherige & Nächste Video-Tasten ausblenden + Buttons sind ausgeblendet + Tasten werden angezeigt + Cast-Button ausblenden + Der Cast button ist ausgeblendet + Der Cast button wird angezeigt + + Beschriftungstaste ausblenden + Beschriftungs-Button ist ausgeblendet + Schaltfläche für Untertitel wird angezeigt + Verstecke Autoplay-Schaltfläche + Autoplay Button ist ausgeblendet + Autoplay Button wird angezeigt Endkarte ausblenden Endbildschirmkarten sind ausgeblendet Endbildschirmkarten werden angezeigt - - Filterleiste - Verstecke oder zeige die Filterleiste im Feed, in der Suche und verwandten Videos - Im Feed ausblenden - Versteckt im Feed - Im Feed angezeigt - In der Suche ausblenden - Versteckt in der Suche - In der Suche angezeigt - In verwandten Videos ausblenden - Versteckt in verwandten Videos - In verwandten Videos angezeigt - - - Schwebende Mikrofon-Taste ausblenden - Mikrofon-Button ausgeblendet - Mikrofon-Taste angezeigt - Ambient-Modus im Vollbildmodus deaktivieren Ambient-Modus deaktiviert @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Miniaturansicht-Suchleiste wird angezeigt + Shorts Spieler + Komponenten im Shorts Spieler ausblenden oder anzeigen Shorts im Home Feed ausblenden Shorts im Home Feed sind ausgeblendet @@ -988,6 +974,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Der Shorts Player wird beim Start der App nicht fortgesetzt Shorts-Player wird beim Start der App fortgesetzt + + Autoplay Shorts + Shorts werden autoplay + Shorts wiederholen + Autoplay Shorts Hintergrund spielen + Shorts-Hintergrund spielen wird automatisch abgespielt + Shorts-Hintergrundwiedergabe wiederholt sich + Tablet Layout aktivieren Tablet Layout ist aktiviert diff --git a/src/main/resources/addresources/values-el-rGR/strings.xml b/src/main/resources/addresources/values-el-rGR/strings.xml index be889d110d..29f931cb05 100644 --- a/src/main/resources/addresources/values-el-rGR/strings.xml +++ b/src/main/resources/addresources/values-el-rGR/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Απενεργοποίηση λάμψης των κουμπιών «Μου αρέσει» και «Εγγραφή» Τα κουμπιά «Μου αρέσει» και «Εγγραφή» δεν θα λάμπουν όταν αναφέρονται Τα κουμπιά «Μου αρέσει» και «Εγγραφή» θα λάμπουν όταν αναφέρονται + Κάρτες άλμπουμ + Κρυμμένες + Εμφανίζονται + Πλαίσιο δωρεών + Κρυμμένο + Εμφανίζεται + Αιωρούμενο κουμπί μικροφώνου + Κρυμμένο + Εμφανίζεται Γκρι διαχωριστικά Κρυμμένα Εμφανίζονται @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Εμφανίζεται Περιγραφή βίντεο Απόκρυψη ή εμφάνιση στοιχείων περιγραφής βίντεο + Γραμμή φίλτρων + Απόκρυψη η εμφάνιση της γραμμής φίλτρων στη ροή, αναζήτηση και τα σχετικά βίντεο + Απόκρυψη στη ροή + Κρυμμένη + Εμφανίζεται + Απόκρυψη στα αποτελέσματα αναζήτησης + Κρυμμένη + Εμφανίζεται + Απόκρυψη στα σχετικά βίντεο + Κρυμμένη + Εμφανίζεται + Σχόλια + Απόκρυψη ή εμφάνιση στοιχείων στα σχόλια + Ετικέτα «Σχόλια από μέλη» + Κρυμμένη + Εμφανίζεται + Ενότητα σχολίων + Κρυμμένη + Εμφανίζεται + Κουμπί «Δημιουργία Short» + Κρυμμένο + Εμφανίζεται + Προεπισκόπηση σχολίου + Κρυμμένη + Εμφανίζεται + Κουμπί «Σας ευχαριστούμε» + Κρυμμένο + Εμφανίζεται + Κουμπιά χρονοσήμανσης και emoji + Κρυμμένα + Εμφανίζονται YouTube Doodles Κρυμμένα @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Κρυμμένο Εμφανίζεται - - Κουμπί αυτόματης αναπαραγωγής - Κρυμμένο - Εμφανίζεται - - - - Κουμπί υποτίτλων - Κρυμμένο - Εμφανίζεται - - - Κουμπί μετάδοσης - Κρυμμένο - Εμφανίζεται - Κουμπιά γραμμής πλοήγησης Απόκρυψη ή αλλαγή κουμπιών στη γραμμή πλοήγησης @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Κρυμμένο Εμφανίζεται - - Κουμπιά προηγούμενου & επόμενου βίντεο - Κρυμμένα - Εμφανίζονται - - - Κάρτες άλμπουμ - Κρυμμένες - Εμφανίζονται - - - Σχόλια - Απόκρυψη ή εμφάνιση στοιχείων στα σχόλια - Ετικέτα «Σχόλια από μέλη» - Κρυμμένη - Εμφανίζεται - Ενότητα σχολίων - Κρυμμένη - Εμφανίζεται - Κουμπί «Δημιουργία Short» - Κρυμμένο - Εμφανίζεται - Προεπισκόπηση σχολίου - Κρυμμένη - Εμφανίζεται - Κουμπί «Σας ευχαριστούμε» - Κρυμμένο - Εμφανίζεται - Κουμπιά χρονοσήμανσης και emoji - Κρυμμένα - Εμφανίζονται - - - Πλαίσιο δωρεών - Κρυμμένο - Εμφανίζεται + + Κουμπιά προηγούμενου & επόμενου βίντεο + Κρυμμένα + Εμφανίζονται + Κουμπί μετάδοσης + Κρυμμένο + Εμφανίζεται + + Κουμπί υποτίτλων + Κρυμμένο + Εμφανίζεται + Κουμπί αυτόματης αναπαραγωγής + Κρυμμένο + Εμφανίζεται Κάρτες τελικής οθόνης Κρυμμένες Εμφανίζονται - - Γραμμή φίλτρων - Απόκρυψη η εμφάνιση της γραμμής φίλτρων στη ροή, αναζήτηση και τα σχετικά βίντεο - Απόκρυψη στη ροή - Κρυμμένη - Εμφανίζεται - Απόκρυψη στα αποτελέσματα αναζήτησης - Κρυμμένη - Εμφανίζεται - Απόκρυψη στα σχετικά βίντεο - Κρυμμένη - Εμφανίζεται - - - Αιωρούμενο κουμπί μικροφώνου - Κρυμμένο - Εμφανίζεται - Απενεργοποίηση λειτουργίας περιβάλλοντος σε πλήρη οθόνη Η λειτουργία περιβάλλοντος σε πλήρη οθόνη είναι απενεργοποιημένη @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Εμφανίζεται + Οθόνη αναπαραγωγής Shorts + Απόκρυψη ή εμφάνιση στοιχείων στην οθόνη αναπαραγωγής Shorts Shorts στην αρχική σελίδα Κρυμμένα @@ -966,7 +952,7 @@ This is because Crowdin requires temporarily flattening this file and removing t 17.33.42 - Επαναφορά της παλιάς εμφάνισης UI - Αλλαγή της αρχικής σελίδας + Ορισμός αρχικής σελίδας Προεπιλογή Περιήγηση καναλιών Εξερεύνηση @@ -988,6 +974,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Η αναπαραγωγή Shorts δε θα συνεχίζεται κατά την εκκίνηση της εφαρμογής Η αναπαραγωγή Shorts θα συνεχίζεται κατά την εκκίνηση της εφαρμογής + + Αυτόματη αναπαραγωγή Shorts + Τα επόμενα Shorts θα αναπαράγονται αυτόματα + Τα Shorts θα επαναλαμβάνονται + Αυτόματη αναπαραγωγή Shorts παρασκηνίου + Τα επόμενα Shorts θα αναπαράγονται αυτόματα στο παρασκήνιο + Τα Shorts στο παρασκήνιο θα επαναλαμβάνονται + Λειτουργία διεπαφής τάμπλετ Η διεπαφή τάμπλετ είναι ενεργοποιημένη diff --git a/src/main/resources/addresources/values-es-rES/strings.xml b/src/main/resources/addresources/values-es-rES/strings.xml index a25a3ae740..811db83ef4 100644 --- a/src/main/resources/addresources/values-es-rES/strings.xml +++ b/src/main/resources/addresources/values-es-rES/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Desactivar el brillo del botón de like / suscripción El botón de \"Me gusta\" y \"Suscribir\" no brillará cuando se mencione El botón de \"Me gusta\" y \"Suscribir\" brillará cuando se mencione + Ocultar álbumes + Las tarjetas de álbum están ocultas + Se muestran las tarjetas de álbum + Ocultar caja de recaudación + La caja de Crowdfunding está oculta + Se muestra la caja de Crowdfunding + Ocultar botón de micrófono flotante + Botón de micrófono oculto + Botón del micrófono mostrado Ocultar separador gris Los separadores de grises están ocultos Se muestran los separadores grises @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Se muestra la sección transcripción Descripción del vídeo Ocultar o mostrar componentes de descripción de vídeo + Barra de filtros + Ocultar o mostrar la barra de filtros en el feed, la búsqueda y vídeos relacionados + Ocultar en el feed + Escondido en el feed + Mostrar en el feed + Ocultar en búsqueda + Oculto en la búsqueda + Mostrar en búsqueda + Ocultar en vídeos relacionados + Escondido en videos relacionados + Mostrar en vídeos relacionados + Comentarios + Ocultar o mostrar los componentes de sección de comentarios + Ocultar encabezado \'Comentarios por miembros\' + El encabezado \'Comentarios por miembros\' está oculto + La cabecera \'Comentarios por miembros\' se muestra + Ocultar sección de comentarios + La sección de comentarios está oculta + Sección de comentarios mostrada + Ocultar botón \'Crear un Short\' + El botón \'Crear un Short\' está oculto + Se muestra el botón \'Crear un Short\' + Ocultar comentario de vista previa + El comentario de la vista previa está oculto + Vista previa del comentario se muestra + Ocultar botón de gracias + El botón de gracias está oculto + Se muestra el botón de gracias + Ocultar botones de hora y emoji + Botones Timestamp y emoji están ocultos + Se muestran los botones Timestamp y emoji Ocultar YouTube Doodles Barra de búsqueda Doodles están ocultos @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t El botón Guardar a la lista de reproducción está oculto Mostrar el botón Guardar a la lista - - Ocultar botón de reproducción automática - El botón de reproducción automática está oculto - Se muestra el botón de reproducción automática - - - - Ocultar botón de subtítulos - Botón de subtítulos oculto - Botón de subtítulos mostrado - - - Ocultar botón de reparto - El botón de envío a otros dispositivos está oculto - El botón de envío a otros dispositivos es visible - Navigation buttons Ocultar o cambiar botones en la barra de navegación @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Ver en el menú VR está oculto Ver en el menú VR se muestra - - Ocultar botones de vídeo anteriores & siguiente - Los botones están ocultos - Los botones se muestran - - - Ocultar álbumes - Las tarjetas de álbum están ocultas - Se muestran las tarjetas de álbum - - - Comentarios - Ocultar o mostrar los componentes de sección de comentarios - Ocultar encabezado \'Comentarios por miembros\' - El encabezado \'Comentarios por miembros\' está oculto - La cabecera \'Comentarios por miembros\' se muestra - Ocultar sección de comentarios - La sección de comentarios está oculta - Sección de comentarios mostrada - Ocultar botón \'Crear un Short\' - El botón \'Crear un Short\' está oculto - Se muestra el botón \'Crear un Short\' - Ocultar comentario de vista previa - El comentario de la vista previa está oculto - Vista previa del comentario se muestra - Ocultar botón de gracias - El botón de gracias está oculto - Se muestra el botón de gracias - Ocultar botones de hora y emoji - Botones Timestamp y emoji están ocultos - Se muestran los botones Timestamp y emoji - - - Ocultar caja de recaudación - La caja de Crowdfunding está oculta - Se muestra la caja de Crowdfunding + + Ocultar botones de vídeo anteriores & siguiente + Los botones están ocultos + Los botones se muestran + Ocultar botón de reparto + El botón de envío a otros dispositivos está oculto + El botón de envío a otros dispositivos es visible + + Ocultar botón de subtítulos + Botón de subtítulos oculto + Botón de subtítulos mostrado + Ocultar botón de reproducción automática + El botón de reproducción automática está oculto + Se muestra el botón de reproducción automática Ocultar tarjetas de pantalla final Las tarjetas de pantalla de fin están ocultas Se muestran las tarjetas de la pantalla final - - Barra de filtros - Ocultar o mostrar la barra de filtros en el feed, la búsqueda y vídeos relacionados - Ocultar en el feed - Escondido en el feed - Mostrar en el feed - Ocultar en búsqueda - Oculto en la búsqueda - Mostrar en búsqueda - Ocultar en vídeos relacionados - Escondido en videos relacionados - Mostrar en vídeos relacionados - - - Ocultar botón de micrófono flotante - Botón de micrófono oculto - Botón del micrófono mostrado - Desactivar el modo ambiente en pantalla completa Modo ambiente desactivado @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t La barra de búsqueda de miniaturas se muestra + Reproductor de Shorts + Oculta o muestra componentes en el reproductor de Shorts Ocultar Shorts en el Inicio Los Shorts en el Inicio están ocultos @@ -988,6 +974,14 @@ This is because Crowdin requires temporarily flattening this file and removing t El reproductor de Shorts no se reanudará al iniciar la aplicación El reproductor de Shorts se reanudará al iniciar la aplicación + + Reproducción automática de Shorts + Los Shorts se reproducirán automáticamente + Los Shorts se repetirán + Reproducción automática de Shorts en segundo plano + Los Shorts se reproducirán automáticamente en segundo plano + Los Shorts se repetirán en segundo plano + Habilitar diseño de tablet Diseño de tablet habilitado diff --git a/src/main/resources/addresources/values-et-rEE/strings.xml b/src/main/resources/addresources/values-et-rEE/strings.xml index 27f20ce37b..dc4d0b308d 100644 --- a/src/main/resources/addresources/values-et-rEE/strings.xml +++ b/src/main/resources/addresources/values-et-rEE/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -185,6 +169,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-eu-rES/strings.xml b/src/main/resources/addresources/values-eu-rES/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-eu-rES/strings.xml +++ b/src/main/resources/addresources/values-eu-rES/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-fa-rIR/strings.xml b/src/main/resources/addresources/values-fa-rIR/strings.xml index 1dee473f21..55c3530765 100644 --- a/src/main/resources/addresources/values-fa-rIR/strings.xml +++ b/src/main/resources/addresources/values-fa-rIR/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-fi-rFI/strings.xml b/src/main/resources/addresources/values-fi-rFI/strings.xml index f645af99ae..34af32486b 100644 --- a/src/main/resources/addresources/values-fi-rFI/strings.xml +++ b/src/main/resources/addresources/values-fi-rFI/strings.xml @@ -57,7 +57,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Tuonti/vienti Tuo/vie ReVanced-asetukset - Käytät ReVanced Patches versiota <i>%s</i> + Käytät ReVanced Patchesin versiota <i>%s</i> Huomautus Tämä versio on ennakkojulkaisuversio, joten voit kokea odottamattomia ongelmia Viralliset linkit @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Poista tykkää-/tilaa-painikkeiden hehku käytöstä Tykkää- ja tilaa-painikkeet eivät hehku, kun ne mainitaan Tykkää- ja tilaa-painikkeet hehkuvat, kun ne mainitaan + Piilota albumikortit + Albumikortit on piilotettu + Albumikortit näytetään + Piilota joukkorahoituslaatikko + Joukkorahoituslaatikko on piilotettu + Joukkorahoituslaatikko näytetään + Piilota kelluva mikrofonipainike + Mikrofonipainike piilotettu + Mikrofonipainike näytetään Piilota harmaa erotin Harmaat erottimet on piilotettu Harmaat erottimet näytetään @@ -214,7 +223,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Piilota attribuutit osio \'Paikat\', Pelit ja Musiikki -osiot on piilotettu \'Paikat\', Pelit ja Musiikki -osiot näytetään - Piilota Osat-osio + Piilota Videon osat -osio Osat-osio on piilotettu Osat-osio näytetään Piilota \'Tutki podcastia\' -osio @@ -231,11 +240,42 @@ This is because Crowdin requires temporarily flattening this file and removing t Transkriptio-osio näytetään Videon kuvaus Piilota tai näytä videon kuvauskomponentteja + Suodatuspalkki + Piilota tai näytä suodatinpalkki syötteessä, haussa ja liittyvissä videoissa + Piilota syötteessä + Piilotettu syötteessä + Näytetään syötteessä + Piilota haussa + Piilotettu haussa + Näytetään haussa + Piilota liittyvissä videoissa + Piilotettu liittyvissä videoissa + Näytetään liittyvissä videoissa + Kommentit + Piilota tai näytä kommenttiosion komponentteja + Piilota \"Jäsenten kommentit\" -ylätunniste + \"Jäsenten kommentit\" -ylätunniste on piilotettu + \"Jäsenten kommentit\"-ylätunniste näytetään + Piilota kommenttiosio + Kommenttiosio on piilotettu + Kommenttiosio näytetään + Piilota \"Luo Shorts-video\" -painike + \"Luo Shorts-video\" -painike on piilotettu + \"Luo Shorts-video\" -painike näytetään + Piilota kommentin esikatselu + Kommentin esikatselu on piilotettu + Kommentin esikatselu näytetään + Piilota kiitos-painike + Kiitos-painike on piilotettu + Kiitos-painike näytetään + Piilota aikaleima- ja emoji-painikkeet + Aikaleima- ja emoji-painikkeet on piilotettu + Aikaleima- ja emoji-painikkeet näytetään - Piilota YouTube-ovet - Hakupalkki Doodles on piilotettu - Hakupalkki Doodles näytetään - YouTube Doodles esiintyy muutaman päivän joka vuosi.\n\nJos Doodle näyttää tällä hetkellä alueellasi ja tämä piilon asetus on päällä, sitten myös hakupalkin alla oleva suodatinpalkki piilotetaan. + Piilota YouTube Doodlet + Hakupalkin Doodlet on piilotettu + Hakupalkin Doodlet näytetään + YouTube Doodlet näkyvät muutamana päivänä vuodessa.\n\nJos Doodle näkyy tällä hetkellä alueellasi ja tämä piilotusasetus on käytössä, myös hakupalkin alla oleva suodatinpalkki piilotetaan. Mukautettu suodatin Piilota komponentteja mukautetuilla suodattimilla Käytä mukautettua suodatinta @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Tallenna soittolistalle -painike on piilotettu Tallenna soittolistalle -painike näytetään - - Piilota automaattisen toiston -painike - Automaattinen toisto -painike on piilotettu - Automaattinen toisto -painike näytetään - - - - Piilota tekstitykset-painike - Tekstitykset-painike on piilotettu - Tekstitykset-painike näytetään - - - Piilota cast-painike - Cast-painike on piilotettu - Cast-painike näytetään - Navigointipainikkeet Piilota tai muuta navigointipalkin painikkeita @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Katso VR-tilassa -valinta on piilotettu Katso VR-tilassa -valinta näytetään - - Piilota edellinen- ja seuraava video -painikkeet - Painikkeet on piilotettu - Painikkeet näytetään - - - Piilota albumikortit - Albumikortit on piilotettu - Albumikortit näytetään - - - Kommentit - Piilota tai näytä kommenttiosion komponentteja - Piilota \"Jäsenten kommentit\" -ylätunniste - \"Jäsenten kommentit\" -ylätunniste on piilotettu - \"Jäsenten kommentit\"-ylätunniste näytetään - Piilota kommenttiosio - Kommenttiosio on piilotettu - Kommenttiosio näytetään - Piilota \"Luo Shorts-video\" -painike - \"Luo Shorts-video\" -painike on piilotettu - \"Luo Shorts-video\" -painike näytetään - Piilota kommentin esikatselu - Kommentin esikatselu on piilotettu - Kommentin esikatselu näytetään - Piilota kiitos-painike - Kiitos-painike on piilotettu - Kiitos-painike näytetään - Piilota aikaleima- ja emoji-painikkeet - Aikaleima- ja emoji-painikkeet on piilotettu - Aikaleima- ja emoji-painikkeet näytetään - - - Piilota joukkorahoituslaatikko - Joukkorahoituslaatikko on piilotettu - Joukkorahoituslaatikko näytetään + + Piilota edellinen- ja seuraava video -painikkeet + Painikkeet on piilotettu + Painikkeet näytetään + Piilota cast-painike + Cast-painike on piilotettu + Cast-painike näytetään + + Piilota tekstitykset-painike + Tekstitykset-painike on piilotettu + Tekstitykset-painike näytetään + Piilota automaattisen toiston -painike + Automaattinen toisto -painike on piilotettu + Automaattinen toisto -painike näytetään Piilota loppunäytön kortit Loppunäytön kortit on piilotettu Loppunäytön kortit näytetään - - Suodatuspalkki - Piilota tai näytä suodatinpalkki syötteessä, haussa ja liittyvissä videoissa - Piilota syötteessä - Piilotettu syötteessä - Näytetään syötteessä - Piilota haussa - Piilotettu haussa - Näytetään haussa - Piilota liittyvissä videoissa - Piilotettu liittyvissä videoissa - Näytetään liittyvissä videoissa - - - Piilota kelluva mikrofonipainike - Mikrofonipainike piilotettu - Mikrofonipainike näytetään - Poista elokuvatila käytöstä kokoruututilassa Elokuvatila ei ole käytössä @@ -599,6 +583,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Pikkukuvan liukusäädin näytetään + Shorts-soitin + Piilota tai näytä Shorts-soittimen osia Piilota Shortsit koti-syötteessä Shortsit on piilotettu koti-syötteessä @@ -633,7 +619,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Piilota sijaintitieto Sijaintitieto on piilotettu Sijaintitieto näytetään - Piilota musiikin tallennus painike + Piilota musiikin tallennuspainike Tallenna musiikki-painike on piilotettu Tallenna musiikkipainike näytetään Piilota käytä mallinappia @@ -989,6 +975,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts-soitin ei jatku sovelluksen käynnistyessä Shorts-soitin jatkuu sovelluksen käynnistyessä + + Shortsien automaattinen toisto + Shortsit toistetaan automaattisesti + Shortsit toistuvat uudelleen + Toista Shortsit automaattisesti taustalla + Shortsit toistetaan automaattisesti myös taustalla + Shorsit toistetaan uudelleen myös taustatoiston aikana + Ota tablettiasettelu käyttöön Tablettiasettelu on käytössä @@ -1023,7 +1017,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Piilota alatekstit Alatekstit on piilotettu Alatekstit näytetään - Piilota ohita etu- ja takapainikkeet + Piilota eteenpäin- ja taaksepäin-painikkeet Ohita eteenpäin ja takaisin on piilotettu Ohita eteenpäin ja takaisin näytetään Alkukoko @@ -1049,6 +1043,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Ohita kuvan alueen rajoitukset Käyttämällä kuvan isäntä yt4.ggpht.com + Käytetään alkuperistä kuvapalvelua\n\nTämän käyttöönotto voi korjata joillakin alueilla estetyt puuttuvat kuvat @@ -1072,7 +1067,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Videon kuvakaappaukset Kuvakaappaukset otetaan kunkin videon alusta/keskeltä/lopusta. Nämä kuvat on sisäänrakennettu YouTubeen, eikä ulkoista API:a käytetä Käytä nopeita kuvakaappauksia - Käytetään keskilaatuisia kuvakaappauksia. Pikkukuvat latautuvat nopeammin, mutta suorilla lähetyksillä, julkaisemattomilla tai hyvin vanhoilla videoilla saattaa näkyä tyhjiä pikkukuvia + Käytetään keskilaatuisia kuvakaappauksia. Pikkukuvat latautuvat nopeammin, mutta suoratoistoilla, julkaisemattomilla tai hyvin vanhoilla videoilla saattaa näkyä tyhjiä pikkukuvia Käytetään korkealaatuisia kuvakaappauksia Videon aika, josta kuvakaappaukset otetaan Videon alku @@ -1170,13 +1165,13 @@ This is because Crowdin requires temporarily flattening this file and removing t Kelaus liu\'uttamalla ei ole käytössä - Naamioi videovirrat - Naamioi asiakkaan videovirrat toisto-ongelmien estämiseksi - Naamioi videovirrat - Videovirrat naamioidaan - Videovirtoja ei naamioida\n\nViden toisto ei ehkä toimi + Naamioi videostriimit + Naamioi asiakasohjelman videostriimit toisto-ongelmien estämiseksi + Naamioi videostriimit + Videostriimit naamioidaan + Videostriimejä ei naamioida\n\nViden toisto ei ehkä toimi Tämän asetuksen poistaminen käytöstä voi aiheuttaa ongelmia videotoistossa. - Oletusasiakas + Oletusasiakasohjelma Pakota AVC (H.264) Videokoodekki on AVC (H.264) Videokoodekki on VP9 tai AV1 diff --git a/src/main/resources/addresources/values-fil-rPH/strings.xml b/src/main/resources/addresources/values-fil-rPH/strings.xml index 74f2ec399a..eab17c0972 100644 --- a/src/main/resources/addresources/values-fil-rPH/strings.xml +++ b/src/main/resources/addresources/values-fil-rPH/strings.xml @@ -93,6 +93,15 @@ This is because Crowdin requires temporarily flattening this file and removing t I-disable ang glow ng like / subscribe button Hindi magliliwanag ang like and subscribe button kapag nabanggit Ang like and subscribe button ay magliliwanag kapag nabanggit + Itago ang mga album card + Nakatago ang mga card ng album + Ipinapakita ang mga album card + Itago ang crowdfunding box + Nakatago ang crowdfunding box + Ipinapakita ang kahon ng crowdfunding + Itago ang lumulutang na pindutan ng mikropono + Nakatago ang button ng mikropono + Ipinapakita ang pindutan ng mikropono Itago ang gray na separator Nakatago ang mga gray na separator Ipinapakita ang mga gray na separator @@ -214,6 +223,36 @@ This is because Crowdin requires temporarily flattening this file and removing t Ipinapakita ang seksyon ng transcript Paglalarawan ng video Itago o ipakita ang mga bahagi ng paglalarawan ng video + Itago o ipakita ang filter bar sa feed, paghahanap, at mga kaugnay na video + Itago sa feed + Nakatago sa feed + Ipinapakita sa feed + Itago sa paghahanap + Nakatago sa paghahanap + Ipinapakita sa paghahanap + Itago sa mga kaugnay na video + Nakatago sa mga kaugnay na video + Ipinapakita sa mga kaugnay na video + Mga komento + Itago o ipakita ang mga bahagi ng seksyon ng komento + Itago ang header ng \"Mga komento ng mga miyembro\" + Nakatago ang header ng \"Mga komento ng mga miyembro\" + Ipinapakita ang header ng \"Mga komento ng mga miyembro\" + Itago ang seksyon ng mga komento + Nakatago ang seksyon ng mga komento + Ipinapakita ang seksyon ng mga komento + Itago ang pindutang \"Gumawa ng Maikling\" + Nakatago ang pindutang \"Gumawa ng Maikling\" + Ipinapakita ang pindutang \"Gumawa ng Maikling\" + Itago ang preview na komento + Nakatago ang preview ng komento + Ang pag-preview ng komento ay ipinapakita + Itago ang pindutan ng pasasalamat + Nakatago ang buton ng salamat + Ang pindutan ng salamat ay ipinapakita + Itago ang mga pindutan ng timestamp at emoji + Nakatago ang mga pindutan ng timestamp at emoji + Ipinapakita ang mga pindutan ng timestamp at emoji Custom na filter Itago ang mga bahagi gamit ang mga custom na filter @@ -396,22 +435,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Nakatago ang button na I-save sa playlist I-save sa playlist button ay ipinapakita - - Itago ang autoplay na button - Nakatago ang autoplay button - Ang autoplay na button ay ipinapakita - - - - Button na itago ang mga caption - Nakatago ang button ng mga caption - Ang pindutan ng mga caption ay ipinapakita - - - Itago ang cast button - Nakatago ang pindutan sa cast - Nakikita ang cast button - Mga pindutan ng nabigasyon Itago o baguhin ang mga button sa navigation bar @@ -484,65 +507,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Nakatago ang panonood sa VR menu Ang panonood sa VR menu ay ipinapakita - - Itago ang nakaraang & susunod na mga pindutan ng video - Nakatago ang mga pindutan - Ang mga pindutan ay ipinapakita - - - Itago ang mga album card - Nakatago ang mga card ng album - Ipinapakita ang mga album card - - - Mga komento - Itago o ipakita ang mga bahagi ng seksyon ng komento - Itago ang header ng \"Mga komento ng mga miyembro\" - Nakatago ang header ng \"Mga komento ng mga miyembro\" - Ipinapakita ang header ng \"Mga komento ng mga miyembro\" - Itago ang seksyon ng mga komento - Nakatago ang seksyon ng mga komento - Ipinapakita ang seksyon ng mga komento - Itago ang pindutang \"Gumawa ng Maikling\" - Nakatago ang pindutang \"Gumawa ng Maikling\" - Ipinapakita ang pindutang \"Gumawa ng Maikling\" - Itago ang preview na komento - Nakatago ang preview ng komento - Ang pag-preview ng komento ay ipinapakita - Itago ang pindutan ng pasasalamat - Nakatago ang buton ng salamat - Ang pindutan ng salamat ay ipinapakita - Itago ang mga pindutan ng timestamp at emoji - Nakatago ang mga pindutan ng timestamp at emoji - Ipinapakita ang mga pindutan ng timestamp at emoji - - - Itago ang crowdfunding box - Nakatago ang crowdfunding box - Ipinapakita ang kahon ng crowdfunding + + Itago ang nakaraang & susunod na mga pindutan ng video + Nakatago ang mga pindutan + Ang mga pindutan ay ipinapakita + Itago ang cast button + Nakatago ang pindutan sa cast + Nakikita ang cast button + + Button na itago ang mga caption + Nakatago ang button ng mga caption + Ang pindutan ng mga caption ay ipinapakita + Itago ang autoplay na button + Nakatago ang autoplay button + Ang autoplay na button ay ipinapakita Itago ang mga end screen card Nakatago ang mga end screen card Ipinapakita ang mga end screen card - - Itago o ipakita ang filter bar sa feed, paghahanap, at mga kaugnay na video - Itago sa feed - Nakatago sa feed - Ipinapakita sa feed - Itago sa paghahanap - Nakatago sa paghahanap - Ipinapakita sa paghahanap - Itago sa mga kaugnay na video - Nakatago sa mga kaugnay na video - Ipinapakita sa mga kaugnay na video - - - Itago ang lumulutang na pindutan ng mikropono - Nakatago ang button ng mikropono - Ipinapakita ang pindutan ng mikropono - I-disable ang ambient mode sa fullscreen Naka-disable ang ambient mode @@ -917,6 +901,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Hindi magpapatuloy ang shorts player sa pagsisimula ng app Magpapatuloy ang manlalaro ng shorts sa pagsisimula ng app + + Paganahin ang layout ng tablet Naka-enable ang layout ng tablet diff --git a/src/main/resources/addresources/values-fr-rFR/strings.xml b/src/main/resources/addresources/values-fr-rFR/strings.xml index 2e0a60836a..6090aff3ea 100644 --- a/src/main/resources/addresources/values-fr-rFR/strings.xml +++ b/src/main/resources/addresources/values-fr-rFR/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Désactiver la lueur des boutons \"J\'aime\" / \"Je n\'aime pas\" Les boutons \"J\'aime\" et \"Je n\'aime pas\" ne s\'illuminerons pas lorsqu\'ils sont mentionné Les boutons \"J\'aime\" et \"Je n\'aime pas\" s\'illuminerons lorsqu\'ils sont mentionné + Cacher les cartes d\'album + Les cartes d\'album sont cachées + Les cartes de l\'album sont affichées + Masquer la boîte de financement participatif + La boîte de Crowdfunding est cachée + La boîte de financement participatif est affichée + Masquer le bouton de microphone flottant + Bouton du microphone masqué + Bouton du microphone affiché Masquer les séparateurs gris Les séparateurs gris sont masqués Les séparateurs gris sont affichés @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t La section \'Transcription\' est affiché Description vidéo Masquer ou afficher des éléments de la description de la vidéo + Barre de filtre + Masquer ou afficher la barre de filtre dans le flux, la recherche et les vidéos connexes + Cacher dans le flux + Caché dans le flux + Affiché dans le flux + Cacher dans la recherche + Caché dans la recherche + Affiché dans la recherche + Cacher dans les vidéos liées + Caché dans les vidéos liées + Affiché dans des vidéos connexes + Commentaires + Masquer ou afficher les composants de la section commentaires + Cacher l\'en-tête \'Commentaires par membres\' + L\'en-tête \'Commentaires par membres\' est masqué + L\'en-tête \'Commentaires par membres\' est affiché + Cacher la section des commentaires + La section Commentaires est cachée + La section Commentaires est affichée + Masquer le bouton « Créer un court » + Le bouton « Créer un court » est masqué + Le bouton « Créer un court » est affiché + Masquer le commentaire de prévisualisation + L\'aperçu du commentaire est masqué + L\'aperçu du commentaire est affiché + Masquer le bouton de remerciement + Le bouton de remerciement est caché + Le bouton de remerciement est affiché + Masquer les boutons d\'horodatage et d\'émoji + Les boutons d\'horodatage et d\'émoji sont cachés + Les boutons d\'horodatage et d\'émoji sont affichés Masquer les Doodles YouTube Les Doodles de la barre de recherche sont masquées @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Le bouton \'Enregistrer dans la playlist\' est masqué Le bouton \'Enregistrer dans la playlist\' est affiché - - Masquer le bouton \'Lecture automatique\' - Le bouton \"Lecture automatique\" est masqué - Le bouton \"Lecture automatique\" est affiché - - - - Masquer le bouton \'Sous-titres\' - Le bouton \'Sous-titres\' est masqué - Le bouton \'Sous-titres\' est affiché - - - Masquer le bouton \'Caster\' - Le bouton \'Caster\' est masqué - Le bouton \'Caster\' est affiché - Boutons de navigation Masquer ou modifier les boutons dans la barre de navigation @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Le menu Regarder en VR est masqué Le menu Regarder en VR est affiché - - Masquer les boutons de la vidéo précédente & suivants - Les boutons sont cachés - Les boutons sont affichés - - - Cacher les cartes d\'album - Les cartes d\'album sont cachées - Les cartes de l\'album sont affichées - - - Commentaires - Masquer ou afficher les composants de la section commentaires - Cacher l\'en-tête \'Commentaires par membres\' - L\'en-tête \'Commentaires par membres\' est masqué - L\'en-tête \'Commentaires par membres\' est affiché - Cacher la section des commentaires - La section Commentaires est cachée - La section Commentaires est affichée - Masquer le bouton « Créer un court » - Le bouton « Créer un court » est masqué - Le bouton « Créer un court » est affiché - Masquer le commentaire de prévisualisation - L\'aperçu du commentaire est masqué - L\'aperçu du commentaire est affiché - Masquer le bouton de remerciement - Le bouton de remerciement est caché - Le bouton de remerciement est affiché - Masquer les boutons d\'horodatage et d\'émoji - Les boutons d\'horodatage et d\'émoji sont cachés - Les boutons d\'horodatage et d\'émoji sont affichés - - - Masquer la boîte de financement participatif - La boîte de Crowdfunding est cachée - La boîte de financement participatif est affichée + + Masquer les boutons de la vidéo précédente & suivants + Les boutons sont cachés + Les boutons sont affichés + Masquer le bouton \'Caster\' + Le bouton \'Caster\' est masqué + Le bouton \'Caster\' est affiché + + Masquer le bouton \'Sous-titres\' + Le bouton \'Sous-titres\' est masqué + Le bouton \'Sous-titres\' est affiché + Masquer le bouton \'Lecture automatique\' + Le bouton \"Lecture automatique\" est masqué + Le bouton \"Lecture automatique\" est affiché Masquer les cartes de fin d\'écran Les cartes de fin d\'écran sont masquées Les cartes de fin d\'écran sont affichées - - Barre de filtre - Masquer ou afficher la barre de filtre dans le flux, la recherche et les vidéos connexes - Cacher dans le flux - Caché dans le flux - Affiché dans le flux - Cacher dans la recherche - Caché dans la recherche - Affiché dans la recherche - Cacher dans les vidéos liées - Caché dans les vidéos liées - Affiché dans des vidéos connexes - - - Masquer le bouton de microphone flottant - Bouton du microphone masqué - Bouton du microphone affiché - Désactiver le mode ambiant en plein écran Mode Veille désactivé @@ -599,6 +583,8 @@ This is because Crowdin requires temporarily flattening this file and removing t La barre de recherche des miniatures est affichée + Joueur Shorts + Cacher ou afficher les composants dans le joueur Shorts Cacher les Shorts dans la page d\'accueil Les courts dans le flux domestique sont cachés @@ -989,6 +975,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Le lecteur court ne reprendra pas au démarrage de l\'application Le lecteur court reprendra au démarrage de l\'application + + Jouer les Shorts + Les Shorts joueront automatiquement + Les Shorts se répéteront + Lecture automatique des Shorts en arrière-plan + La lecture en arrière-plan des Shorts sera automatique + La lecture en arrière-plan des Shorts se répétera + Activer la disposition de la tablette Mise en page de la tablette est activée diff --git a/src/main/resources/addresources/values-ga-rIE/strings.xml b/src/main/resources/addresources/values-ga-rIE/strings.xml index f77e1b0976..d173f85379 100644 --- a/src/main/resources/addresources/values-ga-rIE/strings.xml +++ b/src/main/resources/addresources/values-ga-rIE/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Díchumasaigh cosúil/liostáil chnaipe glow Ní ghlacfaidh an cnaipe Like agus Liostáil nuair a luaitear Taispeánfaidh an cnaipe Like agus Liostáil nuair a luait + Folaigh cártaí albam + Tá cártaí albam i bhfolach + Taispeántar cártaí albam + Folaigh bosca slua-mhaoiniú + Tá bosca crowdfunding i bhfolach + Taispeántar bosca slua-mhaoiniú + Cnaipe micreafón ar snámh + Cnaipe micreafón folach + Taispeántar an cnaipe micreafón Folaigh deighilteoir liath Tá deighilteoirí liath i bhfolach Taispeántar deighilteoirí liath @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Taispeántar alt an tras-scríbhinn Cur síos físeán Folaigh nó taispeáint comhpháirteanna tuairisc + Barra scagaire + Folaigh nó taispeáin an barra scagaire sna físeáin beatha, cuardaigh agus gaolmhara + Folaigh i mbeatha + I bhfolach i mbeatha + Taispeántar i mbeatha + Folaigh i gcuardach + I bhfolach i gcuardach + Taispeántar i gcuardach + Folaigh i bhfíseáin gaolmhara + I bhfolach i bhfíseáin ghaolmhara + Taispeántar i bhfíseáin ghaolmhara + Tuairimí + Folaigh nó taispeáin comhpháirteanna na rannóige tuairimí + Folaigh ceanntásc \'Tuairimí ag baill \' + Tá ceanntásc \'Tuairimí ag comhaltaí \'i bhfolach + Taispeántar ceanntásc \'Tuairimí ag comhaltaí\' + Folaigh roinn tuairimí + Tá an chuid tuairimí i bhfolach + Taispeántar an chuid tuairimí + Folaigh cnaipe \'Cruthaigh gearr\' + Tá cnaipe \'Cruthaigh gearr\' i bhfolach + Taispeántar cnaipe \'Cruthaigh gearr\' + Folaigh trácht réamhamharc + Tá trácht réamhamhar i bhfolach + Taispeántar trácht réamhamharc + Folaigh cnaipe buíochas + Tá cnaipe buíochas i bhfolach + Taispeántar cnaipe buíochas + Folaigh cnaipí ama agus emoji + Tá cnaipí ama agus emoji i bhfolach + Taispeántar cnaipí ama agus emoji Folaigh YouTube Doodles Barra cuardaigh Tá Doodles i bhfolach @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Tá cnaipe Sábháil go seinmliosta i bhfolach Taispeántar cnaipe Sábháil go seinmliosta - - Folaigh cnaipe autoplay - Tá cnaipe Autoplay i bhfolach - Taispeántar cnaipe Autoplay - - - - Folaigh cnaipe fotheidil - Tá cnaipe fotheidil i bhfolach - Taispeántar cnaipe fotheidil - - - Folaigh cnaipe teilgthe - Tá cnaipe teilgthe i bhfolach - Taispeántar cnaipe teilgthe - Cnaipí nascleanúna Folaigh nó athraigh cnaipí sa bharra nascleanúna @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Tá faire i roghchlár VR i bhfolach Taispeántar an faire sa roghchlár VR - - Folaigh & cnaipí físeáin seo chugainn - Tá cnaipí i bhfolach - Taispeántar cnaipí - - - Folaigh cártaí albam - Tá cártaí albam i bhfolach - Taispeántar cártaí albam - - - Tuairimí - Folaigh nó taispeáin comhpháirteanna na rannóige tuairimí - Folaigh ceanntásc \'Tuairimí ag baill \' - Tá ceanntásc \'Tuairimí ag comhaltaí \'i bhfolach - Taispeántar ceanntásc \'Tuairimí ag comhaltaí\' - Folaigh roinn tuairimí - Tá an chuid tuairimí i bhfolach - Taispeántar an chuid tuairimí - Folaigh cnaipe \'Cruthaigh gearr\' - Tá cnaipe \'Cruthaigh gearr\' i bhfolach - Taispeántar cnaipe \'Cruthaigh gearr\' - Folaigh trácht réamhamharc - Tá trácht réamhamhar i bhfolach - Taispeántar trácht réamhamharc - Folaigh cnaipe buíochas - Tá cnaipe buíochas i bhfolach - Taispeántar cnaipe buíochas - Folaigh cnaipí ama agus emoji - Tá cnaipí ama agus emoji i bhfolach - Taispeántar cnaipí ama agus emoji - - - Folaigh bosca slua-mhaoiniú - Tá bosca crowdfunding i bhfolach - Taispeántar bosca slua-mhaoiniú + + Folaigh & cnaipí físeáin seo chugainn + Tá cnaipí i bhfolach + Taispeántar cnaipí + Folaigh cnaipe teilgthe + Tá cnaipe teilgthe i bhfolach + Taispeántar cnaipe teilgthe + + Folaigh cnaipe fotheidil + Tá cnaipe fotheidil i bhfolach + Taispeántar cnaipe fotheidil + Folaigh cnaipe autoplay + Tá cnaipe Autoplay i bhfolach + Taispeántar cnaipe Autoplay Folaigh cártaí scáileáin deireadh Tá cártaí scáileáin deiridh i bhfolach Taispeántar cártaí scáileáin deireadh - - Barra scagaire - Folaigh nó taispeáin an barra scagaire sna físeáin beatha, cuardaigh agus gaolmhara - Folaigh i mbeatha - I bhfolach i mbeatha - Taispeántar i mbeatha - Folaigh i gcuardach - I bhfolach i gcuardach - Taispeántar i gcuardach - Folaigh i bhfíseáin gaolmhara - I bhfolach i bhfíseáin ghaolmhara - Taispeántar i bhfíseáin ghaolmhara - - - Cnaipe micreafón ar snámh - Cnaipe micreafón folach - Taispeántar an cnaipe micreafón - Díchumasaigh modh comhthimpeallach sa scáile Díchumasaíodh mód comhthimpeallach @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Taispeántar barra cuardaigh mionsamhail + Shorts seinnteoir + Folaigh nó taispeáin comhpháirteanna san seinnteoir Shorts Folaigh Shorts i mbeatha baile Tá shorts i mbeatha baile i bhfolach @@ -644,6 +630,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Folaigh an cnaipe scáileán glas Tá cnaipe an scáileáin glas i bhfolach Taispeántar cnaipe an scáileáin glas + Folaigh an cnaipe hashtag + Tá cnaipe hashtag i bhfolach + Taispeántar an cnaipe hashtag Folaigh moltaí cuardaigh Tá moltaí cuardaigh i bhfolach Taispeántar moltaí cuardaigh @@ -965,18 +954,34 @@ This is because Crowdin requires temporarily flattening this file and removing t Socraigh leathanach tosaigh Réamhshocraithe + Brabhsáil cainéil Déan iniúchadh + Cluichíocht Stair + Leabharlann Físeáin a thaitin + Beo + Scannáin + Ceol Cuardaigh + Spóirt Síntiúis Ag treocht + Féach ar níos déanaí Díchumasaigh an t-imreoir Shorts atá ag tosú arís Ní thosóidh an t-imreoir shorts ar thosú an aip Athosóidh an t-imreoir Shorts ar thosú an aip + + Shorts Autoplay + Seinnfidh Shorts go huathoibríoch + Déanfaidh shorts arís + Cluiche Cúlra Shorts Autoplay + Déanfar súgradh cúlra Shorts go huathoibríoch + Athdhéanfar súgradh cúlra Shorts + Cumasaigh leagan amach na táibléad Tá leagan amach an táibléad cumasaithe @@ -993,7 +998,20 @@ This is because Crowdin requires temporarily flattening this file and removing t Nua-aimseartha 1 Nua-Aimseartha 2 Nua-aimseartha 3 + Cumasaigh coirnéil chothromú + Déantar coirnéil a shlánú + Tá coirnéil cearnach + Cumasaigh sconna dúbailte agus pinch chun méid a athrú + Tá gníomh tapáil faoi dhó agus pinch chun méid a athrú cumasaithe\n\n• Tapáil faoi dhó chun méid mion-imreora a mhéadú\n• Tapáil faoi dhó arís chun an bunmhéid a aischur + Díchumasaíodh gníomh tapáil faoi dhó agus pinch chun méid a athrú + Cumasaigh tarraing agus scaoil + Cumasaítear tarraing agus scaoil\n\nIs féidir mion-imreoir a tharraingt go cúinne ar bith den scáileán + Tá tarraing agus scaoil díchumasaithe + Folaigh cnaipe dúnta + Tá an cnaipe dúnta i bhfolach + Taispeántar an cnaipe dúnta Folaigh cnaipí leathnú agus dún + Tá cnaipí i bhfolach\n\nSwipe chun leathnú nó dúnadh Taispeántar cnaipí leathnaigh agus dún Folaigh fothéacsanna Tá fothéacsanna i bhfolach @@ -1001,6 +1019,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Folaigh cnaipí scipeáil ar aghaidh agus ar ais Tá scipeanna ar aghaidh agus ar ais i bhfolach Taispeántar scipeáil ar aghaidh agus ar ais + Méid tosaigh + Tosaigh ar mhéid an scáileáin, i bpicteilíní + Caithfidh méid picteilíní a bheith idir %1$s agus %2$s Trédhearcacht forleagan Luach trédhearcachta idir 0-100, áit a bhfuil 0 trédhearcach Caithfidh trédhearcacht forleagtha mionaimreora a bheith idir 0-100 @@ -1016,6 +1037,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Taispeántar dath barr cuardaigh bunaidh Dath barra cuardaigh saincheaptha Dath an bharra cuardaigh + Luach datha barra cuardaigh neamhbhailí Seachbhóthar srianta réigiún íomhá diff --git a/src/main/resources/addresources/values-gl-rES/strings.xml b/src/main/resources/addresources/values-gl-rES/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-gl-rES/strings.xml +++ b/src/main/resources/addresources/values-gl-rES/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-gu-rIN/strings.xml b/src/main/resources/addresources/values-gu-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-gu-rIN/strings.xml +++ b/src/main/resources/addresources/values-gu-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-hi-rIN/strings.xml b/src/main/resources/addresources/values-hi-rIN/strings.xml index 93a6ba1f37..5409e4c7bb 100644 --- a/src/main/resources/addresources/values-hi-rIN/strings.xml +++ b/src/main/resources/addresources/values-hi-rIN/strings.xml @@ -97,13 +97,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -124,20 +117,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -191,6 +175,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-hr-rHR/strings.xml b/src/main/resources/addresources/values-hr-rHR/strings.xml index 30ad42f16e..83eb1c1961 100644 --- a/src/main/resources/addresources/values-hr-rHR/strings.xml +++ b/src/main/resources/addresources/values-hr-rHR/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-hu-rHU/strings.xml b/src/main/resources/addresources/values-hu-rHU/strings.xml index cc414016f2..19250de3dd 100644 --- a/src/main/resources/addresources/values-hu-rHU/strings.xml +++ b/src/main/resources/addresources/values-hu-rHU/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Like / feliratkozás gomb ragyogásának kikapcsolása Like / feliratkozás gomb nem fog ragyogni mikor megemlítik Like / feliratkozás gomb ragyogni fog mikor megemlítik + Album kártyák elrejtése + Az album kártyák el vannak rejtve + Az album kártyák láthatóak + Közösségi finanszírozási doboz elrejtése + A közösségi finanszírozási doboz el van rejtve + A közösségi finanszírozási doboz megjelenik + Lebegő mikrofon gomb elrejtése + A mikrofon gomb elrejtve + A mikrofon gomb látható Szürke elválasztó elrejtése A szürke elválasztók el vannak rejtve A szürke elválasztók láthatóak @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Az átirat rész megjelenik Videóleírás A videóleírás komponenseinek elrejtése vagy megjelenítése + Szűrősáv + Szűrősáv elrejtése vagy megjelenítése a feedekben, a keresésben és a kapcsolódó videók között + Elrejtés a feedekben + Elrejtve a feedekben + Megjelenítés a feedekben + Elrejtés a keresésben + Elrejtve a keresésben + Megjelenik a keresésben + Elrejtés a kapcsolódó videók között + Elrejtve a kapcsolódó videók között + Megjelenik a kapcsolódó videók között + Megjegyzések + Megjegyzések rész elrejtése vagy megjelenítése + A „Tagok megjegyzései” fejléc elrejtése + A „tagok megjegyzései” fejléc el van rejtve + Megjelenik a „Tagok megjegyzései” fejléc + A megjegyzések szekció elrejtése + A megjegyzések szakasz el van rejtve + Megjelenik a megjegyzések rész + A „Rövid létrehozása” gomb elrejtése + A „Short létrehozása” gomb el van rejtve + Megjelenik a „Short létrehozása” gomb + Megjegyzés előnézet elrejtése + A megjegyzés előnézet el van rejtve + A megjegyzés előnézet megjelenik + Köszönet gomb elrejtése + A köszönet gomb el van rejtve + A köszönet gomb látható + Időbélyeg és az emoji gombok elrejtése + Az időbélyeg és az emoji gombok el vannak rejtve + Megjelennek az időbélyeg és az emoji gombok YouTube Doodles elrejtése A Doodles Keresősáv el van rejtve @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t A mentés gomb el van rejtve A mentés gomb látható - - Automatikus lejátszás gomb elrejtése - Az automatikus lejátszás gomb el van rejtve - Az automatikus lejátszás gomb látható - - - - Feliratok gomb elrejtése - A feliratok gomb el van rejtve - A feliratok gomb látható - - - Átküldés gomb elrejtése - Az átküldés gomb rejtve van - Az átküldés gomb látható - Navigációs gombok Gombok elrejtése vagy módosítása a navigációs sávon @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t A megtekintés VR-módban menü el van rejtve A „Megtekintés VR-módban” menü megjelenik - - Az előző és következő videó gombok elrejtése - A gombok elrejtve - A gombok megjelennek - - - Album kártyák elrejtése - Az album kártyák el vannak rejtve - Az album kártyák láthatóak - - - Megjegyzések - Megjegyzések rész elrejtése vagy megjelenítése - A „Tagok megjegyzései” fejléc elrejtése - A „tagok megjegyzései” fejléc el van rejtve - Megjelenik a „Tagok megjegyzései” fejléc - A megjegyzések szekció elrejtése - A megjegyzések szakasz el van rejtve - Megjelenik a megjegyzések rész - A „Rövid létrehozása” gomb elrejtése - A „Short létrehozása” gomb el van rejtve - Megjelenik a „Short létrehozása” gomb - Megjegyzés előnézet elrejtése - A megjegyzés előnézet el van rejtve - A megjegyzés előnézet megjelenik - Köszönet gomb elrejtése - A köszönet gomb el van rejtve - A köszönet gomb látható - Időbélyeg és az emoji gombok elrejtése - Az időbélyeg és az emoji gombok el vannak rejtve - Megjelennek az időbélyeg és az emoji gombok - - - Közösségi finanszírozási doboz elrejtése - A közösségi finanszírozási doboz el van rejtve - A közösségi finanszírozási doboz megjelenik + + Az előző és következő videó gombok elrejtése + A gombok elrejtve + A gombok megjelennek + Átküldés gomb elrejtése + Az átküldés gomb rejtve van + Az átküldés gomb látható + + Feliratok gomb elrejtése + A feliratok gomb el van rejtve + A feliratok gomb látható + Automatikus lejátszás gomb elrejtése + Az automatikus lejátszás gomb el van rejtve + Az automatikus lejátszás gomb látható Záróképernyő kártyák elrejtése A záróképernyő kártyák el vannak rejtve A záróképernyő kártyák megjelennek - - Szűrősáv - Szűrősáv elrejtése vagy megjelenítése a feedekben, a keresésben és a kapcsolódó videók között - Elrejtés a feedekben - Elrejtve a feedekben - Megjelenítés a feedekben - Elrejtés a keresésben - Elrejtve a keresésben - Megjelenik a keresésben - Elrejtés a kapcsolódó videók között - Elrejtve a kapcsolódó videók között - Megjelenik a kapcsolódó videók között - - - Lebegő mikrofon gomb elrejtése - A mikrofon gomb elrejtve - A mikrofon gomb látható - Mozifilmes világítás letiltása teljes képernyős módban Mozifilmes világítás kikapcsolva @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t A minilejátszó folyamatsávja megjelenik + Shorts lejátszó + Összetevők elrejtése vagy megjelenítése a Shorts lejátszóban Shorts elrejtése a Kezdőlap feedben A Shortsok elrejtve a Kezdőlap feedben @@ -988,6 +974,14 @@ This is because Crowdin requires temporarily flattening this file and removing t A Shorts lejátszás nem indul el az alkalmazás indításakor A Shorts lejátszás folytatódik az alkalmazás indításakor + + Shorts automatikus lejátszása + Shorts automatikusan elindul + Shorts ismétlődik + Shorts automatikus lejátszása a háttérben + Shorts automatikusan elindul a háttérben + Shorts háttérben történő lejátszása megismétlődik + Táblagépes elrendezés engedélyezése Táblagépes elrendezés engedélyezve diff --git a/src/main/resources/addresources/values-hy-rAM/strings.xml b/src/main/resources/addresources/values-hy-rAM/strings.xml index 89d212bf5d..13d87b2803 100644 --- a/src/main/resources/addresources/values-hy-rAM/strings.xml +++ b/src/main/resources/addresources/values-hy-rAM/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-in-rID/strings.xml b/src/main/resources/addresources/values-in-rID/strings.xml index df3f8941f4..2ea6d5b9ff 100644 --- a/src/main/resources/addresources/values-in-rID/strings.xml +++ b/src/main/resources/addresources/values-in-rID/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Nonaktifkan kilau tombol suka / langganan Tombol suka dan langganan tidak akan berkilau saat ditekan Tombol suka dan langganan akan berkilau saat ditekan + Sembunyikan kartu album + Kartu album disembunyikan + Kartu album ditampilkan + Sembunyikan kotak penggalangan dana + Kotak penggalangan dana disembunyikan + Kotak penggalangan dana ditampilkan + Sembunyikan tombol mikrofon mengambang + Tombol mikrofon disembunyikan + Tombol mikrofon ditampilkan Sembunyikan pemisah abu-abu Pemisah abu-abu disembunyikan Pemisah abu-abu ditampilkan @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Bagian transkrip sudah ditampilkan Keterangan video Sembunyi/tampilkan komponen keterangan video + Bilah saring + Sembunyikan atau tampilkan bilah filter di feed, pencarian, dan video terkait + Sembunyikan di feed + Sembunyikan di feed + Tampilkan di feed + Sembunyikan di pencarian + Disembunyikan di pencarian + Ditampilkan di pencarian + Sembunyikan di video terkait + Disembunyikan di video terkait + Ditampilkan di video terkait + Komentar + Sembunyikan atau tampilkan komponen bagian komentar + Sembunyikan Header \'Komentar oleh anggota\' + Header \'Komentar oleh anggota\' disembunyikan + Header \'Komentar oleh anggota\' disembunyikan + Sembunyikan bagian komentar + Bagian komentar disembunyikan + Bagian komentar ditampilkan + Sembunyikan tombol \"Buat Short\" + Tombol \'Buat Short\' disembunyikan + Tombol \'Buat Short\' ditampilkan + Sembunyikan komentar pratinjau + Komentar pratinjau disembunyikan + Komentar pratinjau ditampilkan + Sembunyikan \'terima kasih\' + Tombol terima kasih disembunyikan + Tombol terima kasih ditampilkan + Sembunyikan timestamp dan tombol emoji + Tombol timestamp dan emoji disembunyikan + Tombol timestamp dan emoji ditampilkan Sembunyikan YouTube Doodles Bilah pencarian Doodle disembunyikan @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Tombol Simpan ke daftar putar disembunyikan Tombol Simpan ke daftar putar ditampilkan - - Sembunyikan tombol putar otomatis - Tombol putar otomatis disembunyikan - Tombol putar otomatis ditampilkan - - - - Sembunyikan tombol teks - Tombol teks disembunyikan - Tombol teks ditampilkan - - - Sembunyikan tombol transmisi - Tombol membagikan layar disembunyikan - Tombol membagikan layar ditampilkan - Tombol navigasi Sembunyikan atau ganti tombol di bilah navigasi @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Menu tonton di VR disembunyikan Menu tonton di VR ditampilkan - - Sembunyikan tombol sebelumnya & berikutnya - Tombol disembunyikan - Tombol ditampilkan - - - Sembunyikan kartu album - Kartu album disembunyikan - Kartu album ditampilkan - - - Komentar - Sembunyikan atau tampilkan komponen bagian komentar - Sembunyikan Header \'Komentar oleh anggota\' - Header \'Komentar oleh anggota\' disembunyikan - Header \'Komentar oleh anggota\' disembunyikan - Sembunyikan bagian komentar - Bagian komentar disembunyikan - Bagian komentar ditampilkan - Sembunyikan tombol \"Buat Short\" - Tombol \'Buat Short\' disembunyikan - Tombol \'Buat Short\' ditampilkan - Sembunyikan komentar pratinjau - Komentar pratinjau disembunyikan - Komentar pratinjau ditampilkan - Sembunyikan \'terima kasih\' - Tombol terima kasih disembunyikan - Tombol terima kasih ditampilkan - Sembunyikan timestamp dan tombol emoji - Tombol timestamp dan emoji disembunyikan - Tombol timestamp dan emoji ditampilkan - - - Sembunyikan kotak penggalangan dana - Kotak penggalangan dana disembunyikan - Kotak penggalangan dana ditampilkan + + Sembunyikan tombol sebelumnya & berikutnya + Tombol disembunyikan + Tombol ditampilkan + Sembunyikan tombol transmisi + Tombol membagikan layar disembunyikan + Tombol membagikan layar ditampilkan + + Sembunyikan tombol teks + Tombol teks disembunyikan + Tombol teks ditampilkan + Sembunyikan tombol putar otomatis + Tombol putar otomatis disembunyikan + Tombol putar otomatis ditampilkan Sembunyikan kartu layar akhir Kartu layar akhir disembunyikan Kartu layar akhir ditampilkan - - Bilah saring - Sembunyikan atau tampilkan bilah filter di feed, pencarian, dan video terkait - Sembunyikan di feed - Sembunyikan di feed - Tampilkan di feed - Sembunyikan di pencarian - Disembunyikan di pencarian - Ditampilkan di pencarian - Sembunyikan di video terkait - Disembunyikan di video terkait - Ditampilkan di video terkait - - - Sembunyikan tombol mikrofon mengambang - Tombol mikrofon disembunyikan - Tombol mikrofon ditampilkan - Nonaktifkan mode ambien di layar penuh Mide ambien dinonaktifkan @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Seekbar thumbnail ditampilkan + Pemutar Short + Sembunyikan atau tampilkan komponen di pemutar Shorts Sembunyikan Shorts di feed beranda Shorts di feed beranda disembunyikan @@ -644,6 +630,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Sembunyikan tombol layar hijau Tombol layar hijau disembunyikan Tombol layar hijau ditampilkan + Sembunyikan tombol tagar + Tombol tagar disembunyikan + Tombol tagar ditampilkan Sembunyikan saran penelusuran Saran penelusuran disembunyikan Saran penelusuran ditampilkan @@ -965,18 +954,34 @@ This is because Crowdin requires temporarily flattening this file and removing t Tetapkan halaman awal Bawaan + Jelajahi saluran Jelajahi + Permainan Riwayat + Pustaka Video yang disukai + Siaran langsunng + Film + Musik Pencarian + Olahraga Langganan Sedang tren + Tonton nanti Matikan melanjutkan pemutar video Shorts Pemutaran Shorts tidak akan dilanjutkan saat aplikasi dimulai Pemutaran Shorts akan dilanjutkan saat aplikasi dimulai + + Putar otomatis Shorts + Shorts akan diputar otomatis + Shorts akan diulangi + Putar otomatis Shorts di latar belakang + Pemutaran latar belakang Shorts akan diputar otomatis + Pemutaran latar belakang Shorts akan diulangi + Aktifkan tata letak tablet Tata letak tablet diaktifkan @@ -993,7 +998,20 @@ This is because Crowdin requires temporarily flattening this file and removing t Modern 1 Modern 2 Modern 3 + Aktifkan sudut membulat + Sudutnya membulat + Sudutnya persegi + Aktifkan ketuk dua kali dan cubit untuk mengubah ukuran + Tindakan ketuk dua kali dan cubit untuk mengubah ukuran diaktifkan\n\n• Ketuk dua kali untuk menambah ukuran miniplayer\n• Ketuk dua kali lagi untuk mengembalikan ukuran asli + Tindakan ketuk dua kali dan cubit untuk mengubah ukuran dinonaktifkan + Aktifkan seret dan lepas + Seret dan lepas diaktifkan\n\nMiniplayer dapat diseret ke sudut mana pun di layar + Seret dan lepas dinonaktifkan + Sembunyikan tombol tutup + Tombol tutup disembunyikan + Tombol tutup ditampilkan Sembunyikan perbesar dan tutup + Tombol disembunyikan\n\nGeser untuk memperluas atau menutup Tombol bentang dan tutup ditampilkan Sembunyikan subteks Subteks disembunyikan @@ -1001,6 +1019,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Sembunyikan percepat dan kembali Kembali dan percepat disembunyikan Kembali dan percepat ditampilkan + Ukuran awal + Awal pada ukuran layar, dalam piksel + Ukuran piksel harus antara %1$s dan %2$s Kelegapan hamparan Nilai opasitas antara 0-100, di mana 0 adalah transparan Opasiti overlay miniplayer harus antara 0-100 @@ -1016,6 +1037,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Warna bilah pencarian asli ditampilkan Warna seekbar kustom Warna dari seekbar + Nilai warna seekbar tidak sah Abaikan pembatasan wilayah gambar @@ -1033,19 +1055,19 @@ This is because Crowdin requires temporarily flattening this file and removing t Hasil pencarian Thumbnail asli DeArrow & Thumbnail asli - DeArrow & Gambar diam + DeArrow & Tangkapan diam Tangkapan diam - DeArrow menyediakan thumbnail yang dibuat oleh banyak orang untuk video YouTube. Thumbnail ini seringkali lebih relevan daripada yang disediakan oleh YouTube\n\nJika dinyalakan, URL video akan dikirim ke server API dan tidak ada data lain yang dikirim. Jika video tidak memiliki thumbnail DeArrow, maka rekaman asli atau gambar diam akan ditampilkan\n\nKetuk di sini untuk mempelajari lebih lanjut tentang DeArrow + DeArrow menyediakan thumbnail yang dibuat oleh banyak orang untuk video YouTube. Thumbnail-thumbnail ini seringkali lebih relevan daripada yang disediakan oleh YouTube\n\nJika dinyalakan, URL video akan dikirim ke server API dan tidak ada data lain yang dikirim. Jika video tidak memiliki thumbnail DeArrow, maka gambar asli atau tangkapan diam akan ditampilkan\n\nKetuk di sini untuk mempelajari lebih lanjut tentang DeArrow Tampilkan pemberitahuan halus jika API tidak tersedia Pemberitahuan halus ditampilkan jika DeArrow tidak tersedia Pemberitahuan halus tidak ditampilkan jika DeArrow tidak tersedia Titik akhir API DeArrow URL titik akhir cache thumbnail DeArrow Tangkapan video diam - Tangkapan gambar diam diambil dari awal/tengah/akhir setiap video. Gambar-gambar ini dibuat di YouTube dan tidak ada API eksternal yang digunakan + Tangkapan diam diambil dari awal/tengah/akhir setiap video. Gambar-gambar ini dibuat di YouTube dan tidak ada API eksternal yang digunakan Gunakan tangkapan diam cepat - Menggunakan tangkapan kualitas sedang. Gambar mini akan dimuat lebih cepat, tetapi siaran langsung, video yang belum dirilis, atau video yang sangat lama mungkin menampilkan gambar mini kosong - Menggunakan gambar diam berkualitas tinggi + Menggunakan tangkapan diam kualitas sedang. Thumbnail akan dimuat lebih cepat, tetapi siaran langsung, video yang belum dirilis, atau video yang sangat lama mungkin menampilkan thumbnail kosong + Menggunakan tangkapan diam berkualitas tinggi Lama waktu menangkap layar video Awal video Pertengahan video diff --git a/src/main/resources/addresources/values-is-rIS/strings.xml b/src/main/resources/addresources/values-is-rIS/strings.xml index 2d7cfe1e72..c1496db437 100644 --- a/src/main/resources/addresources/values-is-rIS/strings.xml +++ b/src/main/resources/addresources/values-is-rIS/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-it-rIT/strings.xml b/src/main/resources/addresources/values-it-rIT/strings.xml index 0a1628615e..164255268d 100644 --- a/src/main/resources/addresources/values-it-rIT/strings.xml +++ b/src/main/resources/addresources/values-it-rIT/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Disabilita il bagliore del pulsante di / sottoscrizione Come e il pulsante di sottoscrizione non brillerà quando menzionato Come e il pulsante di sottoscrizione brillerà quando menzionato + Nascondi schede album + Le schede degli album sono nascoste + Le schede degli album sono mostrate + Nascondi box crowdfunding + Crowdfunding box è nascosto + Il Crowdfunding box è mostrato + Nascondi il pulsante del microfono fluttuante + Pulsante microfono nascosto + Pulsante microfono mostrato Nascondi il separatore grigio I separatori grigi sono nascosti I separatori grigi sono visibili @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t La sezione della trascrizione è mostrata Descrizione video Nascondi o mostra i componenti della descrizione video + Barra dei filtri + Nascondi o mostra la barra dei filtri nel feed, nella ricerca e nei video correlati + Nascondi nel feed + Nascosto nel feed + Mostrato nel feed + Nascondi nella ricerca + Nascosto nella ricerca + Mostrato nella ricerca + Nascondi nei video correlati + Nascosto nei video correlati + Mostrato in video correlati + Commenti + Nascondi o mostra i componenti della sezione commenti + Nascondi l\'intestazione \'Commenti dai membri\' + \'Commenti dei membri\' intestazione è nascosta + L\'intestazione \'Commenti dei membri\' è mostrata + Nascondi sezione commenti + La sezione commenti è nascosta + La sezione Commenti è mostrata + Nascondi il pulsante \'Crea un Short\' + Il pulsante \'Crea uno Short\' è nascosto + Il pulsante \'Crea uno Short\' è visibile + Nascondi commento anteprima + Il commento nell\'anteprima è nascosto + Anteprima commento mostrata + Nascondi pulsante grazie + Grazie pulsante è nascosto + Il pulsante di ringraziamento è mostrato + Nascondi i pulsanti timestamp ed emoji + I pulsanti Timestamp ed emoji sono nascosti + I pulsanti Timestamp ed emoji sono visibili Nascondi Doodles Di YouTube Barra di ricerca Doodles sono nascosti @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Il pulsante Salva nella playlist è nascosto Il pulsante Salva nella playlist è mostrato - - Nascondi pulsante autoplay - Il pulsante Autoplay è nascosto - Il pulsante Autoplay è mostrato - - - - Nascondi il pulsante didascalie - Il pulsante sottotitoli è nascosto - Il pulsante sottotitoli è mostrato - - - Nascondi pulsante cast - Il pulsante Trasmetti è nascosto - Il pulsante Trasmetti è visibile - Navigation buttons Nascondi o cambia i pulsanti nella barra di navigazione @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Guarda nel menu VR è nascosto Guarda nel menu VR - - Nascondi i pulsanti video precedenti & - I pulsanti sono nascosti - I pulsanti sono mostrati - - - Nascondi schede album - Le schede degli album sono nascoste - Le schede degli album sono mostrate - - - Commenti - Nascondi o mostra i componenti della sezione commenti - Nascondi l\'intestazione \'Commenti dai membri\' - \'Commenti dei membri\' intestazione è nascosta - L\'intestazione \'Commenti dei membri\' è mostrata - Nascondi sezione commenti - La sezione commenti è nascosta - La sezione Commenti è mostrata - Nascondi il pulsante \'Crea un Short\' - Il pulsante \'Crea uno Short\' è nascosto - Il pulsante \'Crea uno Short\' è visibile - Nascondi commento anteprima - Il commento nell\'anteprima è nascosto - Anteprima commento mostrata - Nascondi pulsante grazie - Grazie pulsante è nascosto - Il pulsante di ringraziamento è mostrato - Nascondi i pulsanti timestamp ed emoji - I pulsanti Timestamp ed emoji sono nascosti - I pulsanti Timestamp ed emoji sono visibili - - - Nascondi box crowdfunding - Crowdfunding box è nascosto - Il Crowdfunding box è mostrato + + Nascondi i pulsanti video precedenti & + I pulsanti sono nascosti + I pulsanti sono mostrati + Nascondi pulsante cast + Il pulsante Trasmetti è nascosto + Il pulsante Trasmetti è visibile + + Nascondi il pulsante didascalie + Il pulsante sottotitoli è nascosto + Il pulsante sottotitoli è mostrato + Nascondi pulsante autoplay + Il pulsante Autoplay è nascosto + Il pulsante Autoplay è mostrato Nascondi schede di fine schermo Le schede di fine schermo sono nascoste Vengono mostrate le schede di fine schermo - - Barra dei filtri - Nascondi o mostra la barra dei filtri nel feed, nella ricerca e nei video correlati - Nascondi nel feed - Nascosto nel feed - Mostrato nel feed - Nascondi nella ricerca - Nascosto nella ricerca - Mostrato nella ricerca - Nascondi nei video correlati - Nascosto nei video correlati - Mostrato in video correlati - - - Nascondi il pulsante del microfono fluttuante - Pulsante microfono nascosto - Pulsante microfono mostrato - Disabilita la modalità ambiente a schermo intero Modalità ambiente disabilitata @@ -598,6 +582,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Barra di ricerca miniature mostrata + Giocatore di Shorts Nascondi Shorts nella scheda Home Pantaloncini nel feed domestico sono nascosti @@ -988,6 +973,12 @@ This is because Crowdin requires temporarily flattening this file and removing t Il giocatore Shorts non riprenderà all\'avvio dell\'app Il giocatore Shorts riprenderà all\'avvio dell\'app + + Shorts saranno autoplay + Shorts si ripeterà + Riproduci automaticamente sfondo Shorts + Riproduzione sfondo Shorts si ripeterà + Abilita disposizione tablet Disposizione tablet abilitata diff --git a/src/main/resources/addresources/values-iw-rIL/strings.xml b/src/main/resources/addresources/values-iw-rIL/strings.xml index 58ee6e5df9..57877b361a 100644 --- a/src/main/resources/addresources/values-iw-rIL/strings.xml +++ b/src/main/resources/addresources/values-iw-rIL/strings.xml @@ -99,15 +99,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - לחצן שידור מסך מוסתר - לחצן שידור מסך מוצג - @@ -128,20 +119,13 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + לחצן שידור מסך מוסתר + לחצן שידור מסך מוצג + - - - - @@ -244,6 +228,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-ja-rJP/strings.xml b/src/main/resources/addresources/values-ja-rJP/strings.xml index ef2f99d840..8301261729 100644 --- a/src/main/resources/addresources/values-ja-rJP/strings.xml +++ b/src/main/resources/addresources/values-ja-rJP/strings.xml @@ -105,6 +105,15 @@ This is because Crowdin requires temporarily flattening this file and removing t 高評価 / チャンネル登録ボタンのアニメーションを無効にする 「高評価」と「チャンネル登録」ボタンのアニメーションは無効です 「高評価」と「チャンネル登録」ボタンのアニメーションは有効です + アルバムカードを隠す + アルバムカードは非表示です + アルバムカードは表示されます + クラウドファンディングボックスを非表示 + クラウドファンディングボックスは非表示です + クラウドファンディングボックスは表示されます + 音声入力のフローティングボタンを非表示 + マイクボタンは非表示です + マイクボタンは表示されます 灰色のセパレータを非表示 灰色のセパレータは非表示です 灰色のセパレータは表示されます @@ -229,6 +238,37 @@ This is because Crowdin requires temporarily flattening this file and removing t 文字起こしセクションは表示されます 概要欄 概要欄のコンポーネントを非表示または表示 + フィルタバー + フィード、検索、および関連する動画のフィルターバーを非表示または表示 + フィードで非表示 + フィードで非表示です + フィードでは表示されます + 検索時に隠す + 検索で非表示です + 検索で表示されます + 関連動画で非表示 + 関連動画で非表示です + 関連動画で表示されます + コメント + コメントセクションのコンポーネントを非表示または表示 + 「メンバーによるコメント」ヘッダーを非表示 + 「メンバーによるコメント」ヘッダーは非表示です + 「メンバーによるコメント」ヘッダーは表示されています + コメントセクションを非表示 + コメントセクションは非表示です + コメントセクションは表示されます + 「Shortsを作成」ボタンを非表示 + 「Shortsを作成」ボタンは非表示です + 「Shorsを作成」ボタンは表示されます + コメントのプレビューを非表示 + コメントのプレビューは非表示です + コメントのプレビューは表示されます + Thanks ボタンを非表示 + 「Thanks」ボタンは非表示です + 「Thanks」ボタンは表示されます + タイムスタンプと絵文字ボタンを隠す + タイムスタンプと絵文字ボタンは非表示です + タイムスタンプと絵文字ボタンは表示されます YouTubeのDoodlesを隠す 検索バーの落書きは非表示です @@ -423,22 +463,6 @@ This is because Crowdin requires temporarily flattening this file and removing t プレイリストに保存ボタンは非表示です プレイリストに保存ボタンは表示されます - - 自動再生ボタンを隠す - 自動再生ボタンは非表示です - 自動再生ボタンは表示されます - - - - 字幕ボタンを隠す - 字幕ボタンは非表示です - 字幕ボタンが表示されます - - - キャストボタンを隠す - キャスト ボタンは非表示です - キャスト ボタンは表示されます - ナビゲーションボタン ナビゲーションバーのボタンを非表示または変更 @@ -511,66 +535,26 @@ This is because Crowdin requires temporarily flattening this file and removing t VRで見るメニューは非表示です VR で見るメニューは表示されます - - 前の動画に戻る/次の動画に進むボタンを非表示 - ボタンは非表示です - ボタンは表示されます - - - アルバムカードを隠す - アルバムカードは非表示です - アルバムカードは表示されます - - - コメント - コメントセクションのコンポーネントを非表示または表示 - 「メンバーによるコメント」ヘッダーを非表示 - 「メンバーによるコメント」ヘッダーは非表示です - 「メンバーによるコメント」ヘッダーは表示されています - コメントセクションを非表示 - コメントセクションは非表示です - コメントセクションは表示されます - 「Shortsを作成」ボタンを非表示 - 「Shortsを作成」ボタンは非表示です - 「Shorsを作成」ボタンは表示されます - コメントのプレビューを非表示 - コメントのプレビューは非表示です - コメントのプレビューは表示されます - Thanks ボタンを非表示 - 「Thanks」ボタンは非表示です - 「Thanks」ボタンは表示されます - タイムスタンプと絵文字ボタンを隠す - タイムスタンプと絵文字ボタンは非表示です - タイムスタンプと絵文字ボタンは表示されます - - - クラウドファンディングボックスを非表示 - クラウドファンディングボックスは非表示です - クラウドファンディングボックスは表示されます + + 前の動画に戻る/次の動画に進むボタンを非表示 + ボタンは非表示です + ボタンは表示されます + キャストボタンを隠す + キャスト ボタンは非表示です + キャスト ボタンは表示されます + + 字幕ボタンを隠す + 字幕ボタンは非表示です + 字幕ボタンが表示されます + 自動再生ボタンを隠す + 自動再生ボタンは非表示です + 自動再生ボタンは表示されます 終了画面のカードを非表示にする 終了画面のカードは非表示です 終了画面のカードは表示されます - - フィルタバー - フィード、検索、および関連する動画のフィルターバーを非表示または表示 - フィードで非表示 - フィードで非表示です - フィードでは表示されます - 検索時に隠す - 検索で非表示です - 検索で表示されます - 関連動画で非表示 - 関連動画で非表示です - 関連動画で表示されます - - - 音声入力のフローティングボタンを非表示 - マイクボタンは非表示です - マイクボタンは表示されます - 全画面表示でアンビエントモードを無効にする アンビエントモードは無効です @@ -595,6 +579,8 @@ This is because Crowdin requires temporarily flattening this file and removing t サムネイルシークバーが表示されます + Shortsプレイヤー + Shorts プレーヤーのコンポーネントを非表示または表示 ホームフィードにショートパンツを隠す ホームフィード内のショートパンツが表示されません @@ -985,6 +971,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts プレイヤーはアプリの起動時に再開しません Shorts プレイヤーはアプリの起動時に再開します + + タブレットのレイアウトを有効にする タブレットのレイアウトは有効です diff --git a/src/main/resources/addresources/values-ka-rGE/strings.xml b/src/main/resources/addresources/values-ka-rGE/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-ka-rGE/strings.xml +++ b/src/main/resources/addresources/values-ka-rGE/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-kk-rKZ/strings.xml b/src/main/resources/addresources/values-kk-rKZ/strings.xml index 942ca5b502..d092bfcbe6 100644 --- a/src/main/resources/addresources/values-kk-rKZ/strings.xml +++ b/src/main/resources/addresources/values-kk-rKZ/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-km-rKH/strings.xml b/src/main/resources/addresources/values-km-rKH/strings.xml index 4ed6c04386..a9daf88fa4 100644 --- a/src/main/resources/addresources/values-km-rKH/strings.xml +++ b/src/main/resources/addresources/values-km-rKH/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-kn-rIN/strings.xml b/src/main/resources/addresources/values-kn-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-kn-rIN/strings.xml +++ b/src/main/resources/addresources/values-kn-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-ko-rKR/strings.xml b/src/main/resources/addresources/values-ko-rKR/strings.xml index 3fdc87c402..bc8089aa74 100644 --- a/src/main/resources/addresources/values-ko-rKR/strings.xml +++ b/src/main/resources/addresources/values-ko-rKR/strings.xml @@ -96,8 +96,8 @@ This is because Crowdin requires temporarily flattening this file and removing t 디버그 로그에 프로토콜 버퍼를 포함합니다 디버그 로그에 프로토콜 버퍼를 포함하지 않습니다 로그 스택 트레이스 - 디버그 로그에 로그 스택 트레이스을 포함합니다 - 디버그 로그에 로그 스택 트레이스을 포함하지 않습니다 + 디버그 로그에 로그 스택 트레이스를 포함합니다 + 디버그 로그에 로그 스택 트레이스를 포함하지 않습니다 ReVanced 오류 팝업 메시지 표시하기 오류가 발생하면 팝업 메시지를 표시합니다 오류가 발생하면 팝업 메시지를 표시하지 않습니다 @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t 빛나는 \'좋아요\' / \'구독\' 버튼 비활성화하기 동영상에서 \'Like (좋아요)\' 또는 \'Subscribe (구독)\' 버튼이 언급되었을 때, 해당 버튼에 빛나는 애니메이션을 적용하지 않습니다\n• 일부 언어는 아직 지원되지 않습니다 동영상에서 \'Like (좋아요)\' 또는 \'Subscribe (구독)\' 버튼이 언급되었을 때, 해당 버튼에 빛나는 애니메이션을 적용합니다\n• 일부 언어는 아직 지원되지 않습니다 + 음악 앨범 카드 숨기기 + 검색 결과에서 음악 앨범 카드가 숨겨집니다 + 검색 결과에서 음악 앨범 카드가 표시됩니다 + 크라우드 펀딩 박스 숨기기 + 플레이어 하단에서 크라우드 펀딩 박스가 숨겨집니다 + 플레이어 하단에서 크라우드 펀딩 박스가 표시됩니다 + 플로팅 마이크 버튼 숨기기 + 플로팅 마이크 버튼이 숨겨집니다 + 플로팅 마이크 버튼이 표시됩니다 회색 구분선 숨기기 동영상들 사이에서 회색 구분선이 숨겨집니다 동영상들 사이에서 회색 구분선이 표시됩니다 @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t 스크립트 섹션이 표시됩니다 동영상 설명 동영상 설명에서 구성요소를 숨기거나 표시할 수 있습니다 + 카테고리 바 + 피드, 검색 결과, 관련 동영상에서 카테고리 바를 숨기거나 표시할 수 있습니다 + 피드에서 카테고리 바 숨기기 + 피드에서 카테고리 바가 숨겨집니다 + 피드에서 카테고리 바가 표시됩니다 + 검색 결과에서 카테고리 바 숨기기 + 검색 결과에서 카테고리 바가 숨겨집니다 + 검색 결과에서 카테고리 바가 표시됩니다 + 관련 동영상에서 카테고리 바 숨기기 + 플레이어 하단에 있는 관련 동영상에서 카테고리 바가 숨겨집니다 + 플레이어 하단에 있는 관련 동영상에서 카테고리 바가 표시됩니다 + 댓글 + 댓글 섹션에서 구성요소가 숨기거나 표시할 수 있습니다 + \'회원별 댓글\' 헤더 숨기기 + \'회원별 댓글\' 헤더가 숨겨집니다 + \'회원별 댓글\' 헤더가 표시됩니다 + 댓글 섹션 숨기기 + 댓글 섹션이 숨겨집니다 + 댓글 섹션이 표시됩니다 + \'Shorts 만들기\' 버튼 숨기기 + \'Shorts 만들기\' 버튼이 숨겨집니다 + \'Shorts 만들기\' 버튼이 표시됩니다 + 댓글 미리보기 숨기기 + 댓글 미리보기가 숨겨집니다 + 댓글 미리보기가 표시됩니다 + Thanks 버튼 숨기기 + Thanks 버튼이 숨겨집니다 + Thanks 버튼이 표시됩니다 + 타임스탬프 & 이모지 버튼 숨기기 + 타임스탬프 & 이모지 버튼이 숨겨집니다 + 타임스탬프 & 이모지 버튼이 표시됩니다 YouTube Doodles 숨기기 YouTube Doodles가 숨겨집니다\n• 이벤트성 YouTube 헤더 @@ -301,9 +341,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 웹 검색 결과 숨기기 웹 검색 결과가 숨겨집니다 웹 검색 결과가 표시됩니다 - 태그된 제품 선반 숨기기 - 태그된 제품 선반이 숨겨집니다 - 태그된 제품 선반이 표시됩니다 + 제품 쇼핑 선반 숨기기 + 제품 쇼핑 선반이 숨겨집니다 + 제품 쇼핑 선반이 표시됩니다 \'전체 화면 광고 숨기기\'는 구형 기기에서만 사용할 수 있습니다 @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t (재생목록에) 저장 버튼이 숨겨집니다 (재생목록에) 저장 버튼이 표시됩니다 - - 자동재생 버튼 숨기기 - 자동재생 버튼이 숨겨집니다 - 자동재생 버튼이 표시됩니다 - - - - 자막 버튼 숨기기 - 자막 버튼이 숨겨집니다 - 자막 버튼이 표시됩니다 - - - 크롬캐스트 버튼 숨기기 - 크롬캐스트 버튼이 숨겨집니다 - 크롬캐스트 버튼이 표시됩니다 - 하단바 버튼 하단바에서 버튼을 숨기거나 변경할 수 있습니다 @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t VR로 보기 메뉴가 숨겨집니다 VR로 보기 메뉴가 표시됩니다 - - 이전 & 다음 동영상 버튼 숨기기 - 이전 & 다음 동영상 버튼이 숨겨집니다 - 이전 & 다음 동영상 버튼이 표시됩니다 - - - 음악 앨범 카드 숨기기 - 검색 결과에서 음악 앨범 카드가 숨겨집니다 - 검색 결과에서 음악 앨범 카드가 표시됩니다 - - - 댓글 - 댓글 섹션에서 구성요소가 숨기거나 표시할 수 있습니다 - \'회원별 댓글\' 헤더 숨기기 - \'회원별 댓글\' 헤더가 숨겨집니다 - \'회원별 댓글\' 헤더가 표시됩니다 - 댓글 섹션 숨기기 - 댓글 섹션이 숨겨집니다 - 댓글 섹션이 표시됩니다 - \'Shorts 만들기\' 버튼 숨기기 - \'Shorts 만들기\' 버튼이 숨겨집니다 - \'Shorts 만들기\' 버튼이 표시됩니다 - 댓글 미리보기 숨기기 - 댓글 미리보기가 숨겨집니다 - 댓글 미리보기가 표시됩니다 - Thanks 버튼 숨기기 - Thanks 버튼이 숨겨집니다 - Thanks 버튼이 표시됩니다 - 타임스탬프 & 이모지 버튼 숨기기 - 타임스탬프 & 이모지 버튼이 숨겨집니다 - 타임스탬프 & 이모지 버튼이 표시됩니다 - - - 크라우드 펀딩 박스 숨기기 - 플레이어 하단에서 크라우드 펀딩 박스가 숨겨집니다 - 플레이어 하단에서 크라우드 펀딩 박스가 표시됩니다 + + 이전 & 다음 동영상 버튼 숨기기 + 이전 & 다음 동영상 버튼이 숨겨집니다 + 이전 & 다음 동영상 버튼이 표시됩니다 + 크롬캐스트 버튼 숨기기 + 크롬캐스트 버튼이 숨겨집니다 + 크롬캐스트 버튼이 표시됩니다 + + 자막 버튼 숨기기 + 자막 버튼이 숨겨집니다 + 자막 버튼이 표시됩니다 + 자동재생 버튼 숨기기 + 자동재생 버튼이 숨겨집니다 + 자동재생 버튼이 표시됩니다 최종 화면 카드 숨기기 최종 화면 카드가 숨겨집니다 최종 화면 카드가 표시됩니다 - - 카테고리 바 - 피드, 검색 결과, 관련 동영상에서 카테고리 바를 숨기거나 표시할 수 있습니다 - 피드에서 카테고리 바 숨기기 - 피드에서 카테고리 바가 숨겨집니다 - 피드에서 카테고리 바가 표시됩니다 - 검색 결과에서 카테고리 바 숨기기 - 검색 결과에서 카테고리 바가 숨겨집니다 - 검색 결과에서 카테고리 바가 표시됩니다 - 관련 동영상에서 카테고리 바 숨기기 - 플레이어 하단에 있는 관련 동영상에서 카테고리 바가 숨겨집니다 - 플레이어 하단에 있는 관련 동영상에서 카테고리 바가 표시됩니다 - - - 플로팅 마이크 버튼 숨기기 - 플로팅 마이크 버튼이 숨겨집니다 - 플로팅 마이크 버튼이 표시됩니다 - 전체 화면에서 앰비언트 모드 비활성화하기 앰비언트 모드를 비활성화합니다 @@ -599,6 +583,8 @@ This is because Crowdin requires temporarily flattening this file and removing t 썸네일 재생바가 표시됩니다 + Shorts 플레이어 + Shorts 플레이어에서 구성요소를 숨기거나 표시할 수 있습니다 홈 피드에서 Shorts 선반 숨기기 홈 피드에서 Shorts 선반이 숨겨집니다 @@ -968,20 +954,20 @@ This is because Crowdin requires temporarily flattening this file and removing t 앱 시작 페이지 변경하기 - SponsorBlock 활성화 + 홈 (기본값) 채널 둘러보기 - 플레이어에서 구간 투표 버튼을 표시합니다 + 탐색 게임 - 일반적인 건너뛰기 버튼을 표시합니다 - 나 (보관함) - 최소화된 건너뛰기 버튼을 표시합니다 + 기록 + 내 페이지 + 좋아요 표시한 동영상 실시간 영화 음악 - 외관 + 검색 스포츠 - 투표 버튼 표시하기 - 자동으로 건너뛰기 버튼 숨기기 + 구독 + 인기 급상승 나중에 볼 동영상 @@ -989,6 +975,14 @@ This is because Crowdin requires temporarily flattening this file and removing t 앱을 시작할 때, Shorts 플레이어를 다시 실행하지 않습니다 앱을 시작할 때, Shorts 플레이어를 다시 실행합니다 + + Shorts 자동재생 + Shorts 동영상이 자동재생됩니다 + Shorts 동영상이 반복재생됩니다 + Shorts 자동 백그라운드 제생 + Shorts 동영상 백그라운드 재생이 자동재생됩니다 + Shorts 동영상 백그라운드 재생이 반복재생됩니다 + 태블릿 레이아웃 활성화하기 일부 레이아웃을 태블릿 레이아웃으로 활성화합니다 @@ -1012,17 +1006,17 @@ This is because Crowdin requires temporarily flattening this file and removing t 두 번 누르기 동작 및 핀치하여 크기 조정을 활성화합니다\n\n• 두 번 눌러서 미니 플레이어 크기를 확대합니다\n• 다시 두 번 눌러서 원래 크기로 복원합니다 두 번 누르기 동작 및 핀치하여 크기 조정을 비활성화합니다 드래그 & 드롭 활성화하기 - 드래그 & 드롭이 활성화합니다\n\n미니 플레이어를 화면의 어느 곳이든 드래그할 수 있습니다 + 드래그 & 드롭을 활성화합니다\n\n• 미니 플레이어를 화면의 어느 곳이든 드래그할 수 있습니다 드래그 & 드롭을 비활성화합니다 닫기 버튼 숨기기 닫기 버튼이 숨겨집니다 닫기 버튼이 표시됩니다 \'펼치기\' & \'닫기\' 버튼 숨기기 - 버튼들이 숨겨집니다\n\n스와이프하여 미니 플레이어를 펼치거나 닫을 수 있습니다 + \'펼치기\' & \'닫기\' 버튼이 숨겨집니다\n\n• 스와이프하여 미니 플레이어를 펼치거나 닫을 수 있습니다 \'펼치기\' & \'닫기\' 버튼이 표시됩니다 서브텍스트 숨기기 - 서브텍스트가 숨겨집니다\n• 왼쪽 하단에서 표시되는 \'유료 광고 포함\'과 같은 라벨 - 서브텍스트가 표시됩니다\n• 왼쪽 하단에서 표시되는 \'유료 광고 포함\'과 같은 라벨 + 서브텍스트가 숨겨집니다\n\n• 왼쪽 하단에서 표시되는 \'유료 광고 포함\'과 같은 라벨 + 서브텍스트가 표시됩니다\n\n• 왼쪽 하단에서 표시되는 \'유료 광고 포함\'과 같은 라벨 \'되감기\' & \'빨리 감기\' 버튼 숨기기 \'되감기\' & \'빨리 감기\' 버튼이 숨겨집니다 \'되감기\' & \'빨리 감기\' 버튼이 표시됩니다 diff --git a/src/main/resources/addresources/values-ky-rKG/strings.xml b/src/main/resources/addresources/values-ky-rKG/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-ky-rKG/strings.xml +++ b/src/main/resources/addresources/values-ky-rKG/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-lo-rLA/strings.xml b/src/main/resources/addresources/values-lo-rLA/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-lo-rLA/strings.xml +++ b/src/main/resources/addresources/values-lo-rLA/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-lt-rLT/strings.xml b/src/main/resources/addresources/values-lt-rLT/strings.xml index 4f75f9866d..affb5c8c0b 100644 --- a/src/main/resources/addresources/values-lt-rLT/strings.xml +++ b/src/main/resources/addresources/values-lt-rLT/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -188,6 +172,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-lv-rLV/strings.xml b/src/main/resources/addresources/values-lv-rLV/strings.xml index d3f8eb875f..79e4ecd5d2 100644 --- a/src/main/resources/addresources/values-lv-rLV/strings.xml +++ b/src/main/resources/addresources/values-lv-rLV/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -185,6 +169,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-mk-rMK/strings.xml b/src/main/resources/addresources/values-mk-rMK/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-mk-rMK/strings.xml +++ b/src/main/resources/addresources/values-mk-rMK/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-ml-rIN/strings.xml b/src/main/resources/addresources/values-ml-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-ml-rIN/strings.xml +++ b/src/main/resources/addresources/values-ml-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-mn-rMN/strings.xml b/src/main/resources/addresources/values-mn-rMN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-mn-rMN/strings.xml +++ b/src/main/resources/addresources/values-mn-rMN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-mr-rIN/strings.xml b/src/main/resources/addresources/values-mr-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-mr-rIN/strings.xml +++ b/src/main/resources/addresources/values-mr-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-ms-rMY/strings.xml b/src/main/resources/addresources/values-ms-rMY/strings.xml index 66dc6696bb..0fbfbbef30 100644 --- a/src/main/resources/addresources/values-ms-rMY/strings.xml +++ b/src/main/resources/addresources/values-ms-rMY/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-my-rMM/strings.xml b/src/main/resources/addresources/values-my-rMM/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-my-rMM/strings.xml +++ b/src/main/resources/addresources/values-my-rMM/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-nb-rNO/strings.xml b/src/main/resources/addresources/values-nb-rNO/strings.xml index a31f921319..7ce357a893 100644 --- a/src/main/resources/addresources/values-nb-rNO/strings.xml +++ b/src/main/resources/addresources/values-nb-rNO/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktiver \'liker\' / abonnér på glød Som og abonner-knappen vil ikke glød når den nevnes Som og abbonér vil glød når det nevnes + Skjul albumkort + Albumkort er skjult + Albumkort vises + Skjul crowdfunding boks + Crowdfunding box er skjult + Det er vist dekningsboks + Skjul flytende mikrofonknapp + Mikrofonknappen er skjult + Mikrofon knapp vist Skjul grå skilletegn Grå skilletegn er skjult Grå skilletegn vises @@ -227,6 +236,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Transskripsjonsseksjonen vises Beskrivelse av videoen Skjul eller vis video-beskrivelse komponenter + Filtrer bar + Skjul eller vis filterlinjen i matingen, søk og relaterte videoer + Skjul i feed + Skjult i feed + Vist i feed + Skjul i søk + Skjult i søk + Vist i søk + Skjul i relaterte videoer + Skjult i relaterte videoer + Vises i relaterte videoer + Kommentarer + Skjul eller vis kommentar-deler komponenter + Skjul \'Kommentarer etter medlemmer\' topptekst + \'Kommentarer av medlemmer\' topptekst er skjult + \'Kommentarer etter medlemmer\' topptekst er vist + Skjul kommentarfeltet + Kommentarer seksjonen er skjult + Kommentarer er vist + Skjul \"Lag en kort\"-knapp + \'Opprett en kort\' knapp er skjult + \'Opprett en kort\' knapp vises + Skjul forhåndsvisningskommentar + Forhåndsvisningskommentaren er skjult + Forhåndsvisningskommentar vises + Skjul takks-knappen + Tusen takk er skjult + Tusen takk vises + Skjul knapper for tidsstempel og emoji + Tidsstempel og emoji-knapper er skjult + Det vises tidsstempel og emoji-knapper Skjul YouTube dører Søk i barnerøvinger er skjult @@ -422,22 +462,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Lagre i spilleliste-knappen er skjult Lagre i spilleliste-knappen vises - - Skjul automatisk avspillingsknapp - Autospill-knappen er skjult - Autospill-knappen vises - - - - Skjul undertekst-knappen - Tekstknappen er skjult - Tekst-knappen vises - - - Skjul kast knapp - Støp-knappen er skjult - Oversiktsknappen er vist - Navigation buttons Skjul eller endre knapper i navigasjonslinjen @@ -510,66 +534,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Se i VR-menyen er skjult Se i VR-meny for å vise - - Skjul forrige & neste videoknapper - Knappene er skjult - Det vises knapper - - - Skjul albumkort - Albumkort er skjult - Albumkort vises - - - Kommentarer - Skjul eller vis kommentar-deler komponenter - Skjul \'Kommentarer etter medlemmer\' topptekst - \'Kommentarer av medlemmer\' topptekst er skjult - \'Kommentarer etter medlemmer\' topptekst er vist - Skjul kommentarfeltet - Kommentarer seksjonen er skjult - Kommentarer er vist - Skjul \"Lag en kort\"-knapp - \'Opprett en kort\' knapp er skjult - \'Opprett en kort\' knapp vises - Skjul forhåndsvisningskommentar - Forhåndsvisningskommentaren er skjult - Forhåndsvisningskommentar vises - Skjul takks-knappen - Tusen takk er skjult - Tusen takk vises - Skjul knapper for tidsstempel og emoji - Tidsstempel og emoji-knapper er skjult - Det vises tidsstempel og emoji-knapper - - - Skjul crowdfunding boks - Crowdfunding box er skjult - Det er vist dekningsboks + + Skjul forrige & neste videoknapper + Knappene er skjult + Det vises knapper + Skjul kast knapp + Støp-knappen er skjult + Oversiktsknappen er vist + + Skjul undertekst-knappen + Tekstknappen er skjult + Tekst-knappen vises + Skjul automatisk avspillingsknapp + Autospill-knappen er skjult + Autospill-knappen vises Skjul avsluttende skjerm-kort Sluttskjerm kort er skjult Sluttskjerm vises - - Filtrer bar - Skjul eller vis filterlinjen i matingen, søk og relaterte videoer - Skjul i feed - Skjult i feed - Vist i feed - Skjul i søk - Skjult i søk - Vist i søk - Skjul i relaterte videoer - Skjult i relaterte videoer - Vises i relaterte videoer - - - Skjul flytende mikrofonknapp - Mikrofonknappen er skjult - Mikrofon knapp vist - Deaktiver omgivelsesmodus i fullskjerm Ambient modus deaktivert @@ -976,6 +960,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts spiller vil ikke gjenoppta ved oppstart av app Shorts spiller vil gjenoppta ved oppstart av app + + Aktiver oppsett for nettbrett Nettbrettets oppsett er aktivert diff --git a/src/main/resources/addresources/values-ne-rIN/strings.xml b/src/main/resources/addresources/values-ne-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-ne-rIN/strings.xml +++ b/src/main/resources/addresources/values-ne-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-nl-rNL/strings.xml b/src/main/resources/addresources/values-nl-rNL/strings.xml index a051e09430..b57cdc4810 100644 --- a/src/main/resources/addresources/values-nl-rNL/strings.xml +++ b/src/main/resources/addresources/values-nl-rNL/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Uitschakelen like- / abonneer-knop gloed De knop \'like\' en \'abonneren\' zal niet gloeien wanneer deze genoemd wordt \'Like en abonneren\' knop zal gloeien wanneer genoemd + Verberg albumkaarten + Albumkaarten zijn verborgen + Albumkaarten worden weergegeven + Crowdfunding box verbergen + Crowdfunding box is verborgen + Crowdfunding box wordt getoond + Zwevende microfoon knop verbergen + Microfoon knop verborgen + Microfoon knop weergegeven Grijze scheidingsbalken verbergen Grijze scheidingsbalken zijn verborgen Grijze scheidingsbalken worden weergegeven @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Transcriptsectie wordt weergegeven Video beschrijving Verberg of toon video beschrijving componenten + Filter balk + Verberg of toon de filterbalk in de feed, zoeken, en gerelateerde video\'s + Verberg in feed + Verborgen in feed + Zichtbaar in feed + Verbergen in zoekopdracht + Verborgen in zoekopdracht + Weergegeven in zoekopdracht + Verbergen in gerelateerde video\'s + Verborgen in gerelateerde video\'s + Weergegeven in gerelateerde video\'s + Opmerkingen + Opmerkingen sectie onderdelen verbergen of weergeven + Verberg de titel \'Opmerkingen van leden\' + De header \'Reacties door leden\' is verborgen + De header \'Reacties door leden\' wordt getoond + Reacties sectie verbergen + Reacties sectie is verborgen + Reacties sectie wordt weergegeven + \'Maak een kort\' knop verbergen + \'Maak een kort\' knop is verborgen + De \'Maak een korting\' knop wordt weergegeven + Verberg commentaar op voorbeeld + Voorbeeldcommentaar is verborgen + Voorbeeld commentaar wordt weergegeven + Knop voor dank(en) verbergen + Bedankt knop is verborgen + Bedankt knop wordt weergegeven + Verberg tijdstempel en emoji-knoppen + Tijdstempel en emoji knoppen zijn verborgen + Tijdstempel en emoji-knoppen worden getoond Youtube Doodles verbergen Zoekbalk Doodsen zijn verborgen @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Opslaan naar afspeellijst knop is verborgen Opslaan naar afspeellijst knop wordt weergegeven - - Knop voor automatisch afspelen verbergen - Automatisch afspelen knop is verborgen - Automatisch afspelen knop wordt weergegeven - - - - Knop voor onderschriften verbergen - Knop voor bijschriften is verborgen - Knop voor bijschriften wordt weergegeven - - - Verberg cast knop - Cast knop is verborgen - Cast knop wordt weergegeven - Navigation buttons Verberg of wijzig knoppen in de navigatiebalk @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Bekijk in het VR-menu is verborgen Bekijk in het VR-menu - - Vorige & volgende video knoppen verbergen - Knoppen zijn verborgen - Knoppen worden weergegeven - - - Verberg albumkaarten - Albumkaarten zijn verborgen - Albumkaarten worden weergegeven - - - Opmerkingen - Opmerkingen sectie onderdelen verbergen of weergeven - Verberg de titel \'Opmerkingen van leden\' - De header \'Reacties door leden\' is verborgen - De header \'Reacties door leden\' wordt getoond - Reacties sectie verbergen - Reacties sectie is verborgen - Reacties sectie wordt weergegeven - \'Maak een kort\' knop verbergen - \'Maak een kort\' knop is verborgen - De \'Maak een korting\' knop wordt weergegeven - Verberg commentaar op voorbeeld - Voorbeeldcommentaar is verborgen - Voorbeeld commentaar wordt weergegeven - Knop voor dank(en) verbergen - Bedankt knop is verborgen - Bedankt knop wordt weergegeven - Verberg tijdstempel en emoji-knoppen - Tijdstempel en emoji knoppen zijn verborgen - Tijdstempel en emoji-knoppen worden getoond - - - Crowdfunding box verbergen - Crowdfunding box is verborgen - Crowdfunding box wordt getoond + + Vorige & volgende video knoppen verbergen + Knoppen zijn verborgen + Knoppen worden weergegeven + Verberg cast knop + Cast knop is verborgen + Cast knop wordt weergegeven + + Knop voor onderschriften verbergen + Knop voor bijschriften is verborgen + Knop voor bijschriften wordt weergegeven + Knop voor automatisch afspelen verbergen + Automatisch afspelen knop is verborgen + Automatisch afspelen knop wordt weergegeven Verberg eindschermkaarten Eindscherm kaarten zijn verborgen Eindschermkaarten worden weergegeven - - Filter balk - Verberg of toon de filterbalk in de feed, zoeken, en gerelateerde video\'s - Verberg in feed - Verborgen in feed - Zichtbaar in feed - Verbergen in zoekopdracht - Verborgen in zoekopdracht - Weergegeven in zoekopdracht - Verbergen in gerelateerde video\'s - Verborgen in gerelateerde video\'s - Weergegeven in gerelateerde video\'s - - - Zwevende microfoon knop verbergen - Microfoon knop verborgen - Microfoon knop weergegeven - Schakel omgevingsmodus uit in volledig scherm Actieve modus uitgeschakeld @@ -989,6 +973,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts-speler wordt niet hervat bij het opstarten van de app Shorts-speler wordt hervat bij het opstarten van de app + + Tablet lay-out inschakelen Tablet lay-out is ingeschakeld diff --git a/src/main/resources/addresources/values-or-rIN/strings.xml b/src/main/resources/addresources/values-or-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-or-rIN/strings.xml +++ b/src/main/resources/addresources/values-or-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-pa-rIN/strings.xml b/src/main/resources/addresources/values-pa-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-pa-rIN/strings.xml +++ b/src/main/resources/addresources/values-pa-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-pl-rPL/strings.xml b/src/main/resources/addresources/values-pl-rPL/strings.xml index bb46bac4dc..96a87f337d 100644 --- a/src/main/resources/addresources/values-pl-rPL/strings.xml +++ b/src/main/resources/addresources/values-pl-rPL/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Wyłącz polubienie/subskrybuj podświetlenie przycisków Przycisk polubienia i subskrybuj nie pojawi się po wzmiance Przycisk polubienia i subskrybuj się po wzmiance + Ukryj karty albumu + Karty albumów są ukryte + Karty albumów są wyświetlane + Ukryj program finansowania społecznościowego + Crowdfunding box jest ukryty + Pokazywany jest program Crowdfunding + Ukryj pływający przycisk mikrofonu + Przycisk mikrofonu ukryty + Przycisk mikrofonu pokazany Szare separatory Ukryte Widoczne @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Sekcja transkryptu jest wyświetlana Opis wideo Ukryj lub pokaż elementy opisu wideo + Pasek filtra + Ukryj lub pokaż pasek filtrów w kanale, wyszukiwaniu i powiązanych filmach + Ukryj w kanale + Ukryte w kanale + Pokazane w kanale + Ukryj w wyszukiwaniu + Ukryte w wyszukiwaniu + Pokaż w wyszukiwarce + Ukryj w powiązanych filmach + Ukryte w powiązanych filmach + Wyświetlane w powiązanych filmach + Komentarze + Ukryj lub pokaż składniki sekcji komentarzy + Ukryj nagłówek \'Komentarze wg użytkowników\' + Nagłówek \'Komentarze użytkowników\' jest ukryty + Wyświetlony jest nagłówek \'Komentarze użytkowników\' + Ukryj sekcję komentarzy + Sekcja komentarzy jest ukryta + Sekcja komentarzy jest wyświetlana + Ukryj przycisk \'Utwórz krótki\' + Przycisk \'Utwórz krótki\' jest ukryty + Przycisk \'Utwórz krótki\' jest pokazany + Ukryj podgląd komentarza + Podgląd komentarza jest ukryty + Podgląd komentarza jest wyświetlany + Ukryj przycisk podziękowania + Przycisk podziękowania jest ukryty + Przycisk podziękowania jest pokazany + Ukryj znaczniki czasu i przyciski emoji + Przyciski znacznika czasu i emoji są ukryte + Wyświetlane są przyciski znacznika czasu i emoji Ukryj Doodles YouTube Pasek wyszukiwania jest ukryty @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Zapisz do przycisku playlisty jest ukryty Pokaż przycisk Zapisz do playlisty - - Ukryj przycisk automatycznego odtwarzania - Przycisk automatycznego odtwarzania jest ukryty - Przycisk automatycznego odtwarzania jest wyświetlany - - - - Przycisk ukrycia podpisów - Przycisk podpisów jest ukryty - Przycisk podpisów jest pokazany - - - Ukryj przycisk Cofania - Przycisk Przesyłaj jest ukryty - Przycisk Przesyłaj jest widoczny - Navigation buttons Ukryj lub zmień przyciski na pasku nawigacji @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Obejrzyj menu VR jest ukryte Obejrzyj w menu VR - - Ukryj poprzednie przyciski & następnego filmu - Przyciski są ukryte - Przyciski są wyświetlane - - - Ukryj karty albumu - Karty albumów są ukryte - Karty albumów są wyświetlane - - - Komentarze - Ukryj lub pokaż składniki sekcji komentarzy - Ukryj nagłówek \'Komentarze wg użytkowników\' - Nagłówek \'Komentarze użytkowników\' jest ukryty - Wyświetlony jest nagłówek \'Komentarze użytkowników\' - Ukryj sekcję komentarzy - Sekcja komentarzy jest ukryta - Sekcja komentarzy jest wyświetlana - Ukryj przycisk \'Utwórz krótki\' - Przycisk \'Utwórz krótki\' jest ukryty - Przycisk \'Utwórz krótki\' jest pokazany - Ukryj podgląd komentarza - Podgląd komentarza jest ukryty - Podgląd komentarza jest wyświetlany - Ukryj przycisk podziękowania - Przycisk podziękowania jest ukryty - Przycisk podziękowania jest pokazany - Ukryj znaczniki czasu i przyciski emoji - Przyciski znacznika czasu i emoji są ukryte - Wyświetlane są przyciski znacznika czasu i emoji - - - Ukryj program finansowania społecznościowego - Crowdfunding box jest ukryty - Pokazywany jest program Crowdfunding + + Ukryj poprzednie przyciski & następnego filmu + Przyciski są ukryte + Przyciski są wyświetlane + Ukryj przycisk Cofania + Przycisk Przesyłaj jest ukryty + Przycisk Przesyłaj jest widoczny + + Przycisk ukrycia podpisów + Przycisk podpisów jest ukryty + Przycisk podpisów jest pokazany + Ukryj przycisk automatycznego odtwarzania + Przycisk automatycznego odtwarzania jest ukryty + Przycisk automatycznego odtwarzania jest wyświetlany Ukryj karty ekranu końcowego Karty ekranu końcowego są ukryte Karty ekranu końcowego są wyświetlane - - Pasek filtra - Ukryj lub pokaż pasek filtrów w kanale, wyszukiwaniu i powiązanych filmach - Ukryj w kanale - Ukryte w kanale - Pokazane w kanale - Ukryj w wyszukiwaniu - Ukryte w wyszukiwaniu - Pokaż w wyszukiwarce - Ukryj w powiązanych filmach - Ukryte w powiązanych filmach - Wyświetlane w powiązanych filmach - - - Ukryj pływający przycisk mikrofonu - Przycisk mikrofonu ukryty - Przycisk mikrofonu pokazany - Wyłącz tryb otoczenia na pełnym ekranie Tryb otoczenia wyłączony @@ -988,6 +972,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Odtwarzacz Shorts nie będzie wznawiany przy starcie aplikacji Odtwarzacz Shorts zostanie wznowiony przy starcie aplikacji + + Włącz układ tabletu Układ tabletu jest włączony diff --git a/src/main/resources/addresources/values-pt-rBR/strings.xml b/src/main/resources/addresources/values-pt-rBR/strings.xml index 9d2eabc19b..f00b934e59 100644 --- a/src/main/resources/addresources/values-pt-rBR/strings.xml +++ b/src/main/resources/addresources/values-pt-rBR/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Desativar brilho do botão de Inscrever-se / Curtir O botão de curtir e de inscrever-se não vai brilhar quando clicado O botão de curtir e de inscrever-se vai brilhar quando clicado + Ocultar cartões de álbum + Cartões de álbum estão ocultos + Cartões de álbum não estão ocultos + Ocultar caixa de financiamento coletivo + Caixa de financiamento coletivo está oculta + Caixa de financiamento coletivo não está oculta + Ocultar botão de microfone flutuante + Botão microfone está oculto + Botão microfone não está oculto Ocultar separador cinza Os separadores cinza está oculto Separador cinza não está oculto @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Seção de transcrição não está oculta Descrição do vídeo Ocultar ou mostrar componentes de descrição do vídeo + Barra de filtro + Ocultar ou mostrar a barra de filtro na tela inicial, pesquisa e vídeos relacionados + Ocultar na tela inicial + Está oculto na tela inicial + Não está oculto na tela inicial + Ocultar na pesquisa + Está oculto na busca + Não está oculto na busca + Ocultar nos vídeos relacionados + Está oculto nos vídeos relacionados + Não está oculto nos vídeos relacionados + Comentários + Ocultar ou mostrar componentes da seção de comentários + Ocultar cabeçalho \'Comentários por membros\' + O cabeçalho \'Comentários dos membros\' está oculto + O cabeçalho \'Comentários dos membros\' é exibido + Ocultar seção de comentários + Seção de comentários está oculta + Seção de comentários exibida + Ocultar botão \'Criar um Short\' + O botão \'Criar um Short\' está oculto + O botão \'Criar um Short\' é exibido + Ocultar prévia de comentário + Prévia de comentário está oculta + Prévia de comentário não está oculta + Ocultar botão valeu + Botão valeu está oculto + Botão valeu não está oculto + Ocultar botões de tempo e emoji + Os botões de cronograma e emoji estão ocultos + Botões de tempo e emoji são mostrados Filtro personalizado Ocultar componentes usando filtros personalizados @@ -423,22 +463,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Botão salvar na playlist está oculto Botão salvar na playlist não está oculto - - Ocultar botão de reprodução automática - Botão de reprodução automática está oculto - Botão de reprodução automática não está oculto - - - - Ocultar botão legendas - Botão legendas está oculto - Botão legendas não está oculto - - - Ocultar botão de transmitir - Botão transmitir está oculto - Botão transmitir não está oculto - Botões de navegação Ocultar ou alterar botões na barra de navegação @@ -511,66 +535,24 @@ This is because Crowdin requires temporarily flattening this file and removing t Menu assistir no VR está oculto Menu assistir no VR não está oculto - - Ocultar botões anterior & próxima vídeo - Botões estão ocultos - Botões não estão ocultos - - - Ocultar cartões de álbum - Cartões de álbum estão ocultos - Cartões de álbum não estão ocultos - - - Comentários - Ocultar ou mostrar componentes da seção de comentários - Ocultar cabeçalho \'Comentários por membros\' - O cabeçalho \'Comentários dos membros\' está oculto - O cabeçalho \'Comentários dos membros\' é exibido - Ocultar seção de comentários - Seção de comentários está oculta - Seção de comentários exibida - Ocultar botão \'Criar um Short\' - O botão \'Criar um Short\' está oculto - O botão \'Criar um Short\' é exibido - Ocultar prévia de comentário - Prévia de comentário está oculta - Prévia de comentário não está oculta - Ocultar botão valeu - Botão valeu está oculto - Botão valeu não está oculto - Ocultar botões de tempo e emoji - Os botões de cronograma e emoji estão ocultos - Botões de tempo e emoji são mostrados - - - Ocultar caixa de financiamento coletivo - Caixa de financiamento coletivo está oculta - Caixa de financiamento coletivo não está oculta + + Ocultar botões anterior & próxima vídeo + Ocultar botão de transmitir + Botão transmitir está oculto + Botão transmitir não está oculto + + Ocultar botão legendas + Botão legendas está oculto + Botão legendas não está oculto + Ocultar botão de reprodução automática + Botão de reprodução automática está oculto + Botão de reprodução automática não está oculto Ocultar cartões de tela final Cartões de tela final estão ocultos Cartões de tela final não estão ocultos - - Barra de filtro - Ocultar ou mostrar a barra de filtro na tela inicial, pesquisa e vídeos relacionados - Ocultar na tela inicial - Está oculto na tela inicial - Não está oculto na tela inicial - Ocultar na pesquisa - Está oculto na busca - Não está oculto na busca - Ocultar nos vídeos relacionados - Está oculto nos vídeos relacionados - Não está oculto nos vídeos relacionados - - - Ocultar botão de microfone flutuante - Botão microfone está oculto - Botão microfone não está oculto - Desativar o modo ambiente em tela cheia Modo ambiente desativado @@ -963,6 +945,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts não irá continuar reproduzindo ao iniciar o aplicativo Shorts irá continuar reproduzindo ao iniciar o aplicativo + + Ativar layout de tablet Layout de tablet está ativado diff --git a/src/main/resources/addresources/values-pt-rPT/strings.xml b/src/main/resources/addresources/values-pt-rPT/strings.xml index f19aefbf9b..8a42b77e44 100644 --- a/src/main/resources/addresources/values-pt-rPT/strings.xml +++ b/src/main/resources/addresources/values-pt-rPT/strings.xml @@ -105,6 +105,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Desativar brilho do botão de inscrição / Curtir O botão de curtir e assinar não brilhará quando mencionado O botão de curtir e subscrever brilhará quando mencionado + Esconder cartões de álbuns + Cartões de álbuns estão escondidos + Cartões de álbum são visíveis + Esconder caixa de crowdfunding + Caixa de Crowdfunding está escondida + Caixa de Crowdfunding é visível + Esconder o botão do microfone flutuante + Botão do microfone escondido + Botão do microfone visível Esconder separador cinza Os separadores cinzentos estão escondidos Separadores cinzas são visíveis @@ -229,6 +238,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Secção de transcrição exibida Descrição do vídeo Esconder ou mostrar componentes de descrição do vídeo + Barra de filtro + Esconder ou mostrar a barra de filtros no feed, pesquisa e vídeos relacionados + Esconder no feed + Escondido no feed + Mostrar no feed + Esconder na busca + Oculto em pesquisa + Mostrado na busca + Esconder em vídeos relacionados + Oculto em vídeos relacionados + Mostrar em vídeos relacionados + Comentários + Esconder ou mostrar componentes da seção de comentários + Ocultar cabeçalho \'Comentários por membros\' + O cabeçalho \'Comentários dos membros\' está oculto + O cabeçalho \'Comentários dos membros\' é exibido + Esconder seção de comentários + Seção de comentários está oculta + Seção de comentários exibida + Ocultar o botão \'Criar um Short\' + O botão \'Criar um Short\' está oculto + O botão \'Criar um Short\' é mostrado + Esconder comentário de pré-visualização + Visualização do comentário está escondida + Pré-visualização de comentário é exibida + Esconder botão de agradecimento + O botão de agradecimento está escondido + O botão Obrigado é visível + Ocultar botões de tempo e emoji + Os botões de cronograma e emoji estão ocultos + Botões de tempo e emoji são mostrados Ocultar Doodles do YouTube Doodles da barra de pesquisa estão escondidos @@ -424,22 +464,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Salvar no botão de playlist está escondido Botão de salvar lista de reprodução é visível - - Esconder botão de reprodução automática - O botão de reprodução automática está escondido - Botão de reprodução automática é visível - - - - Esconder botão de legendas - O botão de legendas está escondido - Botão de legendas é visível - - - Esconder botão transmitir - Botão \"Transmitir\" está escondido - Botão \"Transmitir\" é visível - Navigation buttons Esconder ou alterar botões na barra de navegação @@ -512,66 +536,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Assista no menu VR está escondido Assistir no menu VR é visível - - Esconder botões anteriores & próxima vídeo - Botões estão escondidos - Botões são exibidos - - - Esconder cartões de álbuns - Cartões de álbuns estão escondidos - Cartões de álbum são visíveis - - - Comentários - Esconder ou mostrar componentes da seção de comentários - Ocultar cabeçalho \'Comentários por membros\' - O cabeçalho \'Comentários dos membros\' está oculto - O cabeçalho \'Comentários dos membros\' é exibido - Esconder seção de comentários - Seção de comentários está oculta - Seção de comentários exibida - Ocultar o botão \'Criar um Short\' - O botão \'Criar um Short\' está oculto - O botão \'Criar um Short\' é mostrado - Esconder comentário de pré-visualização - Visualização do comentário está escondida - Pré-visualização de comentário é exibida - Esconder botão de agradecimento - O botão de agradecimento está escondido - O botão Obrigado é visível - Ocultar botões de tempo e emoji - Os botões de cronograma e emoji estão ocultos - Botões de tempo e emoji são mostrados - - - Esconder caixa de crowdfunding - Caixa de Crowdfunding está escondida - Caixa de Crowdfunding é visível + + Esconder botões anteriores & próxima vídeo + Botões estão ocultos + Botões são exibidos + Esconder botão transmitir + Botão \"Transmitir\" está escondido + Botão \"Transmitir\" é visível + + Esconder botão de legendas + O botão de legendas está escondido + Botão de legendas é visível + Esconder botão de reprodução automática + O botão de reprodução automática está escondido + Botão de reprodução automática é visível Esconder cartões de ecrã final Cartões de fim de ecrã estão escondidos Cartões de fim de ecrã são exibidos - - Barra de filtro - Esconder ou mostrar a barra de filtros no feed, pesquisa e vídeos relacionados - Esconder no feed - Escondido no feed - Mostrar no feed - Esconder na busca - Oculto em pesquisa - Mostrado na busca - Esconder em vídeos relacionados - Oculto em vídeos relacionados - Mostrar em vídeos relacionados - - - Esconder o botão do microfone flutuante - Botão do microfone escondido - Botão do microfone visível - Desativar o modo ambiente em ecrã cheia Modo ambiente desativado @@ -596,6 +580,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Barra de busca de miniaturas visível + Ocultar ou mostrar componentes no player Shorts Esconder Shorts no feed inicial Os Shorts no feed inicial estão ocultos @@ -986,6 +971,10 @@ This is because Crowdin requires temporarily flattening this file and removing t Curta o reprodutor não continuará na inicialização do aplicativo Shorts que o reprodutor continuará na inicialização do aplicativo + + Reprodução automática de Shorts + Shorts irão repetir + Habilitar layout do tablet O layout do tablet está ativado diff --git a/src/main/resources/addresources/values-ro-rRO/strings.xml b/src/main/resources/addresources/values-ro-rRO/strings.xml index 8283c58e24..e93422ef99 100644 --- a/src/main/resources/addresources/values-ro-rRO/strings.xml +++ b/src/main/resources/addresources/values-ro-rRO/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Dezactivează ca / abonare strălucire buton Butonul Îmi place și abonare nu va străluci când este menționat Butonul de like-uri și abonare va străluci când este menționat + Ascundeți cardurile de album + Cardurile de album sunt ascunse + Cardurile de album sunt afișate + Ascunde caseta de crowdfunding + Caseta multifinanțare este ascunsă + Cutia multifinanțare este afișată + Ascunde butonul de microfon plutitor + Butonul microfon ascuns + Butonul Microfon afișat Ascunde separatorul gri Separatoarele gri sunt ascunse Se afișează separatoarele gri @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Secțiunea de Transcriere este afișată Descriere video Ascunde sau afișează componentele descrierii video + Bară de filtrare + Ascunde sau afișează bara de filtrare în flux, căutare și videoclipuri asociate + Ascunde în feed + Ascuns în feed + Afișat în feed + Ascunde în căutare + Ascuns în căutare + Afișat în căutare + Ascunde în videoclipurile asociate + Ascuns în videoclipuri conexe + Afișat în videoclipuri conexe + Comentarii + Ascunde sau afișează componentele secțiunii comentarii + Ascunde \'Comentarii după antetul membrilor + \'Comentarii de la antetul membrilor este ascuns + \'Comentarii de la antetul membrilor este afișat + Ascunde secțiunea comentarii + Secţiunea de comentarii este ascunsă + Secțiunea comentariilor este afișată + Ascunde butonul \'Creare Short\' + Butonul \'Crează un Short\' este ascuns + Butonul \'Crează un Short\' este afișat + Ascunde previzualizarea comentariului + Previzualizarea comentariului este ascunsă + Previzualizarea comentariului este afișată + Ascunde butonul de mulțumire + Butonul de multumire este ascuns + Butonul de multumire este afisat + Ascunde butoanele timestamp și emoji + Butoanele de timp și emoji sunt ascunse + Butoanele de timp și emoji sunt afișate Ascunde Doodle-urile YouTube Doodles bară de căutare sunt ascunse @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Butonul Salvare în lista de redare este ascuns Butonul Salvare în lista de redare este afișat - - Ascunde butonul de redare automată - Butonul Autoplay este ascuns - Butonul Auto-redare este afișat - - - - Ascunde butonul de legendă - Butonul subtitrari este ascuns - Se afișează butonul de subtitrări - - - Ascunde butonul de redare - Butonul de distribuție este ascuns - Butonul de execuție este afișat - Navigation buttons Ascunde sau modifică butoanele din bara de navigare @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Vizionarea în meniul VR este ascunsă Vizionați în meniul VR este afișat - - Ascunde butoanele anterioare & următorul video - Butoanele sunt ascunse - Butoanele sunt afișate - - - Ascundeți cardurile de album - Cardurile de album sunt ascunse - Cardurile de album sunt afișate - - - Comentarii - Ascunde sau afișează componentele secțiunii comentarii - Ascunde \'Comentarii după antetul membrilor - \'Comentarii de la antetul membrilor este ascuns - \'Comentarii de la antetul membrilor este afișat - Ascunde secțiunea comentarii - Secţiunea de comentarii este ascunsă - Secțiunea comentariilor este afișată - Ascunde butonul \'Creare Short\' - Butonul \'Crează un Short\' este ascuns - Butonul \'Crează un Short\' este afișat - Ascunde previzualizarea comentariului - Previzualizarea comentariului este ascunsă - Previzualizarea comentariului este afișată - Ascunde butonul de mulțumire - Butonul de multumire este ascuns - Butonul de multumire este afisat - Ascunde butoanele timestamp și emoji - Butoanele de timp și emoji sunt ascunse - Butoanele de timp și emoji sunt afișate - - - Ascunde caseta de crowdfunding - Caseta multifinanțare este ascunsă - Cutia multifinanțare este afișată + + Ascunde butoanele anterioare & următorul video + Butoanele sunt ascunse + Butoanele sunt afișate + Ascunde butonul de redare + Butonul de distribuție este ascuns + Butonul de execuție este afișat + + Ascunde butonul de legendă + Butonul subtitrari este ascuns + Se afișează butonul de subtitrări + Ascunde butonul de redare automată + Butonul Autoplay este ascuns + Butonul Auto-redare este afișat Ascunde cardurile ecranului final Cardurile de pe ecranul de închidere sunt ascunse Cardurile de închidere ecran sunt afișate - - Bară de filtrare - Ascunde sau afișează bara de filtrare în flux, căutare și videoclipuri asociate - Ascunde în feed - Ascuns în feed - Afișat în feed - Ascunde în căutare - Ascuns în căutare - Afișat în căutare - Ascunde în videoclipurile asociate - Ascuns în videoclipuri conexe - Afișat în videoclipuri conexe - - - Ascunde butonul de microfon plutitor - Butonul microfon ascuns - Butonul Microfon afișat - Dezactivează modul ambiental pe tot ecranul Mod ambiental dezactivat @@ -988,6 +972,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Scurtătura jucătorului nu va fi reluată la pornirea aplicației Scurtătura va fi reluată la pornirea aplicației + + Activează aspectul tabletei Aspectul tabletei este activat diff --git a/src/main/resources/addresources/values-ru-rRU/strings.xml b/src/main/resources/addresources/values-ru-rRU/strings.xml index 55cf38401d..33e6d939ea 100644 --- a/src/main/resources/addresources/values-ru-rRU/strings.xml +++ b/src/main/resources/addresources/values-ru-rRU/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Подсветка кнопок Кнопки \"Лайк\" и \"Подписаться\" не будут подсвечиваться при упоминании Кнопки \"Лайк\" и \"Подписаться\" будут подсвечиваться при упоминании + Карточки альбомов + Карточки альбомов под описанием артистов скрыты + Карточки альбомов под описанием артистов отображены + Колонка \"Коллективный сбор\" + Колонка \"Коллективный сбор\" между плеером и описанием видео скрыта + Колонка \"Коллективный сбор\" между плеером и описанием видео отображена + Плавающая кнопка микрофона + Плавающая кнопка микрофона в поиске скрыта + Плавающая кнопка микрофона в поиске отображена Серые разделители Серые разделители в ленте между видео и публикациями сообщества скрыты Серые разделители в ленте между видео и публикациями сообщества отображены @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Раздел расшифровки в описании видео отображен Описание видео Скрыть или отобразить компоненты описания видео + Панель фильтров + Скрыть или отобразить панель фильтров в ленте, поиске и похожих видео + Панель фильтров в ленте + Панель фильтров в ленте скрыта + Панель фильтров в ленте отображена + Панель фильтров в поиске + Панель фильтров в поиске скрыта + Панель фильтров в поиске отображена + Панель фильтров в похожих видео + Панель фильтров в похожих видео скрыта + Панель фильтров в похожих видео отображена + Комментарии + Скрыть или отобразить компоненты раздела комментариев + Заголовок \"Комментарии спонсоров\" + Заголовок \"Комментарии спонсоров\" скрыт + Заголовок \"Комментарии спонсоров\" отображен + Раздел комментариев + Раздел комментариев под плеером скрыт + Раздел комментариев под плеером отображен + Кнопка \"Создать Short\" + Кнопка \"Создать Short\" скрыта + Кнопка \"Создать Short\" отображена + Предпросмотр комментария + Предпросмотр комментария под плеером скрыт + Предпросмотр комментария под плеером отображен + Кнопка \"Спасибо\" + Кнопка \"Спасибо\" скрыта + Кнопка \"Спасибо\" отображена + Метка времени и кнопки эмодзи + Метка времени и кнопки эмодзи в разделе комментариев скрыты + Метка времени и кнопки эмодзи в разделе комментариев отображены YouTube Doodles Doodles на панели поиска скрыты @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Кнопка \"Сохранить в плейлист\" под плеером скрыта Кнопка \"Сохранить в плейлист\" под плеером отображена - - Кнопка \"Автовоспроизведение\" - Кнопка \"Автовоспроизведение\" в плеере скрыта - Кнопка \"Автовоспроизведение\" в плеере отображена - - - - Кнопка \"Субтитры\" - Кнопка \"Субтитры\" в плеере скрыта - Кнопка \"Субтитры\" в плеере отображена - - - Кнопка \"Трансляция\" - Кнопка \"Трансляция\" в плеере скрыта - Кнопка \"Трансляция\" в плеере отображена - Кнопки навигации Скрыть или изменить кнопки в панели навигации @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Пункт \"Смотреть в VR-режиме\" в выдвижном меню плеера скрыт Пункт \"Смотреть в VR-режиме\" в выдвижном меню плеера отображен - - Кнопки переключения видео - Кнопки предыдущего и следующего видео скрыты - Кнопки предыдущего и следующего видео отображены - - - Карточки альбомов - Карточки альбомов под описанием артистов скрыты - Карточки альбомов под описанием артистов отображены - - - Комментарии - Скрыть или отобразить компоненты раздела комментариев - Заголовок \"Комментарии спонсоров\" - Заголовок \"Комментарии спонсоров\" скрыт - Заголовок \"Комментарии спонсоров\" отображен - Раздел комментариев - Раздел комментариев под плеером скрыт - Раздел комментариев под плеером отображен - Кнопка \"Создать Short\" - Кнопка \"Создать Short\" скрыта - Кнопка \"Создать Short\" отображена - Предпросмотр комментария - Предпросмотр комментария под плеером скрыт - Предпросмотр комментария под плеером отображен - Кнопка \"Спасибо\" - Кнопка \"Спасибо\" скрыта - Кнопка \"Спасибо\" отображена - Метка времени и кнопки эмодзи - Метка времени и кнопки эмодзи в разделе комментариев скрыты - Метка времени и кнопки эмодзи в разделе комментариев отображены - - - Колонка \"Коллективный сбор\" - Колонка \"Коллективный сбор\" между плеером и описанием видео скрыта - Колонка \"Коллективный сбор\" между плеером и описанием видео отображена + + Кнопки переключения видео + Кнопки скрыты + Кнопки предыдущего и следующего видео отображены + Кнопка \"Трансляция\" + Кнопка \"Трансляция\" в плеере скрыта + Кнопка \"Трансляция\" в плеере отображена + + Кнопка \"Субтитры\" + Кнопка \"Субтитры\" в плеере скрыта + Кнопка \"Субтитры\" в плеере отображена + Кнопка \"Автовоспроизведение\" + Кнопка \"Автовоспроизведение\" в плеере скрыта + Кнопка \"Автовоспроизведение\" в плеере отображена Заставки следущих видео Заставки следующих видео в конце просмотра скрыты Заставки следующих видео в конце просмотра отображены - - Панель фильтров - Скрыть или отобразить панель фильтров в ленте, поиске и похожих видео - Панель фильтров в ленте - Панель фильтров в ленте скрыта - Панель фильтров в ленте отображена - Панель фильтров в поиске - Панель фильтров в поиске скрыта - Панель фильтров в поиске отображена - Панель фильтров в похожих видео - Панель фильтров в похожих видео скрыта - Панель фильтров в похожих видео отображена - - - Плавающая кнопка микрофона - Плавающая кнопка микрофона в поиске скрыта - Плавающая кнопка микрофона в поиске отображена - Фоновая подсветка Фоновая подсветка в полноэкранном режиме отключена @@ -599,6 +583,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Миниатюры прогресса воспроизведения отображены + Плеер Shorts + Скрыть или отобразить компоненты в плеере Shorts Shorts в ленте \"Главной\" Shorts в ленте \"Главной\" скрыты @@ -645,9 +631,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Кнопка \"Зеленый экран\" Кнопка \"Зеленый экран\" скрыта Кнопка \"Зеленый экран\" отображена - Скрыть хэштег + Кнопка хэштега Кнопка хэштега скрыта - Отображена кнопка хештега + Кнопка хэштега отображена Поисковые подсказки Поисковые подсказки скрыты Поисковые подсказки отображены @@ -971,11 +957,11 @@ This is because Crowdin requires temporarily flattening this file and removing t По умолчанию Просмотр каналов Навигатор - Играть - История + Игры + История просмотров Библиотека Понравившиеся видео - Онлайн + В эфире Фильмы Музыка Поиск @@ -989,6 +975,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Возобновление плеера Shorts при запуске приложения отключено Возобновление плеера Shorts при запуске приложения включено + + Автопроигрывание Shorts + Shorts будут автоматически воспроизводиться одно за другим + Shorts будут повторяться + Автопроигрывание Shorts в фоне + Shorts будут автоматически воспроизводиться одно за другим в фоновом режиме + Shorts будут повторяться в фоновом режиме + Планшетный интерфейс Планшетный интерфейс включен @@ -1005,21 +999,21 @@ This is because Crowdin requires temporarily flattening this file and removing t Современный 1 Современный 2 Современный 3 - Включить закругленные углы + Закругленные углы Углы закруглены Углы квадратны - Включить двойное нажатие и закрепление для изменения размера - Действие двойного нажатия и устройство для изменения размера включено\n\n• Двойное нажатие для увеличения размера мини-плеера\n• Двойное нажатие еще раз для восстановления исходного размера - Действие двойного нажатия и закрепление для изменения размера отключены - Включить перетаскивание + Двойное нажатие и щипок для изменения размера + Двойное нажатие и щипок для изменения размера включено\n\n• Двойное нажатие для увеличения размера мини-плеера\n• Двойное нажатие еще раз для восстановления исходного размера + Двойное нажатие и щипок для изменения размера отключены + Перетаскивание Перетаскивание включено\n\nМини-плеер можно перетащить в любой угол экрана Перетаскивание отключено - Скрыть кнопку закрытия + Кнопка закрытия Кнопка закрытия скрыта - Кнопка закрытия отображается + Кнопка закрытия отображена Кнопки \"Развернуть\" и \"Закрыть\" - Кнопки скрыты\n\nдля разворачивания или закрытия - Показывать кнопки разворачивания и закрытия + Кнопки скрыты\n\nПроведите по мини-плееру для разворачивания или закрытия + Кнопки разворачивания и закрытия отображены Скрыть подтексты Субтексты скрыты Подтексты показаны @@ -1027,8 +1021,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Кнопки перемотки вперед и назад скрыты Кнопки перемотки вперед и назад отображены Начальный размер - Начальный размер экрана в пикселях - Размер пикселя должен быть между %1$s и %2$s + Начальный размер на экране, в пикселях + Размер в пикселях должен быть между %1$s и %2$s Непрозрачность оверлея мини-плеера Значение непрозрачности в пределах 0-100, где 0 - это прозрачно Непрозрачность оверлея мини-плеера должна быть от 0 до 100 diff --git a/src/main/resources/addresources/values-si-rLK/strings.xml b/src/main/resources/addresources/values-si-rLK/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-si-rLK/strings.xml +++ b/src/main/resources/addresources/values-si-rLK/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-sk-rSK/strings.xml b/src/main/resources/addresources/values-sk-rSK/strings.xml index 9f11677c97..1ebfc22358 100644 --- a/src/main/resources/addresources/values-sk-rSK/strings.xml +++ b/src/main/resources/addresources/values-sk-rSK/strings.xml @@ -93,6 +93,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Zakázať podsvietenie tlačidla Páči sa mi/Prihlásiť sa na odber Tlačidlo Páči sa mi a Prihlásiť sa pri zmienke nebude svietiť Pri zmienke sa rozsvieti tlačidlo Páči sa mi a Prihlásiť sa na odber + Skryť karty albumov + Karty albumov sú skryté + Zobrazia sa karty albumov + Skryť crowdfunding box + Crowdfundingový box je skrytý + Je zobrazené pole crowdfundingu + Skryť plávajúce tlačidlo mikrofónu + Tlačidlo mikrofónu je skryté + Zobrazené tlačidlo mikrofónu Skryť sivý oddeľovač Sivé oddeľovače sú skryté Sú zobrazené sivé oddeľovače @@ -214,6 +223,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Zobrazí sa sekcia prepisu Popis videa Skryť alebo zobraziť komponenty popisu videa + Panel filtra + Skryť alebo zobraziť panel filtra v informačnom kanáli, vo vyhľadávaní a v súvisiacich videách + Skryť v feede + Skryté v krmive + Zobrazené v informačnom kanáli + Skryť vo vyhľadávaní + Skryté vo vyhľadávaní + Zobrazuje sa pri vyhľadávaní + Skryť v súvisiacich videách + Skryté v súvisiacich videách + Zobrazuje sa v súvisiacich videách + Komentáre + Skryť alebo zobraziť komponenty sekcie komentárov + Skryť hlavičku \"Komentáre členov\" + Hlavička \"Komentáre členov\" je skrytá + Zobrazí sa hlavička \"Komentáre členov\" + Skryť sekciu komentárov + Sekcia komentárov je skrytá + Zobrazí sa sekcia komentárov + Skryť tlačidlo \"Vytvoriť krátke\" + Tlačidlo \"Vytvoriť krátke\" je skryté + Zobrazí sa tlačidlo \"Vytvoriť krátke\" + Skryť ukážkový komentár + Komentár ukážky je skrytý + Zobrazí sa ukážka komentára + Skryť tlačidlo poďakovania + Tlačidlo poďakovania je skryté + Zobrazí sa tlačidlo Ďakujem + Skryť tlačidlá časovej pečiatky a emodži + Tlačidlá časovej pečiatky a emotikonov sú skryté + Zobrazia sa tlačidlá časovej pečiatky a emoji Vlastný filter Skryť komponenty pomocou vlastných filtrov @@ -396,22 +436,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Tlačidlo Uložiť do zoznamu skladieb je skryté Zobrazí sa tlačidlo Uložiť do zoznamu skladieb - - Skryť tlačidlo automatického prehrávania - Tlačidlo automatického prehrávania je skryté - Zobrazí sa tlačidlo automatického prehrávania - - - - Tlačidlo skryť titulky - Tlačidlo titulkov je skryté - Zobrazí sa tlačidlo titulkov - - - Skryť tlačidlo prenášania - Tlačidlo zdieľania obrazovky je skryté - Tlačidlo zdieľania obrazovky je zobrazené - Navigačné tlačidlá Skryť alebo zmeniť tlačidlá na navigačnom paneli @@ -484,66 +508,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Sledovanie v ponuke VR je skryté Zobrazí sa ponuka Sledovať vo VR - - Skryť predchádzajúce & tlačidlá ďalšieho videa - Tlačidlá sú skryté - Zobrazia sa tlačidlá - - - Skryť karty albumov - Karty albumov sú skryté - Zobrazia sa karty albumov - - - Komentáre - Skryť alebo zobraziť komponenty sekcie komentárov - Skryť hlavičku \"Komentáre členov\" - Hlavička \"Komentáre členov\" je skrytá - Zobrazí sa hlavička \"Komentáre členov\" - Skryť sekciu komentárov - Sekcia komentárov je skrytá - Zobrazí sa sekcia komentárov - Skryť tlačidlo \"Vytvoriť krátke\" - Tlačidlo \"Vytvoriť krátke\" je skryté - Zobrazí sa tlačidlo \"Vytvoriť krátke\" - Skryť ukážkový komentár - Komentár ukážky je skrytý - Zobrazí sa ukážka komentára - Skryť tlačidlo poďakovania - Tlačidlo poďakovania je skryté - Zobrazí sa tlačidlo Ďakujem - Skryť tlačidlá časovej pečiatky a emodži - Tlačidlá časovej pečiatky a emotikonov sú skryté - Zobrazia sa tlačidlá časovej pečiatky a emoji - - - Skryť crowdfunding box - Crowdfundingový box je skrytý - Je zobrazené pole crowdfundingu + + Skryť predchádzajúce & tlačidlá ďalšieho videa + Tlačidlá sú skryté + Zobrazia sa tlačidlá + Skryť tlačidlo prenášania + Tlačidlo zdieľania obrazovky je skryté + Tlačidlo zdieľania obrazovky je zobrazené + + Tlačidlo skryť titulky + Tlačidlo titulkov je skryté + Zobrazí sa tlačidlo titulkov + Skryť tlačidlo automatického prehrávania + Tlačidlo automatického prehrávania je skryté + Zobrazí sa tlačidlo automatického prehrávania Skryť karty záverečnej obrazovky Karty záverečnej obrazovky sú skryté Zobrazia sa karty záverečnej obrazovky - - Panel filtra - Skryť alebo zobraziť panel filtra v informačnom kanáli, vo vyhľadávaní a v súvisiacich videách - Skryť v feede - Skryté v krmive - Zobrazené v informačnom kanáli - Skryť vo vyhľadávaní - Skryté vo vyhľadávaní - Zobrazuje sa pri vyhľadávaní - Skryť v súvisiacich videách - Skryté v súvisiacich videách - Zobrazuje sa v súvisiacich videách - - - Skryť plávajúce tlačidlo mikrofónu - Tlačidlo mikrofónu je skryté - Zobrazené tlačidlo mikrofónu - Zakázať ambientný režim na celej obrazovke Ambientný režim je vypnutý @@ -926,6 +910,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Prehrávač Shorts videí sa pri spustení aplikácie neobnoví Prehrávač Shorts videí sa obnoví pri spustení aplikácie + + Povoliť rozloženie tabletu Rozloženie tabletu je povolené diff --git a/src/main/resources/addresources/values-sl-rSI/strings.xml b/src/main/resources/addresources/values-sl-rSI/strings.xml index 091492c518..225c4f08c0 100644 --- a/src/main/resources/addresources/values-sl-rSI/strings.xml +++ b/src/main/resources/addresources/values-sl-rSI/strings.xml @@ -102,13 +102,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -129,20 +122,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -197,6 +181,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-sq-rAL/strings.xml b/src/main/resources/addresources/values-sq-rAL/strings.xml index 48859b4567..58ee3ebe8c 100644 --- a/src/main/resources/addresources/values-sq-rAL/strings.xml +++ b/src/main/resources/addresources/values-sq-rAL/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-sr-rCS/strings.xml b/src/main/resources/addresources/values-sr-rCS/strings.xml index 6f1832d5d0..c3e2825f59 100644 --- a/src/main/resources/addresources/values-sr-rCS/strings.xml +++ b/src/main/resources/addresources/values-sr-rCS/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Onemogući sjaj dugmadi „Sviđanje” / „Zaprati” Dugmad „Sviđanje” i „Zaprati” neće svetleti kada se pritisnu Dugmad „Sviđanje” i „Zaprati” će svetleti kada se pritisnu + Sakrij kartice albuma + Kartice albuma su skrivene + Kartice albuma su prikazane + Sakrij polje za kolektivno finansiranje + Polje za kolektivno finansiranje je skriveno + Polje za kolektivno finansiranje je prikazano + Sakrij plutajuće dugme mikrofona + Plutajuće dugme mikrofona je skriveno + Plutajuće dugme mikrofona je prikazano Sakrij sive razdelnike Sivi razdelnici su skriveni Sivi razdelnici su prikazani @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Odeljak za transkripciju je prikazan Opis videa Sakrijte ili prikažite komponente opisa videa + Traka filtera + Sakrijte ili prikažite traku filtera u fidu, pretrazi ili srodnim videima + Sakrij u fidu + Skriveno u fidu + Prikazano u fidu + Sakrij u pretrazi + Skriveno u pretrazi + Prikazano u pretrazi + Sakrij u srodnim videima + Skriveno u srodnim videima + Prikazano u srodnim videima + Komentari + Sakrijte ili prikažite komponente odeljka za komentare + Sakrij zaglavlje „Komentari od članova” + Zaglavlje „Komentari od članova” je skriveno + Zaglavlje „Komentari od članova” je prikazano + Sakrij odeljak za komentare + Odeljak za komentare je skriven + Odeljak za komentare je prikazan + Sakrij dugme „Napravi Short” + Dugme „Napravi Short” je skriveno + Dugme „Napravi Short” je prikazano + Sakrij komentar za pregled + Komentar za pregled je skriven + Komentar za pregled je prikazan + Sakrij dugme „Hvala” + Dugme „Hvala” je skriveno + Dugme „Hvala” je prikazano + Sakrij dugmad za vremensku oznaku i emodžije + Dugmad za vremensku oznaku i emodžije su skrivena + Dugmad za vremensku oznaku i emodžije su prikazana Sakrij YouTube Doodles YouTube Doodles u traci za pretragu su skriveni @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Dugme „Sačuvaj na plejlistu” je skriveno Dugme „Sačuvaj na plejlistu” je prikazano - - Sakrij dugme „Autoplej” - Dugme „Autoplej” je skriveno - Dugme „Autoplej” je prikazano - - - - Sakrij dugme „Titl” - Dugme „Titl” je skriveno - Dugme „Titl” je prikazano - - - Sakrij dugme „Prebacuj” - Dugme „Prebacuj” je skriveno - Dugme „Prebacuj” je prikazano - Dugmad navigacije Sakrijte ili promenite dugmad na traci za navigaciju @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Dugme „Gledaj u VR” je skriveno Dugme „Gledaj u VR” je prikazano - - Sakrij dugmad za prethodni i sledeći video - Dugmad za prethodni i sledeći video su skrivena - Dugmad za prethodni i sledeći video su prikazana - - - Sakrij kartice albuma - Kartice albuma su skrivene - Kartice albuma su prikazane - - - Komentari - Sakrijte ili prikažite komponente odeljka za komentare - Sakrij zaglavlje „Komentari od članova” - Zaglavlje „Komentari od članova” je skriveno - Zaglavlje „Komentari od članova” je prikazano - Sakrij odeljak za komentare - Odeljak za komentare je skriven - Odeljak za komentare je prikazan - Sakrij dugme „Napravi Short” - Dugme „Napravi Short” je skriveno - Dugme „Napravi Short” je prikazano - Sakrij komentar za pregled - Komentar za pregled je skriven - Komentar za pregled je prikazan - Sakrij dugme „Hvala” - Dugme „Hvala” je skriveno - Dugme „Hvala” je prikazano - Sakrij dugmad za vremensku oznaku i emodžije - Dugmad za vremensku oznaku i emodžije su skrivena - Dugmad za vremensku oznaku i emodžije su prikazana - - - Sakrij polje za kolektivno finansiranje - Polje za kolektivno finansiranje je skriveno - Polje za kolektivno finansiranje je prikazano + + Sakrij dugmad za prethodni i sledeći video + Dugmad za prethodni i sledeći video su skrivena + Dugmad za prethodni i sledeći video su prikazana + Sakrij dugme „Prebacuj” + Dugme „Prebacuj” je skriveno + Dugme „Prebacuj” je prikazano + + Sakrij dugme „Titl” + Dugme „Titl” je skriveno + Dugme „Titl” je prikazano + Sakrij dugme „Autoplej” + Dugme „Autoplej” je skriveno + Dugme „Autoplej” je prikazano Sakrij kartice završnog ekrana Kartice završnog ekrana su skrivene Kartice završnog ekrana su prikazane - - Traka filtera - Sakrijte ili prikažite traku filtera u fidu, pretrazi ili srodnim videima - Sakrij u fidu - Skriveno u fidu - Prikazano u fidu - Sakrij u pretrazi - Skriveno u pretrazi - Prikazano u pretrazi - Sakrij u srodnim videima - Skriveno u srodnim videima - Prikazano u srodnim videima - - - Sakrij plutajuće dugme mikrofona - Plutajuće dugme mikrofona je skriveno - Plutajuće dugme mikrofona je prikazano - Onemogući ambijentalni režim u režimu celog ekrana Ambijentalni režim u režimu celog ekrana je onemogućen @@ -989,6 +973,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts plejer neće nastaviti reprodukciju pri pokretanju aplikacije Shorts plejer će nastaviti reprodukciju pri pokretanju aplikacije + + Omogući korisnički interfejs tableta Korisnički interfejs tableta je omogućen diff --git a/src/main/resources/addresources/values-sr-rSP/strings.xml b/src/main/resources/addresources/values-sr-rSP/strings.xml index 1448611836..6eb5424a9c 100644 --- a/src/main/resources/addresources/values-sr-rSP/strings.xml +++ b/src/main/resources/addresources/values-sr-rSP/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Онемогући сјај дугмади „Свиђање” / „Запрати” Дугмад „Свиђање” и „Запрати” неће светлети када се притисну Дугмад „Свиђање” и „Запрати” ће светлети када се притисну + Сакриј картице албума + Картице албума су скривене + Картице албума су приказане + Сакриј поље за колективно финансирање + Поље за колективно финансирање је скривено + Поље за колективно финансирање је приказано + Сакриј плутајуће дугме микрофона + Плутајуће дугме микрофона је скривено + Плутајуће дугме микрофона је приказано Сакриј сиве разделнике Сиви разделници су скривени Сиви разделници су приказани @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Одељак за транскрипцију је скривен Опис видеа Сакријте или прикажите компоненте описа видеа + Трака филтера + Сакријте или прикажите траку филтера у фиду, претрази или сродним видеима + Сакриј у фиду + Скривено у фиду + Приказано у фиду + Сакриј у претрази + Скривено у претрази + Приказано у претрази + Сакриј у сродним видеима + Скривено у сродним видеима + Приказано у сродним видеима + Коментари + Сакријте или прикажите компоненте одељка за коментаре + Сакриј заглавље „Коментари од чланова” + Заглавље „Коментари од чланова” је скривено + Заглавље „Коментари од чланова” је приказано + Сакриј одељак за коментаре + Одељак за коментаре је скривен + Одељак за коментаре је приказан + Сакриј дугме „Направи Short” + Дугме „Направи Short” је скривено + Дугме „Направи Short” је приказано + Сакриј коментар за преглед + Коментар за преглед је скривен + Коментар за преглед је приказан + Сакриј дугме „Хвала” + Дугме „Хвала” је скривено + Дугме „Хвала” је приказано + Сакриј дугмад за временску ознаку и емоџије + Дугмад за временску ознаку и емоџије су скривена + Дугмад за временску ознаку и емоџије су приказана Сакриј YouTube Doodles YouTube Doodles у траци за претрагу су скривени @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Дугме „Сачувај на плејлисту” је скривено Дугме „Сачувај на плејлисту” је приказано - - Сакриј дугме „Аутоплеј” - Дугме „Аутоплеј” је скривено - Дугме „Аутоплеј” је приказано - - - - Сакриј дугме „Титл” - Дугме „Титл” је скривено - Дугме „Титл” је приказано - - - Сакриј дугме „Пребацуј” - Дугме „Пребацуј” је скривено - Дугме „Пребацуј” је приказано - Дугмад навигације Сакријте или промените дугмад на траци за навигацију @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Дугме „Гледај у ВР” је скривено Дугме „Гледај у ВР” је приказано - - Сакриј дугмад за претходни и следећи видео - Дугмад за претходни и следећи видео су скривена - Дугмад за претходни и следећи видео су приказана - - - Сакриј картице албума - Картице албума су скривене - Картице албума су приказане - - - Коментари - Сакријте или прикажите компоненте одељка за коментаре - Сакриј заглавље „Коментари од чланова” - Заглавље „Коментари од чланова” је скривено - Заглавље „Коментари од чланова” је приказано - Сакриј одељак за коментаре - Одељак за коментаре је скривен - Одељак за коментаре је приказан - Сакриј дугме „Направи Short” - Дугме „Направи Short” је скривено - Дугме „Направи Short” је приказано - Сакриј коментар за преглед - Коментар за преглед је скривен - Коментар за преглед је приказан - Сакриј дугме „Хвала” - Дугме „Хвала” је скривено - Дугме „Хвала” је приказано - Сакриј дугмад за временску ознаку и емоџије - Дугмад за временску ознаку и емоџије су скривена - Дугмад за временску ознаку и емоџије су приказана - - - Сакриј поље за колективно финансирање - Поље за колективно финансирање је скривено - Поље за колективно финансирање је приказано + + Сакриј дугмад за претходни и следећи видео + Дугмад за претходни и следећи видео су скривена + Дугмад за претходни и следећи видео су приказана + Сакриј дугме „Пребацуј” + Дугме „Пребацуј” је скривено + Дугме „Пребацуј” је приказано + + Сакриј дугме „Титл” + Дугме „Титл” је скривено + Дугме „Титл” је приказано + Сакриј дугме „Аутоплеј” + Дугме „Аутоплеј” је скривено + Дугме „Аутоплеј” је приказано Сакриј картице завршног екрана Картице завршног екрана су скривене Картице завршног екрана су приказане - - Трака филтера - Сакријте или прикажите траку филтера у фиду, претрази или сродним видеима - Сакриј у фиду - Скривено у фиду - Приказано у фиду - Сакриј у претрази - Скривено у претрази - Приказано у претрази - Сакриј у сродним видеима - Скривено у сродним видеима - Приказано у сродним видеима - - - Сакриј плутајуће дугме микрофона - Плутајуће дугме микрофона је скривено - Плутајуће дугме микрофона је приказано - Онемогући амбијентални режим у режиму целог екрана Амбијентални режим у режиму целог екрана је онемогућен @@ -989,6 +973,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts плејер неће наставити репродукцију при покретању апликације Shorts плејер ће наставити репродукцију при покретању апликације + + Омогући кориснички интерфејс таблета Кориснички интерфејс таблета је омогућен diff --git a/src/main/resources/addresources/values-sv-rSE/strings.xml b/src/main/resources/addresources/values-sv-rSE/strings.xml index bee30b259e..1849c920c5 100644 --- a/src/main/resources/addresources/values-sv-rSE/strings.xml +++ b/src/main/resources/addresources/values-sv-rSE/strings.xml @@ -107,6 +107,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Inaktivera gilla- / prenumerationsknapp glow Gilla och prenumerera knappen kommer inte att lysa när det nämns Gilla och prenumerera knappen lyser när det nämns + Dölj albumkort + Skivkorten är dolda + Albumkort är synliga + Dölj crowdfunding-rutan + Crowdfunding-rutan är dold + Crowdfunding-rutan är synlig + Dölj flytande mikrofonknapp + Mikrofon knapp dold + Mikrofonknappen är synlig Dölj den gråa avgränsaren De gråa separatorerna är dolda De gråa separatorerna är synliga @@ -231,6 +240,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Transkriptsektionen är synlig Videobeskrivning Dölj eller visa videobeskrivningskomponenter + Filterfält + Dölj eller visa filterfältet i flödet, sök och relaterade videor + Dölj i flöde + Dold i flödet + Visas i flöde + Dölj i sökningen + Dold i sökningen + Visas i sök + Dölj i relaterade videor + Dold i relaterade videor + Visas i relaterade videor + Kommentarer + Dölj eller visa kommentarskomponenter + Dölj \'Kommentarer från medlemmar\' header + \'Kommentarer från medlemmar\' huvudet är dolt + \'Kommentarer från medlemmar\' header visas + Dölj kommentarsfältet + Sektionen för kommentarer är dold + Sektionen för kommentarer visas + Dölj knappen \'Skapa en kort\' + \'Skapa en kort\' knappen är dold + Knappen \'Skapa en kort\' visas + Dölj förhandsgranskningskommentar + Förhandsgranska kommentaren är dold + Förhandsgranska kommentar är synlig + Dölj tack-knappen + Tack-knappen är dold + Tackknappen är synlig + Dölj tidsstämplar och emoji-knappar + Tidsstämpel och emoji-knappar är dolda + Knappar för tidsstämpel och emoji visas Dölj YouTube Doodles Sökfältet Doodles är dolda @@ -427,22 +467,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Spara till spellistan knappen är dold Knappen spara till spellista är synlig - - Dölj knappen för automatisk uppspelning - Knappen för automatisk uppspelning är dold - Knappen för automatisk uppspelning är synlig - - - - Dölj bildtextknapp - Bildtext-knappen är dold - Bildtextsknappen är synlig - - - Dölj cast-knapp - Cast-knappen är dold - Cast-knappen är synlig - Navigeringsknappar Dölj eller ändra knappar i navigeringsfältet @@ -515,66 +539,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Titta i VR-menyn är dold Titta i VR-menyn är synlig - - Dölj tidigare & nästa videoknappar - Knappar är dolda - Knappar är synliga - - - Dölj albumkort - Skivkorten är dolda - Albumkort är synliga - - - Kommentarer - Dölj eller visa kommentarskomponenter - Dölj \'Kommentarer från medlemmar\' header - \'Kommentarer från medlemmar\' huvudet är dolt - \'Kommentarer från medlemmar\' header visas - Dölj kommentarsfältet - Sektionen för kommentarer är dold - Sektionen för kommentarer visas - Dölj knappen \'Skapa en kort\' - \'Skapa en kort\' knappen är dold - Knappen \'Skapa en kort\' visas - Dölj förhandsgranskningskommentar - Förhandsgranska kommentaren är dold - Förhandsgranska kommentar är synlig - Dölj tack-knappen - Tack-knappen är dold - Tackknappen är synlig - Dölj tidsstämplar och emoji-knappar - Tidsstämpel och emoji-knappar är dolda - Knappar för tidsstämpel och emoji visas - - - Dölj crowdfunding-rutan - Crowdfunding-rutan är dold - Crowdfunding-rutan är synlig + + Dölj tidigare & nästa videoknappar + Knappar är dolda + Knappar är synliga + Dölj cast-knapp + Cast-knappen är dold + Cast-knappen är synlig + + Dölj bildtextknapp + Bildtext-knappen är dold + Bildtextsknappen är synlig + Dölj knappen för automatisk uppspelning + Knappen för automatisk uppspelning är dold + Knappen för automatisk uppspelning är synlig Dölj slutskärmskort Slutskärmskort är dolda Slutskärmskort är synliga - - Filterfält - Dölj eller visa filterfältet i flödet, sök och relaterade videor - Dölj i flöde - Dold i flödet - Visas i flöde - Dölj i sökningen - Dold i sökningen - Visas i sök - Dölj i relaterade videor - Dold i relaterade videor - Visas i relaterade videor - - - Dölj flytande mikrofonknapp - Mikrofon knapp dold - Mikrofonknappen är synlig - Inaktivera omgivningsläge i helskärm Omgivningsläge inaktiverat @@ -599,6 +583,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Miniatyrens sökfält är synlig + Shorts spelare + Dölj eller visa komponenter i Shorts spelaren Dölj Shorts i hemmatningsflödet Shorts i hemmatningsflödet är dolda @@ -989,6 +975,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts spelare kommer inte att återupptas vid appstart Shorts spelare kommer att återupptas vid appstart + + Autoplay Shorts + Shorts kommer automatiskt spela upp + Shorts kommer att upprepas + Autoplay Shorts bakgrund spela + Shorts bakgrundsuppspelning kommer automatiskt spela + Shorts bakgrundsuppspelning upprepas + Aktivera surfplattans layout Surfplattans layout är aktiverad diff --git a/src/main/resources/addresources/values-sw-rKE/strings.xml b/src/main/resources/addresources/values-sw-rKE/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-sw-rKE/strings.xml +++ b/src/main/resources/addresources/values-sw-rKE/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-ta-rIN/strings.xml b/src/main/resources/addresources/values-ta-rIN/strings.xml index 6305be548e..f90be04c50 100644 --- a/src/main/resources/addresources/values-ta-rIN/strings.xml +++ b/src/main/resources/addresources/values-ta-rIN/strings.xml @@ -102,13 +102,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -129,20 +122,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -197,6 +181,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-te-rIN/strings.xml b/src/main/resources/addresources/values-te-rIN/strings.xml index be82d6b2fd..8ca6240c50 100644 --- a/src/main/resources/addresources/values-te-rIN/strings.xml +++ b/src/main/resources/addresources/values-te-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-th-rTH/strings.xml b/src/main/resources/addresources/values-th-rTH/strings.xml index 58b62a7172..2c52519760 100644 --- a/src/main/resources/addresources/values-th-rTH/strings.xml +++ b/src/main/resources/addresources/values-th-rTH/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -256,6 +240,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-tr-rTR/strings.xml b/src/main/resources/addresources/values-tr-rTR/strings.xml index 056dfbc3c3..bdd9685b57 100644 --- a/src/main/resources/addresources/values-tr-rTR/strings.xml +++ b/src/main/resources/addresources/values-tr-rTR/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Beğen / Abone ol düğmesi parlamasını devre dışı bırak Beğen ve abone ol düğmesi bahsedildiğinde parlamayacak Beğen ve abone ol düğmesi bahsedildiğinde parlayacak + Albüm kartlarını gizle + Albüm kartları gizleniyor + Albüm kartları gösteriliyor + Bağış etkinliklerini gizle + Bağış etkinliği kutuları gizleniyor + Bağış etkinliği kutuları gösteriliyor + Alttaki mikrofon butonunu gizle + Alttaki mikrofon butonu gizleniyor + Alttaki mikrofon butonu gösteriliyor Gri ayırıcıları gizle Gri ayırıcılar gizleniyor Gri ayırıcılar gösteriliyor @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Transkript kısmı gösteriliyor Video açıklaması Video açıklamasındaki öğeleri gizle veya göster + Filtreleme çubuğu + Akışta, arama sonuçlarında ve ilgili videolardaki filtreleme çubuğunu gizle veya göster + Akıştakini gizle + Akıştaki gizleniyor + Akıştaki gösteriliyor + Arama sonuçlarındakini gizle + Arama sonuçlarındaki gizleniyor + Arama sonuçlarındaki gösteriliyor + Alakalı videolardakini gizle + Alakalı videolardaki gizleniyor + Alakalı videolardaki gösteriliyor + Yorumlar + Yorumlar kısmındaki öğeleri gizle veya göster + \'Üyeler tarafından yapılan yorumlar\' başlığını gizle + \'Üyeler tarafından yapılan yorumlar\' başlığı gizli + \'Üyeler tarafından yapılan yorumlar\' başlığı görünür + Yorumlar kısmını gizle + Yorumlar kısmı gizli + Yorumlar kısmı görünür + \'Short oluştur\' düğmesini gizle + \'Short oluştur\' düğmesi gizli + \'Short oluştur\' düğmesi görünür + Önizlenen yorumu gizle + Önizlenen yorum gizleniyor + Önizlenen yorum gösteriliyor + Teşekkürler butonunu gizle + \"Teşekkürler\" butonu gizleniyor + \"Teşekkürler\" butonu gösteriliyor + Zaman damgasını ve emoji butonlarını gizle + Zaman damgası ve emoji düğmeleri gizli + Zaman damgası ve emoji düğmeleri görünür YouTube Doodle\'larını gizle Arama çubuğu Doodle\'ları gizli @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t \"Kaydet\" butonu gizleniyor \"Kaydet\" butonu gösteriliyor - - Otomatik oynatma butonunu gizle - Otomatik oynatma butonu gizleniyor - Otomatik oynatma butonu gösteriliyor - - - - Altyazı butonunu gizle - Altyazı butonu gizleniyor - Altyazı butonu gösteriliyor - - - Yansıtma butonunu gizle - Yansıtma butonu gizleniyor - Yansıtma butonu gösteriliyor - Gezinme butonları Gezinme çubuğundaki butonları gizle veya değiştir @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t \"VR modunda izle\" butonu gizleniyor \"VR modunda izle\" butonu gösteriliyor - - Önceki ve Sonraki video butonlarını gizle - Önceki ve Sonraki video butonları gizleniyor - Önceki ve Sonraki video butonları gösteriliyor - - - Albüm kartlarını gizle - Albüm kartları gizleniyor - Albüm kartları gösteriliyor - - - Yorumlar - Yorumlar kısmındaki öğeleri gizle veya göster - \'Üyeler tarafından yapılan yorumlar\' başlığını gizle - \'Üyeler tarafından yapılan yorumlar\' başlığı gizli - \'Üyeler tarafından yapılan yorumlar\' başlığı görünür - Yorumlar kısmını gizle - Yorumlar kısmı gizli - Yorumlar kısmı görünür - \'Short oluştur\' düğmesini gizle - \'Short oluştur\' düğmesi gizli - \'Short oluştur\' düğmesi görünür - Önizlenen yorumu gizle - Önizlenen yorum gizleniyor - Önizlenen yorum gösteriliyor - Teşekkürler butonunu gizle - \"Teşekkürler\" butonu gizleniyor - \"Teşekkürler\" butonu gösteriliyor - Zaman damgasını ve emoji butonlarını gizle - Zaman damgası ve emoji düğmeleri gizli - Zaman damgası ve emoji düğmeleri görünür - - - Bağış etkinliklerini gizle - Bağış etkinliği kutuları gizleniyor - Bağış etkinliği kutuları gösteriliyor + + Önceki ve Sonraki video butonlarını gizle + Önceki ve Sonraki video butonları gizleniyor + Önceki ve Sonraki video butonları gösteriliyor + Yansıtma butonunu gizle + Yansıtma butonu gizleniyor + Yansıtma butonu gösteriliyor + + Altyazı butonunu gizle + Altyazı butonu gizleniyor + Altyazı butonu gösteriliyor + Otomatik oynatma butonunu gizle + Otomatik oynatma butonu gizleniyor + Otomatik oynatma butonu gösteriliyor Bitiş ekranı kartlarını gizle Bitiş ekranı kartları gizleniyor Bitiş ekranı kartları gösteriliyor - - Filtreleme çubuğu - Akışta, arama sonuçlarında ve ilgili videolardaki filtreleme çubuğunu gizle veya göster - Akıştakini gizle - Akıştaki gizleniyor - Akıştaki gösteriliyor - Arama sonuçlarındakini gizle - Arama sonuçlarındaki gizleniyor - Arama sonuçlarındaki gösteriliyor - Alakalı videolardakini gizle - Alakalı videolardaki gizleniyor - Alakalı videolardaki gösteriliyor - - - Alttaki mikrofon butonunu gizle - Alttaki mikrofon butonu gizleniyor - Alttaki mikrofon butonu gösteriliyor - Tam ekranda ambiyans modunu devre dışı bırak Tam ekranda ambiyans modu devre dışı @@ -978,6 +962,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts oynatıcı açılışta devam etmeyecek Shorts oynatıcı açılışta devam edecek + + Tablet düzenini etkinleştir Tablet düzeni etkin diff --git a/src/main/resources/addresources/values-uk-rUA/strings.xml b/src/main/resources/addresources/values-uk-rUA/strings.xml index b5bba6597d..b721d166d8 100644 --- a/src/main/resources/addresources/values-uk-rUA/strings.xml +++ b/src/main/resources/addresources/values-uk-rUA/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Вимкнути відблиск кнопок \"Подобається\" / \"Підписатися\" Кнопки \"Подобається\" та \"Підписатися\" не відблискуватимуть при згадуванні Кнопки \"Подобається\" та \"Підписатися\" відблискуватимуть при згадуванні + Приховати картки альбому + Картки альбому приховано + Картки альбому показуються + Приховати скриню фінансування + Скриньку фінансування приховано + Скринька фінансування показується + Приховати кнопку мікрофона + Плаваючу кнопку мікрофона приховано + Плаваюча кнопка мікрофона показується Приховати сірі роздільники Сірі роздільники між елементами інтерфейсу приховано Сірі роздільники між елементами інтерфейсу показуються @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Секція \"Текст відео\" показується Опис відео Приховати або показувати компоненти опису відео + Панель фільтрів + Приховати або показувати панель фільтрів у стрічці, пошуку та пов\'язаних відео + Приховати панель у стрічці + Панель фільтрів у стрічці приховано + Панель фільтрів у стрічці показується + Приховати панель у пошуку + Панель фільтрів у пошуку приховано + Панель фільтрів у пошуку показується + Приховати панель у пов\'язаних відео + Панель фільтрів у пов\'язаних відео приховано + Панель фільтрів у пов\'язаних відео показується + Коментарі + Приховати або показувати компоненти секції коментарів + Приховати \"Коментарі від спонсорів\" + Заголовок \"Коментарі від спонсорів\" приховано + Заголовок \"Коментарі від спонсорів\" показується + Приховати секцію коментарів + Секцію коментарів приховано + Секція коментарів показується + Приховати \"Створити Short\" + Кнопку \"Створити Short\" приховано + Кнопка \"Створити Short\" показується + Приховати прев\'ю коментар + Прев\'ю коментар в секції коментарів приховано + Прев\'ю коментар в секції коментарів показується + Приховати \"Дякую\" + Кнопку \"Дякую\" приховано + Кнопка \"Дякую\" показується + Приховати мітку часу та емодзі + Кнопки мітки часу та емодзі приховано + Кнопки мітки часу та емодзі показуються Приховати YouTube Doodles Doodles у пошуковій панелі приховано @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Кнопку \"Зберегти\" приховано Кнопка \"Зберегти\" показується - - Приховати \"Автовідтворення\" - Кнопку \"Автовідтворення\" у відеоплеєрі приховано - Кнопка \"Автовідтворення\" у відеоплеєрі показується - - - - Приховати \"Субтитри\" - Кнопку \"Субтитри\" у відеоплеєрі приховано - Кнопка \"Субтитри\" у відеоплеєрі показується - - - Приховати \"Трансляція\" - Кнопку \"Трансляція\" у відеоплеєрі приховано - Кнопка \"Трансляція\" у відеоплеєрі показується - Кнопки панелі навігації Приховати або змінити кнопки на панелі навігації @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Пункт меню \"Дивитись у VR\" приховано Пункт меню \"Дивитись у VR\" показується - - Приховати кнопки попереднє та наступне - Кнопки попереднього та наступного відео приховано - Кнопки попереднього та наступного відео показуються - - - Приховати картки альбому - Картки альбому приховано - Картки альбому показуються - - - Коментарі - Приховати або показувати компоненти секції коментарів - Приховати \"Коментарі від спонсорів\" - Заголовок \"Коментарі від спонсорів\" приховано - Заголовок \"Коментарі від спонсорів\" показується - Приховати секцію коментарів - Секцію коментарів приховано - Секція коментарів показується - Приховати \"Створити Short\" - Кнопку \"Створити Short\" приховано - Кнопка \"Створити Short\" показується - Приховати прев\'ю коментар - Прев\'ю коментар в секції коментарів приховано - Прев\'ю коментар в секції коментарів показується - Приховати \"Дякую\" - Кнопку \"Дякую\" приховано - Кнопка \"Дякую\" показується - Приховати мітку часу та емодзі - Кнопки мітки часу та емодзі приховано - Кнопки мітки часу та емодзі показуються - - - Приховати скриню фінансування - Скриньку фінансування приховано - Скринька фінансування показується + + Приховати кнопки попереднє та наступне + Кнопки попереднього та наступного відео приховано + Кнопки попереднього та наступного відео показуються + Приховати \"Трансляція\" + Кнопку \"Трансляція\" у відеоплеєрі приховано + Кнопка \"Трансляція\" у відеоплеєрі показується + + Приховати \"Субтитри\" + Кнопку \"Субтитри\" у відеоплеєрі приховано + Кнопка \"Субтитри\" у відеоплеєрі показується + Приховати \"Автовідтворення\" + Кнопку \"Автовідтворення\" у відеоплеєрі приховано + Кнопка \"Автовідтворення\" у відеоплеєрі показується Приховати картки кінцевого екрана Картки кінцевого екрана приховано Картки кінцевого екрана показуються - - Панель фільтрів - Приховати або показувати панель фільтрів у стрічці, пошуку та пов\'язаних відео - Приховати панель у стрічці - Панель фільтрів у стрічці приховано - Панель фільтрів у стрічці показується - Приховати панель у пошуку - Панель фільтрів у пошуку приховано - Панель фільтрів у пошуку показується - Приховати панель у пов\'язаних відео - Панель фільтрів у пов\'язаних відео приховано - Панель фільтрів у пов\'язаних відео показується - - - Приховати кнопку мікрофона - Плаваючу кнопку мікрофона приховано - Плаваюча кнопка мікрофона показується - Вимкнути кінематографічне освітлення Кінематографічне освітлення в повноекранному режимі вимкнуто @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Панель прогресу у прев\'ю переглянутих відео показується + Плеєр Shorts + Приховати або показувати компоненти у плеєрі Shorts Приховати Shorts у стрічці Shorts у домашній стрічці приховано @@ -988,6 +974,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Плеєр Shorts вимкнуто при запуску додатку Плеєр Shorts увімкнуто при запуску додатку + + Автовідтворення Shorts + Shorts будуть автоматично відтворюватися одне за одним + Shorts будуть повторюватися + Автовідтворення Shorts у фоні + Shorts будуть автоматично відтворюватися одне за одним у фоновому режимі + Shorts будуть повторюватися у фоновому режимі + Увімкнути планшетний інтерфейс Планшетний інтерфейс увімкнуто diff --git a/src/main/resources/addresources/values-ur-rIN/strings.xml b/src/main/resources/addresources/values-ur-rIN/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-ur-rIN/strings.xml +++ b/src/main/resources/addresources/values-ur-rIN/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-uz-rUZ/strings.xml b/src/main/resources/addresources/values-uz-rUZ/strings.xml index cf161c4f4b..9e4aa8006f 100644 --- a/src/main/resources/addresources/values-uz-rUZ/strings.xml +++ b/src/main/resources/addresources/values-uz-rUZ/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + diff --git a/src/main/resources/addresources/values-vi-rVN/strings.xml b/src/main/resources/addresources/values-vi-rVN/strings.xml index 86064038c2..57b086d0a8 100644 --- a/src/main/resources/addresources/values-vi-rVN/strings.xml +++ b/src/main/resources/addresources/values-vi-rVN/strings.xml @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t Nút thích / đăng ký tỏa sáng Nút thích và đăng ký sẽ không tỏa sáng khi được nhắc đến Nút thích và đăng ký sẽ tỏa sáng khi được nhắc đến + Ẩn các thẻ album + Các thẻ album được ẩn + Các thẻ album được hiện + Ẩn hộp chiến dịch gây quỹ + Hộp chiến dịch gây quỹ được ẩn + Hộp chiến dịch gây quỹ được hiện + Ẩn nút micrô nổi + Nút micrô được ẩn + Nút micrô được hiện Ẩn dải phân cách màu xám Dải phân cách màu xám được ẩn Dải phân cách màu xám được hiện @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t Phần bản chép lời được hiện Mô tả video Ẩn hoặc hiện các thành phần mô tả video + Thanh bộ lọc + Ẩn hoặc hiện thanh bộ lọc trong bảng tin, tìm kiếm và video liên quan + Ẩn trong bảng tin + Đã ẩn trong bảng tin + Đã hiện trong bảng tin + Ẩn tròn tìm kiếm + Đã ẩn trong tìm kiếm + Đã hiện trong tìm kiếm + Ẩn trong video liên quan + Đã ẩn trong video liên quan + Đã hiện trong video liên quan + Bình luận + Ẩn hoặc hiện các thành phần bình luận + Ẩn tiêu đề \'Bình luận bởi hội viên\' + Tiêu đề \'Bình luận bởi hội viên\' được ẩn + Tiêu đề \'Bình luận bởi hội viên\' được hiện + Ẩn phần bình luận + Phần Bình luận được ẩn + Phần Bình luận được hiện + Ẩn nút \'Tạo video ngắn\' + Nút \'Tạo video ngắn\' được ẩn + Nút \'Tạo video ngắn\' được hiện + Ẩn xem trước bình luận + Xem trước bình luận được ẩn + Xem trước bình luận được hiện + Ẩn nút cảm ơn + Nút cảm ơn được ẩn + Nút cảm ơn được hiện + Ẩn mốc thời gian và các nút biểu tượng cảm xúc + Mốc thời gian và các nút biểu tượng cảm xúc được ẩn + Mốc thời gian và các nút biểu tượng cảm xúc được hiện Bộ lọc tùy chỉnh Ẩn các thành phần dùng bộ lọc tùy chỉnh @@ -422,22 +462,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Nút lưu vào danh sách phát được ẩn Nút lưu vào danh sách phát được hiện - - Ẩn nút tự động phát - Nút tự động phát được ẩn - Nút tự động phát được hiện - - - - Ẩn nút phụ đề - Nút phụ đề được ẩn - Nút phụ đề được hiện - - - Ẩn nút truyền - Nút Truyền được ẩn - Nút Truyền được hiển thị - Các nút điều hướng Ẩn hoặc hiện các nút ở thanh điều hướng @@ -510,66 +534,26 @@ This is because Crowdin requires temporarily flattening this file and removing t Trình đơn xem trong thực tế ảo được ẩn Trình đơn xem trong thực tế ảo được hiện - - Ẩn các nút video trước đó & tiếp theo - Các nút được ẩn - Các nút được hiện - - - Ẩn các thẻ album - Các thẻ album được ẩn - Các thẻ album được hiện - - - Bình luận - Ẩn hoặc hiện các thành phần bình luận - Ẩn tiêu đề \'Bình luận bởi hội viên\' - Tiêu đề \'Bình luận bởi hội viên\' được ẩn - Tiêu đề \'Bình luận bởi hội viên\' được hiện - Ẩn phần bình luận - Phần Bình luận được ẩn - Phần Bình luận được hiện - Ẩn nút \'Tạo video ngắn\' - Nút \'Tạo video ngắn\' được ẩn - Nút \'Tạo video ngắn\' được hiện - Ẩn xem trước bình luận - Xem trước bình luận được ẩn - Xem trước bình luận được hiện - Ẩn nút cảm ơn - Nút cảm ơn được ẩn - Nút cảm ơn được hiện - Ẩn mốc thời gian và các nút biểu tượng cảm xúc - Mốc thời gian và các nút biểu tượng cảm xúc được ẩn - Mốc thời gian và các nút biểu tượng cảm xúc được hiện - - - Ẩn hộp chiến dịch gây quỹ - Hộp chiến dịch gây quỹ được ẩn - Hộp chiến dịch gây quỹ được hiện + + Ẩn các nút video trước đó & tiếp theo + Các nút được ẩn + Các nút được hiện + Ẩn nút truyền + Nút Truyền được ẩn + Nút Truyền được hiển thị + + Ẩn nút phụ đề + Nút phụ đề được ẩn + Nút phụ đề được hiện + Ẩn nút tự động phát + Nút tự động phát được ẩn + Nút tự động phát được hiện Ẩn thẻ kết thúc màn hình Thẻ kết thúc màn hình được ẩn Thẻ kết thúc màn hình được hiện - - Thanh bộ lọc - Ẩn hoặc hiện thanh bộ lọc trong bảng tin, tìm kiếm và video liên quan - Ẩn trong bảng tin - Đã ẩn trong bảng tin - Đã hiện trong bảng tin - Ẩn tròn tìm kiếm - Đã ẩn trong tìm kiếm - Đã hiện trong tìm kiếm - Ẩn trong video liên quan - Đã ẩn trong video liên quan - Đã hiện trong video liên quan - - - Ẩn nút micrô nổi - Nút micrô được ẩn - Nút micrô được hiện - Tắt chế độ môi trường trong toàn màn hình Chế độ môi trường được tắt @@ -962,6 +946,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Trinh phát Shorts sẽ không tiếp tục khi ứng dụng khởi chạy Trinh phát Shorts sẽ tiếp tục khi ứng dụng khởi chạy + + Bật bố cục máy tính bảng Bố cục máy tính bảng được bật diff --git a/src/main/resources/addresources/values-zh-rCN/strings.xml b/src/main/resources/addresources/values-zh-rCN/strings.xml index 72f5244119..d5af678615 100644 --- a/src/main/resources/addresources/values-zh-rCN/strings.xml +++ b/src/main/resources/addresources/values-zh-rCN/strings.xml @@ -105,6 +105,15 @@ This is because Crowdin requires temporarily flattening this file and removing t 禁用点赞 / 订阅按钮动效 点赞和订阅按钮点击时无动效 点赞和订阅按钮点击时有动效 + 隐藏相册卡 + 相册已隐藏 + 显示相册卡 + 隐藏众筹框 + 聚合供资框已隐藏 + 显示集资框 + 隐藏浮动麦克风 + 麦克风按钮隐藏 + 麦克风按钮显示 隐藏灰色分隔符 灰色分隔符已隐藏 灰色分隔符已显示 @@ -229,6 +238,37 @@ This is because Crowdin requires temporarily flattening this file and removing t 显示字幕部分 视频描述 隐藏或显示视频描述组件 + 过滤栏 + 在新闻源、搜索和相关视频中隐藏或显示过滤栏 + 在新闻源中隐藏 + 在订阅中隐藏 + 在订阅源中显示 + 在搜索中隐藏 + 在搜索中隐藏 + 在搜索中显示 + 隐藏相关视频 + 在相关视频中隐藏 + 显示在相关视频 + 评论 + 隐藏或显示评论部分组件 + 隐藏会员头部的评论 + 会员头的评论已隐藏 + 成员标题的评论已显示 + 隐藏评论部分 + 评论部分已隐藏 + 评论部分已显示 + 隐藏“创建 Short按钮 + “创建 Short 按钮被隐藏 + 显示“创建 Short”按钮 + 隐藏预览评论 + 预览已隐藏 + 显示预览评论 + 隐藏感谢按钮 + 感谢按钮已隐藏 + 已显示感谢按钮 + 隐藏时间戳和表情按钮 + 隐藏时间戳和表情按钮 + 显示时间戳和表情按钮 隐藏 YouTube Doodles 搜索栏门框已隐藏 @@ -425,22 +465,6 @@ This is because Crowdin requires temporarily flattening this file and removing t 保存到播放列表按钮隐藏 显示播放列表按钮 - - 隐藏自动播放按钮 - 自动播放按钮已隐藏 - 显示自动播放按钮 - - - - 隐藏标题按钮 - 字幕按钮已隐藏 - 显示字幕按钮 - - - 隐藏投射按钮 - 已隐藏投屏按钮 - 已显示投屏按钮 - Navigation buttons 隐藏或更改导航栏中的按钮 @@ -513,66 +537,26 @@ This is because Crowdin requires temporarily flattening this file and removing t 在 VR 菜单中观看是隐藏的 在VR菜单中查看 - - 隐藏之前的 & 下一个视频按钮 - 按钮被隐藏 - 显示按钮 - - - 隐藏相册卡 - 相册已隐藏 - 显示相册卡 - - - 评论 - 隐藏或显示评论部分组件 - 隐藏会员头部的评论 - 会员头的评论已隐藏 - 成员标题的评论已显示 - 隐藏评论部分 - 评论部分已隐藏 - 评论部分已显示 - 隐藏“创建 Short按钮 - “创建 Short 按钮被隐藏 - 显示“创建 Short”按钮 - 隐藏预览评论 - 预览已隐藏 - 显示预览评论 - 隐藏感谢按钮 - 感谢按钮已隐藏 - 已显示感谢按钮 - 隐藏时间戳和表情按钮 - 隐藏时间戳和表情按钮 - 显示时间戳和表情按钮 - - - 隐藏众筹框 - 聚合供资框已隐藏 - 显示集资框 + + 隐藏之前的 & 下一个视频按钮 + 按钮被隐藏 + 显示按钮 + 隐藏投射按钮 + 已隐藏投屏按钮 + 已显示投屏按钮 + + 隐藏标题按钮 + 字幕按钮已隐藏 + 显示字幕按钮 + 隐藏自动播放按钮 + 自动播放按钮已隐藏 + 显示自动播放按钮 隐藏结束屏幕卡片 结束屏幕卡已隐藏 显示结束屏卡 - - 过滤栏 - 在新闻源、搜索和相关视频中隐藏或显示过滤栏 - 在新闻源中隐藏 - 在订阅中隐藏 - 在订阅源中显示 - 在搜索中隐藏 - 在搜索中隐藏 - 在搜索中显示 - 隐藏相关视频 - 在相关视频中隐藏 - 显示在相关视频 - - - 隐藏浮动麦克风 - 麦克风按钮隐藏 - 麦克风按钮显示 - 全屏禁用环境模式 环境模式已禁用 @@ -986,6 +970,8 @@ This is because Crowdin requires temporarily flattening this file and removing t 应用启动时短暂播放器将不会恢复 应用启动时短暂播放器将恢复 + + 启用平板电脑布局 平板电脑布局已启用 diff --git a/src/main/resources/addresources/values-zh-rTW/strings.xml b/src/main/resources/addresources/values-zh-rTW/strings.xml index c74f37b81c..b2e934c08f 100644 --- a/src/main/resources/addresources/values-zh-rTW/strings.xml +++ b/src/main/resources/addresources/values-zh-rTW/strings.xml @@ -36,7 +36,7 @@ This is because Crowdin requires temporarily flattening this file and removing t 檢查失敗 開啟官方網站 忽略 - <h5>此應用程式似乎並非由您修補。</h5><br>該應用程式可能無法正常運作,<b>可能有害甚至存在危險。</b><br><br>這些檢查表明該應用程式是預先修補的或來自其他來源:<br><br><small>%1$s</small><br>強烈建議<b>解除安裝此應用程式並自行修補</b>,以確保您使用的是經過驗證且安全的應用程式。<p><br>如果忽略,此警告僅會顯示兩次。 + <h5>這個應用程式似乎並非由您修補。</h5><br>這個應用程式可能無法正常運作,<b>可能有害甚至存在危險。</b><br><br>這些檢查表明該應用程式是預先修補的或來自其他來源:<br><br><small>%1$s</small><br>強烈建議<b>解除安裝此應用程式並自行修補</b>,以確保您使用的是經過驗證且安全的應用程式。<p><br>如果忽略,此警告僅會顯示兩次。 在其他裝置上修補 未由 ReVanced Manager 安裝 修補時間超過 10 分鐘 @@ -106,6 +106,15 @@ This is because Crowdin requires temporarily flattening this file and removing t 停用「喜歡」和「訂閱」按鈕的發光效果 提及時,「喜歡」和「訂閱」按鈕不會發光 提及時,「喜歡」和「訂閱」按鈕會發光 + 隱藏專輯卡 + 已隱藏專輯卡 + 已顯示專輯卡 + 隱藏群眾募資 + 已隱藏群眾募資 + 已顯示群眾募資 + 隱藏語音辨識按鈕 + 已隱藏語音辨識按鈕 + 已顯示語音辨識按鈕 隱藏灰色分隔線 已隱藏灰色分隔線 已顯示灰色分隔線 @@ -230,6 +239,37 @@ This is because Crowdin requires temporarily flattening this file and removing t 已顯示字幕記錄區 影片描述欄 隱藏或顯示影片描述欄內容 + 篩選列 + 隱藏或顯示動態消息、搜尋、相關影片篩選列 + 在動態消息中隱藏 + 在動態消息中隱藏 + 在動態消息中顯示 + 在搜尋中隱藏 + 在搜尋中隱藏 + 在搜尋中顯示 + 隱藏相關影片 + 在相關影片中隱藏 + 在相關影片中顯示 + 留言區 + 隱藏或顯示影片留言區內容 + 隱藏「會員留言」標題 + 已隱藏「會員留言」標題 + 已顯示「會員留言」標題 + 隱藏留言區 + 已隱藏留言區塊 + 已顯示留言區塊 + 隱藏「建立 Short」按鈕 + 已隱藏「建立 Short」按鈕 + 已顯示「建立 Short」按鈕 + 隱藏留言預覽 + 已隱藏留言預覽 + 已顯示留言預覽 + 隱藏超級感謝按鈕 + 已隱藏超級感謝按鈕 + 已顯示超級感謝按鈕 + 隱藏時間戳記和表情按鈕 + 已隱藏時間戳記和表情符號按鈕 + 已顯示時間戳記和表情符號按鈕 隱藏 YouTube Doodles 已隱藏位於搜尋欄的 Doodles @@ -426,22 +466,6 @@ This is because Crowdin requires temporarily flattening this file and removing t 已隱藏儲存到播放列表 已顯示儲存到播放列表 - - 隱藏自動播放按鈕 - 已隱藏自動播放按鈕 - 已顯示自動播放按鈕 - - - - 隱藏字幕按鈕 - 已隱藏字幕按鈕 - 已顯示字幕按鈕 - - - 隱藏投放按鈕 - 已隱藏投放按鈕 - 已顯示投放按鈕 - 導覽列按鈕 隱藏或變更導覽區按鈕 @@ -514,66 +538,26 @@ This is because Crowdin requires temporarily flattening this file and removing t 已隱藏在 VR 觀看 已顯示在 VR 觀看 - - 隱藏上一部和下一部影片按紐 - 已隱藏按鈕 - 已顯示按鈕 - - - 隱藏專輯卡 - 已隱藏專輯卡 - 已顯示專輯卡 - - - 留言區 - 隱藏或顯示影片留言區內容 - 隱藏「會員留言」標題 - 已隱藏「會員留言」標題 - 已顯示「會員留言」標題 - 隱藏留言區 - 已隱藏留言區塊 - 已顯示留言區塊 - 隱藏「建立 Short」按鈕 - 已隱藏「建立 Short」按鈕 - 已顯示「建立 Short」按鈕 - 隱藏留言預覽 - 已隱藏留言預覽 - 已顯示留言預覽 - 隱藏超級感謝按鈕 - 已隱藏超級感謝按鈕 - 已顯示超級感謝按鈕 - 隱藏時間戳記和表情按鈕 - 已隱藏時間戳記和表情符號按鈕 - 已顯示時間戳記和表情符號按鈕 - - - 隱藏群眾募資 - 已隱藏群眾募資 - 已顯示群眾募資 + + 隱藏上一部和下一部影片按紐 + 已隱藏按鈕 + 已顯示按鈕 + 隱藏投放按鈕 + 已隱藏投放按鈕 + 已顯示投放按鈕 + + 隱藏字幕按鈕 + 已隱藏字幕按鈕 + 已顯示字幕按鈕 + 隱藏自動播放按鈕 + 已隱藏自動播放按鈕 + 已顯示自動播放按鈕 隱藏片尾資訊卡 已隱藏片尾資訊卡 已顯示片尾資訊卡 - - 篩選列 - 隱藏或顯示動態消息、搜尋、相關影片篩選列 - 在動態消息中隱藏 - 在動態消息中隱藏 - 在動態消息中顯示 - 在搜尋中隱藏 - 在搜尋中隱藏 - 在搜尋中顯示 - 隱藏相關影片 - 在相關影片中隱藏 - 在相關影片中顯示 - - - 隱藏語音辨識按鈕 - 已隱藏語音辨識按鈕 - 已顯示語音辨識按鈕 - 在全螢幕狀態下停用微光效果 已停用微光效果 @@ -598,6 +582,8 @@ This is because Crowdin requires temporarily flattening this file and removing t 已顯示縮圖進度列 + Shorts 播放器 + 隱藏或顯示 Shorts 播放器的元件 隱藏 Shorts 首頁動態消息 已在首頁動態影片中隱藏 Shorts @@ -644,6 +630,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 隱藏 [綠色畫面] 按鈕 已隱藏 [綠色畫面] 按鈕 已顯示 [綠色畫面] 按鈕 + 隱藏主題標籤按鈕 + 主題標籤按鈕已隱藏 + 主題標籤按鈕已顯示 隱藏搜尋建議 已隱藏搜尋建議 已顯示搜尋建議 @@ -985,6 +974,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts 播放器將不會在應用程式啟動後復原 Shorts 播放器將會在應用程式啟動後復原 + + 自動播放 Shorts + Shorts 將自動播放 + Shorts 將重複播放 + 在背景自動播放 Shorts + Shorts 將在背景自動播放 + Shorts 將在背景重複播放 + 啟用平板介面 已啟用平板介面 diff --git a/src/main/resources/addresources/values-zu-rZA/strings.xml b/src/main/resources/addresources/values-zu-rZA/strings.xml index 4281ce7a40..57cf7f2c5b 100644 --- a/src/main/resources/addresources/values-zu-rZA/strings.xml +++ b/src/main/resources/addresources/values-zu-rZA/strings.xml @@ -95,13 +95,6 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - @@ -122,20 +115,11 @@ This is because Crowdin requires temporarily flattening this file and removing t - - - - - - - + + - - - - @@ -184,6 +168,8 @@ This is because Crowdin requires temporarily flattening this file and removing t + + From 1952f3b3c4bca08ed0f6e5b1117e0a6c51f00ed2 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:13:24 -0400 Subject: [PATCH 10/65] feat(YouTube - Hide layout components): Hide player shopping shelf (#3804) --- .../layout/hide/general/HideLayoutComponentsPatch.kt | 6 +++++- src/main/resources/addresources/values/strings.xml | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index b9e8c8598f..dc7333fcf6 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -91,6 +91,10 @@ object HideLayoutComponentsPatch : BytecodePatch( override fun execute(context: BytecodeContext) { AddResourcesPatch(this::class) + SettingsPatch.PreferenceScreen.ADS.addPreferences( + SwitchPreference("revanced_hide_player_store_shelf"), + ) + SettingsPatch.PreferenceScreen.PLAYER.addPreferences( PreferenceScreen( key = "revanced_hide_description_components_screen", @@ -113,7 +117,7 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_comments_thanks_button"), SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons") ), - sorting = PreferenceScreen.Sorting.UNSORTED + sorting = Sorting.UNSORTED ), SwitchPreference("revanced_hide_channel_bar"), SwitchPreference("revanced_hide_channel_guidelines"), diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index 5dfc34f8b4..cda396bd01 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Disable like / subscribe button glow Like and subscribe button will not glow when mentioned Like and subscribe button will glow when mentioned + Hide player shopping shelf + Shopping shelf is hidden + Shopping shelf is shown Hide album cards Album cards are hidden Album cards are shown From fd57f581dd8dc5a68744067735170f15a0aa6061 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Oct 2024 02:15:28 +0000 Subject: [PATCH 11/65] chore: Release v4.18.0-dev.4 [skip ci] # [4.18.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.3...v4.18.0-dev.4) (2024-10-23) ### Features * **YouTube - Hide layout components:** Hide player shopping shelf ([#3804](https://github.com/ReVanced/revanced-patches/issues/3804)) ([1952f3b](https://github.com/ReVanced/revanced-patches/commit/1952f3b3c4bca08ed0f6e5b1117e0a6c51f00ed2)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a50f20d6e5..89190fbb29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [4.18.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.3...v4.18.0-dev.4) (2024-10-23) + + +### Features + +* **YouTube - Hide layout components:** Hide player shopping shelf ([#3804](https://github.com/ReVanced/revanced-patches/issues/3804)) ([1952f3b](https://github.com/ReVanced/revanced-patches/commit/1952f3b3c4bca08ed0f6e5b1117e0a6c51f00ed2)) + # [4.18.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.2...v4.18.0-dev.3) (2024-10-22) diff --git a/gradle.properties b/gradle.properties index 0c5922d460..73e55cf7da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 4.18.0-dev.3 +version = 4.18.0-dev.4 From 99851dd03278aca015c6243bfd500df3206b5e28 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:51:52 -0400 Subject: [PATCH 12/65] chore: Sync translations (#3807) --- .../resources/addresources/values-ar-rSA/strings.xml | 3 +++ .../resources/addresources/values-cs-rCZ/strings.xml | 3 +++ .../resources/addresources/values-da-rDK/strings.xml | 3 +++ .../resources/addresources/values-de-rDE/strings.xml | 3 +++ .../resources/addresources/values-el-rGR/strings.xml | 3 +++ .../resources/addresources/values-es-rES/strings.xml | 3 +++ .../resources/addresources/values-fi-rFI/strings.xml | 3 +++ .../resources/addresources/values-fr-rFR/strings.xml | 3 +++ .../resources/addresources/values-in-rID/strings.xml | 9 ++++++--- .../resources/addresources/values-it-rIT/strings.xml | 3 +++ .../resources/addresources/values-ja-rJP/strings.xml | 3 +++ .../resources/addresources/values-nb-rNO/strings.xml | 3 +++ .../resources/addresources/values-nl-rNL/strings.xml | 3 +++ .../resources/addresources/values-pl-rPL/strings.xml | 3 +++ .../resources/addresources/values-pt-rPT/strings.xml | 3 +++ .../resources/addresources/values-ro-rRO/strings.xml | 3 +++ .../resources/addresources/values-ru-rRU/strings.xml | 5 ++++- .../resources/addresources/values-sv-rSE/strings.xml | 3 +++ .../resources/addresources/values-uk-rUA/strings.xml | 3 +++ .../resources/addresources/values-zh-rCN/strings.xml | 3 +++ .../resources/addresources/values-zh-rTW/strings.xml | 3 +++ 21 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/main/resources/addresources/values-ar-rSA/strings.xml b/src/main/resources/addresources/values-ar-rSA/strings.xml index ac85b3a8c8..c49c8c6240 100644 --- a/src/main/resources/addresources/values-ar-rSA/strings.xml +++ b/src/main/resources/addresources/values-ar-rSA/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t تعطيل توهج زر أعجبني / اشتراك لن يتوهج زر أعجبني واشتراك عند ذكره أعجبني واشتراك سوف يتوهج عند الإشارة + إخفاء رف تسوق اللاعب + تم إخفاء رفوف التسوق + يتم عرض رصيف التسوق إخفاء بطاقات الألبوم تم إخفاء بطاقات الألبوم يتم عرض بطاقات الألبوم diff --git a/src/main/resources/addresources/values-cs-rCZ/strings.xml b/src/main/resources/addresources/values-cs-rCZ/strings.xml index 40c099d7a1..53e1dc207b 100644 --- a/src/main/resources/addresources/values-cs-rCZ/strings.xml +++ b/src/main/resources/addresources/values-cs-rCZ/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Zakázat záři tlačítka jako / odebírat Tlačítko se mi líbí a odebírá, pokud je zmíněno Tlačítko se mi líbí a odebírá, když je zmíněno + Skrýt nákupní šelf hráče + Nákupní polička je skrytá + Nákupní polička je zobrazena Skrýt alba Album karty jsou skryty Alba karty jsou zobrazeny diff --git a/src/main/resources/addresources/values-da-rDK/strings.xml b/src/main/resources/addresources/values-da-rDK/strings.xml index 9f04fa7339..858a3886d0 100644 --- a/src/main/resources/addresources/values-da-rDK/strings.xml +++ b/src/main/resources/addresources/values-da-rDK/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktivér som / abonnér knap glow Ligesom og abonnér knap vil ikke gløde når nævnt Ligesom og abonnér knappen vil gløde, når nævnt + Skjul spillerens indkøbshylde + Shopping hylde er skjult + Shopping hylde er vist Skjul albumkort Albumkort er skjult Albumkort vises diff --git a/src/main/resources/addresources/values-de-rDE/strings.xml b/src/main/resources/addresources/values-de-rDE/strings.xml index 4ae6950774..22e39d036a 100644 --- a/src/main/resources/addresources/values-de-rDE/strings.xml +++ b/src/main/resources/addresources/values-de-rDE/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktiviere das Like / Abonnieren Button aufleuchten \"Gefällt mir\" und \"Abonnieren\"-Button wird nicht aufleuchten, wenn er erwähnt wird \"Gefällt mir\" und \"Abonnieren\"-Button wird aufleuchten, wenn er erwähnt wird + Spieler-Einkaufsregal ausblenden + Einkaufsregal ist ausgeblendet + Einkaufsregal wird angezeigt Albumkarten ausblenden Albumkarten sind ausgeblendet Albumkarten werden angezeigt diff --git a/src/main/resources/addresources/values-el-rGR/strings.xml b/src/main/resources/addresources/values-el-rGR/strings.xml index 29f931cb05..a7a0c53b23 100644 --- a/src/main/resources/addresources/values-el-rGR/strings.xml +++ b/src/main/resources/addresources/values-el-rGR/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Απενεργοποίηση λάμψης των κουμπιών «Μου αρέσει» και «Εγγραφή» Τα κουμπιά «Μου αρέσει» και «Εγγραφή» δεν θα λάμπουν όταν αναφέρονται Τα κουμπιά «Μου αρέσει» και «Εγγραφή» θα λάμπουν όταν αναφέρονται + Απόκρυψη ράφι αγορών παίχτη + Το ράφι αγορών είναι κρυμμένο + Το ράφι αγορών εμφανίζεται Κάρτες άλμπουμ Κρυμμένες Εμφανίζονται diff --git a/src/main/resources/addresources/values-es-rES/strings.xml b/src/main/resources/addresources/values-es-rES/strings.xml index 811db83ef4..7a5402e109 100644 --- a/src/main/resources/addresources/values-es-rES/strings.xml +++ b/src/main/resources/addresources/values-es-rES/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Desactivar el brillo del botón de like / suscripción El botón de \"Me gusta\" y \"Suscribir\" no brillará cuando se mencione El botón de \"Me gusta\" y \"Suscribir\" brillará cuando se mencione + Ocultar estampilla de compra del jugador + El armazón de compras está oculto + Se muestra el shelf de la compra Ocultar álbumes Las tarjetas de álbum están ocultas Se muestran las tarjetas de álbum diff --git a/src/main/resources/addresources/values-fi-rFI/strings.xml b/src/main/resources/addresources/values-fi-rFI/strings.xml index 34af32486b..15ee86f552 100644 --- a/src/main/resources/addresources/values-fi-rFI/strings.xml +++ b/src/main/resources/addresources/values-fi-rFI/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Poista tykkää-/tilaa-painikkeiden hehku käytöstä Tykkää- ja tilaa-painikkeet eivät hehku, kun ne mainitaan Tykkää- ja tilaa-painikkeet hehkuvat, kun ne mainitaan + Piilota pelaajan ostoshylly + Ostoshylly on piilotettu + Ostoshylly näytetään Piilota albumikortit Albumikortit on piilotettu Albumikortit näytetään diff --git a/src/main/resources/addresources/values-fr-rFR/strings.xml b/src/main/resources/addresources/values-fr-rFR/strings.xml index 6090aff3ea..12b4d95803 100644 --- a/src/main/resources/addresources/values-fr-rFR/strings.xml +++ b/src/main/resources/addresources/values-fr-rFR/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Désactiver la lueur des boutons \"J\'aime\" / \"Je n\'aime pas\" Les boutons \"J\'aime\" et \"Je n\'aime pas\" ne s\'illuminerons pas lorsqu\'ils sont mentionné Les boutons \"J\'aime\" et \"Je n\'aime pas\" s\'illuminerons lorsqu\'ils sont mentionné + Masquer l\'étagère d\'achat du joueur + L\'étagère d\'achat est cachée + L\'étagère d\'achat est affichée Cacher les cartes d\'album Les cartes d\'album sont cachées Les cartes de l\'album sont affichées diff --git a/src/main/resources/addresources/values-in-rID/strings.xml b/src/main/resources/addresources/values-in-rID/strings.xml index 2ea6d5b9ff..058903a851 100644 --- a/src/main/resources/addresources/values-in-rID/strings.xml +++ b/src/main/resources/addresources/values-in-rID/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Nonaktifkan kilau tombol suka / langganan Tombol suka dan langganan tidak akan berkilau saat ditekan Tombol suka dan langganan akan berkilau saat ditekan + Sembunyikan rak belanja pemutar + Rak belanja disembunyikan + Rak belanja ditampilkan Sembunyikan kartu album Kartu album disembunyikan Kartu album ditampilkan @@ -156,9 +159,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Sembunyikan Panduan Saluran Panduan saluran disembunyikan Panduan saluran ditampilkan - Sembunyikan rak chip - Rak opsi deret disembunyikan - Rak chip ditampilkan + Sembunyikan kepingan rak + Kepingan rak disembunyikan + Kepingan rak ditampilkan Sembunyikan kepingan deret di bawah video Kepingan deret disembunyikan Kepingan yang dapat diperluas ditampilkan diff --git a/src/main/resources/addresources/values-it-rIT/strings.xml b/src/main/resources/addresources/values-it-rIT/strings.xml index 164255268d..6a34ebd15b 100644 --- a/src/main/resources/addresources/values-it-rIT/strings.xml +++ b/src/main/resources/addresources/values-it-rIT/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Disabilita il bagliore del pulsante di / sottoscrizione Come e il pulsante di sottoscrizione non brillerà quando menzionato Come e il pulsante di sottoscrizione brillerà quando menzionato + Nascondi lo scaffale dello shopping + Lo scaffale è nascosto + Lo scaffale è mostrato Nascondi schede album Le schede degli album sono nascoste Le schede degli album sono mostrate diff --git a/src/main/resources/addresources/values-ja-rJP/strings.xml b/src/main/resources/addresources/values-ja-rJP/strings.xml index 8301261729..30a822c8aa 100644 --- a/src/main/resources/addresources/values-ja-rJP/strings.xml +++ b/src/main/resources/addresources/values-ja-rJP/strings.xml @@ -105,6 +105,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 高評価 / チャンネル登録ボタンのアニメーションを無効にする 「高評価」と「チャンネル登録」ボタンのアニメーションは無効です 「高評価」と「チャンネル登録」ボタンのアニメーションは有効です + プレイヤーのショッピング棚を非表示 + ショッピング棚は非表示です + ショッピング棚が表示されます アルバムカードを隠す アルバムカードは非表示です アルバムカードは表示されます diff --git a/src/main/resources/addresources/values-nb-rNO/strings.xml b/src/main/resources/addresources/values-nb-rNO/strings.xml index 7ce357a893..a6bb3d2717 100644 --- a/src/main/resources/addresources/values-nb-rNO/strings.xml +++ b/src/main/resources/addresources/values-nb-rNO/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktiver \'liker\' / abonnér på glød Som og abonner-knappen vil ikke glød når den nevnes Som og abbonér vil glød når det nevnes + Skjul spiller shopping hylle + Handlehylle er skjult + Handlehold/hylle vises Skjul albumkort Albumkort er skjult Albumkort vises diff --git a/src/main/resources/addresources/values-nl-rNL/strings.xml b/src/main/resources/addresources/values-nl-rNL/strings.xml index b57cdc4810..27ac11738b 100644 --- a/src/main/resources/addresources/values-nl-rNL/strings.xml +++ b/src/main/resources/addresources/values-nl-rNL/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Uitschakelen like- / abonneer-knop gloed De knop \'like\' en \'abonneren\' zal niet gloeien wanneer deze genoemd wordt \'Like en abonneren\' knop zal gloeien wanneer genoemd + Verberg spelers boodschappenplank + Winkelwagen is verborgen + Winkelwagen wordt weergegeven Verberg albumkaarten Albumkaarten zijn verborgen Albumkaarten worden weergegeven diff --git a/src/main/resources/addresources/values-pl-rPL/strings.xml b/src/main/resources/addresources/values-pl-rPL/strings.xml index 96a87f337d..b81f9fed10 100644 --- a/src/main/resources/addresources/values-pl-rPL/strings.xml +++ b/src/main/resources/addresources/values-pl-rPL/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Wyłącz polubienie/subskrybuj podświetlenie przycisków Przycisk polubienia i subskrybuj nie pojawi się po wzmiance Przycisk polubienia i subskrybuj się po wzmiance + Ukryj półkę na zakupy gracza + Półka Zakupów jest ukryta + Półka Zakupów jest pokazana Ukryj karty albumu Karty albumów są ukryte Karty albumów są wyświetlane diff --git a/src/main/resources/addresources/values-pt-rPT/strings.xml b/src/main/resources/addresources/values-pt-rPT/strings.xml index 8a42b77e44..501dccab27 100644 --- a/src/main/resources/addresources/values-pt-rPT/strings.xml +++ b/src/main/resources/addresources/values-pt-rPT/strings.xml @@ -105,6 +105,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Desativar brilho do botão de inscrição / Curtir O botão de curtir e assinar não brilhará quando mencionado O botão de curtir e subscrever brilhará quando mencionado + Ocultar prateleira de compras do jogador + Prateleira de compras está escondida + Prateleira de compras é mostrada Esconder cartões de álbuns Cartões de álbuns estão escondidos Cartões de álbum são visíveis diff --git a/src/main/resources/addresources/values-ro-rRO/strings.xml b/src/main/resources/addresources/values-ro-rRO/strings.xml index e93422ef99..a499601855 100644 --- a/src/main/resources/addresources/values-ro-rRO/strings.xml +++ b/src/main/resources/addresources/values-ro-rRO/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Dezactivează ca / abonare strălucire buton Butonul Îmi place și abonare nu va străluci când este menționat Butonul de like-uri și abonare va străluci când este menționat + Ascunde raftul jucătorului + Platforma de cumpărături este ascunsă + Platforma de cumpărături este afișată Ascundeți cardurile de album Cardurile de album sunt ascunse Cardurile de album sunt afișate diff --git a/src/main/resources/addresources/values-ru-rRU/strings.xml b/src/main/resources/addresources/values-ru-rRU/strings.xml index 33e6d939ea..e7f68670bf 100644 --- a/src/main/resources/addresources/values-ru-rRU/strings.xml +++ b/src/main/resources/addresources/values-ru-rRU/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Подсветка кнопок Кнопки \"Лайк\" и \"Подписаться\" не будут подсвечиваться при упоминании Кнопки \"Лайк\" и \"Подписаться\" будут подсвечиваться при упоминании + Секция покупок в плеере + Секция покупок в плеере скрыта + Секция покупок в плеере отображена Карточки альбомов Карточки альбомов под описанием артистов скрыты Карточки альбомов под описанием артистов отображены @@ -541,7 +544,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Кнопки переключения видео - Кнопки скрыты + Кнопки предыдущего и следующего видео скрыты Кнопки предыдущего и следующего видео отображены Кнопка \"Трансляция\" Кнопка \"Трансляция\" в плеере скрыта diff --git a/src/main/resources/addresources/values-sv-rSE/strings.xml b/src/main/resources/addresources/values-sv-rSE/strings.xml index 1849c920c5..0c8745a208 100644 --- a/src/main/resources/addresources/values-sv-rSE/strings.xml +++ b/src/main/resources/addresources/values-sv-rSE/strings.xml @@ -107,6 +107,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Inaktivera gilla- / prenumerationsknapp glow Gilla och prenumerera knappen kommer inte att lysa när det nämns Gilla och prenumerera knappen lyser när det nämns + Dölj spelarbutikshyllan + Hyllan för shopping är dold + Hyllan för shopping visas Dölj albumkort Skivkorten är dolda Albumkort är synliga diff --git a/src/main/resources/addresources/values-uk-rUA/strings.xml b/src/main/resources/addresources/values-uk-rUA/strings.xml index b721d166d8..5404b3434b 100644 --- a/src/main/resources/addresources/values-uk-rUA/strings.xml +++ b/src/main/resources/addresources/values-uk-rUA/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Вимкнути відблиск кнопок \"Подобається\" / \"Підписатися\" Кнопки \"Подобається\" та \"Підписатися\" не відблискуватимуть при згадуванні Кнопки \"Подобається\" та \"Підписатися\" відблискуватимуть при згадуванні + Приховати панель покупок + Покупка прихована + Відображення покубка Приховати картки альбому Картки альбому приховано Картки альбому показуються diff --git a/src/main/resources/addresources/values-zh-rCN/strings.xml b/src/main/resources/addresources/values-zh-rCN/strings.xml index d5af678615..de6cc29d31 100644 --- a/src/main/resources/addresources/values-zh-rCN/strings.xml +++ b/src/main/resources/addresources/values-zh-rCN/strings.xml @@ -105,6 +105,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 禁用点赞 / 订阅按钮动效 点赞和订阅按钮点击时无动效 点赞和订阅按钮点击时有动效 + 隐藏玩家购物架 + 购物架已隐藏 + 购物架已显示 隐藏相册卡 相册已隐藏 显示相册卡 diff --git a/src/main/resources/addresources/values-zh-rTW/strings.xml b/src/main/resources/addresources/values-zh-rTW/strings.xml index b2e934c08f..c64c50dde6 100644 --- a/src/main/resources/addresources/values-zh-rTW/strings.xml +++ b/src/main/resources/addresources/values-zh-rTW/strings.xml @@ -106,6 +106,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 停用「喜歡」和「訂閱」按鈕的發光效果 提及時,「喜歡」和「訂閱」按鈕不會發光 提及時,「喜歡」和「訂閱」按鈕會發光 + 隱藏播放器 [購物匣] + 已隱藏 [購物匣] + 已顯示 [購物匣] 隱藏專輯卡 已隱藏專輯卡 已顯示專輯卡 From a553a13c0326ef2fff7f785fed592d553a7963ce Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:56:02 -0400 Subject: [PATCH 13/65] feat(YouTube): Hide player shopping shelf in playlists (#3806) --- .../patches/youtube/ad/general/HideAdsResourcePatch.kt | 1 + .../layout/hide/general/HideLayoutComponentsPatch.kt | 4 ---- src/main/resources/addresources/values/strings.xml | 6 +++--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt index 1ef5e77234..307f39ad08 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt @@ -31,6 +31,7 @@ object HideAdsResourcePatch : ResourcePatch() { SwitchPreference("revanced_hide_fullscreen_ads"), SwitchPreference("revanced_hide_buttoned_ads"), SwitchPreference("revanced_hide_paid_promotion_label"), + SwitchPreference("revanced_hide_player_store_shelf"), SwitchPreference("revanced_hide_self_sponsor_ads"), SwitchPreference("revanced_hide_products_banner"), SwitchPreference("revanced_hide_shopping_links"), diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index dc7333fcf6..229f08d306 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -91,10 +91,6 @@ object HideLayoutComponentsPatch : BytecodePatch( override fun execute(context: BytecodeContext) { AddResourcesPatch(this::class) - SettingsPatch.PreferenceScreen.ADS.addPreferences( - SwitchPreference("revanced_hide_player_store_shelf"), - ) - SettingsPatch.PreferenceScreen.PLAYER.addPreferences( PreferenceScreen( key = "revanced_hide_description_components_screen", diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index cda396bd01..295c840772 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Disable like / subscribe button glow Like and subscribe button will not glow when mentioned Like and subscribe button will glow when mentioned - Hide player shopping shelf - Shopping shelf is hidden - Shopping shelf is shown Hide album cards Album cards are hidden Album cards are shown @@ -339,6 +336,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Hide banner to view products Banner is hidden Banner is shown + Hide player shopping shelf + Shopping shelf is hidden + Shopping shelf is shown Hide shopping links in video description Shopping links are hidden Shopping links are shown From df3be10717c5c3cd400ac7f31557e7cb60afed55 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 23 Oct 2024 03:58:10 +0000 Subject: [PATCH 14/65] chore: Release v4.18.0-dev.5 [skip ci] # [4.18.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.4...v4.18.0-dev.5) (2024-10-23) ### Features * **YouTube:** Hide player shopping shelf in playlists ([#3806](https://github.com/ReVanced/revanced-patches/issues/3806)) ([a553a13](https://github.com/ReVanced/revanced-patches/commit/a553a13c0326ef2fff7f785fed592d553a7963ce)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89190fbb29..dea943176a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [4.18.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.4...v4.18.0-dev.5) (2024-10-23) + + +### Features + +* **YouTube:** Hide player shopping shelf in playlists ([#3806](https://github.com/ReVanced/revanced-patches/issues/3806)) ([a553a13](https://github.com/ReVanced/revanced-patches/commit/a553a13c0326ef2fff7f785fed592d553a7963ce)) + # [4.18.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.3...v4.18.0-dev.4) (2024-10-23) diff --git a/gradle.properties b/gradle.properties index 73e55cf7da..9d8e3d2a22 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 4.18.0-dev.4 +version = 4.18.0-dev.5 From c35091305e1332e91f7131ad53a6dac46909bf24 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 07:44:37 -0400 Subject: [PATCH 15/65] chore: Sync translations (#3814) --- .../addresources/values-ar-rSA/strings.xml | 6 +-- .../addresources/values-az-rAZ/strings.xml | 5 ++- .../addresources/values-cs-rCZ/strings.xml | 6 +-- .../addresources/values-da-rDK/strings.xml | 6 +-- .../addresources/values-de-rDE/strings.xml | 6 +-- .../addresources/values-el-rGR/strings.xml | 8 ++-- .../addresources/values-es-rES/strings.xml | 6 +-- .../addresources/values-fi-rFI/strings.xml | 6 +-- .../addresources/values-fr-rFR/strings.xml | 6 +-- .../addresources/values-ga-rIE/strings.xml | 3 ++ .../addresources/values-in-rID/strings.xml | 38 +++++++++---------- .../addresources/values-it-rIT/strings.xml | 6 +-- .../addresources/values-ja-rJP/strings.xml | 6 +-- .../addresources/values-ko-rKR/strings.xml | 9 +++-- .../addresources/values-nb-rNO/strings.xml | 6 +-- .../addresources/values-nl-rNL/strings.xml | 6 +-- .../addresources/values-pl-rPL/strings.xml | 6 +-- .../addresources/values-pt-rPT/strings.xml | 6 +-- .../addresources/values-ro-rRO/strings.xml | 6 +-- .../addresources/values-ru-rRU/strings.xml | 6 +-- .../addresources/values-sr-rCS/strings.xml | 11 ++++++ .../addresources/values-sr-rSP/strings.xml | 11 ++++++ .../addresources/values-sv-rSE/strings.xml | 6 +-- .../addresources/values-uk-rUA/strings.xml | 6 +-- .../addresources/values-zh-rCN/strings.xml | 6 +-- .../addresources/values-zh-rTW/strings.xml | 6 +-- 26 files changed, 115 insertions(+), 84 deletions(-) diff --git a/src/main/resources/addresources/values-ar-rSA/strings.xml b/src/main/resources/addresources/values-ar-rSA/strings.xml index c49c8c6240..4907a3a069 100644 --- a/src/main/resources/addresources/values-ar-rSA/strings.xml +++ b/src/main/resources/addresources/values-ar-rSA/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t تعطيل توهج زر أعجبني / اشتراك لن يتوهج زر أعجبني واشتراك عند ذكره أعجبني واشتراك سوف يتوهج عند الإشارة - إخفاء رف تسوق اللاعب - تم إخفاء رفوف التسوق - يتم عرض رصيف التسوق إخفاء بطاقات الألبوم تم إخفاء بطاقات الألبوم يتم عرض بطاقات الألبوم @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t إخفاء لافتة لعرض المنتجات تم إخفاء البانر يتم عرض البانر + إخفاء رف مشغل التسوق + تم إخفاء رفوف التسوق + يتم عرض رفوف التسوق إخفاء روابط التسوق في وصف الفيديو تم إخفاء روابط التسوق يتم عرض روابط التسوق diff --git a/src/main/resources/addresources/values-az-rAZ/strings.xml b/src/main/resources/addresources/values-az-rAZ/strings.xml index 070d7ee3ee..e81efaf726 100644 --- a/src/main/resources/addresources/values-az-rAZ/strings.xml +++ b/src/main/resources/addresources/values-az-rAZ/strings.xml @@ -330,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Məhsullara baxma etiketin gizlət Etiket gizlədilib Etiket göstərilir + Oynadıcı alış-veriş rəfini gizlət + Alış-veriş rəfi gizlidir + Alış-veriş rəfi göstərilir Video açıqlama alış-veriş linklər gizlə Alış-veriş bağlantıları gizlədilir Alış-veriş bağlantıları göstərilir @@ -539,7 +542,7 @@ This is because Crowdin requires temporarily flattening this file and removing t VR menyusunda izləmə göstərilir - Əvvəlki/növbəti video düymələrin gizlə + Əvvəlki/növbəti video düymələrin gizlət Düymələr gizlidir Düymələr göstərilir Yayımla düyməsini gizlət diff --git a/src/main/resources/addresources/values-cs-rCZ/strings.xml b/src/main/resources/addresources/values-cs-rCZ/strings.xml index 53e1dc207b..0dcb3547d7 100644 --- a/src/main/resources/addresources/values-cs-rCZ/strings.xml +++ b/src/main/resources/addresources/values-cs-rCZ/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Zakázat záři tlačítka jako / odebírat Tlačítko se mi líbí a odebírá, pokud je zmíněno Tlačítko se mi líbí a odebírá, když je zmíněno - Skrýt nákupní šelf hráče - Nákupní polička je skrytá - Nákupní polička je zobrazena Skrýt alba Album karty jsou skryty Alba karty jsou zobrazeny @@ -334,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Skrýt banner pro zobrazení produktů Banner je skrytý Banner je zobrazen + Skrýt nákupní šelf hráče + Nákupní polička je skrytá + Nákupní polička je zobrazena Skrýt nákupní odkazy v popisu videa Nákupní odkazy jsou skryté Nákupní odkazy jsou zobrazeny diff --git a/src/main/resources/addresources/values-da-rDK/strings.xml b/src/main/resources/addresources/values-da-rDK/strings.xml index 858a3886d0..28891c2a98 100644 --- a/src/main/resources/addresources/values-da-rDK/strings.xml +++ b/src/main/resources/addresources/values-da-rDK/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktivér som / abonnér knap glow Ligesom og abonnér knap vil ikke gløde når nævnt Ligesom og abonnér knappen vil gløde, når nævnt - Skjul spillerens indkøbshylde - Shopping hylde er skjult - Shopping hylde er vist Skjul albumkort Albumkort er skjult Albumkort vises @@ -334,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Skjul banner for at se produkter Banner er skjult Banner er vist + Skjul spillerens indkøbshylde + Shopping hylde er skjult + Shopping hylde er vist Skjul shopping links i video beskrivelse Shopping links er skjult Shopping links vises diff --git a/src/main/resources/addresources/values-de-rDE/strings.xml b/src/main/resources/addresources/values-de-rDE/strings.xml index 22e39d036a..86be75ed25 100644 --- a/src/main/resources/addresources/values-de-rDE/strings.xml +++ b/src/main/resources/addresources/values-de-rDE/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktiviere das Like / Abonnieren Button aufleuchten \"Gefällt mir\" und \"Abonnieren\"-Button wird nicht aufleuchten, wenn er erwähnt wird \"Gefällt mir\" und \"Abonnieren\"-Button wird aufleuchten, wenn er erwähnt wird - Spieler-Einkaufsregal ausblenden - Einkaufsregal ist ausgeblendet - Einkaufsregal wird angezeigt Albumkarten ausblenden Albumkarten sind ausgeblendet Albumkarten werden angezeigt @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Banner ausblenden, um Produkte anzuzeigen Banner ist ausgeblendet Banner wird angezeigt + Spieler-Einkaufsregal ausblenden + Einkaufsregal ist ausgeblendet + Einkaufsregal wird angezeigt Einkaufslinks in der Videobeschreibung ausblenden Shopping-Links sind ausgeblendet Einkaufslinks werden angezeigt diff --git a/src/main/resources/addresources/values-el-rGR/strings.xml b/src/main/resources/addresources/values-el-rGR/strings.xml index a7a0c53b23..5a3709dd4c 100644 --- a/src/main/resources/addresources/values-el-rGR/strings.xml +++ b/src/main/resources/addresources/values-el-rGR/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Απενεργοποίηση λάμψης των κουμπιών «Μου αρέσει» και «Εγγραφή» Τα κουμπιά «Μου αρέσει» και «Εγγραφή» δεν θα λάμπουν όταν αναφέρονται Τα κουμπιά «Μου αρέσει» και «Εγγραφή» θα λάμπουν όταν αναφέρονται - Απόκρυψη ράφι αγορών παίχτη - Το ράφι αγορών είναι κρυμμένο - Το ράφι αγορών εμφανίζεται Κάρτες άλμπουμ Κρυμμένες Εμφανίζονται @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Διαφημίσεις προβολής προϊόντων Κρυμμένες Εμφανίζονται + Ενότητα αγορών οθόνης αναπαραγωγής + Κρυμμένη + Εμφανίζεται Σύνδεσμοι αγορών στην περιγραφή βίντεο Κρυμμένοι Εμφανίζονται @@ -981,7 +981,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Αυτόματη αναπαραγωγή Shorts Τα επόμενα Shorts θα αναπαράγονται αυτόματα Τα Shorts θα επαναλαμβάνονται - Αυτόματη αναπαραγωγή Shorts παρασκηνίου + Αυτόματη αναπαραγωγή Shorts στο παρασκήνιο Τα επόμενα Shorts θα αναπαράγονται αυτόματα στο παρασκήνιο Τα Shorts στο παρασκήνιο θα επαναλαμβάνονται diff --git a/src/main/resources/addresources/values-es-rES/strings.xml b/src/main/resources/addresources/values-es-rES/strings.xml index 7a5402e109..0b9837ad01 100644 --- a/src/main/resources/addresources/values-es-rES/strings.xml +++ b/src/main/resources/addresources/values-es-rES/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Desactivar el brillo del botón de like / suscripción El botón de \"Me gusta\" y \"Suscribir\" no brillará cuando se mencione El botón de \"Me gusta\" y \"Suscribir\" brillará cuando se mencione - Ocultar estampilla de compra del jugador - El armazón de compras está oculto - Se muestra el shelf de la compra Ocultar álbumes Las tarjetas de álbum están ocultas Se muestran las tarjetas de álbum @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Ocultar banner para ver los productos Banner oculto Banner mostrado + Ocultar estampilla de compra del jugador + El armazón de compras está oculto + Se muestra el shelf de la compra Ocultar enlaces de compras en la descripción de vídeo Enlaces de compras están ocultos Se muestran enlaces de compras diff --git a/src/main/resources/addresources/values-fi-rFI/strings.xml b/src/main/resources/addresources/values-fi-rFI/strings.xml index 15ee86f552..a254d668a1 100644 --- a/src/main/resources/addresources/values-fi-rFI/strings.xml +++ b/src/main/resources/addresources/values-fi-rFI/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Poista tykkää-/tilaa-painikkeiden hehku käytöstä Tykkää- ja tilaa-painikkeet eivät hehku, kun ne mainitaan Tykkää- ja tilaa-painikkeet hehkuvat, kun ne mainitaan - Piilota pelaajan ostoshylly - Ostoshylly on piilotettu - Ostoshylly näytetään Piilota albumikortit Albumikortit on piilotettu Albumikortit näytetään @@ -334,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Piilota tuotebanneri Banneri on piilotettu Banneri näytetään + Piilota pelaajan ostoshylly + Ostoshylly on piilotettu + Ostoshylly näytetään Piilota ostoslinkit videon kuvauksessa Ostoslinkit on piilotettu Ostoslinkit näytetään diff --git a/src/main/resources/addresources/values-fr-rFR/strings.xml b/src/main/resources/addresources/values-fr-rFR/strings.xml index 12b4d95803..ad22baf34d 100644 --- a/src/main/resources/addresources/values-fr-rFR/strings.xml +++ b/src/main/resources/addresources/values-fr-rFR/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Désactiver la lueur des boutons \"J\'aime\" / \"Je n\'aime pas\" Les boutons \"J\'aime\" et \"Je n\'aime pas\" ne s\'illuminerons pas lorsqu\'ils sont mentionné Les boutons \"J\'aime\" et \"Je n\'aime pas\" s\'illuminerons lorsqu\'ils sont mentionné - Masquer l\'étagère d\'achat du joueur - L\'étagère d\'achat est cachée - L\'étagère d\'achat est affichée Cacher les cartes d\'album Les cartes d\'album sont cachées Les cartes de l\'album sont affichées @@ -334,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Masquer la bannière \'Afficher les produits\' La bannière est masquée La bannière est affichée + Masquer l\'étagère d\'achat du joueur + L\'étagère d\'achat est cachée + L\'étagère d\'achat est affichée Masquer les liens des produits dans la description de la vidéo Les liens des produits sont masqués Les liens de produits sont affichés diff --git a/src/main/resources/addresources/values-ga-rIE/strings.xml b/src/main/resources/addresources/values-ga-rIE/strings.xml index d173f85379..8c88c2acbc 100644 --- a/src/main/resources/addresources/values-ga-rIE/strings.xml +++ b/src/main/resources/addresources/values-ga-rIE/strings.xml @@ -330,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Folaigh meirge chun táirgí a fheiceáil Tá bratach i bhfolach Taispeántar an bhratach + Folaigh seilf siopadóireachta an t-imreoir + Tá seilf siopadóireachta i bhfolach + Taispeántar seilf siopadóireachta Folaigh naisc siopadóireachta sa chur síos físeáin Tá naisc siopadóireachta i bhfolach Taispeántar naisc siopadóireachta diff --git a/src/main/resources/addresources/values-in-rID/strings.xml b/src/main/resources/addresources/values-in-rID/strings.xml index 058903a851..31ef1dae12 100644 --- a/src/main/resources/addresources/values-in-rID/strings.xml +++ b/src/main/resources/addresources/values-in-rID/strings.xml @@ -77,7 +77,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Tentang Iklan Thumbnail alternatif - Feed + Umpan Pemutar Layout umum Seekbar @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Nonaktifkan kilau tombol suka / langganan Tombol suka dan langganan tidak akan berkilau saat ditekan Tombol suka dan langganan akan berkilau saat ditekan - Sembunyikan rak belanja pemutar - Rak belanja disembunyikan - Rak belanja ditampilkan Sembunyikan kartu album Kartu album disembunyikan Kartu album ditampilkan @@ -147,7 +144,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Anjuran ditampilkan - Sembunyikan \'Tampilkan Lebih\' + Sembunyikan tombol \'Tampilkan selengkapnya\' Tombol disembunyikan Tombol ditampilkan Sembunyikan waktu reaksi @@ -243,10 +240,10 @@ This is because Crowdin requires temporarily flattening this file and removing t Keterangan video Sembunyi/tampilkan komponen keterangan video Bilah saring - Sembunyikan atau tampilkan bilah filter di feed, pencarian, dan video terkait - Sembunyikan di feed - Sembunyikan di feed - Tampilkan di feed + Sembunyikan atau tampilkan bilah filter di bagian umpan, pencarian, dan video terkait + Sembunyikan di bagian umpan + Disembunyikan di bagian umpan + Tampilkan di bagian umpan Sembunyikan di pencarian Disembunyikan di pencarian Ditampilkan di pencarian @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Sembunyikan banner untuk melihat produk Banner disembunyikan Banner ditampilkan + Sembunyikan rak belanja pemutar + Rak belanja disembunyikan + Rak belanja ditampilkan Sembunyikan tautan belanja dalam deskripsi video Tautan belanja disembunyikan Tautan belanja ditampilkan @@ -454,8 +454,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Tombol remix ditampilkan Sembunyikan Unduhan - Tombol download disembunyikan - Tombol download ditampilkan + Tombol unduh disembunyikan + Tombol unduh ditampilkan Sembunyikan Terima kasih Tombol terima kasih disembunyikan @@ -527,7 +527,7 @@ This is because Crowdin requires temporarily flattening this file and removing t This menu only appears for some videos. Translate the name normally if the menu cannot be found. --> Sembunyikan Info lanjut Menu info lanjut disembunyikan - Menu info lanjut ditampilkan + Menu info selengkapnya ditampilkan Sembunyikan Kunci layar Menu kunci layar disembunyikan @@ -588,13 +588,13 @@ This is because Crowdin requires temporarily flattening this file and removing t Pemutar Short Sembunyikan atau tampilkan komponen di pemutar Shorts - Sembunyikan Shorts di feed beranda - Shorts di feed beranda disembunyikan - Shorts di feed beranda ditampilkan + Sembunyikan Shorts di umpan beranda + Shorts di umpan beranda disembunyikan + Shorts di umpan beranda ditampilkan - Sembunyikan Shorts di feed subscription - Shorts di feed subscription disembunyikan - Shorts di feed subscription ditampilkan + Sembunyikan Shorts di umpan langganan + Shorts di umpan langganan disembunyikan + Shorts di umpan langganan ditampilkan Sembunyikan Shorts di hasil pencarian Shorts di hasil pencarian disembunyikan Shorts di hasil pencarian ditampilkan @@ -937,7 +937,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Reset warna Setel ulang Tentang - Data yang disediakan API SponsorBlock. Tekan di sini untuk mempelajari lebih lanjut dan melihat hasil download untuk platform lain + Data yang disediakan API SponsorBlock. Tekan di sini untuk mempelajari lebih lanjut dan melihat hasil pengunduhan untuk platform lain Palsukan versi app diff --git a/src/main/resources/addresources/values-it-rIT/strings.xml b/src/main/resources/addresources/values-it-rIT/strings.xml index 6a34ebd15b..45620b71c5 100644 --- a/src/main/resources/addresources/values-it-rIT/strings.xml +++ b/src/main/resources/addresources/values-it-rIT/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Disabilita il bagliore del pulsante di / sottoscrizione Come e il pulsante di sottoscrizione non brillerà quando menzionato Come e il pulsante di sottoscrizione brillerà quando menzionato - Nascondi lo scaffale dello shopping - Lo scaffale è nascosto - Lo scaffale è mostrato Nascondi schede album Le schede degli album sono nascoste Le schede degli album sono mostrate @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Nascondi banner per visualizzare i prodotti Banner nascosto Banner mostrato + Nascondi lo scaffale dello shopping + Lo scaffale è nascosto + Lo scaffale è mostrato Nascondi link agli acquisti nella descrizione del video I link commerciali sono nascosti I collegamenti commerciali sono mostrati diff --git a/src/main/resources/addresources/values-ja-rJP/strings.xml b/src/main/resources/addresources/values-ja-rJP/strings.xml index 30a822c8aa..961b135023 100644 --- a/src/main/resources/addresources/values-ja-rJP/strings.xml +++ b/src/main/resources/addresources/values-ja-rJP/strings.xml @@ -105,9 +105,6 @@ This is because Crowdin requires temporarily flattening this file and removing t 高評価 / チャンネル登録ボタンのアニメーションを無効にする 「高評価」と「チャンネル登録」ボタンのアニメーションは無効です 「高評価」と「チャンネル登録」ボタンのアニメーションは有効です - プレイヤーのショッピング棚を非表示 - ショッピング棚は非表示です - ショッピング棚が表示されます アルバムカードを隠す アルバムカードは非表示です アルバムカードは表示されます @@ -330,6 +327,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 商品を見るためのバナーを隠す バナーは非表示です バナーは表示されます + プレイヤーのショッピング棚を非表示 + ショッピング棚は非表示です + ショッピング棚が表示されます ビデオの説明内のショッピングリンクを非表示 ショッピングのリンクは非表示です ショッピングのリンクは表示されます diff --git a/src/main/resources/addresources/values-ko-rKR/strings.xml b/src/main/resources/addresources/values-ko-rKR/strings.xml index bc8089aa74..52327a6ee6 100644 --- a/src/main/resources/addresources/values-ko-rKR/strings.xml +++ b/src/main/resources/addresources/values-ko-rKR/strings.xml @@ -331,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 제품 보기 배너 숨기기 플레이어에서 제품 보기 배너가 숨겨집니다 플레이어에서 제품 보기 배너가 표시됩니다 + 판매자 쇼핑 선반 숨기기 + 판매자 쇼핑 선반이 숨겨집니다 + 판매자 쇼핑 선반이 표시됩니다 동영상 설명에서 쇼핑 링크 숨기기 쇼핑 링크가 숨겨집니다 쇼핑 링크가 표시됩니다 @@ -341,9 +344,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 웹 검색 결과 숨기기 웹 검색 결과가 숨겨집니다 웹 검색 결과가 표시됩니다 - 제품 쇼핑 선반 숨기기 - 제품 쇼핑 선반이 숨겨집니다 - 제품 쇼핑 선반이 표시됩니다 + 매장 쇼핑 선반 숨기기 + 매장 쇼핑 선반이 숨겨집니다 + 매장 쇼핑 선반이 표시됩니다 \'전체 화면 광고 숨기기\'는 구형 기기에서만 사용할 수 있습니다 diff --git a/src/main/resources/addresources/values-nb-rNO/strings.xml b/src/main/resources/addresources/values-nb-rNO/strings.xml index a6bb3d2717..408b504746 100644 --- a/src/main/resources/addresources/values-nb-rNO/strings.xml +++ b/src/main/resources/addresources/values-nb-rNO/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Deaktiver \'liker\' / abonnér på glød Som og abonner-knappen vil ikke glød når den nevnes Som og abbonér vil glød når det nevnes - Skjul spiller shopping hylle - Handlehylle er skjult - Handlehold/hylle vises Skjul albumkort Albumkort er skjult Albumkort vises @@ -330,6 +327,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Skjul banner for å se produkter Banner er skjult Banner vises + Skjul spiller shopping hylle + Handlehylle er skjult + Handlehold/hylle vises Skjul innkjøpslenker i videobeskrivelse Handlelenker er skjult Lenker for handlekurv vises diff --git a/src/main/resources/addresources/values-nl-rNL/strings.xml b/src/main/resources/addresources/values-nl-rNL/strings.xml index 27ac11738b..180ee0cfce 100644 --- a/src/main/resources/addresources/values-nl-rNL/strings.xml +++ b/src/main/resources/addresources/values-nl-rNL/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Uitschakelen like- / abonneer-knop gloed De knop \'like\' en \'abonneren\' zal niet gloeien wanneer deze genoemd wordt \'Like en abonneren\' knop zal gloeien wanneer genoemd - Verberg spelers boodschappenplank - Winkelwagen is verborgen - Winkelwagen wordt weergegeven Verberg albumkaarten Albumkaarten zijn verborgen Albumkaarten worden weergegeven @@ -334,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Verberg banner om producten te bekijken Banner is verborgen Banner wordt getoond + Verberg spelers boodschappenplank + Winkelwagen is verborgen + Winkelwagen wordt weergegeven Verberg shopping links in video beschrijving Shopping links zijn verborgen Shopping links worden weergegeven diff --git a/src/main/resources/addresources/values-pl-rPL/strings.xml b/src/main/resources/addresources/values-pl-rPL/strings.xml index b81f9fed10..ce8de9a278 100644 --- a/src/main/resources/addresources/values-pl-rPL/strings.xml +++ b/src/main/resources/addresources/values-pl-rPL/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Wyłącz polubienie/subskrybuj podświetlenie przycisków Przycisk polubienia i subskrybuj nie pojawi się po wzmiance Przycisk polubienia i subskrybuj się po wzmiance - Ukryj półkę na zakupy gracza - Półka Zakupów jest ukryta - Półka Zakupów jest pokazana Ukryj karty albumu Karty albumów są ukryte Karty albumów są wyświetlane @@ -334,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Ukryj baner do wyświetlania produktów Baner jest ukryty Baner jest pokazany + Ukryj półkę na zakupy gracza + Półka Zakupów jest ukryta + Półka Zakupów jest pokazana Ukryj linki do zakupów w opisie wideo Linki do zakupów są ukryte Odnośniki do zakupów są wyświetlane diff --git a/src/main/resources/addresources/values-pt-rPT/strings.xml b/src/main/resources/addresources/values-pt-rPT/strings.xml index 501dccab27..33a28b4fa7 100644 --- a/src/main/resources/addresources/values-pt-rPT/strings.xml +++ b/src/main/resources/addresources/values-pt-rPT/strings.xml @@ -105,9 +105,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Desativar brilho do botão de inscrição / Curtir O botão de curtir e assinar não brilhará quando mencionado O botão de curtir e subscrever brilhará quando mencionado - Ocultar prateleira de compras do jogador - Prateleira de compras está escondida - Prateleira de compras é mostrada Esconder cartões de álbuns Cartões de álbuns estão escondidos Cartões de álbum são visíveis @@ -331,6 +328,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Esconder banner para ver os produtos Banner está escondido Banner é visível + Ocultar prateleira de compras do jogador + Prateleira de compras está escondida + Prateleira de compras é mostrada Esconder links de compras na descrição do vídeo Links de compras estão escondidos Links de compras são visíveis diff --git a/src/main/resources/addresources/values-ro-rRO/strings.xml b/src/main/resources/addresources/values-ro-rRO/strings.xml index a499601855..5cb902c05f 100644 --- a/src/main/resources/addresources/values-ro-rRO/strings.xml +++ b/src/main/resources/addresources/values-ro-rRO/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Dezactivează ca / abonare strălucire buton Butonul Îmi place și abonare nu va străluci când este menționat Butonul de like-uri și abonare va străluci când este menționat - Ascunde raftul jucătorului - Platforma de cumpărături este ascunsă - Platforma de cumpărături este afișată Ascundeți cardurile de album Cardurile de album sunt ascunse Cardurile de album sunt afișate @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Ascunde banner-ul pentru a vizualiza produsele Banner-ul este ascuns Banner-ul este afișat + Ascunde raftul jucătorului + Platforma de cumpărături este ascunsă + Platforma de cumpărături este afișată Ascunde link-urile de cumpărături în descrierea video Linkurile de cumpărături sunt ascunse Linkurile de cumpărături sunt afișate diff --git a/src/main/resources/addresources/values-ru-rRU/strings.xml b/src/main/resources/addresources/values-ru-rRU/strings.xml index e7f68670bf..9ec0bf68e3 100644 --- a/src/main/resources/addresources/values-ru-rRU/strings.xml +++ b/src/main/resources/addresources/values-ru-rRU/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Подсветка кнопок Кнопки \"Лайк\" и \"Подписаться\" не будут подсвечиваться при упоминании Кнопки \"Лайк\" и \"Подписаться\" будут подсвечиваться при упоминании - Секция покупок в плеере - Секция покупок в плеере скрыта - Секция покупок в плеере отображена Карточки альбомов Карточки альбомов под описанием артистов скрыты Карточки альбомов под описанием артистов отображены @@ -334,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Баннеры просмотра товаров Баннеры просмотра товаров в плеере скрыты Баннеры просмотра товаров в плеере отображены + Секция покупок в плеере + Секция покупок в плеере скрыта + Секция покупок в плеере отображена Ссылки на товары Ссылки на товары в описании видео скрыты Ссылки на товары в описании видео отображены diff --git a/src/main/resources/addresources/values-sr-rCS/strings.xml b/src/main/resources/addresources/values-sr-rCS/strings.xml index c3e2825f59..e82d09b3aa 100644 --- a/src/main/resources/addresources/values-sr-rCS/strings.xml +++ b/src/main/resources/addresources/values-sr-rCS/strings.xml @@ -331,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Sakrij baner za gledanje proizvoda Baner za gledanje proizvoda je skriven Baner za gledanje proizvoda je prikazan + Sakrij policu „Kupovina” u plejeru + Polica „Kupovina” u plejeru je skrivena + Polica „Kupovina” u plejeru je prikazana Sakrij linkove za kupovinu u opisu videa Linkovi za kupovinu u opisu videa su skriveni Linkovi za kupovinu u opisu videa su prikazani @@ -583,6 +586,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Traka za premotavanje na sličici videa je prikazana + Shorts plejer + Sakrijte ili prikažite komponente u Shorts plejeru Sakrij Shorts videe u fidu „Početna” Shorts videi u fidu „Početna” su skriveni @@ -974,6 +979,12 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts plejer će nastaviti reprodukciju pri pokretanju aplikacije + Autoplej Shorts videa + Shorts videi će se automatski puštati + Shorts videi će se ponavljati + Autoplej Shorts videa u pozadini + Shorts videi će se automatski puštati u pozadini + Shorts videi će se ponavljati u pozadini Omogući korisnički interfejs tableta diff --git a/src/main/resources/addresources/values-sr-rSP/strings.xml b/src/main/resources/addresources/values-sr-rSP/strings.xml index 6eb5424a9c..6096a74cc9 100644 --- a/src/main/resources/addresources/values-sr-rSP/strings.xml +++ b/src/main/resources/addresources/values-sr-rSP/strings.xml @@ -331,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Сакриј банер за гледање производа Банер за гледање производа је скривен Банер за гледање производа је приказан + Сакриј полицу „Куповина” у плејеру + Полица „Куповина” у плејеру је скривена + Полица „Куповина” у плејеру је приказана Сакриј линкове за куповину у опису видеа Линкови за куповину у опису видеа су скривени Линкови за куповину у опису видеа су приказани @@ -583,6 +586,8 @@ This is because Crowdin requires temporarily flattening this file and removing t Трака за премотавање на сличици видеа је приказана + Shorts плејер + Сакријте или прикажите компоненте у Shorts плејеру Сакриј Shorts видее у фиду „Почетна” Shorts видеи у фиду „Почетна” су скривени @@ -974,6 +979,12 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts плејер ће наставити репродукцију при покретању апликације + Аутоплеј Shorts видеа + Shorts видеи ће се аутоматски пуштати + Shorts видеи ће се понављати + Аутоплеј Shorts видеа у позадини + Shorts видеи ће се аутоматски пуштати у позадини + Shorts видеи ће се понављати у позадини Омогући кориснички интерфејс таблета diff --git a/src/main/resources/addresources/values-sv-rSE/strings.xml b/src/main/resources/addresources/values-sv-rSE/strings.xml index 0c8745a208..22af9fd2b4 100644 --- a/src/main/resources/addresources/values-sv-rSE/strings.xml +++ b/src/main/resources/addresources/values-sv-rSE/strings.xml @@ -107,9 +107,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Inaktivera gilla- / prenumerationsknapp glow Gilla och prenumerera knappen kommer inte att lysa när det nämns Gilla och prenumerera knappen lyser när det nämns - Dölj spelarbutikshyllan - Hyllan för shopping är dold - Hyllan för shopping visas Dölj albumkort Skivkorten är dolda Albumkort är synliga @@ -334,6 +331,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Dölj banner för att visa produkter Banner är dold Banner är synlig + Dölj spelarbutikshyllan + Hyllan för shopping är dold + Hyllan för shopping visas Dölj shoppinglänkar i videobeskrivning Shoppinglänkar är dolda Shoppinglänkar är synliga diff --git a/src/main/resources/addresources/values-uk-rUA/strings.xml b/src/main/resources/addresources/values-uk-rUA/strings.xml index 5404b3434b..437b59b14c 100644 --- a/src/main/resources/addresources/values-uk-rUA/strings.xml +++ b/src/main/resources/addresources/values-uk-rUA/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t Вимкнути відблиск кнопок \"Подобається\" / \"Підписатися\" Кнопки \"Подобається\" та \"Підписатися\" не відблискуватимуть при згадуванні Кнопки \"Подобається\" та \"Підписатися\" відблискуватимуть при згадуванні - Приховати панель покупок - Покупка прихована - Відображення покубка Приховати картки альбому Картки альбому приховано Картки альбому показуються @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t Приховати банер для перегляду товарів Банер для перегляду товарів приховано Банер для перегляду товарів показується + Приховати полицю покупок в плеєрі + Полицю покупок в плеєрі приховано + Полиця покупок в плеєрі показується Приховати посилання на покупки Посилання на покупки в описі відео приховано Посилання на покупки в описі відео показуються diff --git a/src/main/resources/addresources/values-zh-rCN/strings.xml b/src/main/resources/addresources/values-zh-rCN/strings.xml index de6cc29d31..07d09288b2 100644 --- a/src/main/resources/addresources/values-zh-rCN/strings.xml +++ b/src/main/resources/addresources/values-zh-rCN/strings.xml @@ -105,9 +105,6 @@ This is because Crowdin requires temporarily flattening this file and removing t 禁用点赞 / 订阅按钮动效 点赞和订阅按钮点击时无动效 点赞和订阅按钮点击时有动效 - 隐藏玩家购物架 - 购物架已隐藏 - 购物架已显示 隐藏相册卡 相册已隐藏 显示相册卡 @@ -332,6 +329,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 隐藏产品横幅 横幅已隐藏 横幅已显示 + 隐藏玩家购物架 + 购物架已隐藏 + 购物架已显示 在视频描述中隐藏购物链接 购物链接已隐藏 购物链接已显示 diff --git a/src/main/resources/addresources/values-zh-rTW/strings.xml b/src/main/resources/addresources/values-zh-rTW/strings.xml index c64c50dde6..b8d24f55ea 100644 --- a/src/main/resources/addresources/values-zh-rTW/strings.xml +++ b/src/main/resources/addresources/values-zh-rTW/strings.xml @@ -106,9 +106,6 @@ This is because Crowdin requires temporarily flattening this file and removing t 停用「喜歡」和「訂閱」按鈕的發光效果 提及時,「喜歡」和「訂閱」按鈕不會發光 提及時,「喜歡」和「訂閱」按鈕會發光 - 隱藏播放器 [購物匣] - 已隱藏 [購物匣] - 已顯示 [購物匣] 隱藏專輯卡 已隱藏專輯卡 已顯示專輯卡 @@ -333,6 +330,9 @@ This is because Crowdin requires temporarily flattening this file and removing t 隱藏檢視產品橫幅 已隱藏橫幅 已顯示橫幅 + 隱藏播放器 [購物匣] + 已隱藏 [購物匣] + 已顯示 [購物匣] 隱藏影片描述欄商店連結 已隱藏商店連結 已顯示商店連結 From c3a5e14a0a24973a0f9956845c9e0f99c1301d42 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Thu, 24 Oct 2024 07:47:01 -0400 Subject: [PATCH 16/65] fix(YouTube - Playback speed): Remember playback speed with new speed menu (#3810) --- .../NewVideoQualityChangedFingerprint.kt | 2 +- .../information/VideoInformationPatch.kt | 49 ++++++++++++++----- .../quality/RememberVideoQualityPatch.kt | 2 +- ...laybackSpeedMenuSpeedChangedFingerprint.kt | 29 +++++++++++ .../remember/RememberPlaybackSpeedPatch.kt | 10 ++-- 5 files changed, 73 insertions(+), 19 deletions(-) rename src/main/kotlin/app/revanced/patches/youtube/{video/quality => shared}/fingerprints/NewVideoQualityChangedFingerprint.kt (93%) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/PlaybackSpeedMenuSpeedChangedFingerprint.kt diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/NewVideoQualityChangedFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/NewVideoQualityChangedFingerprint.kt similarity index 93% rename from src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/NewVideoQualityChangedFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/NewVideoQualityChangedFingerprint.kt index f0fc4e9e38..64486ba5c4 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/NewVideoQualityChangedFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/NewVideoQualityChangedFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.youtube.video.quality.fingerprints +package app.revanced.patches.youtube.shared.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt index 71c0a2d146..4fac93f397 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt @@ -11,11 +11,12 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableClass import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.shared.fingerprints.NewVideoQualityChangedFingerprint import app.revanced.patches.youtube.video.information.fingerprints.* import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch +import app.revanced.patches.youtube.video.quality.fingerprints.PlaybackSpeedMenuSpeedChangedFingerprint import app.revanced.patches.youtube.video.videoid.VideoIdPatch import app.revanced.util.alsoResolve -import app.revanced.util.exception import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.resultOrThrow @@ -23,15 +24,15 @@ import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.BuilderInstruction import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.iface.Method +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.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.util.MethodUtil -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Patch( description = "Hooks YouTube to get information about the current playing video.", @@ -43,7 +44,8 @@ object VideoInformationPatch : BytecodePatch( MdxPlayerDirectorSetVideoStageFingerprint, CreateVideoPlayerSeekbarFingerprint, PlayerControllerSetTimeReferenceFingerprint, - OnPlaybackSpeedItemClickFingerprint + OnPlaybackSpeedItemClickFingerprint, + NewVideoQualityChangedFingerprint ) ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/VideoInformation;" @@ -60,6 +62,12 @@ object VideoInformationPatch : BytecodePatch( private lateinit var timeMethod: MutableMethod private var timeInitInsertIndex = 2 + // Old speed menu, where speeds are entries in a list. Method is also used by the player speed button. + private lateinit var legacySpeedSelectionInsertMethod: MutableMethod + private var legacySpeedSelectionInsertIndex = -1 + private var legacySpeedSelectionValueRegister = -1 + + // New speed menu, with preset buttons and 0.05x fine adjustments buttons. private lateinit var speedSelectionInsertMethod: MutableMethod private var speedSelectionInsertIndex = -1 private var speedSelectionValueRegister = -1 @@ -158,13 +166,13 @@ object VideoInformationPatch : BytecodePatch( /* * Hook the user playback speed selection */ - OnPlaybackSpeedItemClickFingerprint.result?.mutableMethod?.apply { - speedSelectionInsertMethod = this + OnPlaybackSpeedItemClickFingerprint.resultOrThrow().mutableMethod.apply { + legacySpeedSelectionInsertMethod = this val speedSelectionMethodInstructions = this.implementation!!.instructions val speedSelectionValueInstructionIndex = speedSelectionMethodInstructions.indexOfFirst { it.opcode == Opcode.IGET } - speedSelectionValueRegister = + legacySpeedSelectionValueRegister = getInstruction(speedSelectionValueInstructionIndex).registerA setPlaybackSpeedClassFieldReference = getInstruction(speedSelectionValueInstructionIndex + 1).reference.toString() @@ -172,8 +180,19 @@ object VideoInformationPatch : BytecodePatch( getInstruction(speedSelectionValueInstructionIndex + 2).reference.toString() setPlaybackSpeedContainerClassFieldReference = getReference(speedSelectionMethodInstructions, -1, Opcode.IF_EQZ) - speedSelectionInsertIndex = speedSelectionValueInstructionIndex + 1 - } ?: throw OnPlaybackSpeedItemClickFingerprint.exception + legacySpeedSelectionInsertIndex = speedSelectionValueInstructionIndex + 1 + } + + // New playback speed menu. + PlaybackSpeedMenuSpeedChangedFingerprint.alsoResolve( + context, + NewVideoQualityChangedFingerprint + ).mutableMethod.apply { + val index = indexOfFirstInstructionOrThrow(Opcode.IGET) + speedSelectionInsertMethod = this + speedSelectionInsertIndex = index + 1 + speedSelectionValueRegister = getInstruction(index).registerA + } userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed") } @@ -278,9 +297,15 @@ object VideoInformationPatch : BytecodePatch( /** * Hook the video speed selected by the user. */ - internal fun userSelectedPlaybackSpeedHook(targetMethodClass: String, targetMethodName: String) = + internal fun userSelectedPlaybackSpeedHook(targetMethodClass: String, targetMethodName: String) { + legacySpeedSelectionInsertMethod.addInstruction( + legacySpeedSelectionInsertIndex++, + "invoke-static { v$legacySpeedSelectionValueRegister }, $targetMethodClass->$targetMethodName(F)V" + ) + speedSelectionInsertMethod.addInstruction( speedSelectionInsertIndex++, - "invoke-static {v$speedSelectionValueRegister}, $targetMethodClass->$targetMethodName(F)V" + "invoke-static { v$speedSelectionValueRegister }, $targetMethodClass->$targetMethodName(F)V" ) + } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt index 71849406ab..3e3c7000e6 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt @@ -13,8 +13,8 @@ import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.patches.youtube.shared.fingerprints.NewVideoQualityChangedFingerprint import app.revanced.patches.youtube.video.information.VideoInformationPatch -import app.revanced.patches.youtube.video.quality.fingerprints.NewVideoQualityChangedFingerprint import app.revanced.patches.youtube.video.quality.fingerprints.SetQualityByIndexMethodClassFieldReferenceFingerprint import app.revanced.patches.youtube.video.quality.fingerprints.VideoQualityItemOnClickParentFingerprint import app.revanced.patches.youtube.video.quality.fingerprints.VideoQualitySetterFingerprint diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/PlaybackSpeedMenuSpeedChangedFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/PlaybackSpeedMenuSpeedChangedFingerprint.kt new file mode 100644 index 0000000000..bfa3574846 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/PlaybackSpeedMenuSpeedChangedFingerprint.kt @@ -0,0 +1,29 @@ +package app.revanced.patches.youtube.video.quality.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patches.youtube.shared.fingerprints.NewVideoQualityChangedFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +/** + * Resolves with the class found in [NewVideoQualityChangedFingerprint]. + */ +internal object PlaybackSpeedMenuSpeedChangedFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "L", + parameters = listOf("L"), + opcodes = listOf( + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET, + Opcode.INVOKE_VIRTUAL, + Opcode.SGET_OBJECT, + Opcode.RETURN_OBJECT, + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt index cc30d0de4c..96827168f6 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt @@ -14,7 +14,7 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.video.information.VideoInformationPatch import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedPatch import app.revanced.patches.youtube.video.speed.remember.fingerprint.InitializePlaybackSpeedValuesFingerprint -import app.revanced.util.exception +import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction @Patch( @@ -54,7 +54,7 @@ object RememberPlaybackSpeedPatch : BytecodePatch( /* * Hook the code that is called when the playback speeds are initialized, and sets the playback speed */ - InitializePlaybackSpeedValuesFingerprint.result?.apply { + InitializePlaybackSpeedValuesFingerprint.resultOrThrow().apply { // Infer everything necessary for calling the method setPlaybackSpeed(). val onItemClickListenerClassFieldReference = mutableMethod.getInstruction(0).reference @@ -67,7 +67,7 @@ object RememberPlaybackSpeedPatch : BytecodePatch( move-result v0 # Check if the playback speed is not 1.0x. - const/high16 v1, 0x3f800000 # 1.0f + const/high16 v1, 1.0f cmpg-float v1, v0, v1 if-eqz v1, :do_not_override @@ -82,9 +82,9 @@ object RememberPlaybackSpeedPatch : BytecodePatch( # Invoke setPlaybackSpeed on that class. invoke-virtual {v2, v0}, ${VideoInformationPatch.setPlaybackSpeedMethodReference} - """.trimIndent(), + """, ExternalLabel("do_not_override", mutableMethod.getInstruction(0)) ) - } ?: throw InitializePlaybackSpeedValuesFingerprint.exception + } } } From 5848269c2efb816b4a98cf344e10728c1cf702a3 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 24 Oct 2024 11:48:59 +0000 Subject: [PATCH 17/65] chore: Release v4.18.0-dev.6 [skip ci] # [4.18.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.5...v4.18.0-dev.6) (2024-10-24) ### Bug Fixes * **YouTube - Playback speed:** Remember playback speed with new speed menu ([#3810](https://github.com/ReVanced/revanced-patches/issues/3810)) ([c3a5e14](https://github.com/ReVanced/revanced-patches/commit/c3a5e14a0a24973a0f9956845c9e0f99c1301d42)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dea943176a..cc84dc39a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [4.18.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.5...v4.18.0-dev.6) (2024-10-24) + + +### Bug Fixes + +* **YouTube - Playback speed:** Remember playback speed with new speed menu ([#3810](https://github.com/ReVanced/revanced-patches/issues/3810)) ([c3a5e14](https://github.com/ReVanced/revanced-patches/commit/c3a5e14a0a24973a0f9956845c9e0f99c1301d42)) + # [4.18.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.18.0-dev.4...v4.18.0-dev.5) (2024-10-23) diff --git a/gradle.properties b/gradle.properties index 9d8e3d2a22..5a96ec6367 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 4.18.0-dev.5 +version = 4.18.0-dev.6 From eee16922779f994f5752190a20a9016ea98ec4cb Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 10 Oct 2024 19:43:01 +0200 Subject: [PATCH 18/65] build: Bump ReVanced Patcher BREAKING CHANGE: Various APIs have been changed or removed. --- .github/workflows/build_pull_request.yml | 6 + .github/workflows/open_pull_request.yml | 5 +- .github/workflows/release.yml | 8 +- .gitignore | 5 +- .idea/misc.xml | 2 +- .releaserc | 6 +- api/revanced-patches.api | 2308 ----------------- build.gradle.kts | 155 -- crowdin.yml | 4 +- gradle.properties | 1 + gradle/libs.versions.toml | 28 +- patches/api/patches.api | 1556 +++++++++++ patches/build.gradle.kts | 35 + .../exportall/ExportAllActivitiesPatch.kt | 25 +- .../all/misc/build/BaseSpoofBuildInfoPatch.kt | 92 + .../all/misc/build/SpoofBuildInfoPatch.kt | 149 +- .../location/hide/HideMockLocationPatch.kt | 50 + .../sim/spoof/SpoofSimCountryPatch.kt | 105 + .../connectivity/wifi/spoof/SpoofWifiPatch.kt | 224 ++ .../debugging/EnableAndroidDebuggingPatch.kt | 17 +- .../ChangeDataDirectoryLocationPatch.kt | 58 + .../revanced/patches/all/misc/hex/HexPatch.kt | 49 +- .../gestures/PredictiveBackGesturePatch.kt | 24 + .../OverrideCertificatePinningPatch.kt | 24 +- .../packagename/ChangePackageNamePatch.kt | 62 + .../all/misc/resources/AddResourcesPatch.kt | 397 +++ .../RemoveScreenCaptureRestrictionPatch.kt | 83 + .../RemoveScreenshotRestrictionPatch.kt | 97 + .../sharetargets/RemoveShareTargetsPatch.kt | 17 +- .../all/misc/transformation/MethodCall.kt | 42 +- .../TransformInstructionsPatch.kt | 20 +- .../versioncode/ChangeVersionCodePatch.kt | 21 +- .../patches/amazon/DeepLinkingPatch.kt | 24 + .../revanced/patches/amazon/Fingerprints.kt | 11 + .../backdrops/misc/pro/Fingerprints.kt | 18 + .../backdrops/misc/pro/ProUnlockPatch.kt | 27 + .../bandcamp/limitations/Fingerprints.kt | 7 + .../limitations/RemovePlayLimitsPatch.kt | 18 + .../root/BypassRootChecksPatch.kt | 18 + .../cieid/restrictions/root/Fingerprints.kt | 9 + .../patches/duolingo/ad/DisableAdsPatch.kt | 34 + .../patches/duolingo/ad/Fingerprints.kt | 18 + .../duolingo/debug/EnableDebugMenuPatch.kt | 28 + .../patches/duolingo/debug/Fingerprints.kt | 19 + .../facebook/ads/mainfeed/Fingerprints.kt | 47 + .../ads/mainfeed/HideSponsoredStoriesPatch.kt | 90 + .../facebook/ads/story/Fingerprints.kt | 24 + .../facebook/ads/story/HideStoryAdsPatch.kt | 21 + .../bootloader/BootloaderDetectionPatch.kt | 27 + .../detection/bootloader/Fingerprints.kt | 23 +- .../detection/root/Fingerprints.kt | 19 +- .../detection/root/RootDetectionPatch.kt | 24 + .../customtabs/EnableCustomTabsPatch.kt | 25 + .../googlenews/customtabs/Fingerprints.kt | 17 + .../misc/extension/ExtensionPatch.kt | 6 + .../extension/hooks/StartActivityInitHook.kt | 40 +- .../patches/googlenews/misc/gms/Constants.kt | 0 .../googlenews/misc/gms/Fingerprints.kt | 9 + .../misc/gms/GmsCoreSupportPatch.kt | 30 + .../misc/extension/ExtensionPatch.kt | 5 + .../misc/extension/Fingerprints.kt | 32 +- .../misc/features/Fingerprints.kt | 7 + .../misc/features/SpoofBuildInfoPatch.kt | 17 + .../misc}/features/SpoofFeaturesPatch.kt | 75 +- .../googlephotos/misc/gms/Constants.kt | 0 .../googlephotos/misc/gms/Fingerprints.kt | 9 + .../misc/gms/GmsCoreSupportPatch.kt | 27 + .../misc/preferences/Fingerprints.kt | 8 + ...oreHiddenBackUpWhileChargingTogglePatch.kt | 27 + .../restrictions/Fingerprints.kt | 12 + .../restrictions/RemoveDeviceRestrictions.kt | 31 + .../patches/hexeditor/ad/DisableAdsPatch.kt | 23 + .../patches/hexeditor/ad/Fingerprints.kt | 9 + .../iconpackstudio/misc/pro/Fingerprints.kt | 8 + .../iconpackstudio/misc/pro/UnlockProPatch.kt | 23 + .../idaustria/detection/root/Fingerprints.kt | 31 + .../detection/root/RootDetectionPatch.kt | 20 + .../detection/signature/Fingerprints.kt | 13 + .../signature/SpoofSignaturePatch.kt | 30 +- .../patches/inshorts/ad/Fingerprints.kt | 8 + .../patches/inshorts/ad/InshortsAdsPatch.kt | 22 + .../patches/instagram/ads/Fingerprints.kt | 14 + .../patches/instagram/ads/HideAdsPatch.kt | 25 + .../patches/irplus/ad/Fingerprints.kt | 11 + .../patches/irplus/ad/RemoveAdsPatch.kt | 19 + .../misc/login/DisableMandatoryLoginPatch.kt | 21 + .../lightroom/misc/login/Fingerprints.kt | 18 + .../lightroom/misc/premium/Fingerprints.kt | 17 + .../misc/premium/UnlockPremiumPatch.kt | 18 + .../detection/license/Fingerprints.kt | 23 + .../license/LicenseValidationPatch.kt | 21 + .../detection/signature/Fingerprints.kt | 23 +- .../signature/SignatureVerificationPatch.kt | 21 + .../memegenerator/misc/pro/Fingerprints.kt | 21 + .../misc/pro/UnlockProVersionPatch.kt | 27 + .../patches/messenger/inbox/Fingerprints.kt | 36 + .../messenger/inbox/HideInboxAdsPatch.kt | 18 + .../messenger/inbox/HideInboxSubtabsPatch.kt | 18 + .../DisableSwitchingEmojiToStickerPatch.kt | 26 + .../inputfield/DisableTypingIndicatorPatch.kt | 18 + .../messenger/inputfield/Fingerprints.kt | 31 + .../mifitness/misc/locale/Fingerprints.kt | 12 + .../misc/locale/ForceEnglishLocalePatch.kt | 33 + .../mifitness/misc/login/Fingerprints.kt | 12 + .../mifitness/misc/login/FixLoginPatch.kt | 18 + .../patches/music/ad/video/Fingerprints.kt | 13 + .../patches/music/ad/video/HideVideoAds.kt | 22 + .../EnableExclusiveAudioPlayback.kt | 26 + .../audio/exclusiveaudio/Fingerprints.kt | 19 +- .../permanentrepeat/Fingerprints.kt | 20 + .../permanentrepeat/PermanentRepeatPatch.kt | 31 + .../permanentshuffle/Fingerprints.kt | 19 + .../permanentshuffle/PermanentShufflePatch.kt | 28 + .../layout/compactheader/Fingerprints.kt | 22 + .../layout/compactheader/HideCategoryBar.kt | 32 + .../music/layout/premium/Fingerprints.kt | 31 + .../layout/premium/HideGetPremiumPatch.kt | 48 + .../layout/upgradebutton/Fingerprints.kt | 18 + .../upgradebutton/RemoveUpgradeButtonPatch.kt | 77 + .../BypassCertificateChecksPatch.kt | 24 + .../music/misc/androidauto/Fingerprints.kt | 11 + .../BackgroundPlaybackPatch.kt | 31 + .../misc/backgroundplayback/Fingerprints.kt | 42 + .../misc/extension/SharedExtensionPatch.kt | 6 + .../extension/hooks/ApplicationInitHook.kt | 10 + .../patches/music/misc/gms/Constants.kt | 0 .../patches/music/misc/gms/Fingerprints.kt | 11 + .../music/misc/gms/GmsCoreSupportPatch.kt | 33 + .../backgroundplay/BackgroundPlayPatch.kt | 0 .../myexpenses/misc/pro/Fingerprints.kt | 8 + .../myexpenses/misc/pro/UnlockProPatch.kt | 23 + .../patches/myfitnesspal/ads/Fingerprints.kt | 19 + .../patches/myfitnesspal/ads/HideAdsPatch.kt | 33 + .../RemoveBroadcastsRestrictionPatch.kt | 20 +- .../nfctoolsse/misc/pro/Fingerprints.kt | 10 + .../nfctoolsse/misc/pro/UnlockProPatch.kt | 23 + .../patches/nyx/misc/pro/Fingerprints.kt | 9 + .../patches/nyx/misc/pro/UnlockProPatch.kt | 23 + .../misc/fix/crash/Fingerprints.kt | 12 + .../misc/fix/crash/FixCrashPatch.kt | 107 + .../detection/deviceid/Fingerprints.kt | 16 +- .../detection/deviceid/SpoofDeviceIdPatch.kt | 28 + .../detection/signature/Fingerprints.kt | 14 +- .../signature/SignatureDetectionPatch.kt | 25 + .../photomath/misc/annoyances/Fingerprints.kt | 21 + .../misc/annoyances/HideUpdatePopupPatch.kt | 24 + .../unlock/bookpoint/EnableBookpointPatch.kt | 21 + .../misc/unlock/bookpoint/Fingerprints.kt | 16 + .../misc/unlock/plus/Fingerprints.kt | 13 + .../misc/unlock/plus/UnlockPlusPatch.kt | 27 + .../patches/piccomafr/misc/Fingerprints.kt | 11 + .../misc/SpoofAndroidDeviceIdPatch.kt | 52 + .../tracking/DisableTrackingPatch.kt | 71 + .../piccomafr/tracking/Fingerprints.kt | 24 + .../patches/pixiv/ads/Fingerprints.kt | 12 + .../patches/pixiv/ads/HideAdsPatch.kt | 23 + .../purchasereminder/Fingerprints.kt | 12 + .../HidePurchaseReminderPatch.kt | 19 + .../reddit/ad/banner/HideBannerPatch.kt | 18 +- .../reddit/ad/comments/Fingerprints.kt | 14 + .../reddit/ad/comments/HideCommentAdsPatch.kt | 22 + .../patches/reddit/ad/general/Fingerprints.kt | 17 + .../patches/reddit/ad/general/HideAdsPatch.kt | 81 + .../reddit/customclients/FixSLinksPatch.kt | 17 + .../reddit/customclients/SpoofClientPatch.kt | 34 + .../baconreader/api/Fingerprints.kt | 19 + .../baconreader/api/SpoofClientPatch.kt | 40 + .../boostforreddit/ads/DisableAdsPatch.kt | 20 + .../boostforreddit/ads/Fingerprints.kt | 11 + .../boostforreddit/api/Fingerprints.kt | 15 + .../boostforreddit/api/SpoofClientPatch.kt | 41 + .../fix/downloads/Fingerprints.kt | 7 + .../FixAudioMissingInDownloadsPatch.kt | 32 + .../boostforreddit/fix/slink/Fingerprints.kt | 21 + .../fix/slink/FixSLinksPatch.kt | 53 + .../misc/extension/SharedExtensionPatch.kt | 6 + .../misc/extension/hooks/InitHook.kt | 11 + .../infinityforreddit/api/Fingerprints.kt | 7 + .../infinityforreddit/api/SpoofClientPatch.kt | 24 +- .../subscription/Fingerprints.kt | 15 + .../subscription/UnlockSubscriptionPatch.kt | 22 + .../joeyforreddit/ads/DisableAdsPatch.kt | 26 + .../joeyforreddit/ads/Fingerprints.kt | 10 + .../joeyforreddit/api/Fingerprints.kt | 28 + .../joeyforreddit/api/SpoofClientPatch.kt | 52 + .../piracy/DisablePiracyDetectionPatch.kt | 13 + .../detection/piracy/Fingerprints.kt | 21 + .../redditisfun/api/Fingerprints.kt | 31 + .../redditisfun/api/SpoofClientPatch.kt | 65 +- .../relayforreddit/api/Fingerprints.kt | 26 + .../relayforreddit/api/SpoofClientPatch.kt | 65 + .../customclients/slide/api/Fingerprints.kt | 11 + .../slide/api/SpoofClientPatch.kt | 23 + .../customclients/sync/ads/DisableAdsPatch.kt | 17 + .../customclients/sync/ads/Fingerprints.kt | 10 + .../piracy/DisablePiracyDetectionPatch.kt | 17 + .../sync/detection/piracy/Fingerprints.kt | 25 +- .../sync/syncforlemmy/ads/DisableAdsPatch.kt | 11 + .../sync/syncforreddit/ads/DisableAdsPatch.kt | 8 + .../DisableSyncForLemmyBottomSheetPatch.kt | 26 + .../annoyances/startup/Fingerprints.kt | 9 + .../sync/syncforreddit/api/Fingerprints.kt | 19 + .../syncforreddit/api/SpoofClientPatch.kt | 94 + .../extension/SharedExtensionPatch.kt | 6 + .../syncforreddit/extension/hooks/InitHook.kt | 11 + .../syncforreddit/fix/slink/Fingerprints.kt | 13 + .../syncforreddit/fix/slink/FixSLinksPatch.kt | 57 + .../syncforreddit/fix/user/Fingerprints.kt | 36 + .../fix/user/UseUserEndpointPatch.kt | 51 + .../syncforreddit/fix/video/Fingerprints.kt | 16 +- .../fix/video/FixVideoDownloadsPatch.kt | 57 + .../DisableScreenshotPopupPatch.kt | 18 + .../disablescreenshotpopup/Fingerprints.kt | 15 + .../reddit/layout/premiumicon/Fingerprints.kt | 10 + .../premiumicon/UnlockPremiumIconPatch.kt | 24 + .../reddit/misc/extension/ExtensionPatch.kt | 5 + .../reddit/misc/tracking/url/Fingerprints.kt | 9 + .../tracking/url/SanitizeUrlQueryPatch.kt | 21 + .../detection/root/Fingerprints.kt | 12 + .../detection/root/RootDetectionPatch.kt | 18 + .../revanced/patches/shared/Fingerprints.kt | 11 + .../misc/checks/BaseCheckEnvironmentPatch.kt | 111 + .../shared/misc/checks/Fingerprints.kt | 11 + .../shared/misc/extension/Fingerprints.kt | 13 + .../misc/extension/SharedExtensionPatch.kt | 100 + .../misc/fix/verticalscroll/Fingerprints.kt | 18 + .../fix/verticalscroll/VerticalScrollPatch.kt | 26 + .../patches/shared/misc/gms/Fingerprints.kt | 30 + .../shared/misc/gms/GmsCoreSupportPatch.kt | 614 +++++ .../patches/shared/misc/hex/HexPatch.kt | 123 + .../misc/mapping/ResourceMappingPatch.kt | 41 +- .../shared/misc/settings/SettingsPatch.kt | 45 +- .../settings/preference/BasePreference.kt | 0 .../preference/BasePreferenceScreen.kt | 10 +- .../misc/settings/preference/InputType.kt | 0 .../settings/preference/IntentPreference.kt | 0 .../settings/preference/ListPreference.kt | 0 .../preference/NonInteractivePreference.kt | 13 +- .../settings/preference/PreferenceCategory.kt | 0 .../preference/PreferenceScreenPreference.kt | 4 +- .../misc/settings/preference/SummaryType.kt | 0 .../settings/preference/SwitchPreference.kt | 0 .../settings/preference/TextPreference.kt | 2 +- .../functionality/filesize/Fingerprints.kt | 15 + .../filesize/RemoveFileSizeLimitPatch.kt | 25 + .../patches/songpal/badge/BadgeTabPatch.kt | 36 +- .../patches/songpal/badge/Fingerprints.kt | 54 + .../badge/RemoveNotificationBadgePatch.kt | 18 + .../patches/soundcloud/ad/Fingerprints.kt | 30 + .../patches/soundcloud/ad/HideAdsPatch.kt | 42 +- .../analytics/DisableTelemetryPatch.kt | 18 + .../soundcloud/analytics/Fingerprints.kt | 13 + .../offlinesync/EnableOfflineSyncPatch.kt | 41 +- .../soundcloud/offlinesync/Fingerprints.kt | 29 + .../patches/soundcloud/shared/Fingerprints.kt | 16 + .../spotify/layout/theme/CustomThemePatch.kt | 33 +- .../spotify/lite/ondemand/Fingerprints.kt | 23 + .../spotify/lite/ondemand/OnDemandPatch.kt | 22 + .../patches/spotify/navbar/Fingerprints.kt | 11 + .../spotify/navbar/PremiumNavbarTabPatch.kt | 52 + .../stocard/layout/HideOffersTabPatch.kt | 17 +- .../stocard/layout/HideStoryBubblesPatch.kt | 17 +- .../strava/subscription/Fingerprints.kt | 11 + .../subscription/UnlockSubscriptionPatch.kt | 20 + .../DisableSubscriptionSuggestionsPatch.kt | 69 + .../patches/strava/upselling/Fingerprints.kt | 11 + .../swissid/integritycheck/Fingerprints.kt | 9 + .../RemoveGooglePlayIntegrityCheckPatch.kt | 32 + .../ticktick/misc/themeunlock/Fingerprints.kt | 15 + .../misc/themeunlock/UnlockThemePatch.kt | 28 + .../tiktok/feedfilter/FeedFilterPatch.kt | 48 + .../patches/tiktok/feedfilter/Fingerprints.kt | 9 + .../interaction/cleardisplay/Fingerprints.kt | 10 + .../cleardisplay/RememberClearDisplayPatch.kt | 53 +- .../interaction/downloads/DownloadsPatch.kt | 98 + .../interaction/downloads/Fingerprints.kt | 47 + .../interaction/seekbar/Fingerprints.kt | 11 + .../interaction/seekbar/ShowSeekbarPatch.kt | 38 + .../tiktok/interaction/speed/Fingerprints.kt | 17 + .../interaction/speed/PlaybackSpeedPatch.kt | 74 + .../tiktok/misc/extension/ExtensionPatch.kt | 5 + .../patches/tiktok/misc/extension/Hooks.kt | 14 + .../DisableLoginRequirementPatch.kt | 32 + .../login/disablerequirement/Fingerprints.kt | 17 + .../misc/login/fixgoogle/Fingerprints.kt | 22 + .../login/fixgoogle/FixGoogleLoginPatch.kt | 33 + .../tiktok/misc/settings/Fingerprints.kt | 35 + .../tiktok/misc/settings/SettingsPatch.kt | 102 + .../tiktok/misc/spoof/sim/SpoofSimPatch.kt | 85 +- .../patches/tiktok/shared/Fingerprints.kt | 30 + .../revanced/patches/trakt/Fingerprints.kt | 25 + .../revanced/patches/trakt/UnlockProPatch.kt | 36 + .../tudortmund/lockscreen/Fingerprints.kt | 15 + .../lockscreen}/ShowOnLockscreenPatch.kt | 55 +- .../misc/extension/ExtensionPatch.kt | 5 + .../patches/tumblr/ads/DisableDashboardAds.kt | 25 +- .../adfree/DisableAdFreeBannerPatch.kt | 20 + .../inappupdate/DisableInAppUpdatePatch.kt | 21 + .../DisableBlogNotificationReminderPatch.kt | 25 + .../annoyances/notifications/Fingerprints.kt | 8 +- .../popups/DisableGiftMessagePopupPatch.kt | 18 + .../tumblr/annoyances/popups/Fingerprints.kt | 11 + .../tumblr/featureflags/Fingerprints.kt | 25 +- .../featureflags/OverrideFeatureFlagsPatch.kt | 86 +- .../patches/tumblr/fixes/Fingerprints.kt | 23 + .../tumblr/fixes/FixOldVersionsPatch.kt | 57 + .../tumblr/live/DisableTumblrLivePatch.kt | 29 + .../tumblr/misc/extension/ExtensionPatch.kt | 5 + .../FilterTimelineObjectsPatch.kt | 67 + .../tumblr/timelinefilter/Fingerprints.kt | 36 + .../patches/twitch/ad/audio/AudioAdsPatch.kt | 48 + .../patches/twitch/ad/audio/Fingerprints.kt | 9 + .../twitch/ad/embedded/EmbeddedAdsPatch.kt | 44 + .../twitch/ad/embedded/Fingerprints.kt | 9 + .../patches/twitch/ad/shared/util/AdPatch.kt | 67 + .../patches/twitch/ad/video/Fingerprints.kt | 28 + .../patches/twitch/ad/video/VideoAdsPatch.kt | 167 ++ .../twitch/chat/antidelete/Fingerprints.kt | 24 + .../antidelete/ShowDeletedMessagesPatch.kt | 78 + .../autoclaim/AutoClaimChannelPointsPatch.kt | 47 +- .../twitch/chat/autoclaim/Fingerprints.kt | 10 + .../patches/twitch/debug/DebugModePatch.kt | 52 + .../patches/twitch/debug/Fingerprints.kt | 21 + .../patches/twitch/misc/extension/Hooks.kt | 9 + .../misc/extension/SharedExtensionPatch.kt | 5 + .../twitch/misc/settings/Fingerprints.kt | 34 + .../twitch/misc/settings/SettingsPatch.kt | 208 ++ .../interaction/downloads/Fingerprints.kt | 27 + .../downloads/UnlockDownloadsPatch.kt | 70 + .../twitter/layout/viewcount/Fingerprints.kt | 8 + .../layout/viewcount/HideViewCountPatch.kt | 25 + .../misc/dynamiccolor/DynamicColorPatch.kt | 26 +- .../twitter/misc/extension/ExtensionPatch.kt | 5 + .../twitter/misc/hook/HideAdsHookPatch.kt | 7 + .../misc/hook/HideRecommendedUsersPatch.kt | 7 + .../patches/twitter/misc/hook/HookPatch.kt | 19 + .../twitter/misc/hook/json/Fingerprints.kt | 27 + .../twitter/misc/hook/json/JsonHookPatch.kt | 141 + .../links/ChangeLinkSharingDomainPatch.kt | 96 + .../twitter/misc/links/Fingerprints.kt | 31 + .../links/OpenLinksWithAppChooserPatch.kt | 30 + .../misc/links/SanitizeSharingLinksPatch.kt | 25 + .../patches/vsco/misc/pro/Fingerprints.kt | 11 + .../patches/vsco/misc/pro/UnlockProPatch.kt | 19 + .../misc/firebasegetcert/Fingerprints.kt | 21 + .../firebasegetcert/FirebaseGetCertPatch.kt | 26 + .../warnwetter/misc/promocode/Fingerprints.kt | 9 + .../misc/promocode/PromoCodeUnlockPatch.kt | 27 + .../patches/willhaben/ads/Fingerprints.kt | 26 + .../patches/willhaben/ads/HideAdsPatch.kt | 20 + .../windyapp/misc/unlockpro/Fingerprints.kt | 10 + .../windyapp/misc/unlockpro/UnlockProPatch.kt | 24 + .../youtube/ad/general/HideAdsPatch.kt | 118 + .../youtube/ad/getpremium/Fingerprints.kt | 21 + .../ad/getpremium/HideGetPremiumPatch.kt | 70 + .../patches/youtube/ad/video/Fingerprints.kt | 11 +- .../patches/youtube/ad/video/VideoAdsPatch.kt | 55 + .../copyvideourl/CopyVideoUrlPatch.kt | 76 + .../interaction/dialog/Fingerprints.kt | 20 +- .../RemoveViewerDiscretionDialogPatch.kt | 59 + .../interaction/downloads/DownloadsPatch.kt | 108 + .../interaction/downloads/Fingerprints.kt | 16 + .../DisablePreciseSeekingGesturePatch.kt | 80 + .../seekbar/EnableSeekbarTappingPatch.kt | 86 + .../seekbar/EnableSlideToSeekPatch.kt | 127 + .../interaction/seekbar/Fingerprints.kt | 119 + .../interaction/swipecontrols/Fingerprints.kt | 12 + .../swipecontrols/SwipeControlsPatch.kt | 107 + .../layout/autocaptions/AutoCaptionsPatch.kt | 73 + .../layout/autocaptions/Fingerprints.kt | 33 + .../layout/branding/CustomBrandingPatch.kt | 63 +- .../branding/header/ChangeHeaderPatch.kt | 65 +- .../layout/buttons/action/HideButtonsPatch.kt | 55 + .../layout/buttons/navigation/Fingerprints.kt | 25 + .../navigation/NavigationButtonsPatch.kt | 106 + .../layout/buttons/overlay/Fingerprints.kt | 22 + .../overlay/HidePlayerOverlayButtonsPatch.kt | 158 ++ .../buttons/player/hide/Fingerprints.kt | 9 + .../hide/breakingnews/BreakingNewsPatch.kt | 10 + .../hide/endscreencards/Fingerprints.kt | 40 + .../endscreencards/HideEndscreenCardsPatch.kt | 90 + .../DisableFullscreenAmbientModePatch.kt | 67 + .../fullscreenambientmode/Fingerprints.kt | 13 + .../layout/hide/general/Fingerprints.kt | 116 + .../hide/general/HideLayoutComponentsPatch.kt | 382 +-- .../layout/hide/infocards/Fingerprints.kt | 29 + .../hide/infocards/HideInfoCardsPatch.kt | 109 + .../HidePlayerFlyoutMenuPatch.kt | 62 + .../DisableRollingNumberAnimationPatch.kt | 74 + .../layout/hide/seekbar/HideSeekbarPatch.kt | 61 + .../layout/hide/shorts/Fingerprints.kt | 103 + .../hide/shorts/HideShortsComponentsPatch.kt | 329 +++ .../DisableSuggestedVideoEndScreenPatch.kt | 79 + .../suggestedvideoendscreen/Fingerprints.kt | 18 + .../youtube/layout/hide/time/Fingerprints.kt | 24 + .../layout/hide/time/HideTimestampPatch.kt | 54 + .../youtube/layout/miniplayer/Fingerprints.kt | 152 ++ .../layout/miniplayer/MiniplayerPatch.kt | 560 ++++ .../layout/panels/popup/Fingerprints.kt | 13 + .../panels/popup/PlayerPopupPanelsPatch.kt | 56 + .../PlayerControlsBackgroundPatch.kt | 35 + .../CustomPlayerOverlayOpacityPatch.kt | 71 + .../layout/player/overlay/Fingerprints.kt | 20 + .../returnyoutubedislike/Fingerprints.kt | 155 ++ .../ReturnYouTubeDislikePatch.kt | 389 +++ .../youtube/layout/searchbar/Fingerprints.kt | 31 + .../layout/searchbar/WideSearchbarPatch.kt | 84 + .../youtube/layout/seekbar/Fingerprints.kt | 50 + .../RestoreOldSeekbarThumbnailsPatch.kt | 61 + .../layout/seekbar/SeekbarColorPatch.kt | 151 ++ .../layout/shortsautoplay/Fingerprints.kt | 22 + .../shortsautoplay/ShortsAutoplayPatch.kt | 103 + .../layout/sponsorblock/Fingerprints.kt | 64 + .../layout/sponsorblock/SponsorBlockPatch.kt | 258 ++ .../layout/spoofappversion/Fingerprints.kt | 20 + .../spoofappversion/SpoofAppVersionPatch.kt | 65 + .../layout/startpage/ChangeStartPagePatch.kt | 77 + .../youtube/layout/startpage/Fingerprints.kt | 20 + .../DisableResumingShortsOnStartupPatch.kt | 99 + .../layout/startupshortsreset/Fingerprints.kt | 41 + .../layout/tablet/EnableTabletLayoutPatch.kt | 65 + .../youtube/layout/tablet/Fingerprints.kt | 25 + .../youtube/layout/theme/Fingerprints.kt | 56 + .../layout/theme/LithoColorHookPatch.kt | 28 + .../youtube/layout/theme/ThemePatch.kt | 245 ++ .../thumbnails/AlternativeThumbnailsPatch.kt | 98 + .../BypassImageRegionRestrictionsPatch.kt | 50 + .../misc/announcements/AnnouncementsPatch.kt | 43 + .../misc/autorepeat/AutoRepeatPatch.kt | 68 + .../BackgroundPlaybackPatch.kt | 103 + .../misc/backgroundplayback/Fingerprints.kt | 72 + .../misc/check/CheckEnvironmentPatch.kt | 12 + .../misc/debugging/EnableDebuggingPatch.kt | 41 + .../misc/dimensions/spoof/Fingerprints.kt | 8 + .../spoof/SpoofDeviceDimensionsPatch.kt | 63 + ...ckWatchHistoryDomainNameResolutionPatch.kt | 41 + .../misc/extension/SharedExtensionPatch.kt | 9 + .../extension/hooks/ApplicationInitHook.kt | 11 + .../fix/backtoexitgesture/Fingerprints.kt | 73 + .../FixBackToExitGesturePatch.kt | 61 + .../fix/cairo/DisableCairoSettingsPatch.kt | 41 +- .../youtube/misc/fix/cairo/Fingerprints.kt | 19 + .../youtube/misc/fix/playback/Fingerprints.kt | 261 ++ .../fix/playback/SpoofVideoStreamsPatch.kt | 248 ++ .../fix/playback/UserAgentClientSpoofPatch.kt | 77 +- .../patches/youtube/misc/gms/Constants.kt | 0 .../youtube/misc/gms/GmsCoreSupportPatch.kt | 70 + .../misc/imageurlhook/CronetImageUrlHook.kt | 114 + .../youtube/misc/imageurlhook/Fingerprints.kt | 64 + .../misc/links/BypassURLRedirectsPatch.kt | 86 + .../youtube/misc/links/Fingerprints.kt | 72 + .../misc/links/OpenLinksExternallyPatch.kt | 60 + .../youtube/misc/litho/filter/Fingerprints.kt | 57 + .../misc/litho/filter/LithoFilterPatch.kt | 243 ++ .../youtube/misc/navigation/Fingerprints.kt | 107 + .../misc/navigation/NavigationBarHookPatch.kt | 163 ++ .../misc/playercontrols/Fingerprints.kt | 48 + .../playercontrols/PlayerControlsPatch.kt | 273 ++ .../youtube/misc/playertype/Fingerprints.kt | 28 + .../misc/playertype/PlayerTypeHookPatch.kt | 40 + .../misc/playservice/VersionCheckPatch.kt | 64 +- .../youtube/misc/privacy/Fingerprints.kt | 44 + .../RemoveTrackingQueryParameterPatch.kt | 82 + .../recyclerviewtree/hook/Fingerprints.kt | 18 + .../hook/RecyclerViewTreeHookPatch.kt | 29 + .../youtube/misc/settings/Fingerprints.kt | 23 + .../youtube/misc/settings/SettingsPatch.kt | 272 ++ .../youtube/misc/zoomhaptics/Fingerprints.kt | 7 + .../misc/zoomhaptics/ZoomHapticsPatch.kt | 47 + .../patches/youtube/shared/Fingerprints.kt | 130 + .../youtube/video/information/Fingerprints.kt | 132 + .../information/VideoInformationPatch.kt | 311 +++ .../video/playerresponse/Fingerprints.kt | 52 + .../PlayerResponseMethodHookPatch.kt | 122 + .../youtube/video/quality/Fingerprints.kt | 37 + .../quality/RememberVideoQualityPatch.kt | 150 +- .../youtube/video/speed/PlaybackSpeedPatch.kt | 29 + .../speed/button/PlaybackSpeedButtonPatch.kt | 56 + .../speed/custom/CustomPlaybackSpeedPatch.kt | 173 ++ .../video/speed/custom/Fingerprints.kt | 50 + .../video/speed/remember/Fingerprints.kt | 8 + .../remember/RememberPlaybackSpeedPatch.kt | 87 + .../youtube/video/videoid/Fingerprints.kt | 43 + .../youtube/video/videoid/VideoIdPatch.kt | 123 + .../video/videoqualitymenu/Fingerprints.kt | 43 + .../RestoreOldVideoQualityMenuPatch.kt | 136 + .../yuka/misc/unlockpremium/Fingerprints.kt | 20 + .../misc/unlockpremium/UnlockPremiumPatch.kt | 26 + .../kotlin/app/revanced/util/BytecodeUtils.kt | 87 +- .../kotlin/app/revanced/util/ResourceUtils.kt | 75 +- .../main/kotlin/app/revanced/util/Utils.kt | 0 .../util/microg/MicroGBytecodeHelper.kt | 0 .../util/microg/MicroGResourceHelper.kt | 0 .../revanced/util/resource/ArrayResource.kt | 0 .../revanced/util/resource/BaseResource.kt | 0 .../revanced/util/resource/StringResource.kt | 0 .../addresources/values-af-rZA/strings.xml | 0 .../addresources/values-am-rET/strings.xml | 0 .../addresources/values-ar-rSA/strings.xml | 0 .../addresources/values-as-rIN/strings.xml | 0 .../addresources/values-az-rAZ/strings.xml | 0 .../addresources/values-be-rBY/strings.xml | 0 .../addresources/values-bg-rBG/strings.xml | 0 .../addresources/values-bn-rBD/strings.xml | 0 .../addresources/values-bs-rBA/strings.xml | 0 .../addresources/values-ca-rES/strings.xml | 0 .../addresources/values-cs-rCZ/strings.xml | 0 .../addresources/values-da-rDK/strings.xml | 0 .../addresources/values-de-rDE/strings.xml | 0 .../addresources/values-el-rGR/strings.xml | 0 .../addresources/values-es-rES/strings.xml | 0 .../addresources/values-et-rEE/strings.xml | 0 .../addresources/values-eu-rES/strings.xml | 0 .../addresources/values-fa-rIR/strings.xml | 0 .../addresources/values-fi-rFI/strings.xml | 0 .../addresources/values-fil-rPH/strings.xml | 0 .../addresources/values-fr-rFR/strings.xml | 0 .../addresources/values-gl-rES/strings.xml | 0 .../addresources/values-gu-rIN/strings.xml | 0 .../addresources/values-hi-rIN/strings.xml | 0 .../addresources/values-hr-rHR/strings.xml | 0 .../addresources/values-hu-rHU/strings.xml | 0 .../addresources/values-hy-rAM/strings.xml | 0 .../addresources/values-in-rID/strings.xml | 0 .../addresources/values-is-rIS/strings.xml | 0 .../addresources/values-it-rIT/strings.xml | 0 .../addresources/values-iw-rIL/strings.xml | 0 .../addresources/values-ja-rJP/strings.xml | 0 .../addresources/values-ka-rGE/strings.xml | 0 .../addresources/values-kk-rKZ/strings.xml | 0 .../addresources/values-km-rKH/strings.xml | 0 .../addresources/values-kn-rIN/strings.xml | 0 .../addresources/values-ko-rKR/strings.xml | 0 .../addresources/values-ky-rKG/strings.xml | 0 .../addresources/values-lo-rLA/strings.xml | 0 .../addresources/values-lt-rLT/strings.xml | 0 .../addresources/values-lv-rLV/strings.xml | 0 .../addresources/values-mk-rMK/strings.xml | 0 .../addresources/values-ml-rIN/strings.xml | 0 .../addresources/values-mn-rMN/strings.xml | 0 .../addresources/values-mr-rIN/strings.xml | 0 .../addresources/values-ms-rMY/strings.xml | 0 .../addresources/values-my-rMM/strings.xml | 0 .../addresources/values-nb-rNO/strings.xml | 0 .../addresources/values-ne-rIN/strings.xml | 0 .../addresources/values-nl-rNL/strings.xml | 0 .../addresources/values-or-rIN/strings.xml | 0 .../addresources/values-pa-rIN/strings.xml | 0 .../addresources/values-pl-rPL/strings.xml | 0 .../addresources/values-pt-rBR/strings.xml | 0 .../addresources/values-pt-rPT/strings.xml | 0 .../addresources/values-ro-rRO/strings.xml | 0 .../addresources/values-ru-rRU/strings.xml | 0 .../addresources/values-si-rLK/strings.xml | 0 .../addresources/values-sk-rSK/strings.xml | 0 .../addresources/values-sl-rSI/strings.xml | 0 .../addresources/values-sq-rAL/strings.xml | 0 .../addresources/values-sr-rSP/strings.xml | 0 .../addresources/values-sv-rSE/strings.xml | 0 .../addresources/values-sw-rKE/strings.xml | 0 .../addresources/values-ta-rIN/strings.xml | 0 .../addresources/values-te-rIN/strings.xml | 0 .../addresources/values-th-rTH/strings.xml | 0 .../addresources/values-tr-rTR/strings.xml | 0 .../addresources/values-uk-rUA/strings.xml | 0 .../addresources/values-ur-rIN/strings.xml | 0 .../addresources/values-uz-rUZ/strings.xml | 0 .../addresources/values-vi-rVN/strings.xml | 0 .../addresources/values-zh-rCN/strings.xml | 0 .../addresources/values-zh-rTW/strings.xml | 0 .../addresources/values-zu-rZA/strings.xml | 0 .../resources/addresources/values/arrays.xml | 27 +- .../resources/addresources/values/strings.xml | 140 +- .../drawable-hdpi/yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../drawable-mdpi/yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../drawable-hdpi/yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../drawable-mdpi/yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../yt_wordmark_header_dark.png | Bin .../yt_wordmark_header_light.png | Bin .../drawable/revanced_yt_copy.xml | 0 .../drawable/revanced_yt_copy_timestamp.xml | 0 .../youtube_controls_bottom_ui_container.xml | 0 ...veproduct_youtube_background_color_108.png | Bin ...veproduct_youtube_foreground_color_108.png | Bin .../mipmap-hdpi/ic_launcher.png | Bin .../mipmap-hdpi/ic_launcher_round.png | Bin ...veproduct_youtube_background_color_108.png | Bin ...veproduct_youtube_foreground_color_108.png | Bin .../mipmap-mdpi/ic_launcher.png | Bin .../mipmap-mdpi/ic_launcher_round.png | Bin ...veproduct_youtube_background_color_108.png | Bin ...veproduct_youtube_foreground_color_108.png | Bin .../mipmap-xhdpi/ic_launcher.png | Bin .../mipmap-xhdpi/ic_launcher_round.png | Bin ...veproduct_youtube_background_color_108.png | Bin ...veproduct_youtube_foreground_color_108.png | Bin .../mipmap-xxhdpi/ic_launcher.png | Bin .../mipmap-xxhdpi/ic_launcher_round.png | Bin ...veproduct_youtube_background_color_108.png | Bin ...veproduct_youtube_foreground_color_108.png | Bin .../mipmap-xxxhdpi/ic_launcher.png | Bin .../mipmap-xxxhdpi/ic_launcher_round.png | Bin .../drawable/revanced_yt_download_button.xml | 0 .../youtube_controls_bottom_ui_container.xml | 0 .../resources/settings/host/values/styles.xml | 0 .../layout/revanced_settings_with_toolbar.xml | 0 .../resources/settings/xml/revanced_prefs.xml | 0 .../revanced_playback_speed_dialog_button.xml | 0 .../youtube_controls_bottom_ui_container.xml | 0 .../quantum_ic_skip_next_white_24.png | Bin .../drawable/revanced_sb_adjust.xml | 0 .../drawable/revanced_sb_backward.xml | 0 .../drawable/revanced_sb_compare.xml | 0 .../drawable/revanced_sb_edit.xml | 0 .../drawable/revanced_sb_forward.xml | 0 .../drawable/revanced_sb_logo.xml | 0 .../drawable/revanced_sb_publish.xml | 0 .../drawable/revanced_sb_voting.xml | 0 .../host/layout/youtube_controls_layout.xml | 0 .../revanced_sb_inline_sponsor_overlay.xml | 6 +- .../layout/revanced_sb_new_segment.xml | 0 .../revanced_sb_skip_sponsor_button.xml | 0 .../revanced_ic_sc_brightness_auto.xml | 0 .../revanced_ic_sc_brightness_manual.xml | 0 .../drawable/revanced_ic_sc_volume_mute.xml | 0 .../drawable/revanced_ic_sc_volume_normal.xml | 0 {stub => patches/stub}/build.gradle.kts | 0 .../stub}/src/main/java/android/os/Build.java | 0 settings.gradle.kts | 21 +- .../generator/JsonPatchesFileGenerator.kt | 49 - .../kotlin/app/revanced/generator/Main.kt | 12 - .../generator/PatchesFileGenerator.kt | 7 - .../connectivity/wifi/spoof/SpoofWifiPatch.kt | 206 -- .../ChangeDataDirectoryLocationPatch.kt | 73 - .../gestures/PredictiveBackGesturePatch.kt | 29 - .../location/hide/HideMockLocationPatch.kt | 56 - .../all/misc/build/BaseSpoofBuildInfoPatch.kt | 120 - .../packagename/ChangePackageNamePatch.kt | 70 - .../all/misc/resources/AddResourcesPatch.kt | 384 --- .../RemoveCaptureRestrictionPatch.kt | 62 - .../RemoveCaptureRestrictionResourcePatch.kt | 24 - .../RemoveScreenshotRestrictionPatch.kt | 106 - .../sim/spoof/SpoofSimCountryPatch.kt | 113 - .../deeplinking/DeepLinkingFingerprint.kt | 11 - .../amazon/deeplinking/DeepLinkingPatch.kt | 28 - .../backdrops/misc/pro/ProUnlockPatch.kt | 37 - .../pro/fingerprints/ProUnlockFingerprint.kt | 18 - .../limitations/RemovePlayLimitsPatch.kt | 23 - .../fingerprints/HandlePlaybackLimitsPatch.kt | 7 - .../patches/candylinkvpn/UnlockProPatch.kt | 31 - .../IsPremiumPurchasedFingerprint.kt | 10 - .../root/BypassRootChecksPatch.kt | 24 - .../root/fingerprints/CheckRootFingerprint.kt | 9 - .../patches/duolingo/ad/DisableAdsPatch.kt | 41 - ...izeMonetizationDebugSettingsFingerprint.kt | 21 - .../duolingo/debug/EnableDebugMenuPatch.kt | 35 - ...nitializeBuildConfigProviderFingerprint.kt | 25 - .../ads/mainfeed/HideSponsoredStoriesPatch.kt | 96 - .../BaseModelMapperFingerprint.kt | 21 - ...etSponsoredDataModelTemplateFingerprint.kt | 23 - .../GetStoryVisibilityFingerprint.kt | 23 - .../facebook/ads/story/HideStoryAdsPatch.kt | 26 - .../fingerprints/AdsInsertionFingerprint.kt | 3 - .../fingerprints/FetchMoreAdsFingerprint.kt | 3 - .../fingerprints/FieldMethodFingerprint.kt | 15 - .../bootloader/BootloaderDetectionPatch.kt | 33 - .../fingerprints/CreateKeyFingerprint.kt | 11 - .../detection/root/RootDetectionPatch.kt | 29 - .../googlenews/customtabs/EnableCustomTabs.kt | 32 - .../LaunchCustomTabFingerprint.kt | 18 - .../misc/gms/GmsCoreSupportPatch.kt | 23 - .../misc/gms/GmsCoreSupportResourcePatch.kt | 11 - .../MagazinesActivityOnCreateFingerprint.kt | 9 - .../fingerprints/PrimeMethodFingerprint.kt | 7 - .../misc/integrations/IntegrationsPatch.kt | 10 - .../features/SpoofBuildInfoPatch.kt | 14 - .../InitializeFeaturesEnumFingerprint.kt | 7 - .../misc/gms/GmsCoreSupportPatch.kt | 21 - .../misc/gms/GmsCoreSupportResourcePatch.kt | 11 - .../PhotosActivityOnCreateFingerprint.kt | 9 - .../misc/integrations/IntegrationsPatch.kt | 10 - ...oreHiddenBackUpWhileChargingTogglePatch.kt | 33 - .../BackupPreferencesFingerprint.kt | 10 - .../restrictions/RemoveDeviceRestrictions.kt | 38 - .../OnApplicationCreateFingerprint.kt | 12 - .../patches/hexeditor/ad/DisableAdsPatch.kt | 26 - .../ad/fingerprints/PrimaryAdsFingerprint.kt | 9 - .../iconpackstudio/misc/pro/UnlockProPatch.kt | 28 - .../pro/fingerprints/CheckProFingerprint.kt | 8 - .../detection/root/RootDetectionPatch.kt | 26 - .../AttestationSupportedCheckFingerprint.kt | 13 - .../BootloaderCheckFingerprint.kt | 13 - .../root/fingerprints/RootCheckFingerprint.kt | 13 - .../fingerprints/SpoofSignatureFingerprint.kt | 13 - .../patches/inshorts/ad/InshortsAdsPatch.kt | 31 - .../ad/fingerprints/InshortsAdsFingerprint.kt | 8 - .../instagram/patches/ad/HideAdsPatch.kt | 29 - .../ad/fingerprints/AdInjectorFingerprint.kt | 14 - .../ads/timeline/HideTimelineAdsPatch.kt | 63 - .../fingerprints/IsAdCheckOneFingerprint.kt | 18 - .../fingerprints/IsAdCheckTwoFingerprint.kt | 22 - .../fingerprints/ShowAdFingerprint.kt | 21 - .../patches/irplus/ad/RemoveAdsPatch.kt | 26 - .../ad/fingerprints/IrplusAdsFingerprint.kt | 12 - .../misc/login/DisableMandatoryLoginPatch.kt | 26 - .../fingerprints/IsLoggedInFingerprint.kt | 19 - .../misc/premium/UnlockPremiumPatch.kt | 24 - .../fingerprints/HasPurchasedFingerprint.kt | 18 - .../license/LicenseValidationPatch.kt | 25 - .../LicenseValidationFingerprint.kt | 24 - .../signature/SignatureVerificationPatch.kt | 25 - .../misc/pro/UnlockProVersionPatch.kt | 44 - .../fingerprints/IsFreeVersionFingerprint.kt | 22 - .../messenger/inbox/HideInboxAdsPatch.kt | 25 - .../messenger/inbox/HideInboxSubtabsPatch.kt | 24 - .../CreateInboxSubTabsFingerprint.kt | 23 - .../fingerprints/LoadInboxAdsFingerprint.kt | 17 - .../DisableSwitchingEmojiToStickerPatch.kt | 36 - .../inputfield/DisableTypingIndicatorPatch.kt | 24 - .../SendTypingIndicatorFingerprint.kt | 15 - ...itchMessangeInputEmojiButtonFingerprint.kt | 18 - .../misc/locale/ForceEnglishLocalePatch.kt | 39 - .../SyncBluetoothLanguageFingerprint.kt | 12 - .../mifitness/misc/login/FixLoginPatch.kt | 26 - ...omiAccountManagerConstructorFingerprint.kt | 16 - .../patches/moneymanager/UnlockProPatch.kt | 27 - .../fingerprints/UnlockProFingerprint.kt | 19 - .../patches/music/ad/video/HideVideoAds.kt | 48 - .../ShowVideoAdsParentFingerprint.kt | 13 - .../music/audio/codecs/CodecsUnlockPatch.kt | 48 - .../AllCodecsReferenceFingerprint.kt | 52 - .../fingerprints/CodecsLockFingerprint.kt | 29 - .../EnableExclusiveAudioPlayback.kt | 38 - .../permanentrepeat/PermanentRepeatPatch.kt | 39 - .../fingerprints/RepeatTrackFingerprint.kt | 21 - .../permanentshuffle/PermanentShufflePatch.kt | 43 - .../fingerprints/DisableShuffleFingerprint.kt | 20 - .../layout/compactheader/HideCategoryBar.kt | 49 - .../ConstructCategoryBarFingerprint.kt | 23 - .../MinimizedPlaybackPatch.kt | 13 - .../layout/premium/HideGetPremiumPatch.kt | 60 - .../fingerprints/HideGetPremiumFingerprint.kt | 18 - .../MembershipSettingsFingerprint.kt | 20 - .../upgradebutton/RemoveUpgradeButtonPatch.kt | 86 - .../PivotBarConstructorFingerprint.kt | 19 - .../BypassCertificateChecksPatch.kt | 31 - .../CheckCertificateFingerprint.kt | 12 - .../BackgroundPlaybackPatch.kt | 41 - .../BackgroundPlaybackDisableFingerprint.kt | 23 - ...oundPlaybackPolicyControllerFingerprint.kt | 26 - .../music/misc/gms/GmsCoreSupportPatch.kt | 32 - .../misc/gms/GmsCoreSupportResourcePatch.kt | 11 - .../MusicActivityOnCreateFingerprint.kt | 11 - .../fingerprints/PrimeMethodFingerprint.kt | 8 - .../misc/integrations/IntegrationsPatch.kt | 10 - .../ApplicationInitFingerprint.kt | 10 - .../backgroundplay/BackgroundPlayPatch.kt | 13 - .../myexpenses/misc/pro/UnlockProPatch.kt | 25 - .../pro/fingerprints/IsEnabledFingerprint.kt | 8 - .../patches/myfitnesspal/ads/HideAdsPatch.kt | 38 - .../IsPremiumUseCaseImplFingerprint.kt | 11 - ...avigateToNativePremiumUpsellFingerprint.kt | 13 - .../nfctoolsse/misc/pro/UnlockProPatch.kt | 25 - .../IsLicenseRegisteredFingerprint.kt | 10 - .../patches/nyx/misc/pro/UnlockProPatch.kt | 24 - .../pro/fingerprints/CheckProFingerprint.kt | 9 - .../misc/fix/crash/FixCrashPatch.kt | 115 - .../crash/fingerprints/SetPlaceFingerprint.kt | 12 - .../detection/deviceid/SpoofDeviceIdPatch.kt | 30 - .../signature/SignatureDetectionPatch.kt | 24 - .../misc/annoyances/HideUpdatePopupPatch.kt | 26 - .../HideUpdatePopupFingerprint.kt | 22 - .../unlock/bookpoint/EnableBookpointPatch.kt | 22 - .../IsBookpointEnabledFingerprint.kt | 17 - .../misc/unlock/plus/UnlockPlusPatch.kt | 29 - .../fingerprints/IsPlusUnlockedFingerprint.kt | 16 - .../misc/SpoofAndroidDeviceIdPatch.kt | 59 - .../fingerprints/GetAndroidIDFingerprint.kt | 16 - .../tracking/DisableTrackingPatch.kt | 80 - .../fingerprints/AppMesurementFingerprint.kt | 15 - .../fingerprints/FacebookSDKFingerprint.kt | 15 - .../FirebaseInstallFingerprint.kt | 13 - .../patches/pixiv/ads/HideAdsPatch.kt | 25 - .../fingerprints/ShouldShowAdsFingerprint.kt | 14 - .../HidePurchaseReminderPatch.kt | 24 - .../fingerprints/ShowReminderFingerprint.kt | 13 - .../reddit/ad/comments/HideCommentAdsPatch.kt | 22 - .../fingerprints/HideCommentAdsFingerprint.kt | 14 - .../patches/reddit/ad/general/HideAdsPatch.kt | 85 - .../general/fingerprints/AdPostFingerprint.kt | 10 - .../fingerprints/NewAdPostFingerprint.kt | 10 - .../customclients/BaseFixSLinksPatch.kt | 49 - .../customclients/BaseSpoofClientPatch.kt | 75 - .../patches/reddit/customclients/Constants.kt | 6 - .../customclients/ads/BaseDisableAdsPatch.kt | 19 - .../fingerprints/IsAdsEnabledFingerprint.kt | 11 - .../baconreader/api/SpoofClientPatch.kt | 41 - .../GetAuthorizationUrlFingerprint.kt | 7 - .../fingerprints/GetClientIdFingerprint.kt | 12 - .../fingerprints/RequestTokenFingerprint.kt | 7 - .../boostforreddit/ads/DisableAdsPatch.kt | 23 - .../fingerprints/AdmobMediationFingerprint.kt | 7 - .../fingerprints/MaxMediationFingerprint.kt | 7 - .../boostforreddit/api/SpoofClientPatch.kt | 37 - .../fingerprints/BuildUserAgentFingerprint.kt | 7 - .../fingerprints/GetClientIdFingerprint.kt | 11 - .../FixAudioMissingInDownloadsPatch.kt | 38 - .../fingerprints/DownloadAudioFingerprint.kt | 7 - .../fix/slink/FixSLinksPatch.kt | 44 - .../GetOAuthAccessTokenFingerprint.kt | 10 - .../HandleNavigationFingerprint.kt | 12 - .../misc/integrations/IntegrationsPatch.kt | 10 - .../fingerprints/InitFingerprint.kt | 10 - .../api/fingerprints/APIUtilsFingerprint.kt | 7 - .../GetHttpBasicAuthHeaderFingerprint.kt | 0 .../LoginActivityOnCreateFingerprint.kt | 0 .../subscription/UnlockSubscriptionPatch.kt | 26 - .../BillingClientOnServiceConnected.kt | 7 - .../StartSubscriptionActivityFingerprint.kt | 7 - .../joeyforreddit/ads/DisableAdsPatch.kt | 28 - .../fingerprints/IsAdFreeUserFingerprint.kt | 10 - .../joeyforreddit/api/SpoofClientPatch.kt | 48 - .../api/fingerprints/AuthUtilityUserAgent.kt | 15 - .../fingerprints/GetClientIdFingerprint.kt | 20 - .../piracy/DisablePiracyDetectionPatch.kt | 16 - .../PiracyDetectionFingerprint.kt | 22 - .../fingerprints/BaseClientIdFingerprint.kt | 7 - .../BasicAuthorizationFingerprint.kt | 5 - .../BuildAuthorizationStringFingerprint.kt | 5 - .../fingerprints/GetUserAgentFingerprint.kt | 20 - .../relayforreddit/api/SpoofClientPatch.kt | 68 - .../fingerprints/BaseClientIdFingerprint.kt | 7 - .../GetLoggedInBearerTokenFingerprint.kt | 3 - .../GetLoggedOutBearerTokenFingerprint.kt | 3 - .../GetRefreshTokenFingerprint.kt | 3 - .../LoginActivityClientIdFingerprint.kt | 3 - .../RedditCheckDisableAPIFingerprint.kt | 9 - .../SetRemoteConfigFingerprint.kt | 8 - .../slide/api/SpoofClientPatch.kt | 25 - .../fingerprints/GetClientIdFingerprint.kt | 11 - .../syncforlemmy/ads/DisableAdsPatch.kt | 10 - .../syncforreddit/ads/DisableAdsPatch.kt | 8 - .../DisableSyncForLemmyBottomSheetPatch.kt | 30 - .../MainActivityOnCreateFingerprint.kt | 9 - .../syncforreddit/api/SpoofClientPatch.kt | 91 - .../GetAuthorizationStringFingerprint.kt | 7 - .../fingerprints/GetBearerTokenFingerprint.kt | 7 - .../fingerprints/GetUserAgentFingerprint.kt | 7 - .../fingerprints/ImgurImageAPIFingerprint.kt | 9 - .../fingerprints/LoadBrowserURLFingerprint.kt | 10 - .../piracy/DisablePiracyDetectionPatch.kt | 18 - .../syncforreddit/fix/slink/FixSLinksPatch.kt | 49 - .../LinkHelperOpenLinkFingerprint.kt | 7 - .../SetAuthorizationHeaderFingerprint.kt | 9 - .../fix/user/UseUserEndpointPatch.kt | 61 - .../BaseUserEndpointFingerprint.kt | 10 - .../OAuthFriendRequestFingerprint.kt | 3 - ...redditInfoRequestConstructorFingerprint.kt | 10 - ...thSubredditInfoRequestHelperFingerprint.kt | 10 - .../OAuthUnfriendRequestFingerprint.kt | 3 - .../OAuthUserIdRequestFingerprint.kt | 3 - .../OAuthUserInfoRequestFingerprint.kt | 3 - .../fix/video/FixVideoDownloadsPatch.kt | 62 - .../misc.integrations/IntegrationsPatch.kt | 10 - .../fingerprints/InitFingerprint.kt | 10 - .../DisableScreenshotPopupPatch.kt | 22 - .../DisableScreenshotPopupFingerprint.kt | 14 - .../premiumicon/UnlockPremiumIconPatch.kt | 27 - .../HasPremiumIconAccessFingerprint.kt | 10 - .../tracking/url/SanitizeUrlQueryPatch.kt | 26 - .../ShareLinkFormatterFingerprint.kt | 9 - .../RemoveDebuggingDetectionPatch.kt | 32 - .../DebuggingDetectionFingerprint.kt | 8 - .../detection/root/RootDetectionPatch.kt | 21 - .../fingerprints/RootDetectionFingerprint.kt | 12 - .../CastContextFetchFingerprint.kt | 8 - .../misc/checks/BaseCheckEnvironmentPatch.kt | 114 - .../fingerprints/PatchInfoBuildFingerprint.kt | 7 - .../fingerprints/PatchInfoFingerprint.kt | 9 - .../fix/verticalscroll/VerticalScrollPatch.kt | 30 - .../CanScrollVerticallyFingerprint.kt | 20 - .../misc/gms/BaseGmsCoreSupportPatch.kt | 500 ---- .../gms/BaseGmsCoreSupportResourcePatch.kt | 122 - .../fingerprints/GmsCoreSupportFingerprint.kt | 11 - .../GooglePlayUtilityFingerprint.kt | 12 - .../fingerprints/ServiceCheckFingerprint.kt | 12 - .../patches/shared/misc/hex/BaseHexPatch.kt | 120 - .../integrations/BaseIntegrationsPatch.kt | 158 -- .../ReVancedUtilsPatchesVersionFingerprint.kt | 16 - .../filesize/RemoveFileSizeLimitPatch.kt | 27 - .../fingerprints/OnReadyFingerprint.kt | 15 - .../badge/RemoveNotificationBadgePatch.kt | 22 - .../fingerprints/CreateTabsFingerprint.kt | 26 - .../ShowNotificationFingerprint.kt | 37 - .../ad/fingerprints/InterceptFingerprint.kt | 17 - .../UserConsumerPlanConstructorFingerprint.kt | 11 - .../analytics/DisableTelemetryPatch.kt | 24 - .../CreateTrackingApiFingerprint.kt | 13 - ...OperationsHeaderVerificationFingerprint.kt | 19 - ...DownloadOperationsURLBuilderFingerprint.kt | 17 - .../FeatureConstructorFingerprint.kt | 18 - .../spotify/lite/ondemand/OnDemandPatch.kt | 25 - .../fingerprints/OnDemandFingerprint.kt | 25 - .../spotify/navbar/PremiumNavbarTabPatch.kt | 32 - .../navbar/PremiumNavbarTabResourcePatch.kt | 21 - .../fingerprints/AddNavBarItemFingerprint.kt | 12 - .../subscription/UnlockSubscriptionPatch.kt | 21 - .../fingerprints/GetSubscribedFingerprint.kt | 11 - .../DisableSubscriptionSuggestionsPatch.kt | 72 - .../fingerprints/GetModulesFingerprint.kt | 11 - .../RemoveGooglePlayIntegrityCheck.kt | 37 - .../fingerprints/CheckIntegrityFingerprint.kt | 11 - .../misc/themeunlock/UnlockThemePatch.kt | 32 - .../CheckLockedThemesFingerprint.kt | 9 - .../fingerprints/SetThemeFingerprint.kt | 9 - .../tiktok/feedfilter/FeedFilterPatch.kt | 48 - .../FeedApiServiceLIZFingerprint.kt | 9 - .../OnClearDisplayEventFingerprint.kt | 10 - .../interaction/downloads/DownloadsPatch.kt | 116 - .../fingerprints/ACLCommonShareFingerprint.kt | 14 - .../ACLCommonShareFingerprint2.kt | 14 - .../ACLCommonShareFingerprint3.kt | 14 - .../fingerprints/DownloadUriFingerprint.kt | 20 - .../interaction/seekbar/ShowSeekbarPatch.kt | 43 - .../SetSeekBarShowTypeFingerprint.kt | 9 - .../ShouldShowSeekBarFingerprint.kt | 9 - .../interaction/speed/PlaybackSpeedPatch.kt | 85 - .../speed/fingerprints/GetSpeedFingerprint.kt | 9 - .../speed/fingerprints/SetSpeedFingerprint.kt | 16 - .../misc/integrations/IntegrationsPatch.kt | 10 - .../fingerprints/InitFingerprint.kt | 14 - .../DisableLoginRequirementPatch.kt | 37 - .../MandatoryLoginServiceFingerprint.kt | 9 - .../MandatoryLoginServiceFingerprint2.kt | 10 - .../login/fixgoogle/FixGoogleLoginPatch.kt | 39 - .../GoogleAuthAvailableFingerprint.kt | 14 - .../GoogleOneTapAuthAvailableFingerprint.kt | 14 - .../tiktok/misc/settings/SettingsPatch.kt | 112 - ...sonalizationActivityOnCreateFingerprint.kt | 10 - .../AddSettingsEntryFingerprint.kt | 10 - .../fingerprints/SettingsEntryFingerprint.kt | 9 - .../SettingsEntryInfoFingerprint.kt | 10 - .../SettingsStatusLoadFingerprint.kt | 10 - .../fingerprints/GetEnterFromFingerprint.kt | 24 - .../OnRenderFirstFrameFingerprint.kt | 10 - .../revanced/patches/trakt/UnlockProPatch.kt | 42 - .../trakt/fingerprints/IsVIPEPFingerprint.kt | 11 - .../trakt/fingerprints/IsVIPFingerprint.kt | 11 - .../fingerprints/RemoteUserFingerprint.kt | 9 - .../fingerprints/BrightnessFingerprint.kt | 15 - .../adfree/DisableAdFreeBannerPatch.kt | 21 - .../inappupdate/DisableInAppUpdatePatch.kt | 22 - .../DisableBlogNotificationReminderPatch.kt | 29 - .../popups/DisableGiftMessagePopupPatch.kt | 23 - .../ShowGiftMessagePopupFingerprint.kt | 12 - .../tumblr/fixes/FixOldVersionsPatch.kt | 67 - .../fingerprints/AddQueryParamFingerprint.kt | 11 - .../fingerprints/HttpPathParserFingerprint.kt | 15 - .../tumblr/live/DisableTumblrLivePatch.kt | 26 - .../timelinefilter/TimelineFilterPatch.kt | 68 - .../PostsResponseConstructorFingerprint.kt | 12 - .../TimelineConstructorFingerprint.kt | 12 - .../TimelineFilterIntegrationFingerprint.kt | 16 - .../patches/twitch/ad/audio/AudioAdsPatch.kt | 47 - .../AudioAdsPresenterPlayFingerprint.kt | 10 - .../twitch/ad/embedded/EmbeddedAdsPatch.kt | 50 - .../CreateUsherClientFingerprint.kt | 9 - .../twitch/ad/shared/util/BaseAdPatch.kt | 51 - .../patches/twitch/ad/video/VideoAdsPatch.kt | 130 - .../CheckAdEligibilityLambdaFingerprint.kt | 12 - .../ContentConfigShowAdsFingerprint.kt | 11 - .../GetReadyToShowAdFingerprint.kt | 11 - .../antidelete/ShowDeletedMessagesPatch.kt | 85 - .../ChatUtilCreateDeletedSpanFingerprint.kt | 9 - ...etedMessageClickableSpanCtorFingerprint.kt | 12 - .../SetHasModAccessFingerprint.kt | 10 - ...nityPointsButtonViewDelegateFingerprint.kt | 10 - .../patches/twitch/debug/DebugModePatch.kt | 56 - .../IsDebugConfigEnabledFingerprint.kt | 9 - .../IsOmVerificationEnabledFingerprint.kt | 9 - .../ShouldShowDebugOptionsFingerprint.kt | 9 - .../misc/integrations/IntegrationsPatch.kt | 10 - .../fingerprints/InitFingerprint.kt | 10 - .../twitch/misc/settings/SettingsPatch.kt | 204 -- .../misc/settings/SettingsResourcePatch.kt | 5 - .../MenuGroupsOnClickFingerprint.kt | 15 - .../MenuGroupsUpdatedFingerprint.kt | 10 - .../SettingsActivityOnCreateFingerprint.kt | 10 - .../SettingsMenuItemEnumFingerprint.kt | 9 - .../downloads/UnlockDownloadsPatch.kt | 83 - .../BuildMediaOptionsSheetFingerprint.kt | 14 - .../ConstructMediaOptionsSheetFingerprint.kt | 12 - ...wnloadVideoUpsellBottomSheetFingerprint.kt | 10 - .../layout/viewcount/HideViewCountPatch.kt | 30 - .../ViewCountsEnabledFingerprint.kt | 8 - .../twitter/misc/hook/json/JsonHookPatch.kt | 141 - .../fingerprints/JsonHookPatchFingerprint.kt | 13 - .../JsonInputStreamFingerprint.kt | 10 - .../fingerprints/LoganSquareFingerprint.kt | 7 - .../twitter/misc/hook/patch/BaseHookPatch.kt | 10 - .../misc/hook/patch/ads/HideAdsHookPatch.kt | 14 - .../HideRecommendedUsersPatch.kt | 16 - .../links/ChangeLinkSharingDomainPatch.kt | 93 - .../ChangeLinkSharingDomainResourcePatch.kt | 17 - .../links/OpenLinksWithAppChooserPatch.kt | 35 - .../misc/links/SanitizeSharingLinksPatch.kt | 29 - .../fingerprints/LinkBuilderFingerprint.kt | 8 - .../LinkResourceGetterFingerprint.kt | 13 - .../LinkSharingDomainFingerprint.kt | 7 - .../links/fingerprints/OpenLinkFingerprint.kt | 8 - .../SanitizeSharingLinksFingerprint.kt | 8 - .../patches/vsco/misc/pro/UnlockProPatch.kt | 30 - .../RevCatSubscriptionFingerprint.kt | 11 - .../firebasegetcert/FirebaseGetCertPatch.kt | 40 - .../GetCertMessagingFingerprint.kt | 12 - .../GetCertRegistrationFingerprint.kt | 12 - .../misc/promocode/PromoCodeUnlockPatch.kt | 31 - .../PromoCodeUnlockFingerprint.kt | 8 - .../patches/willhaben/ads/HideAdsPatch.kt | 28 - .../ads/fingerprints/AdResolverFingerprint.kt | 17 - .../WHAdViewInjectorFingerprint.kt | 15 - .../windyapp/misc/unlockpro/UnlockProPatch.kt | 29 - .../fingerprints/CheckProFingerprint.kt | 10 - .../youtube/ad/general/HideAdsPatch.kt | 78 - .../ad/general/HideAdsResourcePatch.kt | 47 - .../ad/getpremium/HideGetPremiumPatch.kt | 69 - .../fingerprints/GetPremiumViewFingerprint.kt | 22 - .../patches/youtube/ad/video/VideoAdsPatch.kt | 60 - .../copyvideourl/CopyVideoUrlBytecodePatch.kt | 45 - .../copyvideourl/CopyVideoUrlResourcePatch.kt | 39 - .../RemoveViewerDiscretionDialogPatch.kt | 59 - .../interaction/downloads/DownloadsPatch.kt | 73 - .../downloads/DownloadsResourcePatch.kt | 47 - .../OfflineVideoEndpointFingerprint.kt | 16 - .../DisablePreciseSeekingGesturePatch.kt | 83 - .../seekbar/EnableSeekbarTappingPatch.kt | 97 - .../seekbar/EnableSlideToSeekPatch.kt | 132 - .../AllowSwipingUpGestureFingerprint.kt | 14 - .../DisableFastForwardGestureFingerprint.kt | 21 - .../DisableFastForwardLegacyFingerprint.kt | 11 - .../DisableFastForwardNoticeFingerprint.kt | 19 - .../OnTouchEventHandlerFingerprint.kt | 31 - .../fingerprints/SeekbarTappingFingerprint.kt | 25 - .../ShowSwipingUpGuideFingerprint.kt | 14 - .../fingerprints/SlideToSeekFingerprint.kt | 19 - .../SwipingUpGestureParentFingerprint.kt | 9 - .../SwipeControlsBytecodePatch.kt | 70 - .../SwipeControlsResourcePatch.kt | 45 - .../SwipeControlsHostActivityFingerprint.kt | 13 - .../layout/autocaptions/AutoCaptionsPatch.kt | 75 - .../StartVideoInformerFingerprint.kt | 16 - .../fingerprints/SubtitleTrackFingerprint.kt | 21 - .../branding/header/PremiumHeadingPatch.kt | 9 - .../layout/buttons/action/HideButtonsPatch.kt | 61 - .../autoplay/HideAutoplayButtonPatch.kt | 14 - .../captions/HideCaptionsButtonPatch.kt | 14 - .../buttons/cast/HideCastButtonPatch.kt | 14 - .../navigation/NavigationButtonsPatch.kt | 116 - .../AddCreateButtonViewFingerprint.kt | 12 - .../fingerprints/CreatePivotBarFingerprint.kt | 20 - .../overlay/HidePlayerOverlayButtonsPatch.kt | 151 -- .../HidePlayerOverlayButtonsResourcePatch.kt | 24 - .../MediaRouteButtonFingerprint.kt | 10 - ...rolsPreviousNextOverlayTouchFingerprint.kt | 20 - .../player/hide/HidePlayerButtonsPatch.kt | 13 - .../layout/hide/albumcards/AlbumCardsPatch.kt | 13 - .../hide/breakingnews/BreakingNewsPatch.kt | 13 - .../layout/hide/comments/CommentsPatch.kt | 13 - .../crowdfundingbox/CrowdfundingBoxPatch.kt | 13 - .../endscreencards/HideEndscreenCardsPatch.kt | 65 - .../HideEndscreenCardsResourcePatch.kt | 36 - .../fingerprints/LayoutCircleFingerprint.kt | 17 - .../fingerprints/LayoutIconFingerprint.kt | 15 - .../fingerprints/LayoutVideoFingerprint.kt | 17 - .../hide/filterbar/HideFilterBarPatch.kt | 12 - .../HideFloatingMicrophoneButtonPatch.kt | 13 - .../DisableFullscreenAmbientModePatch.kt | 57 - .../InitializeAmbientModeFingerprint.kt | 13 - .../HideLayoutComponentsResourcePatch.kt | 70 - .../fingerprints/AlbumCardsFingerprint.kt | 20 - .../CrowdfundingBoxFingerprint.kt | 17 - .../FilterBarHeightFingerprint.kt | 19 - .../HideShowMoreButtonFingerprint.kt | 15 - .../ParseElementFromBufferFingerprint.kt | 14 - .../fingerprints/PlayerOverlayFingerprint.kt | 9 - .../RelatedChipCloudFingerprint.kt | 18 - .../SearchResultsChipBarFingerprint.kt | 20 - ...ShowFloatingMicrophoneButtonFingerprint.kt | 19 - .../fingerprints/ShowWatermarkFingerprint.kt | 9 - .../YoodlesImageViewFingerprint.kt | 13 - .../hide/infocards/HideInfoCardsPatch.kt | 88 - .../infocards/HideInfocardsResourcePatch.kt | 33 - .../InfocardsIncognitoFingerprint.kt | 12 - .../InfocardsIncognitoParentFingerprint.kt | 11 - .../InfocardsMethodCallFingerprint.kt | 15 - .../loadmorebutton/HideLoadMoreButtonPatch.kt | 12 - .../HidePlayerFlyoutMenuPatch.kt | 65 - .../DisableRollingNumberAnimationPatch.kt | 77 - .../layout/hide/seekbar/HideSeekbarPatch.kt | 66 - .../hide/shorts/HideShortsComponentsPatch.kt | 203 -- .../HideShortsComponentsResourcePatch.kt | 128 - .../CreateShortsButtonsFingerprint.kt | 10 - ...derBottomNavigationBarParentFingerprint.kt | 15 - .../ReelConstructorFingerprint.kt | 13 - .../RenderBottomNavigationBarFingerprint.kt | 21 - ...derBottomNavigationBarParentFingerprint.kt | 23 - .../SetPivotBarVisibilityFingerprint.kt | 16 - .../SetPivotBarVisibilityParentFingerprint.kt | 8 - .../ShortsBottomBarContainerFingerprint.kt | 16 - .../DisableSuggestedVideoEndScreenPatch.kt | 51 - ...bleSuggestedVideoEndScreenResourcePatch.kt | 33 - .../CreateEndScreenViewFingerprint.kt | 19 - .../layout/hide/time/HideTimestampPatch.kt | 56 - .../fingerprints/TimeCounterFingerprint.kt | 25 - .../layout/miniplayer/MiniplayerPatch.kt | 515 ---- .../miniplayer/MiniplayerResourcePatch.kt | 89 - ...erDimensionsCalculatorParentFingerprint.kt | 13 - .../MiniplayerMinimumSizeFingerprint.kt | 16 - ...iplayerModernAddViewListenerFingerprint.kt | 14 - .../MiniplayerModernCloseButtonFingerprint.kt | 16 - .../MiniplayerModernConstructorFingerprint.kt | 21 - ...MiniplayerModernExpandButtonFingerprint.kt | 16 - ...erModernExpandCloseDrawablesFingerprint.kt | 16 - ...iniplayerModernForwardButtonFingerprint.kt | 16 - .../MiniplayerModernOverlayViewFingerprint.kt | 16 - ...MiniplayerModernRewindButtonFingerprint.kt | 16 - .../MiniplayerModernViewParentFingerprint.kt | 12 - .../MiniplayerOverrideFingerprint.kt | 11 - .../MiniplayerOverrideNoContextFingerprint.kt | 12 - ...playerResponseModelSizeCheckFingerprint.kt | 20 - .../YouTubePlayerOverlaysLayoutFingerprint.kt | 13 - .../panels/popup/PlayerPopupPanelsPatch.kt | 59 - .../EngagementPanelControllerFingerprint.kt | 14 - .../PlayerControlsBackgroundPatch.kt | 44 - .../CustomPlayerOverlayOpacityPatch.kt | 43 - ...CustomPlayerOverlayOpacityResourcePatch.kt | 30 - .../CreatePlayerOverviewFingerprint.kt | 22 - .../ReturnYouTubeDislikePatch.kt | 386 --- .../ReturnYouTubeDislikeResourcePatch.kt | 36 - .../ConversionContextFingerprint.kt | 15 - .../fingerprints/DislikeFingerprint.kt | 8 - .../DislikesOldLayoutTextViewFingerprint.kt | 22 - .../fingerprints/LikeFingerprint.kt | 8 - .../fingerprints/RemoveLikeFingerprint.kt | 8 - ...ingNumberMeasureAnimatedTextFingerprint.kt | 28 - ...lingNumberMeasureStaticLabelFingerprint.kt | 21 - ...mberMeasureStaticLabelParentFingerprint.kt | 12 - .../RollingNumberSetterFingerprint.kt | 13 - .../RollingNumberTextViewFingerprint.kt | 24 - .../fingerprints/ShortsTextViewFingerprint.kt | 20 - .../TextComponentConstructorFingerprint.kt | 10 - .../TextComponentDataFingerprint.kt | 14 - .../TextComponentLookupFingerprint.kt | 15 - .../layout/searchbar/WideSearchbarPatch.kt | 93 - .../CreateSearchSuggestionsFingerprint.kt | 13 - .../SetWordmarkHeaderFingerprint.kt | 23 - .../RestoreOldSeekbarThumbnailsPatch.kt | 61 - .../seekbar/SeekbarColorBytecodePatch.kt | 113 - .../seekbar/SeekbarColorResourcePatch.kt | 48 - .../FullscreenSeekbarThumbnailsFingerprint.kt | 12 - .../LithoLinearGradientFingerprint.kt | 10 - .../PlayerSeekbarColorFingerprint.kt | 15 - .../PlayerSeekbarGradientConfigFingerprint.kt | 15 - .../SetSeekbarClickedColorFingerprint.kt | 12 - .../ShortsSeekbarColorFingerprint.kt | 11 - .../shortsautoplay/ShortsAutoplayPatch.kt | 119 - .../ReelEnumConstructorFingerprint.kt | 18 - .../ReelPlaybackRepeatFingerprint.kt | 9 - .../sponsorblock/SponsorBlockBytecodePatch.kt | 219 -- .../sponsorblock/SponsorBlockResourcePatch.kt | 63 - .../fingerprints/AppendTimeFingerprint.kt | 29 - .../ControlsOverlayFingerprint.kt | 24 - .../RectangleFieldInvalidatorFingerprint.kt | 22 - .../spoofappversion/SpoofAppVersionPatch.kt | 66 - .../SpoofAppVersionFingerprint.kt | 19 - .../layout/startpage/ChangeStartPagePatch.kt | 82 - .../fingerprints/BrowseIdFingerprint.kt | 15 - .../fingerprints/IntentActionFingerprint.kt | 8 - .../DisableResumingShortsOnStartupPatch.kt | 106 - .../UserWasInShortsConfigFingerprint.kt | 35 - .../UserWasInShortsFingerprint.kt | 12 - .../layout/tablet/EnableTabletLayoutPatch.kt | 69 - .../fingerprints/GetFormFactorFingerprint.kt | 26 - .../tabletminiplayer/TabletMiniPlayerPatch.kt | 11 - .../layout/theme/LithoColorHookPatch.kt | 41 - .../layout/theme/ThemeBytecodePatch.kt | 137 - .../layout/theme/ThemeResourcePatch.kt | 143 - .../fingerprints/LithoThemeFingerprint.kt | 32 - .../ThemeHelperDarkColorFingerprint.kt | 16 - .../ThemeHelperLightColorFingerprint.kt | 16 - .../UseGradientLoadingScreenFingerprint.kt | 8 - .../thumbnails/AlternativeThumbnailsPatch.kt | 93 - .../BypassImageRegionRestrictions.kt | 52 - .../misc/announcements/AnnouncementsPatch.kt | 41 - .../misc/autorepeat/AutoRepeatPatch.kt | 87 - .../fingerprints/AutoRepeatFingerprint.kt | 14 - .../AutoRepeatParentFingerprint.kt | 14 - .../BackgroundPlaybackPatch.kt | 100 - .../BackgroundPlaybackResourcePatch.kt | 17 - .../BackgroundPlaybackManagerFingerprint.kt | 39 - .../BackgroundPlaybackSettingsFingerprint.kt | 23 - ...oundPlaybackPolicyControllerFingerprint.kt | 23 - .../bottomsheet/hook/BottomSheetHookPatch.kt | 0 .../hook/BottomSheetHookResourcePatch.kt | 0 .../CreateBottomSheetFingerprint.kt | 0 .../misc/check/CheckEnvironmentPatch.kt | 13 - .../youtube/misc/debugging/DebuggingPatch.kt | 38 - .../spoof/SpoofDeviceDimensionsPatch.kt | 64 - ...eviceDimensionsModelToStringFingerprint.kt | 8 - ...ckWatchHistoryDomainNameResolutionPatch.kt | 50 - .../FixBackToExitGesturePatch.kt | 69 - .../fingerprints/OnBackPressedFingerprint.kt | 20 - .../RecyclerViewScrollingFingerprint.kt | 24 - .../RecyclerViewTopScrollingFingerprint.kt | 27 - ...cyclerViewTopScrollingParentFingerprint.kt | 21 - .../CarioFragmentConfigFingerprint.kt | 20 - .../misc/fix/playback/SpoofClientPatch.kt | 11 - .../misc/fix/playback/SpoofSignaturePatch.kt | 12 - .../playback/SpoofSignatureResourcePatch.kt | 9 - .../fix/playback/SpoofVideoStreamsPatch.kt | 269 -- .../BuildInitPlaybackRequestFingerprint.kt | 16 - .../BuildMediaDataSourceFingerprint.kt | 22 - .../BuildPlayerRequestURIFingerprint.kt | 21 - .../fingerprints/BuildRequestFingerprint.kt | 36 - .../CreateStreamingDataFingerprint.kt | 24 - ...ProtobufClassParseByteBufferFingerprint.kt | 19 - .../youtube/misc/gms/GmsCoreSupportPatch.kt | 48 - .../misc/gms/GmsCoreSupportResourcePatch.kt | 31 - .../fingerprints/PrimeMethodFingerprint.kt | 8 - .../misc/imageurlhook/CronetImageUrlHook.kt | 131 - .../MessageDigestImageUrlFingerprint.kt | 10 - .../MessageDigestImageUrlParentFingerprint.kt | 12 - .../fingerprints/cronet/RequestFingerprint.kt | 16 - .../request/callback/OnFailureFingerprint.kt | 14 - .../callback/OnResponseStartedFingerprint.kt | 21 - .../callback/OnSucceededFingerprint.kt | 14 - .../misc/integrations/IntegrationsPatch.kt | 12 - .../ApplicationInitFingerprint.kt | 11 - .../misc/links/BypassURLRedirectsPatch.kt | 98 - .../misc/links/OpenLinksExternallyPatch.kt | 72 - .../fingerprints/ABUriParserFingerprint.kt | 22 - .../ABUriParserLegacyFingerprint.kt | 33 - .../fingerprints/HTTPUriParserFingerprint.kt | 19 - .../HTTPUriParserLegacyFingerprint.kt | 17 - .../misc/litho/filter/LithoFilterPatch.kt | 262 -- .../ComponentContextParserFingerprint.kt | 11 - .../fingerprints/EmptyComponentFingerprint.kt | 14 - .../fingerprints/LithoFilterFingerprint.kt | 13 - .../ProtobufBufferReferenceFingerprint.kt | 18 - .../ReadComponentIdentifierFingerprint.kt | 11 - .../misc/microg/MicroGBytecodePatch.kt | 0 .../CastContextFetchFingerprint.kt | 0 .../MinimizedPlaybackPatch.kt | 11 - .../misc/navigation/NavigationBarHookPatch.kt | 161 -- .../NavigationBarHookResourcePatch.kt | 19 - .../ActionBarSearchResultsFingerprint.kt | 12 - .../InitializeButtonsFingerprint.kt | 15 - .../MainActivityOnBackPressedFingerprint.kt | 17 - .../NavigationBarHookCallbackFingerprint.kt | 21 - .../fingerprints/NavigationEnumFingerprint.kt | 21 - ...BarButtonsCreateDrawableViewFingerprint.kt | 17 - ...BarButtonsCreateResourceViewFingerprint.kt | 14 - ...votBarButtonsViewSetSelectedFingerprint.kt | 27 - .../PivotBarConstructorFingerprint.kt | 10 - .../BottomControlsResourcePatch.kt | 20 - .../PlayerControlsBytecodePatch.kt | 144 - .../PlayerControlsResourcePatch.kt | 133 - .../fingerprints/ControlsOverlayVisibility.kt | 14 - .../OverlayViewInflateFingerprint.kt | 17 - .../PlayerBottomControlsInflateFingerprint.kt | 10 - ...layerControlsIntegrationHookFingerprint.kt | 15 - .../PlayerTopControlsInflateFingerprint.kt | 13 - .../playeroverlay/PlayerOverlaysHookPatch.kt | 33 - ...layerOverlaysOnFinishInflateFingerprint.kt | 14 - .../misc/playertype/PlayerTypeHookPatch.kt | 46 - .../fingerprint/PlayerTypeFingerprint.kt | 17 - .../fingerprint/VideoStateFingerprint.kt | 18 - .../RemoveTrackingQueryParameterPatch.kt | 80 - .../fingerprints/CopyTextFingerprint.kt | 21 - .../SystemShareSheetFingerprint.kt | 14 - .../YouTubeShareSheetFingerprint.kt | 18 - .../hook/RecyclerViewTreeHookPatch.kt | 36 - .../RecyclerViewTreeObserverFingerprint.kt | 19 - .../youtube/misc/settings/SettingsPatch.kt | 185 -- .../misc/settings/SettingsResourcePatch.kt | 94 - .../LicenseActivityOnCreateFingerprint.kt | 14 - .../fingerprints/SetThemeFingerprint.kt | 15 - .../misc/zoomhaptics/ZoomHapticsPatch.kt | 45 - .../fingerprints/ZoomHapticsFingerprint.kt | 8 - .../LayoutConstructorFingerprint.kt | 12 - .../fingerprints/MainActivityFingerprint.kt | 15 - .../MainActivityOnCreateFingerprint.kt | 14 - .../NewVideoQualityChangedFingerprint.kt | 32 - ...umberTextViewAnimationUpdateFingerprint.kt | 31 - .../shared/fingerprints/SeekbarFingerprint.kt | 9 - .../fingerprints/SeekbarOnDrawFingerprint.kt | 7 - .../SubtitleButtonControllerFingerprint.kt | 23 - .../video/hdrbrightness/HDRBrightnessPatch.kt | 80 - .../fingerprints/HDRBrightnessFingerprint.kt | 10 - .../information/VideoInformationPatch.kt | 311 --- .../CreateVideoPlayerSeekbarFingerprint.kt | 9 - ...xPlayerDirectorSetVideoStageFingerprint.kt | 8 - .../fingerprints/MdxSeekFingerprint.kt | 26 - .../MdxSeekRelativeFingerprint.kt | 18 - .../OnPlaybackSpeedItemClickFingerprint.kt | 20 - ...erControllerSetTimeReferenceFingerprint.kt | 11 - .../fingerprints/PlayerInitFingerprint.kt | 10 - .../fingerprints/SeekFingerprint.kt | 11 - .../fingerprints/SeekRelativeFingerprint.kt | 19 - .../fingerprints/VideoLengthFingerprint.kt | 23 - .../PlayerResponseMethodHookPatch.kt | 124 - .../PlayerParameterBuilderFingerprint.kt | 30 - ...PlayerParameterBuilderLegacyFingerprint.kt | 28 - ...laybackSpeedMenuSpeedChangedFingerprint.kt | 29 - ...dexMethodClassFieldReferenceFingerprint.kt | 17 - ...ideoQualityItemOnClickParentFingerprint.kt | 9 - .../VideoQualitySetterFingerprint.kt | 21 - .../youtube/video/speed/PlaybackSpeedPatch.kt | 38 - .../speed/button/PlaybackSpeedButtonPatch.kt | 38 - .../PlaybackSpeedButtonResourcePatch.kt | 25 - .../speed/custom/CustomPlaybackSpeedPatch.kt | 185 -- .../CustomPlaybackSpeedResourcePatch.kt | 16 - .../GetOldPlaybackSpeedsFingerprint.kt | 8 - .../ShowOldPlaybackSpeedMenuFingerprint.kt | 10 - ...laybackSpeedMenuIntegrationsFingerprint.kt | 7 - .../SpeedArrayGeneratorFingerprint.kt | 21 - .../fingerprints/SpeedLimiterFingerprint.kt | 22 - .../remember/RememberPlaybackSpeedPatch.kt | 90 - ...nitializePlaybackSpeedValuesFingerprint.kt | 8 - .../youtube/video/videoid/VideoIdPatch.kt | 129 - .../VideoIdBackgroundPlayFingerprint.kt | 32 - .../videoid/fingerprint/VideoIdFingerprint.kt | 22 - .../fingerprint/VideoIdParentFingerprint.kt | 12 - .../RestoreOldVideoQualityMenuPatch.kt | 103 - ...RestoreOldVideoQualityMenuResourcePatch.kt | 36 - .../VideoQualityMenuOptionsFingerprint.kt | 20 - .../VideoQualityMenuViewInflateFingerprint.kt | 30 - .../youtubevanced/ad/general/HideAdsPatch.kt | 57 - .../fingerprints/ContainsAdFingerprint.kt | 24 - .../misc/unlockpremium/UnlockPremiumPatch.kt | 30 - .../fingerprints/IsPremiumFingerprint.kt | 15 - .../YukaUserConstructorFingerprint.kt | 13 - .../util/patch/LiteralValueFingerprint.kt | 35 - .../addresources/values-ga-rIE/strings.xml | 1245 --------- .../addresources/values-sr-rCS/strings.xml | 1249 --------- 1359 files changed, 22892 insertions(+), 29620 deletions(-) delete mode 100644 api/revanced-patches.api delete mode 100644 build.gradle.kts create mode 100644 patches/api/patches.api create mode 100644 patches/build.gradle.kts rename {src/main/kotlin/app/revanced/patches/all => patches/src/main/kotlin/app/revanced/patches/all/misc}/activity/exportall/ExportAllActivitiesPatch.kt (63%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt (57%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt (58%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt (54%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt (81%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt rename {src/main/kotlin/app/revanced/patches/all => patches/src/main/kotlin/app/revanced/patches/all/misc}/shortcut/sharetargets/RemoveShareTargetsPatch.kt (63%) rename {src => patches/src}/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt (67%) rename src/main/kotlin/app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch.kt => patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt (79%) rename {src => patches/src}/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt (69%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt rename src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/fingerprints/BootStateFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt (57%) rename src/main/kotlin/app/revanced/patches/finanzonline/detection/root/fingerprints/RootDetectionFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt (53%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/ExtensionPatch.kt rename src/main/kotlin/app/revanced/patches/googlenews/misc/integrations/fingerprints/StartActivityInitFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt (71%) rename {src => patches/src}/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt (100%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/ExtensionPatch.kt rename src/main/kotlin/app/revanced/patches/googlephotos/misc/integrations/fingerprints/HomeActivityInitFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Fingerprints.kt (69%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatch.kt rename {src/main/kotlin/app/revanced/patches/googlephotos => patches/src/main/kotlin/app/revanced/patches/googlephotos/misc}/features/SpoofFeaturesPatch.kt (53%) rename {src => patches/src}/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt (100%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt (65%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/instagram/ads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt rename src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/fingerprints/VerifySignatureFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt (54%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt rename src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/fingerprints/AllowExclusiveAudioPlaybackFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt (55%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt (100%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt rename src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/fingerprints/AbstractClientIdFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt (100%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt (62%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt rename src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt (55%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt rename src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt (52%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt (65%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt (60%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch.kt (53%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt rename src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/fingerprints/PiracyDetectionFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt (57%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt rename src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/fingerprints/ParseRedditVideoNetworkResponseFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/Fingerprints.kt (53%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/misc/extension/ExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt (54%) rename src/main/kotlin/app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch.kt => patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt (57%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt (92%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt (70%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt (100%) rename src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreen.kt => patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt (95%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt (90%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt (60%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt (66%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt (69%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt (75%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/spotify/navbar/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt (59%) rename {src => patches/src}/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt (58%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/strava/upselling/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt (61%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/ExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt (58%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt rename {src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/patch => patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen}/ShowOnLockscreenPatch.kt (70%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/tudortmund/misc/extension/ExtensionPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt (66%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt rename src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/fingerprints/IsBlogNotifyEnabledFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt (55%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt rename src/main/kotlin/app/revanced/patches/tumblr/featureflags/fingerprints/GetFeatureValueFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt (66%) rename {src => patches/src}/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt (61%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/live/DisableTumblrLivePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/misc/extension/ExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt (50%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/SharedExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt (81%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/extension/ExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HideAdsHookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HideRecommendedUsersPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/UnlockProPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/willhaben/ads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt rename src/main/kotlin/app/revanced/patches/youtube/ad/video/fingerprints/LoadVideoAdsFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/Fingerprints.kt (56%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatch.kt rename src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/fingerprints/CreateDialogFingerprint.kt => patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/Fingerprints.kt (51%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt (68%) rename {src => patches/src}/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt (78%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt (53%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/SharedExtensionPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt (59%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt (54%) rename {src => patches/src}/main/kotlin/app/revanced/patches/youtube/misc/gms/Constants.kt (100%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt (55%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/information/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt rename {src => patches/src}/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt (51%) create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/Fingerprints.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch.kt rename {src => patches/src}/main/kotlin/app/revanced/util/BytecodeUtils.kt (85%) rename {src => patches/src}/main/kotlin/app/revanced/util/ResourceUtils.kt (69%) rename {src => patches/src}/main/kotlin/app/revanced/util/Utils.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/util/microg/MicroGBytecodeHelper.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/util/microg/MicroGResourceHelper.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/util/resource/ArrayResource.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/util/resource/BaseResource.kt (100%) rename {src => patches/src}/main/kotlin/app/revanced/util/resource/StringResource.kt (100%) rename {src => patches/src}/main/resources/addresources/values-af-rZA/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-am-rET/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ar-rSA/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-as-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-az-rAZ/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-be-rBY/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-bg-rBG/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-bn-rBD/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-bs-rBA/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ca-rES/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-cs-rCZ/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-da-rDK/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-de-rDE/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-el-rGR/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-es-rES/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-et-rEE/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-eu-rES/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-fa-rIR/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-fi-rFI/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-fil-rPH/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-fr-rFR/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-gl-rES/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-gu-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-hi-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-hr-rHR/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-hu-rHU/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-hy-rAM/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-in-rID/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-is-rIS/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-it-rIT/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-iw-rIL/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ja-rJP/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ka-rGE/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-kk-rKZ/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-km-rKH/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-kn-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ko-rKR/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ky-rKG/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-lo-rLA/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-lt-rLT/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-lv-rLV/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-mk-rMK/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ml-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-mn-rMN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-mr-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ms-rMY/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-my-rMM/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-nb-rNO/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ne-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-nl-rNL/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-or-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-pa-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-pl-rPL/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-pt-rBR/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-pt-rPT/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ro-rRO/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ru-rRU/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-si-rLK/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-sk-rSK/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-sl-rSI/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-sq-rAL/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-sr-rSP/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-sv-rSE/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-sw-rKE/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ta-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-te-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-th-rTH/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-tr-rTR/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-uk-rUA/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-ur-rIN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-uz-rUZ/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-vi-rVN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-zh-rCN/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-zh-rTW/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values-zu-rZA/strings.xml (100%) rename {src => patches/src}/main/resources/addresources/values/arrays.xml (91%) rename {src => patches/src}/main/resources/addresources/values/strings.xml (96%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_dark.png (100%) rename {src => patches/src}/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_light.png (100%) rename {src => patches/src}/main/resources/copyvideourl/drawable/revanced_yt_copy.xml (100%) rename {src => patches/src}/main/resources/copyvideourl/drawable/revanced_yt_copy_timestamp.xml (100%) rename {src => patches/src}/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_background_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_foreground_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-hdpi/ic_launcher.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-hdpi/ic_launcher_round.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_background_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_foreground_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-mdpi/ic_launcher.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-mdpi/ic_launcher_round.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_background_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_foreground_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xhdpi/ic_launcher.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xhdpi/ic_launcher_round.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_background_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_foreground_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_background_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_foreground_color_108.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher.png (100%) rename {src => patches/src}/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename {src => patches/src}/main/resources/downloads/drawable/revanced_yt_download_button.xml (100%) rename {src => patches/src}/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml (100%) rename {src => patches/src}/main/resources/settings/host/values/styles.xml (100%) rename {src => patches/src}/main/resources/settings/layout/revanced_settings_with_toolbar.xml (100%) rename {src => patches/src}/main/resources/settings/xml/revanced_prefs.xml (100%) rename {src => patches/src}/main/resources/speedbutton/drawable/revanced_playback_speed_dialog_button.xml (100%) rename {src => patches/src}/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable-xxxhdpi/quantum_ic_skip_next_white_24.png (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable/revanced_sb_adjust.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable/revanced_sb_backward.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable/revanced_sb_compare.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable/revanced_sb_edit.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable/revanced_sb_forward.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable/revanced_sb_logo.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable/revanced_sb_publish.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/drawable/revanced_sb_voting.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/host/layout/youtube_controls_layout.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/layout/revanced_sb_inline_sponsor_overlay.xml (86%) rename {src => patches/src}/main/resources/sponsorblock/layout/revanced_sb_new_segment.xml (100%) rename {src => patches/src}/main/resources/sponsorblock/layout/revanced_sb_skip_sponsor_button.xml (100%) rename {src => patches/src}/main/resources/swipecontrols/drawable/revanced_ic_sc_brightness_auto.xml (100%) rename {src => patches/src}/main/resources/swipecontrols/drawable/revanced_ic_sc_brightness_manual.xml (100%) rename {src => patches/src}/main/resources/swipecontrols/drawable/revanced_ic_sc_volume_mute.xml (100%) rename {src => patches/src}/main/resources/swipecontrols/drawable/revanced_ic_sc_volume_normal.xml (100%) rename {stub => patches/stub}/build.gradle.kts (100%) rename {stub => patches/stub}/src/main/java/android/os/Build.java (100%) delete mode 100644 src/main/kotlin/app/revanced/generator/JsonPatchesFileGenerator.kt delete mode 100644 src/main/kotlin/app/revanced/generator/Main.kt delete mode 100644 src/main/kotlin/app/revanced/generator/PatchesFileGenerator.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/connectivity/wifi/spoof/SpoofWifiPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/directory/ChangeDataDirectoryLocationPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/interaction/gestures/PredictiveBackGesturePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/location/hide/HideMockLocationPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/all/telephony/sim/spoof/SpoofSimCountryPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/amazon/deeplinking/DeepLinkingFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/amazon/deeplinking/DeepLinkingPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/backdrops/misc/pro/fingerprints/ProUnlockFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/bandcamp/limitations/fingerprints/HandlePlaybackLimitsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/candylinkvpn/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/candylinkvpn/fingerprints/IsPremiumPurchasedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/cieid/restrictions/root/fingerprints/CheckRootFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/duolingo/ad/fingerprints/InitializeMonetizationDebugSettingsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/duolingo/debug/fingerprints/InitializeBuildConfigProviderFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/fingerprints/BaseModelMapperFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/fingerprints/GetSponsoredDataModelTemplateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/fingerprints/GetStoryVisibilityFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/facebook/ads/story/fingerprints/AdsInsertionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/facebook/ads/story/fingerprints/FetchMoreAdsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/facebook/ads/story/fingerprints/FieldMethodFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/fingerprints/CreateKeyFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabs.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlenews/customtabs/fingerprints/LaunchCustomTabFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlenews/misc/gms/fingerprints/MagazinesActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlenews/misc/gms/fingerprints/PrimeMethodFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlenews/misc/integrations/IntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofBuildInfoPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/fingerprints/PhotosActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlephotos/misc/integrations/IntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlephotos/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlephotos/preferences/fingerprints/BackupPreferencesFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt delete mode 100644 src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/fingerprints/OnApplicationCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/hexeditor/ad/fingerprints/PrimaryAdsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/fingerprints/CheckProFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/AttestationSupportedCheckFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/BootloaderCheckFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/RootCheckFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/signature/fingerprints/SpoofSignatureFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/inshorts/ad/fingerprints/InshortsAdsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/instagram/patches/ad/HideAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/instagram/patches/ad/fingerprints/AdInjectorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/HideTimelineAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/IsAdCheckOneFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/IsAdCheckTwoFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/instagram/patches/ads/timeline/fingerprints/ShowAdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/irplus/ad/fingerprints/IrplusAdsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/lightroom/misc/login/fingerprints/IsLoggedInFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/lightroom/misc/premium/fingerprints/HasPurchasedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/memegenerator/detection/license/fingerprints/LicenseValidationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/fingerprints/IsFreeVersionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/messenger/inbox/fingerprints/CreateInboxSubTabsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/messenger/inbox/fingerprints/LoadInboxAdsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/messenger/inputfield/fingerprints/SendTypingIndicatorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/messenger/inputfield/fingerprints/SwitchMessangeInputEmojiButtonFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/mifitness/misc/locale/fingerprints/SyncBluetoothLanguageFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/mifitness/misc/login/fingerprints/XiaomiAccountManagerConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/moneymanager/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/moneymanager/fingerprints/UnlockProFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/ad/video/fingerprints/ShowVideoAdsParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/audio/codecs/CodecsUnlockPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/audio/codecs/fingerprints/AllCodecsReferenceFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/audio/codecs/fingerprints/CodecsLockFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/fingerprints/RepeatTrackFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/fingerprints/DisableShuffleFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/layout/compactheader/fingerprints/ConstructCategoryBarFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/layout/premium/fingerprints/HideGetPremiumFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/layout/premium/fingerprints/MembershipSettingsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/fingerprints/PivotBarConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/androidauto/fingerprints/CheckCertificateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/fingerprints/BackgroundPlaybackDisableFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/fingerprints/KidsBackgroundPlaybackPolicyControllerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/gms/fingerprints/MusicActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/gms/fingerprints/PrimeMethodFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/integrations/IntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/misc/integrations/fingerprints/ApplicationInitFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/fingerprints/IsEnabledFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/myfitnesspal/ads/fingerprints/IsPremiumUseCaseImplFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/myfitnesspal/ads/fingerprints/MainActivityNavigateToNativePremiumUpsellFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/fingerprints/IsLicenseRegisteredFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/nyx/misc/pro/fingerprints/CheckProFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/fingerprints/SetPlaceFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/fingerprints/HideUpdatePopupFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/fingerprints/IsBookpointEnabledFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/fingerprints/IsPlusUnlockedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/piccomafr/misc/fingerprints/GetAndroidIDFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/AppMesurementFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/FacebookSDKFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/FirebaseInstallFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/pixiv/ads/fingerprints/ShouldShowAdsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/fingerprints/ShowReminderFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/ad/comments/fingerprints/HideCommentAdsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/ad/general/fingerprints/AdPostFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/ad/general/fingerprints/NewAdPostFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/BaseFixSLinksPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/BaseSpoofClientPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/Constants.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/ads/BaseDisableAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/ads/fingerprints/IsAdsEnabledFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/fingerprints/GetAuthorizationUrlFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/fingerprints/GetClientIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/fingerprints/RequestTokenFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/fingerprints/AdmobMediationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/fingerprints/MaxMediationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/fingerprints/BuildUserAgentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/fingerprints/GetClientIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/fingerprints/DownloadAudioFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/fingerprints/GetOAuthAccessTokenFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/fingerprints/HandleNavigationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/fingerprints/InitFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/fingerprints/APIUtilsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/fingerprints/GetHttpBasicAuthHeaderFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/fingerprints/LoginActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/fingerprints/BillingClientOnServiceConnected.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/fingerprints/StartSubscriptionActivityFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/fingerprints/IsAdFreeUserFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/fingerprints/AuthUtilityUserAgent.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/fingerprints/GetClientIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/fingerprints/PiracyDetectionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/fingerprints/BaseClientIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/fingerprints/BasicAuthorizationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/fingerprints/BuildAuthorizationStringFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/fingerprints/GetUserAgentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/fingerprints/BaseClientIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/fingerprints/GetLoggedInBearerTokenFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/fingerprints/GetLoggedOutBearerTokenFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/fingerprints/GetRefreshTokenFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/fingerprints/LoginActivityClientIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/fingerprints/RedditCheckDisableAPIFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/fingerprints/SetRemoteConfigFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/fingerprints/GetClientIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforlemmy/ads/DisableAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/ads/DisableAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/annoyances/startup/fingerprints/MainActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/api/SpoofClientPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/api/fingerprints/GetAuthorizationStringFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/api/fingerprints/GetBearerTokenFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/api/fingerprints/GetUserAgentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/api/fingerprints/ImgurImageAPIFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/api/fingerprints/LoadBrowserURLFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/DisablePiracyDetectionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/fingerprints/LinkHelperOpenLinkFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/fingerprints/SetAuthorizationHeaderFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/user/UseUserEndpointPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/user/fingerprints/BaseUserEndpointFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/user/fingerprints/OAuthFriendRequestFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/user/fingerprints/OAuthSubredditInfoRequestConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/user/fingerprints/OAuthSubredditInfoRequestHelperFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/user/fingerprints/OAuthUnfriendRequestFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/user/fingerprints/OAuthUserIdRequestFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/user/fingerprints/OAuthUserInfoRequestFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/misc.integrations/IntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/misc.integrations/fingerprints/InitFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/fingerprints/DisableScreenshotPopupFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/fingerprints/HasPremiumIconAccessFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/fingerprints/ShareLinkFormatterFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/scbeasy/detection/debugging/RemoveDebuggingDetectionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/scbeasy/detection/debugging/fingerprints/DebuggingDetectionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/fingerprints/RootDetectionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/fingerprints/CastContextFetchFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/checks/fingerprints/PatchInfoBuildFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/checks/fingerprints/PatchInfoFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/fingerprints/CanScrollVerticallyFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/gms/fingerprints/GmsCoreSupportFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/gms/fingerprints/GooglePlayUtilityFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/gms/fingerprints/ServiceCheckFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/hex/BaseHexPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/shared/misc/integrations/fingerprints/ReVancedUtilsPatchesVersionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/fingerprints/OnReadyFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/songpal/badge/fingerprints/CreateTabsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/songpal/badge/fingerprints/ShowNotificationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/soundcloud/ad/fingerprints/InterceptFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/soundcloud/ad/fingerprints/UserConsumerPlanConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/soundcloud/analytics/fingerprints/CreateTrackingApiFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/fingerprints/DownloadOperationsHeaderVerificationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/fingerprints/DownloadOperationsURLBuilderFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/soundcloud/shared/fingerprints/FeatureConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/fingerprints/OnDemandFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/spotify/navbar/fingerprints/AddNavBarItemFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/strava/subscription/fingerprints/GetSubscribedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/strava/upselling/fingerprints/GetModulesFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheck.kt delete mode 100644 src/main/kotlin/app/revanced/patches/swissid/integritycheck/fingerprints/CheckIntegrityFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/fingerprints/CheckLockedThemesFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/fingerprints/SetThemeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/feedfilter/fingerprints/FeedApiServiceLIZFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/fingerprints/OnClearDisplayEventFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/fingerprints/ACLCommonShareFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/fingerprints/ACLCommonShareFingerprint2.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/fingerprints/ACLCommonShareFingerprint3.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/fingerprints/DownloadUriFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/fingerprints/SetSeekBarShowTypeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/fingerprints/ShouldShowSeekBarFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/fingerprints/GetSpeedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/fingerprints/SetSpeedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/integrations/IntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/integrations/fingerprints/InitFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/fingerprints/MandatoryLoginServiceFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/fingerprints/MandatoryLoginServiceFingerprint2.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/fingerprints/GoogleAuthAvailableFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/fingerprints/GoogleOneTapAuthAvailableFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AdPersonalizationActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/AddSettingsEntryFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/SettingsEntryFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/SettingsEntryInfoFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/misc/settings/fingerprints/SettingsStatusLoadFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/shared/fingerprints/GetEnterFromFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tiktok/shared/fingerprints/OnRenderFirstFrameFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/trakt/fingerprints/IsVIPEPFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/trakt/fingerprints/IsVIPFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/trakt/fingerprints/RemoteUserFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/fingerprints/BrightnessFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/fingerprints/ShowGiftMessagePopupFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/fixes/fingerprints/AddQueryParamFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/fixes/fingerprints/HttpPathParserFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/live/DisableTumblrLivePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/TimelineFilterPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/fingerprints/PostsResponseConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/fingerprints/TimelineConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/fingerprints/TimelineFilterIntegrationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/audio/fingerprints/AudioAdsPresenterPlayFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/embedded/fingerprints/CreateUsherClientFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/BaseAdPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/CheckAdEligibilityLambdaFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/ContentConfigShowAdsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/ad/video/fingerprints/GetReadyToShowAdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/fingerprints/ChatUtilCreateDeletedSpanFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/fingerprints/DeletedMessageClickableSpanCtorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/fingerprints/SetHasModAccessFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/fingerprints/CommunityPointsButtonViewDelegateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/debug/fingerprints/IsDebugConfigEnabledFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/debug/fingerprints/IsOmVerificationEnabledFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/debug/fingerprints/ShouldShowDebugOptionsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/misc/integrations/IntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/misc/integrations/fingerprints/InitFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/misc/settings/fingerprints/MenuGroupsOnClickFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/misc/settings/fingerprints/MenuGroupsUpdatedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/misc/settings/fingerprints/SettingsActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitch/misc/settings/fingerprints/SettingsMenuItemEnumFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/fingerprints/BuildMediaOptionsSheetFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/fingerprints/ConstructMediaOptionsSheetFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/fingerprints/ShowDownloadVideoUpsellBottomSheetFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/fingerprints/ViewCountsEnabledFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/fingerprints/JsonHookPatchFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/fingerprints/JsonInputStreamFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/fingerprints/LoganSquareFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/BaseHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/ads/HideAdsHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/hook/patch/recommendation/HideRecommendedUsersPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkBuilderFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkResourceGetterFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkSharingDomainFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/OpenLinkFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/SanitizeSharingLinksFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/vsco/misc/pro/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/vsco/misc/pro/fingerprints/RevCatSubscriptionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/fingerprints/GetCertMessagingFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/fingerprints/GetCertRegistrationFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/fingerprints/PromoCodeUnlockFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/willhaben/ads/fingerprints/AdResolverFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/willhaben/ads/fingerprints/WHAdViewInjectorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/fingerprints/CheckProFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/fingerprints/GetPremiumViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlBytecodePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/OfflineVideoEndpointFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/AllowSwipingUpGestureFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/DisableFastForwardGestureFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/DisableFastForwardLegacyFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/DisableFastForwardNoticeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/OnTouchEventHandlerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/SeekbarTappingFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/ShowSwipingUpGuideFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/SlideToSeekFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/SwipingUpGestureParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsBytecodePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/SwipeControlsHostActivityFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/fingerprints/StartVideoInformerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/fingerprints/SubtitleTrackFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/AddCreateButtonViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/CreatePivotBarFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/MediaRouteButtonFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/fingerprints/PlayerControlsPreviousNextOverlayTouchFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/fingerprints/LayoutCircleFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/fingerprints/LayoutIconFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/fingerprints/LayoutVideoFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/fingerprints/InitializeAmbientModeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/AlbumCardsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/CrowdfundingBoxFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/FilterBarHeightFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/HideShowMoreButtonFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/ParseElementFromBufferFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/PlayerOverlayFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/RelatedChipCloudFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/SearchResultsChipBarFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/ShowFloatingMicrophoneButtonFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/ShowWatermarkFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/fingerprints/YoodlesImageViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfocardsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/fingerprints/InfocardsIncognitoFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/fingerprints/InfocardsIncognitoParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/fingerprints/InfocardsMethodCallFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/loadmorebutton/HideLoadMoreButtonPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/fingerprints/CreateShortsButtonsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/fingerprints/LegacyRenderBottomNavigationBarParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/fingerprints/ReelConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/fingerprints/RenderBottomNavigationBarFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/fingerprints/RenderBottomNavigationBarParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/fingerprints/SetPivotBarVisibilityFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/fingerprints/SetPivotBarVisibilityParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/fingerprints/ShortsBottomBarContainerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/fingerprints/CreateEndScreenViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/fingerprints/TimeCounterFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerDimensionsCalculatorParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerMinimumSizeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernAddViewListenerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernCloseButtonFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernExpandButtonFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernExpandCloseDrawablesFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernForwardButtonFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernOverlayViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernRewindButtonFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernViewParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerOverrideFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerOverrideNoContextFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerResponseModelSizeCheckFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/YouTubePlayerOverlaysLayoutFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/fingerprints/EngagementPanelControllerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/fingerprints/CreatePlayerOverviewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikeResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/ConversionContextFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/DislikeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/DislikesOldLayoutTextViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/LikeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/RemoveLikeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/RollingNumberMeasureAnimatedTextFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/RollingNumberMeasureStaticLabelFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/RollingNumberMeasureStaticLabelParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/RollingNumberSetterFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/RollingNumberTextViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/ShortsTextViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/TextComponentConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/TextComponentDataFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/fingerprints/TextComponentLookupFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/fingerprints/CreateSearchSuggestionsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/fingerprints/SetWordmarkHeaderFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorBytecodePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/fingerprints/FullscreenSeekbarThumbnailsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/fingerprints/LithoLinearGradientFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/fingerprints/PlayerSeekbarColorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/fingerprints/PlayerSeekbarGradientConfigFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/fingerprints/SetSeekbarClickedColorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/fingerprints/ShortsSeekbarColorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelEnumConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/fingerprints/ReelPlaybackRepeatFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/fingerprints/AppendTimeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/fingerprints/ControlsOverlayFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/fingerprints/RectangleFieldInvalidatorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/fingerprints/SpoofAppVersionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/startpage/fingerprints/BrowseIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/startpage/fingerprints/IntentActionFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/fingerprints/UserWasInShortsConfigFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/fingerprints/UserWasInShortsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/tablet/fingerprints/GetFormFactorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemeResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/theme/fingerprints/LithoThemeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/theme/fingerprints/ThemeHelperDarkColorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/theme/fingerprints/ThemeHelperLightColorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/theme/fingerprints/UseGradientLoadingScreenFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictions.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/fingerprints/AutoRepeatFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/fingerprints/AutoRepeatParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/BackgroundPlaybackManagerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/BackgroundPlaybackSettingsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/KidsBackgroundPlaybackPolicyControllerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/bottomsheet/hook/BottomSheetHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/bottomsheet/hook/BottomSheetHookResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/bottomsheet/hook/fingerprints/CreateBottomSheetFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/debugging/DebuggingPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/fingerprints/DeviceDimensionsModelToStringFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/fingerprints/OnBackPressedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/fingerprints/RecyclerViewScrollingFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/fingerprints/RecyclerViewTopScrollingFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/fingerprints/RecyclerViewTopScrollingParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/fingerprints/CarioFragmentConfigFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildInitPlaybackRequestFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildMediaDataSourceFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildPlayerRequestURIFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildRequestFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreateStreamingDataFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ProtobufClassParseByteBufferFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/gms/fingerprints/PrimeMethodFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/fingerprints/MessageDigestImageUrlFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/fingerprints/MessageDigestImageUrlParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/fingerprints/cronet/RequestFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/fingerprints/cronet/request/callback/OnFailureFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/fingerprints/cronet/request/callback/OnResponseStartedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/fingerprints/cronet/request/callback/OnSucceededFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/integrations/IntegrationsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/integrations/fingerprints/ApplicationInitFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/links/fingerprints/ABUriParserFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/links/fingerprints/ABUriParserLegacyFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/links/fingerprints/HTTPUriParserFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/links/fingerprints/HTTPUriParserLegacyFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/ComponentContextParserFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/EmptyComponentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/LithoFilterFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/ProtobufBufferReferenceFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/ReadComponentIdentifierFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/microg/MicroGBytecodePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/microg/fingerprints/CastContextFetchFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/ActionBarSearchResultsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/InitializeButtonsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/MainActivityOnBackPressedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationBarHookCallbackFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationEnumFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateDrawableViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateResourceViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsViewSetSelectedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/fingerprints/ControlsOverlayVisibility.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/fingerprints/OverlayViewInflateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/fingerprints/PlayerBottomControlsInflateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/fingerprints/PlayerControlsIntegrationHookFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/fingerprints/PlayerTopControlsInflateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/fingerprint/PlayerOverlaysOnFinishInflateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playertype/fingerprint/PlayerTypeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/playertype/fingerprint/VideoStateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/privacy/fingerprints/CopyTextFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/privacy/fingerprints/SystemShareSheetFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/privacy/fingerprints/YouTubeShareSheetFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/fingerprints/RecyclerViewTreeObserverFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/settings/fingerprints/LicenseActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/settings/fingerprints/SetThemeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/fingerprints/ZoomHapticsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/LayoutConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/MainActivityFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/MainActivityOnCreateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/NewVideoQualityChangedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/RollingNumberTextViewAnimationUpdateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/SeekbarFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/SeekbarOnDrawFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/shared/fingerprints/SubtitleButtonControllerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/hdrbrightness/HDRBrightnessPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/hdrbrightness/fingerprints/HDRBrightnessFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/CreateVideoPlayerSeekbarFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/MdxPlayerDirectorSetVideoStageFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/MdxSeekFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/MdxSeekRelativeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/OnPlaybackSpeedItemClickFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/PlayerControllerSetTimeReferenceFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/PlayerInitFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/SeekFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/SeekRelativeFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoLengthFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/fingerprint/PlayerParameterBuilderFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/fingerprint/PlayerParameterBuilderLegacyFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/PlaybackSpeedMenuSpeedChangedFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/SetQualityByIndexMethodClassFieldReferenceFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/VideoQualityItemOnClickParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/quality/fingerprints/VideoQualitySetterFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/fingerprints/GetOldPlaybackSpeedsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/fingerprints/ShowOldPlaybackSpeedMenuFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/fingerprints/ShowOldPlaybackSpeedMenuIntegrationsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/fingerprints/SpeedArrayGeneratorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/fingerprints/SpeedLimiterFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/fingerprint/InitializePlaybackSpeedValuesFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/videoid/fingerprint/VideoIdBackgroundPlayFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/videoid/fingerprint/VideoIdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/videoid/fingerprint/VideoIdParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/fingerprints/VideoQualityMenuOptionsFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/fingerprints/VideoQualityMenuViewInflateFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtubevanced/ad/general/HideAdsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtubevanced/ad/general/fingerprints/ContainsAdFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/fingerprints/IsPremiumFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/fingerprints/YukaUserConstructorFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/util/patch/LiteralValueFingerprint.kt delete mode 100644 src/main/resources/addresources/values-ga-rIE/strings.xml delete mode 100644 src/main/resources/addresources/values-sr-rCS/strings.xml diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 250871bcc2..193a26af05 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -16,6 +16,12 @@ jobs: with: fetch-depth: 0 + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + - name: Cache Gradle uses: burrunan/gradle-cache-action@v1 diff --git a/.github/workflows/open_pull_request.yml b/.github/workflows/open_pull_request.yml index 2afa0596cb..33c8a7211f 100644 --- a/.github/workflows/open_pull_request.yml +++ b/.github/workflows/open_pull_request.yml @@ -20,13 +20,12 @@ jobs: - name: Open pull request uses: repo-sync/pull-request@v2 with: - destination_branch: "main" - pr_title: "chore: ${{ env.MESSAGE }}" + destination_branch: main + pr_title: 'chore: ${{ env.MESSAGE }}' pr_body: | This pull request will ${{ env.MESSAGE }}. ## Before merging this PR - - [ ] Remember about https://github.com/revanced/revanced-integrations - [ ] Pull translations from Crowdin pr_draft: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b210aad5c2..498cca4135 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,13 +23,19 @@ jobs: persist-credentials: false fetch-depth: 0 + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + - name: Cache Gradle uses: burrunan/gradle-cache-action@v1 - name: Build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: ./gradlew generateMeta clean + run: ./gradlew build clean - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/.gitignore b/.gitignore index 5015417667..62f6eb424f 100644 --- a/.gitignore +++ b/.gitignore @@ -122,5 +122,8 @@ gradle-app.setting # Dependency directories node_modules/ -# gradle properties, due to Github token +# Gradle properties, due to Github token ./gradle.properties + +# One package is called the same as the Gradle build folder +!**/src/**/build/ \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index e086a70c4e..f1854e4b53 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/.releaserc b/.releaserc index ff81c99adf..b3d61b10b1 100644 --- a/.releaserc +++ b/.releaserc @@ -23,7 +23,6 @@ "assets": [ "CHANGELOG.md", "gradle.properties", - "patches.json" ], "message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" } @@ -33,11 +32,8 @@ { "assets": [ { - "path": "build/libs/revanced-patches*" + "path": "patches/build/libs/patches-!(*sources*|*javadoc*).rvp?(.asc)" }, - { - "path": "patches.json" - } ], successComment: false } diff --git a/api/revanced-patches.api b/api/revanced-patches.api deleted file mode 100644 index 0b4976b98e..0000000000 --- a/api/revanced-patches.api +++ /dev/null @@ -1,2308 +0,0 @@ -public final class app/revanced/generator/MainKt { - public static synthetic fun main ([Ljava/lang/String;)V -} - -public final class app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/all/connectivity/wifi/spoof/SpoofWifiPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/all/connectivity/wifi/spoof/SpoofWifiPatch; - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V -} - -public final class app/revanced/patches/all/directory/ChangeDataDirectoryLocationPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/all/directory/ChangeDataDirectoryLocationPatch; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Integer; - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;I)V - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V -} - -public final class app/revanced/patches/all/interaction/gestures/PredictiveBackGesturePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/all/interaction/gestures/PredictiveBackGesturePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/all/location/hide/HideMockLocationPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/all/location/hide/HideMockLocationPatch; - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V -} - -public abstract class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public fun ()V - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair; - protected fun getBoard ()Ljava/lang/String; - protected fun getBootloader ()Ljava/lang/String; - protected fun getBrand ()Ljava/lang/String; - protected fun getCpuAbi ()Ljava/lang/String; - protected fun getCpuAbi2 ()Ljava/lang/String; - protected fun getDevice ()Ljava/lang/String; - protected fun getDisplay ()Ljava/lang/String; - protected fun getFingerprint ()Ljava/lang/String; - protected fun getHardware ()Ljava/lang/String; - protected fun getHost ()Ljava/lang/String; - protected fun getId ()Ljava/lang/String; - protected fun getManufacturer ()Ljava/lang/String; - protected fun getModel ()Ljava/lang/String; - protected fun getOdmSku ()Ljava/lang/String; - protected fun getProduct ()Ljava/lang/String; - protected fun getRadio ()Ljava/lang/String; - protected fun getSerial ()Ljava/lang/String; - protected fun getSku ()Ljava/lang/String; - protected fun getSocManufacturer ()Ljava/lang/String; - protected fun getSocModel ()Ljava/lang/String; - protected fun getTags ()Ljava/lang/String; - protected fun getTime ()Ljava/lang/Long; - protected fun getType ()Ljava/lang/String; - protected fun getUser ()Ljava/lang/String; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V -} - -public final class app/revanced/patches/all/misc/build/SpoofBuildInfoPatch : app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch { - public fun ()V -} - -public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/all/misc/hex/HexPatch : app/revanced/patches/shared/misc/hex/BaseHexPatch { - public fun ()V - public fun getReplacements ()Ljava/util/List; -} - -public final class app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/all/misc/network/OverrideCertificatePinningPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable { - public static final field INSTANCE Lapp/revanced/patches/all/misc/packagename/ChangePackageNamePatch; - public fun close ()V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V - public final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String; -} - -public final class app/revanced/patches/all/misc/resources/AddResourcesPatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable, java/util/Map, kotlin/jvm/internal/markers/KMutableMap { - public static final field INSTANCE Lapp/revanced/patches/all/misc/resources/AddResourcesPatch; - public fun clear ()V - public fun close ()V - public final fun containsKey (Ljava/lang/Object;)Z - public fun containsKey (Ljava/lang/String;)Z - public final fun containsValue (Ljava/lang/Object;)Z - public fun containsValue (Ljava/util/Set;)Z - public final fun entrySet ()Ljava/util/Set; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V - public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object; - public final fun get (Ljava/lang/Object;)Ljava/util/Set; - public fun get (Ljava/lang/String;)Ljava/util/Set; - public fun getEntries ()Ljava/util/Set; - public fun getKeys ()Ljava/util/Set; - public fun getSize ()I - public fun getValues ()Ljava/util/Collection; - public final fun invoke (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z - public final fun invoke (Ljava/lang/String;Ljava/lang/Iterable;)Z - public final fun invoke (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Z - public final fun invoke (Ljava/lang/String;Ljava/util/List;)Z - public final fun invoke (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Z - public static synthetic fun invoke$default (Lapp/revanced/patches/all/misc/resources/AddResourcesPatch;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;ILjava/lang/Object;)Z - public static synthetic fun invoke$default (Lapp/revanced/patches/all/misc/resources/AddResourcesPatch;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Z - public fun isEmpty ()Z - public final fun keySet ()Ljava/util/Set; - public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun put (Ljava/lang/String;Ljava/util/Set;)Ljava/util/Set; - public fun putAll (Ljava/util/Map;)V - public final synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object; - public final fun remove (Ljava/lang/Object;)Ljava/util/Set; - public fun remove (Ljava/lang/String;)Ljava/util/Set; - public final fun size ()I - public final fun values ()Ljava/util/Collection; -} - -public abstract class app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch : app/revanced/patcher/patch/BytecodePatch { - public fun ()V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public abstract fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public final fun findPatchIndices (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;)Lkotlin/sequences/Sequence; - public abstract fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V -} - -public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall { - public abstract fun getDefinedClassName ()Ljava/lang/String; - public abstract fun getMethodName ()Ljava/lang/String; - public abstract fun getMethodParams ()[Ljava/lang/String; - public abstract fun getReturnType ()Ljava/lang/String; - public abstract fun replaceInvokeVirtualWithIntegrations (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V -} - -public final class app/revanced/patches/all/misc/transformation/IMethodCall$DefaultImpls { - public static fun replaceInvokeVirtualWithIntegrations (Lapp/revanced/patches/all/misc/transformation/IMethodCall;Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V -} - -public final class app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch; - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V -} - -public final class app/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall : java/lang/Enum, app/revanced/patches/all/misc/transformation/IMethodCall { - public static final field SetAllowedCapturePolicyGlobal Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall; - public static final field SetAllowedCapturePolicySingle Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall; - public fun getDefinedClassName ()Ljava/lang/String; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public fun getMethodName ()Ljava/lang/String; - public fun getMethodParams ()[Ljava/lang/String; - public fun getReturnType ()Ljava/lang/String; - public fun replaceInvokeVirtualWithIntegrations (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V - public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall; - public static fun values ()[Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch$MethodCall; -} - -public final class app/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V -} - -public final class app/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall : java/lang/Enum, app/revanced/patches/all/misc/transformation/IMethodCall { - public static final field AddFlags Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall; - public static final field SetFlags Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall; - public fun getDefinedClassName ()Ljava/lang/String; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public fun getMethodName ()Ljava/lang/String; - public fun getMethodParams ()[Ljava/lang/String; - public fun getReturnType ()Ljava/lang/String; - public fun replaceInvokeVirtualWithIntegrations (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V - public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall; - public static fun values ()[Lapp/revanced/patches/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch$MethodCall; -} - -public final class app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/all/telephony/sim/spoof/SpoofSimCountryPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/all/telephony/sim/spoof/SpoofSimCountryPatch; - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V -} - -public final class app/revanced/patches/amazon/deeplinking/DeepLinkingPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/amazon/deeplinking/DeepLinkingPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/backdrops/misc/pro/ProUnlockPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/candylinkvpn/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/candylinkvpn/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/duolingo/ad/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/duolingo/ad/DisableAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/duolingo/debug/EnableDebugMenuPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/facebook/ads/story/HideStoryAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/facebook/ads/story/HideStoryAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/finanzonline/detection/root/RootDetectionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/finanzonline/detection/root/RootDetectionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/googlenews/customtabs/EnableCustomTabs : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/googlenews/customtabs/EnableCustomTabs; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch { - public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch; -} - -public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/gms/GmsCoreSupportResourcePatch; -} - -public final class app/revanced/patches/googlenews/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { - public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/integrations/IntegrationsPatch; -} - -public final class app/revanced/patches/googlephotos/features/SpoofFeaturesPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/googlephotos/features/SpoofFeaturesPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint { - public static final field INSTANCE Lapp/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint; -} - -public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch { - public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch; -} - -public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/gms/GmsCoreSupportResourcePatch; -} - -public final class app/revanced/patches/googlephotos/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { - public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/integrations/IntegrationsPatch; -} - -public final class app/revanced/patches/googlephotos/preferences/RestoreHiddenBackUpWhileChargingTogglePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/googlephotos/preferences/RestoreHiddenBackUpWhileChargingTogglePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/hexeditor/ad/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/hexeditor/ad/DisableAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/idaustria/detection/root/RootDetectionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/idaustria/detection/root/RootDetectionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/inshorts/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/inshorts/ad/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/instagram/patches/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/instagram/patches/ad/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/instagram/patches/ads/timeline/HideTimelineAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/instagram/patches/ads/timeline/HideTimelineAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/irplus/ad/RemoveAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/irplus/ad/RemoveAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/memegenerator/detection/license/LicenseValidationPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/messenger/inbox/HideInboxAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/messenger/inbox/HideInboxAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/messenger/inbox/HideInboxSubtabsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/mifitness/misc/login/FixLoginPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/mifitness/misc/login/FixLoginPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/moneymanager/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/moneymanager/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/ad/video/HideMusicVideoAds : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/ad/video/HideMusicVideoAds; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/ad/video/HideVideoAds : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/ad/video/HideVideoAds; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/ad/video/MusicVideoAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/ad/video/MusicVideoAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/audio/codecs/CodecsUnlockPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/audio/codecs/CodecsUnlockPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/audio/exclusiveaudio/ExclusiveAudioPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/audio/exclusiveaudio/ExclusiveAudioPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShuffleTogglePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/interaction/permanentshuffle/PermanentShuffleTogglePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/layout/compactheader/CompactHeaderPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/layout/compactheader/CompactHeaderPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/layout/compactheader/HideCategoryBar : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/layout/compactheader/HideCategoryBar; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/layout/premium/HideGetPremiumPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/layout/premium/HideGetPremiumPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/music/misc/gms/Constants { - public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/Constants; -} - -public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch { - public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/GmsCoreSupportPatch; -} - -public final class app/revanced/patches/music/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/GmsCoreSupportResourcePatch; -} - -public final class app/revanced/patches/music/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { - public static final field INSTANCE Lapp/revanced/patches/music/misc/integrations/IntegrationsPatch; -} - -public final class app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/myexpenses/misc/pro/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/myfitnesspal/ads/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/myfitnesspal/ads/fingerprints/IsPremiumUseCaseImplFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint { - public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/fingerprints/IsPremiumUseCaseImplFingerprint; -} - -public final class app/revanced/patches/myfitnesspal/ads/fingerprints/MainActivityNavigateToNativePremiumUpsellFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint { - public static final field INSTANCE Lapp/revanced/patches/myfitnesspal/ads/fingerprints/MainActivityNavigateToNativePremiumUpsellFingerprint; -} - -public final class app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/nyx/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/nyx/misc/pro/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/photomath/detection/signature/SignatureDetectionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/piccomafr/tracking/DisableTrackingPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/piccomafr/tracking/DisableTrackingPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/pixiv/ads/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/pixiv/ads/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/fingerprints/ShowReminderFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint { - public static final field INSTANCE Lapp/revanced/patches/rar/misc/annoyances/purchasereminder/fingerprints/ShowReminderFingerprint; -} - -public final class app/revanced/patches/reddit/ad/banner/HideBannerPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/ad/banner/HideBannerPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/ad/comments/HideCommentAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/ad/general/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/ad/general/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public abstract class app/revanced/patches/reddit/customclients/BaseFixSLinksPatch : app/revanced/patcher/patch/BytecodePatch { - public fun (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Ljava/util/Set;)V - public synthetic fun (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - protected abstract fun getIntegrationsClassDescriptor ()Ljava/lang/String; - protected final fun getResolveSLinkMethod ()Ljava/lang/String; - protected final fun getSetAccessTokenMethod ()Ljava/lang/String; - protected abstract fun patchNavigationHandler (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;Lapp/revanced/patcher/data/BytecodeContext;)V - protected abstract fun patchSetAccessToken (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public abstract class app/revanced/patches/reddit/customclients/BaseSpoofClientPatch : app/revanced/patcher/patch/BytecodePatch { - public fun (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public final fun getClientId ()Ljava/lang/String; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public final fun setClientId (Ljava/lang/String;)V -} - -public final class app/revanced/patches/reddit/customclients/Constants { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/Constants; - public static final field OAUTH_USER_AGENT Ljava/lang/String; -} - -public abstract class app/revanced/patches/reddit/customclients/ads/BaseDisableAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public fun (Ljava/util/Set;Ljava/util/Set;)V - public synthetic fun (Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public final class app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch : app/revanced/patches/reddit/customclients/BaseFixSLinksPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch; -} - -public final class app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch; -} - -public final class app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public final class app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public final class app/revanced/patches/reddit/customclients/joeyforreddit/api/fingerprints/AuthUtilityUserAgent : app/revanced/patcher/fingerprint/MethodFingerprint { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/joeyforreddit/api/fingerprints/AuthUtilityUserAgent; -} - -public final class app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public final class app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public final class app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public final class app/revanced/patches/reddit/customclients/syncforlemmy/ads/DisableAdsPatch : app/revanced/patches/reddit/customclients/ads/BaseDisableAdsPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforlemmy/ads/DisableAdsPatch; -} - -public final class app/revanced/patches/reddit/customclients/syncforreddit/ads/DisableAdsPatch : app/revanced/patches/reddit/customclients/ads/BaseDisableAdsPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/ads/DisableAdsPatch; -} - -public final class app/revanced/patches/reddit/customclients/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/syncforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/api/SpoofClientPatch; - public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V - public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V -} - -public final class app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/DisablePiracyDetectionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/DisablePiracyDetectionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch : app/revanced/patches/reddit/customclients/BaseFixSLinksPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch; -} - -public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/user/UseUserEndpointPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/user/UseUserEndpointPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch; -} - -public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/scbeasy/detection/debugging/RemoveDebuggingDetectionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/scbeasy/detection/debugging/RemoveDebuggingDetectionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public abstract class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch : app/revanced/patcher/patch/BytecodePatch { - public fun (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch;)V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public abstract class app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch : app/revanced/patcher/patch/BytecodePatch { - public fun (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lkotlin/reflect/KClass;Lapp/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lkotlin/reflect/KClass;Lapp/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public abstract class app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch : app/revanced/patcher/patch/ResourcePatch { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V - protected final fun getGmsCoreVendor ()Ljava/lang/String; - protected final fun getGmsCoreVendorGroupId ()Ljava/lang/String; -} - -public abstract class app/revanced/patches/shared/misc/hex/BaseHexPatch : app/revanced/patcher/patch/RawResourcePatch { - public fun ()V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V - public abstract fun getReplacements ()Ljava/util/List; -} - -public final class app/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement { - public static final field Companion Lapp/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement$Companion; - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public final fun replacePattern ([B)V -} - -public final class app/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement$Companion { -} - -public abstract class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch : app/revanced/patcher/patch/BytecodePatch { - public fun (Ljava/lang/String;Ljava/util/Set;)V - public fun (Ljava/util/Set;)V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public abstract class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint { - public fun ()V - public fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun invoke (Ljava/lang/String;)V -} - -public abstract interface class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IHookInsertIndexResolver : kotlin/jvm/functions/Function1 { - public abstract fun invoke (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Integer; -} - -public final class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IHookInsertIndexResolver$DefaultImpls { - public static fun invoke (Lapp/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IHookInsertIndexResolver;Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Integer; -} - -public abstract interface class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IRegisterResolver : kotlin/jvm/functions/Function1 { - public abstract fun invoke (Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Integer; -} - -public final class app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IRegisterResolver$DefaultImpls { - public static fun invoke (Lapp/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch$IntegrationsFingerprint$IRegisterResolver;Lcom/android/tools/smali/dexlib2/iface/Method;)Ljava/lang/Integer; -} - -public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/shared/misc/mapping/ResourceMappingPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V - public final fun get (Ljava/lang/String;Ljava/lang/String;)J -} - -public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatch$ResourceElement { - public fun (Ljava/lang/String;Ljava/lang/String;J)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Ljava/lang/String; - public final fun component3 ()J - public final fun copy (Ljava/lang/String;Ljava/lang/String;J)Lapp/revanced/patches/shared/misc/mapping/ResourceMappingPatch$ResourceElement; - public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/mapping/ResourceMappingPatch$ResourceElement;Ljava/lang/String;Ljava/lang/String;JILjava/lang/Object;)Lapp/revanced/patches/shared/misc/mapping/ResourceMappingPatch$ResourceElement; - public fun equals (Ljava/lang/Object;)Z - public final fun getId ()J - public final fun getName ()Ljava/lang/String; - public final fun getType ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public abstract class app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable, java/util/Set, kotlin/jvm/internal/markers/KMutableSet { - public fun ()V - public fun (Lkotlin/Pair;Ljava/util/Set;)V - public synthetic fun (Lkotlin/Pair;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun add (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)Z - public synthetic fun add (Ljava/lang/Object;)Z - public fun addAll (Ljava/util/Collection;)Z - public fun clear ()V - public fun close ()V - public fun contains (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)Z - public final fun contains (Ljava/lang/Object;)Z - public fun containsAll (Ljava/util/Collection;)Z - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V - public fun getSize ()I - public fun isEmpty ()Z - public fun iterator ()Ljava/util/Iterator; - public fun remove (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)Z - public final fun remove (Ljava/lang/Object;)Z - public fun removeAll (Ljava/util/Collection;)Z - public fun retainAll (Ljava/util/Collection;)Z - public final fun size ()I - public fun toArray ()[Ljava/lang/Object; - public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object; -} - -public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreference { - public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion; - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getKey ()Ljava/lang/String; - public final fun getSummaryKey ()Ljava/lang/String; - public final fun getTag ()Ljava/lang/String; - public final fun getTitleKey ()Ljava/lang/String; - public fun hashCode ()I - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/patches/shared/misc/settings/preference/BasePreference$Companion { - public final fun addSummary (Lorg/w3c/dom/Element;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;)V - public static synthetic fun addSummary$default (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;Lorg/w3c/dom/Element;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;ILjava/lang/Object;)V -} - -public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen : java/io/Closeable { - public fun ()V - public fun (Ljava/util/Set;)V - public synthetic fun (Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun close ()V - public abstract fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen;)V -} - -public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { - public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getKey ()Ljava/lang/String; - public final fun getPreferences ()Ljava/util/Set; - public final fun getTitleKey ()Ljava/lang/String; - public abstract fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; -} - -public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { - public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;)V - public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V - public final fun getCategories ()Ljava/util/Set; - public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; - public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen; -} - -public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen$Category : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { - public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V - public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; - public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceCategory; -} - -public final class app/revanced/patches/shared/misc/settings/preference/InputType : java/lang/Enum { - public static final field NUMBER Lapp/revanced/patches/shared/misc/settings/preference/InputType; - public static final field TEXT Lapp/revanced/patches/shared/misc/settings/preference/InputType; - public static final field TEXT_CAP_CHARACTERS Lapp/revanced/patches/shared/misc/settings/preference/InputType; - public static final field TEXT_MULTI_LINE Lapp/revanced/patches/shared/misc/settings/preference/InputType; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public final fun getType ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/InputType; - public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/InputType; -} - -public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getIntent ()Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; - public fun hashCode ()I - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent { - public fun (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V - public final fun copy (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; - public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class app/revanced/patches/shared/misc/settings/preference/ListPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getEntries ()Lapp/revanced/util/resource/ArrayResource; - public final fun getEntriesKey ()Ljava/lang/String; - public final fun getEntryValues ()Lapp/revanced/util/resource/ArrayResource; - public final fun getEntryValuesKey ()Ljava/lang/String; - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getSelectable ()Z - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public class app/revanced/patches/shared/misc/settings/preference/PreferenceCategory : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getPreferences ()Ljava/util/Set; - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getPreferences ()Ljava/util/Set; - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting : java/lang/Enum { - public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting; - public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting; - public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public final fun getKeySuffix ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting; - public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting; -} - -public final class app/revanced/patches/shared/misc/settings/preference/SummaryType : java/lang/Enum { - public static final field DEFAULT Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; - public static final field OFF Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; - public static final field ON Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public final fun getType ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; - public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; -} - -public final class app/revanced/patches/shared/misc/settings/preference/SwitchPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getSummaryOffKey ()Ljava/lang/String; - public final fun getSummaryOnKey ()Ljava/lang/String; - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/patches/shared/misc/settings/preference/TextPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getInputType ()Lapp/revanced/patches/shared/misc/settings/preference/InputType; - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/songpal/badge/BadgeTabPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field ACTIVITY_TAB_DESCRIPTOR Ljava/lang/String; - public static final field INSTANCE Lapp/revanced/patches/songpal/badge/BadgeTabPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/songpal/badge/RemoveNotificationBadgePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/soundcloud/ad/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/soundcloud/ad/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/soundcloud/analytics/DisableTelemetryPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/spotify/layout/theme/CustomThemePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/spotify/layout/theme/CustomThemePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/spotify/lite/ondemand/OnDemandPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/spotify/lite/ondemand/OnDemandPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/spotify/navbar/PremiumNavbarTabPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabResourcePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/spotify/navbar/PremiumNavbarTabResourcePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/stocard/layout/HideOffersTabPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/stocard/layout/HideOffersTabPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/stocard/layout/HideStoryBubblesPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/strava/subscription/UnlockSubscriptionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheck : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheck; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/ticktick/misc/themeunlock/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/ticktick/misc/themeunlock/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/feedfilter/FeedFilterPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/feedfilter/FeedFilterPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/interaction/downloads/DownloadsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/integrations/IntegrationsPatch; -} - -public final class app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/misc/settings/SettingsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/settings/SettingsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/trakt/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/trakt/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/ads/DisableDashboardAds : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/ads/DisableDashboardAds; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/fixes/FixOldVersionsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/fixes/FixOldVersionsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/live/DisableTumblrLivePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/live/DisableTumblrLivePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/tumblr/timelinefilter/TimelineFilterPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/tumblr/timelinefilter/TimelineFilterPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitch/ad/audio/AudioAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitch/ad/audio/AudioAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public abstract class app/revanced/patches/twitch/ad/shared/util/BaseAdPatch : app/revanced/patcher/patch/BytecodePatch { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - protected final fun blockMethods (Lapp/revanced/patcher/data/BytecodeContext;Ljava/lang/String;[Ljava/lang/String;Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod;)Z - public static synthetic fun blockMethods$default (Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch;Lapp/revanced/patcher/data/BytecodeContext;Ljava/lang/String;[Ljava/lang/String;Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod;ILjava/lang/Object;)Z - protected final fun createConditionInstructions (Ljava/lang/String;)Ljava/lang/String; - public static synthetic fun createConditionInstructions$default (Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String; - public final fun getConditionCall ()Ljava/lang/String; - public final fun getSkipLabelName ()Ljava/lang/String; -} - -protected final class app/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod { - public fun ()V - public fun (CLjava/lang/String;)V - public synthetic fun (CLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()C - public final fun component2 ()Ljava/lang/String; - public final fun copy (CLjava/lang/String;)Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod; - public static synthetic fun copy$default (Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod;CLjava/lang/String;ILjava/lang/Object;)Lapp/revanced/patches/twitch/ad/shared/util/BaseAdPatch$ReturnMethod; - public fun equals (Ljava/lang/Object;)Z - public final fun getReturnType ()C - public final fun getValue ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class app/revanced/patches/twitch/ad/video/VideoAdsPatch : app/revanced/patches/twitch/ad/shared/util/BaseAdPatch { - public static final field INSTANCE Lapp/revanced/patches/twitch/ad/video/VideoAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitch/debug/DebugModePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitch/debug/DebugModePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitch/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { - public static final field INSTANCE Lapp/revanced/patches/twitch/misc/integrations/IntegrationsPatch; -} - -public final class app/revanced/patches/twitch/misc/settings/SettingsPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable { - public static final field INSTANCE Lapp/revanced/patches/twitch/misc/settings/SettingsPatch; - public fun close ()V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitch/misc/settings/SettingsResourcePatch : app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/twitch/misc/settings/SettingsResourcePatch; -} - -public final class app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitter/layout/viewcount/HideViewCountPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/twitter/misc/hook/json/JsonHookPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable { - public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/json/JsonHookPatch; - public fun close ()V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public abstract class app/revanced/patches/twitter/misc/hook/patch/BaseHookPatch : app/revanced/patcher/patch/BytecodePatch { - public fun (Ljava/lang/String;)V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitter/misc/hook/patch/ads/HideAdsHookPatch : app/revanced/patches/twitter/misc/hook/patch/BaseHookPatch { - public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/patch/ads/HideAdsHookPatch; -} - -public final class app/revanced/patches/twitter/misc/hook/patch/recommendation/HideRecommendedUsersPatch : app/revanced/patches/twitter/misc/hook/patch/BaseHookPatch { - public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/patch/recommendation/HideRecommendedUsersPatch; -} - -public final class app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/vsco/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/vsco/misc/pro/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/willhaben/ads/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/willhaben/ads/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/ad/general/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/ad/general/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/ad/general/HideAdsResourcePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/ad/general/HideAdsResourcePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/ad/video/VideoAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/ad/video/VideoAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlBytecodePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlBytecodePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/interaction/downloads/DownloadsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/downloads/DownloadsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsBytecodePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsBytecodePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/CustomBrandingPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/autoplay/HideAutoplayButtonPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/captions/HideCaptionsButtonPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/cast/HideCastButtonPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/buttons/player/hide/HidePlayerButtonsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/albumcards/AlbumCardsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/comments/CommentsPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/comments/CommentsPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/crowdfundingbox/CrowdfundingBoxPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/filterbar/HideFilterBarPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/floatingmicrophone/HideFloatingMicrophoneButtonPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfocardsResourcePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/infocards/HideInfocardsResourcePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/hide/loadmorebutton/HideLoadMoreButtonPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/loadmorebutton/HideLoadMoreButtonPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/hide/time/HideTimestampPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/startupshortsreset/fingerprints/UserWasInShortsFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startupshortsreset/fingerprints/UserWasInShortsFingerprint; -} - -public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/theme/ThemeBytecodePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictions : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictions; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/announcements/AnnouncementsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch : app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/check/CheckEnvironmentPatch; -} - -public final class app/revanced/patches/youtube/misc/debugging/DebuggingPatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/debugging/DebuggingPatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch; - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V -} - -public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch; -} - -public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/gms/GmsCoreSupportResourcePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook; - public final fun addImageUrlErrorCallbackHook (Ljava/lang/String;)V - public final fun addImageUrlHook (Ljava/lang/String;Z)V - public static synthetic fun addImageUrlHook$default (Lapp/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook;Ljava/lang/String;ZILjava/lang/Object;)V - public final fun addImageUrlSuccessCallbackHook (Ljava/lang/String;)V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/integrations/IntegrationsPatch; -} - -public final class app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Pair; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Pair;)V -} - -public final class app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch; - public fun close ()V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public final fun getHookNavigationButtonCreated ()Lkotlin/jvm/functions/Function1; -} - -public final class app/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch; - public final fun addControls (Ljava/lang/String;)V - public fun close ()V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public final fun initializeBottomControl (Ljava/lang/String;)V - public final fun initializeControl (Ljava/lang/String;)V - public final fun injectVisibilityCheckCall (Ljava/lang/String;)V -} - -public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch; - public final fun addBottomControls (Ljava/lang/String;)V - public fun close ()V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/misc/settings/SettingsPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/SettingsPatch; - public fun close ()V - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public final fun newIntent (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; -} - -public final class app/revanced/patches/youtube/misc/settings/SettingsPatch$PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/SettingsPatch$PreferenceScreen; - public fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen;)V - public final fun getADS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getALTERNATIVE_THUMBNAILS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getFEED ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getGENERAL_LAYOUT ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getMISC ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getPLAYER ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getSEEKBAR ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getSHORTS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getSWIPE_CONTROLS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; - public final fun getVIDEO ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; -} - -public final class app/revanced/patches/youtube/misc/settings/SettingsResourcePatch : app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/SettingsResourcePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/hdrbrightness/HDRBrightnessPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/hdrbrightness/HDRBrightnessPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/information/VideoInformationPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/information/VideoInformationPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch : app/revanced/patcher/patch/BytecodePatch, java/io/Closeable, java/util/Set, kotlin/jvm/internal/markers/KMutableSet { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch; - public fun add (Lapp/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch$Hook;)Z - public synthetic fun add (Ljava/lang/Object;)Z - public fun addAll (Ljava/util/Collection;)Z - public fun clear ()V - public fun close ()V - public fun contains (Lapp/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch$Hook;)Z - public final fun contains (Ljava/lang/Object;)Z - public fun containsAll (Ljava/util/Collection;)Z - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun getSize ()I - public fun isEmpty ()Z - public fun iterator ()Ljava/util/Iterator; - public fun remove (Lapp/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch$Hook;)Z - public final fun remove (Ljava/lang/Object;)Z - public fun removeAll (Ljava/util/Collection;)Z - public fun retainAll (Ljava/util/Collection;)Z - public final fun size ()I - public fun toArray ()[Ljava/lang/Object; - public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object; -} - -public final class app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/quality/RememberVideoQualityPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/PlaybackSpeedPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/videoid/VideoIdPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/videoid/VideoIdPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public final fun hookBackgroundPlayVideoId (Ljava/lang/String;)V - public final fun hookPlayerResponseVideoId (Ljava/lang/String;)V - public final fun hookVideoId (Ljava/lang/String;)V -} - -public final class app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch : app/revanced/patcher/patch/ResourcePatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch; - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V - public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V -} - -public final class app/revanced/patches/youtubevanced/ad/general/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/youtubevanced/ad/general/HideAdsPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch; - public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V - public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V -} - -public final class app/revanced/util/BytecodeUtilsKt { - public static final fun alsoResolve (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult; - public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z - public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod; - public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List; - public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun forEachLiteralValueInstruction (Lapp/revanced/patcher/data/BytecodeContext;JLkotlin/jvm/functions/Function2;)V - public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException; - public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;)I - public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I - public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)I - public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I - public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I - public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I - public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;)I - public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I - public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)I - public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I - public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I - public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I - public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I - public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I - public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I - public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I - public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I - public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I - public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I - public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I - public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I - public static final fun indexOfFirstWideLiteralInstructionValueReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I - public static final fun indexOfFirstWideLiteralInstructionValueReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I - public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I - public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I - public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V - public static final fun resultOrThrow (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult; - public static final fun returnEarly (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Z)V - public static final fun returnEarly (Ljava/lang/Iterable;Z)V - public static final fun returnEarly (Ljava/util/List;Z)V - public static synthetic fun returnEarly$default (Lapp/revanced/patcher/fingerprint/MethodFingerprint;ZILjava/lang/Object;)V - public static synthetic fun returnEarly$default (Ljava/lang/Iterable;ZILjava/lang/Object;)V - public static synthetic fun returnEarly$default (Ljava/util/List;ZILjava/lang/Object;)V - public static final fun transformMethods (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V - public static final fun traverseClassHierarchy (Lapp/revanced/patcher/data/BytecodeContext;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V -} - -public final class app/revanced/util/ResourceGroup { - public fun (Ljava/lang/String;[Ljava/lang/String;)V - public final fun getResourceDirectoryName ()Ljava/lang/String; - public final fun getResources ()[Ljava/lang/String; -} - -public final class app/revanced/util/ResourceUtilsKt { - public static final fun asSequence (Lorg/w3c/dom/NodeList;)Lkotlin/sequences/Sequence; - public static final fun childElementsSequence (Lorg/w3c/dom/Node;)Lkotlin/sequences/Sequence; - public static final fun copyResources (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;[Lapp/revanced/util/ResourceGroup;)V - public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/DomFileEditor;Lapp/revanced/patcher/util/DomFileEditor;)Ljava/lang/AutoCloseable; - public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V - public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V - public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V - public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V -} - -public abstract class app/revanced/util/patch/LiteralValueFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint { - public fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function0;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V -} - -public final class app/revanced/util/resource/ArrayResource : app/revanced/util/resource/BaseResource { - public static final field Companion Lapp/revanced/util/resource/ArrayResource$Companion; - public fun (Ljava/lang/String;Ljava/util/List;)V - public final fun getItems ()Ljava/util/List; - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/util/resource/ArrayResource$Companion { - public final fun fromNode (Lorg/w3c/dom/Node;)Lapp/revanced/util/resource/ArrayResource; -} - -public abstract class app/revanced/util/resource/BaseResource { - public fun (Ljava/lang/String;Ljava/lang/String;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getName ()Ljava/lang/String; - public final fun getTag ()Ljava/lang/String; - public fun hashCode ()I - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; - public static synthetic fun serialize$default (Lapp/revanced/util/resource/BaseResource;Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/util/resource/StringResource : app/revanced/util/resource/BaseResource { - public static final field Companion Lapp/revanced/util/resource/StringResource$Companion; - public fun (Ljava/lang/String;Ljava/lang/String;Z)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getFormatted ()Z - public final fun getValue ()Ljava/lang/String; - public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; -} - -public final class app/revanced/util/resource/StringResource$Companion { - public final fun fromNode (Lorg/w3c/dom/Node;)Lapp/revanced/util/resource/StringResource; -} - diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 9ed1416786..0000000000 --- a/build.gradle.kts +++ /dev/null @@ -1,155 +0,0 @@ -import org.gradle.kotlin.dsl.support.listFilesOrdered -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - alias(libs.plugins.kotlin) - alias(libs.plugins.binary.compatibility.validator) - `maven-publish` - signing -} - -group = "app.revanced" - -repositories { - mavenCentral() - mavenLocal() - google() - maven { - // A repository must be specified for some reason. "registry" is a dummy. - url = uri("https://maven.pkg.github.com/revanced/registry") - credentials { - username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") - password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") - } - } -} - -dependencies { - implementation(libs.revanced.patcher) - implementation(libs.smali) - // TODO: Required because build fails without it. Find a way to remove this dependency. - implementation(libs.guava) - // Used in JsonGenerator. - implementation(libs.gson) - // Android API stubs defined here. - compileOnly(project(":stub")) -} - -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} - -java { - targetCompatibility = JavaVersion.VERSION_11 -} - -tasks { - withType(Jar::class) { - exclude("app/revanced/meta") - - manifest { - attributes["Name"] = "ReVanced Patches" - attributes["Description"] = "Patches for ReVanced." - attributes["Version"] = version - attributes["Timestamp"] = System.currentTimeMillis().toString() - attributes["Source"] = "git@github.com:revanced/revanced-patches.git" - attributes["Author"] = "ReVanced" - attributes["Contact"] = "contact@revanced.app" - attributes["Origin"] = "https://revanced.app" - attributes["License"] = "GNU General Public License v3.0" - } - } - - register("buildDexJar") { - description = "Build and add a DEX to the JAR file" - group = "build" - - dependsOn(build) - - doLast { - val d8 = File(System.getenv("ANDROID_HOME")).resolve("build-tools") - .listFilesOrdered().last().resolve("d8").absolutePath - - val patchesJar = configurations.archives.get().allArtifacts.files.files.first().absolutePath - val workingDirectory = layout.buildDirectory.dir("libs").get().asFile - - exec { - workingDir = workingDirectory - commandLine = listOf(d8, "--release", patchesJar) - } - - exec { - workingDir = workingDirectory - commandLine = listOf("zip", "-u", patchesJar, "classes.dex") - } - } - } - - register("generatePatchesFiles") { - description = "Generate patches files" - - dependsOn(build) - - classpath = sourceSets["main"].runtimeClasspath - mainClass.set("app.revanced.generator.MainKt") - } - - // Needed by gradle-semantic-release-plugin. - // Tracking: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435 - publish { - dependsOn("buildDexJar") - dependsOn("generatePatchesFiles") - } -} - -publishing { - repositories { - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/revanced/revanced-patches") - credentials { - username = System.getenv("GITHUB_ACTOR") - password = System.getenv("GITHUB_TOKEN") - } - } - } - - publications { - create("revanced-patches-publication") { - from(components["java"]) - - pom { - name = "ReVanced Patches" - description = "Patches for ReVanced." - url = "https://revanced.app" - - licenses { - license { - name = "GNU General Public License v3.0" - url = "https://www.gnu.org/licenses/gpl-3.0.en.html" - } - } - developers { - developer { - id = "ReVanced" - name = "ReVanced" - email = "contact@revanced.app" - } - } - scm { - connection = "scm:git:git://github.com/revanced/revanced-patches.git" - developerConnection = "scm:git:git@github.com:revanced/revanced-patches.git" - url = "https://github.com/revanced/revanced-patches" - } - } - } - } -} - -signing { - useGpgCmd() - - sign(publishing.publications["revanced-patches-publication"]) -} diff --git a/crowdin.yml b/crowdin.yml index 4ac3cb98b3..148f321cd2 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -3,6 +3,6 @@ api_token_env: "CROWDIN_PERSONAL_TOKEN" preserve_hierarchy: false files: - - source: src/main/resources/addresources/values/strings.xml - translation: src/main/resources/addresources/values-%android_code%/strings.xml + - source: patches/src/main/resources/addresources/values/strings.xml + translation: patches/src/main/resources/addresources/values-%android_code%/strings.xml skip_untranslated_strings: true diff --git a/gradle.properties b/gradle.properties index 5a96ec6367..ce9413cb54 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,5 @@ org.gradle.parallel = true org.gradle.caching = true +android.useAndroidX=true kotlin.code.style = official version = 4.18.0-dev.6 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a4060085c..abf731bd36 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,26 @@ [versions] -revanced-patcher = "19.3.1" +revanced-patcher = "20.0.2" +# Tracking https://github.com/google/smali/issues/64. #noinspection GradleDependency -smali = "3.0.5" # 3.0.7 breaks binary compatibility. Tracking https://github.com/google/smali/issues/58. -guava = "33.2.1-jre" +smali = "3.0.5" gson = "2.11.0" -binary-compatibility-validator = "0.15.1" -kotlin = "2.0.0" +# 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818. +#noinspection GradleDependency +agp = "8.2.2" +annotation = "1.9.0" +appcompat = "1.7.0" +okhttp = "5.0.0-alpha.14" +retrofit = "2.11.0" +guava = "33.2.1-jre" [libraries] -revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" } -smali = { module = "com.android.tools.smali:smali", version.ref = "smali" } -guava = { module = "com.google.guava:guava", version.ref = "guava" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } +retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } + [plugins] -binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } -kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +android-library = { id = "com.android.library", version.ref = "agp" } diff --git a/patches/api/patches.api b/patches/api/patches.api new file mode 100644 index 0000000000..b7b1b9b297 --- /dev/null +++ b/patches/api/patches.api @@ -0,0 +1,1556 @@ +public final class app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatchKt { + public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt { + public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/build/BuildInfo { + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getBoard ()Ljava/lang/String; + public final fun getBootloader ()Ljava/lang/String; + public final fun getBrand ()Ljava/lang/String; + public final fun getCpuAbi ()Ljava/lang/String; + public final fun getCpuAbi2 ()Ljava/lang/String; + public final fun getDevice ()Ljava/lang/String; + public final fun getDisplay ()Ljava/lang/String; + public final fun getFingerprint ()Ljava/lang/String; + public final fun getHardware ()Ljava/lang/String; + public final fun getHost ()Ljava/lang/String; + public final fun getId ()Ljava/lang/String; + public final fun getManufacturer ()Ljava/lang/String; + public final fun getModel ()Ljava/lang/String; + public final fun getOdmSku ()Ljava/lang/String; + public final fun getProduct ()Ljava/lang/String; + public final fun getRadio ()Ljava/lang/String; + public final fun getSerial ()Ljava/lang/String; + public final fun getSku ()Ljava/lang/String; + public final fun getSocManufacturer ()Ljava/lang/String; + public final fun getSocModel ()Ljava/lang/String; + public final fun getTags ()Ljava/lang/String; + public final fun getTime ()Ljava/lang/Long; + public final fun getType ()Ljava/lang/String; + public final fun getUser ()Ljava/lang/String; +} + +public final class app/revanced/patches/all/misc/build/SpoofBuildInfoPatchKt { + public static final fun getSpoofBuildInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatchKt { + public static final fun getHideMockLocationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatchKt { + public static final fun getSpoofSimCountryPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatchKt { + public static final fun getSpoofWifiPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatchKt { + public static final fun getEnableAndroidDebuggingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatchKt { + public static final fun getChangeDataDirectoryLocationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/hex/HexPatchKt { + public static final fun getHexPatch ()Lapp/revanced/patcher/patch/RawResourcePatch; +} + +public final class app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatchKt { + public static final fun getPredictiveBackGesturePatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/all/misc/network/OverrideCertificatePinningPatchKt { + public static final fun getOverrideCertificatePinningPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePatchKt { + public static field packageNameOption Lapp/revanced/patcher/patch/Option; + public static final fun getChangePackageNamePatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getPackageNameOption ()Lapp/revanced/patcher/patch/Option; + public static final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String; + public static final fun setPackageNameOption (Lapp/revanced/patcher/patch/Option;)V +} + +public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt { + public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z + public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z + public static final fun addResources (Ljava/lang/String;Ljava/lang/Iterable;)Z + public static final fun addResources (Ljava/lang/String;Ljava/lang/String;)V + public static final fun addResources (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Z + public static final fun addResources (Ljava/lang/String;Ljava/util/List;)Z + public static synthetic fun addResources$default (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Z + public static synthetic fun addResources$default (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;ILjava/lang/Object;)Z + public static final fun getAddResourcesPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatchKt { + public static final fun getRemoveScreenCaptureRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatchKt { + public static final fun getRemoveScreenshotRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatchKt { + public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall { + public abstract fun getDefinedClassName ()Ljava/lang/String; + public abstract fun getMethodName ()Ljava/lang/String; + public abstract fun getMethodParams ()[Ljava/lang/String; + public abstract fun getReturnType ()Ljava/lang/String; + public abstract fun replaceInvokeVirtualWithExtension (Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V +} + +public final class app/revanced/patches/all/misc/transformation/IMethodCall$DefaultImpls { + public static fun replaceInvokeVirtualWithExtension (Lapp/revanced/patches/all/misc/transformation/IMethodCall;Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V +} + +public final class app/revanced/patches/all/misc/transformation/TransformInstructionsPatchKt { + public static final fun transformInstructionsPatch (Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatchKt { + public static final fun getChangeVersionCodePatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/amazon/DeepLinkingPatchKt { + public static final fun getDeepLinkingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatchKt { + public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatchKt { + public static final fun getRemovePlayLimitsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatchKt { + public static final fun getBypassRootChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/duolingo/ad/DisableAdsPatchKt { + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatchKt { + public static final fun getEnableDebugMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatchKt { + public static final fun getHideSponsoredStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/facebook/ads/story/HideStoryAdsPatchKt { + public static final fun getHideStoryAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatchKt { + public static final fun getBootloaderDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/finanzonline/detection/root/RootDetectionPatchKt { + public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatchKt { + public static final fun getEnableCustomTabsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlenews/misc/extension/ExtensionPatchKt { + public static final fun getExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatchKt { + public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlephotos/misc/extension/ExtensionPatchKt { + public static final fun getExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatchKt { + public static final fun getSpoofBuildInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatchKt { + public static final fun getSpoofFeaturesPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatchKt { + public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatchKt { + public static final fun getRestoreHiddenBackUpWhileChargingTogglePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictionsKt { + public static final fun getRemoveDeviceRestrictionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/hexeditor/ad/DisableAdsPatchKt { + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatchKt { + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/idaustria/detection/root/RootDetectionPatchKt { + public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatchKt { + public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/inshorts/ad/InshortsAdsPatchKt { + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/instagram/ads/HideAdsPatchKt { + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt { + public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt { + public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatchKt { + public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/memegenerator/detection/license/LicenseValidationPatchKt { + public static final fun getLicenseValidationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatchKt { + public static final fun getSignatureVerificationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatchKt { + public static final fun getUnlockProVersionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/messenger/inbox/HideInboxAdsPatchKt { + public static final fun getHideInboxAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/messenger/inbox/HideInboxSubtabsPatchKt { + public static final fun getHideInboxSubtabsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatchKt { + public static final fun getDisableSwitchingEmojiToStickerPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatchKt { + public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt { + public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/mifitness/misc/login/FixLoginPatchKt { + public static final fun getFixLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/ad/video/HideVideoAdsKt { + public static final fun getHideVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlaybackKt { + public static final fun getEnableExclusiveAudioPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatchKt { + public static final fun getPermanentRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatchKt { + public static final fun getPermanentShufflePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/layout/compactheader/HideCategoryBarKt { + public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/layout/premium/HideGetPremiumPatchKt { + public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatchKt { + public static final fun getRemoveUpgradeButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatchKt { + public static final fun getBypassCertificateChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatchKt { + public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/misc/extension/SharedExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/music/misc/gms/Constants { + public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/Constants; +} + +public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt { + public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt { + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/myfitnesspal/ads/HideAdsPatchKt { + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatchKt { + public static final fun getRemoveBroadcastsRestrictionPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatchKt { + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/nyx/misc/pro/UnlockProPatchKt { + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatchKt { + public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatchKt { + public static final fun getGetDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/photomath/detection/signature/SignatureDetectionPatchKt { + public static final fun getSignatureDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatchKt { + public static final fun getHideUpdatePopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatchKt { + public static final fun getEnableBookpointPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatchKt { + public static final fun getUnlockPlusPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatchKt { + public static final fun getSpoofAndroidDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/piccomafr/tracking/DisableTrackingPatchKt { + public static final fun getDisableTrackingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt { + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatchKt { + public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/ad/banner/HideBannerPatchKt { + public static final fun getHideBannerPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/reddit/ad/comments/HideCommentAdsPatchKt { + public static final fun getHideCommentAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/ad/general/HideAdsPatchKt { + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/FixSLinksPatchKt { + public static final field RESOLVE_S_LINK_METHOD Ljava/lang/String; + public static final field SET_ACCESS_TOKEN_METHOD Ljava/lang/String; + public static final fun fixSLinksPatch (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; + public static synthetic fun fixSLinksPatch$default (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/SpoofClientPatchKt { + public static final fun spoofClientPatch (Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lapp/revanced/patcher/patch/BytecodePatch; + public static synthetic fun spoofClientPatch$default (Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatchKt { + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatchKt { + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatchKt { + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatchKt { + public static final fun getFixAudioMissingInDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatchKt { + public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String; + public static final fun getFixSlinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatchKt { + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatchKt { + public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatchKt { + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatchKt { + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatchKt { + public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/redditisfun/api/FingerprintsKt { + public static final fun baseClientIdFingerprint (Ljava/lang/String;)Lapp/revanced/patcher/Fingerprint; +} + +public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatchKt { + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatchKt { + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatchKt { + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatchKt { + public static final fun disableAdsPatch (Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; + public static synthetic fun disableAdsPatch$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatchKt { + public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatchKt { + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatchKt { + public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatchKt { + public static final fun getDisableSyncForLemmyBottomSheetPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatchKt { + public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatchKt { + public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String; + public static final fun getFixSLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatchKt { + public static final fun getUseUserEndpointPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatchKt { + public static final fun getFixVideoDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatchKt { + public static final fun getDisableScreenshotPopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatchKt { + public static final fun getUnlockPremiumIconPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/misc/extension/ExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatchKt { + public static final fun getSanitizeUrlQueryPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatchKt { + public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatchKt { + public static final fun checkEnvironmentPatch (Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/Patch;[Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/shared/misc/extension/ExtensionHook { + public final fun getFingerprint ()Lapp/revanced/patcher/Fingerprint; + public final fun invoke (Ljava/lang/String;)V +} + +public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt { + public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; + public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; + public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatchKt { + public static final fun getVerticalScrollPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/shared/misc/gms/FingerprintsKt { + public static final field GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME Ljava/lang/String; +} + +public final class app/revanced/patches/shared/misc/gms/GmsCoreSupportPatchKt { + public static final fun gmsCoreSupportPatch (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/Fingerprint;Ljava/util/Set;Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; + public static synthetic fun gmsCoreSupportPatch$default (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/Fingerprint;Ljava/util/Set;Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun gmsCoreSupportResourcePatch (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/ResourcePatch; + public static synthetic fun gmsCoreSupportResourcePatch$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/shared/misc/hex/HexPatchKt { + public static final fun hexPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/RawResourcePatch; +} + +public final class app/revanced/patches/shared/misc/hex/Replacement { + public static final field Companion Lapp/revanced/patches/shared/misc/hex/Replacement$Companion; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public final fun replacePattern ([B)V +} + +public final class app/revanced/patches/shared/misc/hex/Replacement$Companion { +} + +public final class app/revanced/patches/shared/misc/mapping/ResourceElement { + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()J + public final fun copy (Ljava/lang/String;Ljava/lang/String;J)Lapp/revanced/patches/shared/misc/mapping/ResourceElement; + public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/mapping/ResourceElement;Ljava/lang/String;Ljava/lang/String;JILjava/lang/Object;)Lapp/revanced/patches/shared/misc/mapping/ResourceElement; + public fun equals (Ljava/lang/Object;)Z + public final fun getId ()J + public final fun getName ()Ljava/lang/String; + public final fun getType ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatchKt { + public static final fun get (Ljava/util/List;Ljava/lang/String;Ljava/lang/String;)J + public static final fun getResourceMappingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun getResourceMappings ()Ljava/util/List; +} + +public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt { + public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch; + public static synthetic fun settingsPatch$default (Lkotlin/Pair;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch; +} + +public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreference { + public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Ljava/lang/String; + public final fun getSummaryKey ()Ljava/lang/String; + public final fun getTag ()Ljava/lang/String; + public final fun getTitleKey ()Ljava/lang/String; + public fun hashCode ()I + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/patches/shared/misc/settings/preference/BasePreference$Companion { + public final fun addSummary (Lorg/w3c/dom/Element;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;)V + public static synthetic fun addSummary$default (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;Lorg/w3c/dom/Element;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;ILjava/lang/Object;)V +} + +public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen : java/io/Closeable { + public fun ()V + public fun (Ljava/util/Set;)V + public synthetic fun (Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public abstract fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference;)V +} + +public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getKey ()Ljava/lang/String; + public final fun getPreferences ()Ljava/util/Set; + public final fun getTitleKey ()Ljava/lang/String; + public abstract fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; +} + +public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { + public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V + public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V + public final fun getCategories ()Ljava/util/Set; + public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; + public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference; +} + +public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen$Category : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { + public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V + public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; + public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceCategory; +} + +public final class app/revanced/patches/shared/misc/settings/preference/InputType : java/lang/Enum { + public static final field NUMBER Lapp/revanced/patches/shared/misc/settings/preference/InputType; + public static final field TEXT Lapp/revanced/patches/shared/misc/settings/preference/InputType; + public static final field TEXT_CAP_CHARACTERS Lapp/revanced/patches/shared/misc/settings/preference/InputType; + public static final field TEXT_MULTI_LINE Lapp/revanced/patches/shared/misc/settings/preference/InputType; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public final fun getType ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/InputType; + public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/InputType; +} + +public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getIntent ()Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; + public fun hashCode ()I + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent { + public fun (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V + public final fun copy (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; + public static synthetic fun copy$default (Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class app/revanced/patches/shared/misc/settings/preference/ListPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getEntries ()Lapp/revanced/util/resource/ArrayResource; + public final fun getEntriesKey ()Ljava/lang/String; + public final fun getEntryValues ()Lapp/revanced/util/resource/ArrayResource; + public final fun getEntryValuesKey ()Ljava/lang/String; + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getSelectable ()Z + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public class app/revanced/patches/shared/misc/settings/preference/PreferenceCategory : app/revanced/patches/shared/misc/settings/preference/BasePreference { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getPreferences ()Ljava/util/Set; + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getPreferences ()Ljava/util/Set; + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting : java/lang/Enum { + public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; + public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; + public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public final fun getKeySuffix ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; + public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; +} + +public final class app/revanced/patches/shared/misc/settings/preference/SummaryType : java/lang/Enum { + public static final field DEFAULT Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; + public static final field OFF Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; + public static final field ON Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public final fun getType ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; + public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; +} + +public final class app/revanced/patches/shared/misc/settings/preference/SwitchPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getSummaryOffKey ()Ljava/lang/String; + public final fun getSummaryOnKey ()Ljava/lang/String; + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/patches/shared/misc/settings/preference/TextPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getInputType ()Lapp/revanced/patches/shared/misc/settings/preference/InputType; + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt { + public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/songpal/badge/BadgeTabPatchKt { + public static final fun getBadgeTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/songpal/badge/RemoveNotificationBadgePatchKt { + public static final fun getRemoveNotificationBadgePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/soundcloud/ad/HideAdsPatchKt { + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/soundcloud/analytics/DisableTelemetryPatchKt { + public static final fun getDisableTelemetryPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatchKt { + public static final fun getEnableOfflineSync ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/spotify/layout/theme/CustomThemePatchKt { + public static final fun getCustomThemePatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/spotify/lite/ondemand/OnDemandPatchKt { + public static final fun getOnDemandPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt { + public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/stocard/layout/HideOffersTabPatchKt { + public static final fun getHideOffersTabPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatchKt { + public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatchKt { + public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatchKt { + public static final fun getDisableSubscriptionSuggestionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatchKt { + public static final fun getRemoveGooglePlayIntegrityCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatchKt { + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/feedfilter/FeedFilterPatchKt { + public static final fun getFeedFilterPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatchKt { + public static final fun getRememberClearDisplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/interaction/downloads/DownloadsPatchKt { + public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatchKt { + public static final fun getShowSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatchKt { + public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/misc/extension/ExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatchKt { + public static final fun getDisableLoginRequirementPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatchKt { + public static final fun getFixGoogleLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/misc/settings/SettingsPatchKt { + public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatchKt { + public static final fun getSpoofSimPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/trakt/UnlockProPatchKt { + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatchKt { + public static final fun getShowOnLockscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tudortmund/misc/extension/ExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/ads/DisableDashboardAdsKt { + public static final fun getDisableDashboardAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatchKt { + public static final fun getDisableAdFreeBannerPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatchKt { + public static final fun getDisableInAppUpdatePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatchKt { + public static final fun getDisableBlogNotificationReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatchKt { + public static final fun getDisableGiftMessagePopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatchKt { + public static final fun getOverrideFeatureFlagsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/fixes/FixOldVersionsPatchKt { + public static final fun getFixOldVersionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/live/DisableTumblrLivePatchKt { + public static final fun getDisableTumblrLivePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/misc/extension/ExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatchKt { + public static field addTimelineObjectTypeFilter Lkotlin/jvm/functions/Function1; + public static final fun getAddTimelineObjectTypeFilter ()Lkotlin/jvm/functions/Function1; + public static final fun getFilterTimelineObjectsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun setAddTimelineObjectTypeFilter (Lkotlin/jvm/functions/Function1;)V +} + +public final class app/revanced/patches/twitch/ad/audio/AudioAdsPatchKt { + public static final fun getAudioAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatchKt { + public static final fun getEmbeddedAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitch/ad/shared/util/AdPatchKt { + public static final fun adPatch (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function3;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitch/ad/shared/util/ReturnMethod { + public static final field Companion Lapp/revanced/patches/twitch/ad/shared/util/ReturnMethod$Companion; + public fun (CLjava/lang/String;)V + public final fun getReturnType ()C + public final fun getValue ()Ljava/lang/String; +} + +public final class app/revanced/patches/twitch/ad/shared/util/ReturnMethod$Companion { + public final fun getDefault ()Lapp/revanced/patches/twitch/ad/shared/util/ReturnMethod; +} + +public final class app/revanced/patches/twitch/ad/video/VideoAdsPatchKt { + public static final fun getVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatchKt { + public static final fun getShowDeletedMessagesPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatchKt { + public static final fun getAutoClaimChannelPointsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitch/debug/DebugModePatchKt { + public static final fun getDebugModePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitch/misc/extension/SharedExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitch/misc/settings/SettingsPatchKt { + public static final fun addSettingPreference (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V + public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatchKt { + public static final fun getUnlockDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/layout/viewcount/HideViewCountPatchKt { + public static final fun getHideViewCountPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatchKt { + public static final fun getDynamicColorPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/twitter/misc/extension/ExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/misc/hook/HideAdsHookPatchKt { + public static final fun getHideAdsHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/misc/hook/HideRecommendedUsersPatchKt { + public static final fun getHideRecommendedUsersPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/misc/hook/HookPatchKt { + public static final fun hookPatch (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/misc/hook/json/JsonHook { + public fun (Lapp/revanced/patcher/patch/BytecodePatchContext;Ljava/lang/String;)V +} + +public final class app/revanced/patches/twitter/misc/hook/json/JsonHookPatchHook : java/io/Closeable { + public fun (Lapp/revanced/patcher/Fingerprint;)V + public final fun addHook (Lapp/revanced/patches/twitter/misc/hook/json/JsonHook;)V + public fun close ()V +} + +public final class app/revanced/patches/twitter/misc/hook/json/JsonHookPatchKt { + public static final fun getJsonHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatchKt { + public static final fun getChangeLinkSharingDomainPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatchKt { + public static final fun getOpenLinksWithAppChooserPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatchKt { + public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/vsco/misc/pro/UnlockProPatchKt { + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatchKt { + public static final fun getFirebaseGetCertPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatchKt { + public static final fun getPromoCodeUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatchKt { + public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/ad/general/HideAdsPatchKt { + public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatchKt { + public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/ad/video/VideoAdsPatchKt { + public static final fun getVideoAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatchKt { + public static final fun getCopyVideoUrlPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatchKt { + public static final fun getRemoveViewerDiscretionDialogPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/interaction/downloads/DownloadsPatchKt { + public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatchKt { + public static final fun getDisablePreciseSeekingGesturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatchKt { + public static final fun getEnableSeekbarTappingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatchKt { + public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatchKt { + public static final fun getSwipeControlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatchKt { + public static final fun getAutoCaptionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPatchKt { + public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatchKt { + public static final fun getChangeHeaderPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatchKt { + public static final fun getHideButtonsPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatchKt { + public static final fun getNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatchKt { + public static final fun getHidePlayerOverlayButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatchKt { + public static final fun getBreakingNewsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatchKt { + public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatchKt { + public static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatchKt { + public static final fun getAlbumCardId ()J + public static final fun getBarContainerHeightId ()J + public static final fun getCrowdfundingBoxId ()J + public static final fun getExpandButtonDownId ()J + public static final fun getFabButtonId ()J + public static final fun getFilterBarHeightId ()J + public static final fun getHideLayoutComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRelatedChipCloudMarginId ()J + public static final fun getYouTubeLogo ()J +} + +public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatchKt { + public static final fun getHideInfoCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatchKt { + public static final fun getHidePlayerFlyoutMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatchKt { + public static final fun getDisableRollingNumberAnimationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatchKt { + public static final fun getHideSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatchKt { + public static final fun getHideShortsComponentsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatchKt { + public static final fun getDisableSuggestedVideoEndScreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPatchKt { + public static final fun getHideTimestampPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/miniplayer/FingerprintsKt { + public static final field ANIMATION_INTERPOLATION_FEATURE_KEY J + public static final field DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL J + public static final field DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL J + public static final field DROP_SHADOW_FEATURE_KEY J + public static final field INITIAL_SIZE_FEATURE_KEY_LITERAL J + public static final field MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL J + public static final field MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY J + public static final field ROUNDED_CORNERS_FEATURE_KEY J +} + +public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatchKt { + public static final fun getFloatyBarButtonTopMargin ()J + public static final fun getMiniplayerMaxSize ()J + public static final fun getMiniplayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getModernMiniplayerClose ()J + public static final fun getModernMiniplayerExpand ()J + public static final fun getModernMiniplayerForwardButton ()J + public static final fun getModernMiniplayerRewindButton ()J + public static final fun getPlayerOverlays ()J + public static final fun getScrimOverlay ()J + public static final fun getYtOutlinePictureInPictureWhite24 ()J + public static final fun getYtOutlineXWhite24 ()J +} + +public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatchKt { + public static final fun getPlayerPopupPanelsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatchKt { + public static final fun getPlayerControlsBackgroundPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatchKt { + public static final fun getCustomPlayerOverlayOpacityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatchKt { + public static final fun getReturnYouTubeDislikePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/returnyoutubedislike/Vote : java/lang/Enum { + public static final field DISLIKE Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote; + public static final field LIKE Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote; + public static final field REMOVE_LIKE Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public final fun getValue ()I + public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote; + public static fun values ()[Lapp/revanced/patches/youtube/layout/returnyoutubedislike/Vote; +} + +public final class app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatchKt { + public static final fun getWideSearchbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/seekbar/FingerprintsKt { + public static final field PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG J +} + +public final class app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatchKt { + public static final fun getRestoreOldSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatchKt { + public static final fun getSeekbarColorPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatchKt { + public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt { + public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatchKt { + public static final fun getSpoofAppVersionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatchKt { + public static final fun getChangeStartPagePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatchKt { + public static final fun getDisableResumingShortsOnStartupPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/startupshortsreset/FingerprintsKt { + public static final fun indexOfOptionalInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;)I +} + +public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatchKt { + public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String; + public static final fun getEnableTabletLayoutPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/theme/LithoColorHookPatchKt { + public static final fun getLithoColorHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getLithoColorOverrideHook ()Lkotlin/jvm/functions/Function2; +} + +public final class app/revanced/patches/youtube/layout/theme/ThemePatchKt { + public static final fun getThemePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatchKt { + public static final fun getAlternativeThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatchKt { + public static final fun getBypassImageRegionRestrictionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/announcements/AnnouncementsPatchKt { + public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatchKt { + public static final fun getAutoRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatchKt { + public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatchKt { + public static final fun getCheckEnvironmentPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt { + public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/ResourcePatch; +} + +public final class app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatchKt { + public static final fun getSpoofDeviceDimensionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatchKt { + public static final fun getCheckWatchHistoryDomainNameResolutionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/extension/SharedExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/fix/cairo/FingerprintsKt { + public static final field CAIRO_CONFIG_LITERAL_VALUE J +} + +public final class app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatchKt { + public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatchKt { + public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatchKt { + public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHookKt { + public static final fun addImageUrlErrorCallbackHook (Ljava/lang/String;)V + public static final fun addImageUrlHook (Ljava/lang/String;Z)V + public static synthetic fun addImageUrlHook$default (Ljava/lang/String;ZILjava/lang/Object;)V + public static final fun addImageUrlSuccessCallbackHook (Ljava/lang/String;)V + public static final fun getCronetImageUrlHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatchKt { + public static final fun getBypassURLRedirectsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatchKt { + public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatchKt { + public static final fun getAddLithoFilter ()Lkotlin/jvm/functions/Function1; + public static final fun getLithoFilterPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatchKt { + public static field hookNavigationButtonCreated Lkotlin/jvm/functions/Function1; + public static final fun getHookNavigationButtonCreated ()Lkotlin/jvm/functions/Function1; + public static final fun getNavigationBarHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun setHookNavigationButtonCreated (Lkotlin/jvm/functions/Function1;)V +} + +public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatchKt { + public static final fun getAddBottomControl ()Lkotlin/jvm/functions/Function1; + public static final fun getPlayerControlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getPlayerControlsResourcePatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun initializeBottomControl (Ljava/lang/String;)V + public static final fun injectVisibilityCheckCall (Ljava/lang/String;)V +} + +public final class app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatchKt { + public static final fun getPlayerTypeHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPatchKt { + public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/ResourcePatch; + public static final fun is_19_03_or_greater ()Z + public static final fun is_19_04_or_greater ()Z + public static final fun is_19_16_or_greater ()Z + public static final fun is_19_17_or_greater ()Z + public static final fun is_19_18_or_greater ()Z + public static final fun is_19_23_or_greater ()Z + public static final fun is_19_25_or_greater ()Z + public static final fun is_19_26_or_greater ()Z + public static final fun is_19_29_or_greater ()Z + public static final fun is_19_32_or_greater ()Z + public static final fun is_19_33_or_greater ()Z + public static final fun is_19_34_or_greater ()Z + public static final fun is_19_36_or_greater ()Z + public static final fun is_19_41_or_greater ()Z + public static final fun is_19_43_or_greater ()Z +} + +public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt { + public static final fun getRemoveTrackingQueryParameterPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatchKt { + public static final fun getAddRecyclerViewTreeHook ()Lkotlin/jvm/functions/Function1; + public static final fun getRecyclerViewTreeHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/misc/settings/PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen { + public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/PreferenceScreen; + public fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference;)V + public final fun getADS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getALTERNATIVE_THUMBNAILS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getFEED ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getGENERAL_LAYOUT ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getMISC ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getPLAYER ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getSEEKBAR ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getSHORTS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getSWIPE_CONTROLS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; + public final fun getVIDEO ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; +} + +public final class app/revanced/patches/youtube/misc/settings/SettingsPatchKt { + public static final fun addSettingPreference (Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V + public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun newIntent (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; +} + +public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatchKt { + public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/shared/FingerprintsKt { + public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint; +} + +public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt { + public static final fun getSetPlaybackSpeedClassFieldReference ()Ljava/lang/String; + public static final fun getSetPlaybackSpeedContainerClassFieldReference ()Ljava/lang/String; + public static final fun getSetPlaybackSpeedMethodReference ()Ljava/lang/String; + public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V + public static final fun videoTimeHook (Ljava/lang/String;Ljava/lang/String;)V +} + +public abstract class app/revanced/patches/youtube/video/playerresponse/Hook { + public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun toString ()Ljava/lang/String; +} + +public final class app/revanced/patches/youtube/video/playerresponse/Hook$ProtoBufferParameter : app/revanced/patches/youtube/video/playerresponse/Hook { + public fun (Ljava/lang/String;)V +} + +public final class app/revanced/patches/youtube/video/playerresponse/Hook$ProtoBufferParameterBeforeVideoId : app/revanced/patches/youtube/video/playerresponse/Hook { + public fun (Ljava/lang/String;)V +} + +public final class app/revanced/patches/youtube/video/playerresponse/Hook$VideoId : app/revanced/patches/youtube/video/playerresponse/Hook { + public fun (Ljava/lang/String;)V +} + +public final class app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatchKt { + public static final fun addPlayerResponseMethodHook (Lapp/revanced/patches/youtube/video/playerresponse/Hook;)V + public static final fun getPlayerResponseMethodHookPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/video/quality/RememberVideoQualityPatchKt { + public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt { + public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatchKt { + public static final fun getPlaybackSpeedButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatchKt { + public static final fun getSpeedUnavailableId ()J +} + +public final class app/revanced/patches/youtube/video/videoid/VideoIdPatchKt { + public static final fun getVideoIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun hookBackgroundPlayVideoId (Ljava/lang/String;)V + public static final fun hookPlayerResponseVideoId (Ljava/lang/String;)V + public static final fun hookVideoId (Ljava/lang/String;)V +} + +public final class app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatchKt { + public static final fun getRestoreOldVideoQualityMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatchKt { + public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/util/BytecodeUtilsKt { + public static final fun applyMatch (Lapp/revanced/patcher/Fingerprint;Lapp/revanced/patcher/patch/BytecodePatchContext;Lapp/revanced/patcher/Match;)Lapp/revanced/patcher/Match; + public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z + public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod; + public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List; + public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun forEachLiteralValueInstruction (Lapp/revanced/patcher/patch/BytecodePatchContext;JLkotlin/jvm/functions/Function2;)V + public static final fun getException (Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patcher/patch/PatchException; + public static final fun getMatchOrThrow (Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patcher/Match; + public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;)I + public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I + public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)I + public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I + public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I + public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I + public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;)I + public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I + public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)I + public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I + public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I + public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I + public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I + public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I + public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I + public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I + public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I + public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I + public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I + public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I + public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I + public static final fun indexOfFirstWideLiteralInstructionValueReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I + public static final fun indexOfFirstWideLiteralInstructionValueReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I + public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I + public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I + public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V + public static final fun literal (Lapp/revanced/patcher/FingerprintBuilder;Lkotlin/jvm/functions/Function0;)V + public static final fun returnEarly (Lapp/revanced/patcher/Fingerprint;Z)V + public static final fun returnEarly (Ljava/lang/Iterable;Z)V + public static final fun returnEarly (Ljava/util/List;Z)V + public static synthetic fun returnEarly$default (Lapp/revanced/patcher/Fingerprint;ZILjava/lang/Object;)V + public static synthetic fun returnEarly$default (Ljava/lang/Iterable;ZILjava/lang/Object;)V + public static synthetic fun returnEarly$default (Ljava/util/List;ZILjava/lang/Object;)V + public static final fun transformMethods (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V + public static final fun traverseClassHierarchy (Lapp/revanced/patcher/patch/BytecodePatchContext;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V +} + +public final class app/revanced/util/ResourceGroup { + public fun (Ljava/lang/String;[Ljava/lang/String;)V + public final fun getResourceDirectoryName ()Ljava/lang/String; + public final fun getResources ()[Ljava/lang/String; +} + +public final class app/revanced/util/ResourceUtilsKt { + public static final fun asSequence (Lorg/w3c/dom/NodeList;)Lkotlin/sequences/Sequence; + public static final fun childElementsSequence (Lorg/w3c/dom/Node;)Lkotlin/sequences/Sequence; + public static final fun copyResources (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;[Lapp/revanced/util/ResourceGroup;)V + public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/Document;Lapp/revanced/patcher/util/Document;)Ljava/lang/AutoCloseable; + public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V + public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V + public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V + public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V +} + +public final class app/revanced/util/resource/ArrayResource : app/revanced/util/resource/BaseResource { + public static final field Companion Lapp/revanced/util/resource/ArrayResource$Companion; + public fun (Ljava/lang/String;Ljava/util/List;)V + public final fun getItems ()Ljava/util/List; + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/util/resource/ArrayResource$Companion { + public final fun fromNode (Lorg/w3c/dom/Node;)Lapp/revanced/util/resource/ArrayResource; +} + +public abstract class app/revanced/util/resource/BaseResource { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getName ()Ljava/lang/String; + public final fun getTag ()Ljava/lang/String; + public fun hashCode ()I + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; + public static synthetic fun serialize$default (Lapp/revanced/util/resource/BaseResource;Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/util/resource/StringResource : app/revanced/util/resource/BaseResource { + public static final field Companion Lapp/revanced/util/resource/StringResource$Companion; + public fun (Ljava/lang/String;Ljava/lang/String;Z)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getFormatted ()Z + public final fun getValue ()Ljava/lang/String; + public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; +} + +public final class app/revanced/util/resource/StringResource$Companion { + public final fun fromNode (Lorg/w3c/dom/Node;)Lapp/revanced/util/resource/StringResource; +} + diff --git a/patches/build.gradle.kts b/patches/build.gradle.kts new file mode 100644 index 0000000000..dab82da233 --- /dev/null +++ b/patches/build.gradle.kts @@ -0,0 +1,35 @@ +group = "app.revanced" + +patches { + about { + name = "ReVanced Patches" + description = "Patches for ReVanced" + source = "git@github.com:revanced/revanced-patches.git" + author = "ReVanced" + contact = "contact@revanced.app" + website = "https://revanced.app" + license = "GNU General Public License v3.0" + } +} + +dependencies { + // Used by JsonGenerator. + implementation(libs.gson) + // Required due to smali, or build fails. Can be removed once smali is bumped. + implementation(libs.guava) + // Android API stubs defined here. + compileOnly(project(":patches:stub")) +} + +publishing { + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/revanced/revanced-patches") + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt similarity index 63% rename from src/main/kotlin/app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt index ccb3246942..35c7b24f72 100644 --- a/src/main/kotlin/app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/activity/exportall/ExportAllActivitiesPatch.kt @@ -1,27 +1,22 @@ -package app.revanced.patches.all.activity.exportall +package app.revanced.patches.all.misc.activity.exportall -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch -@Patch( +@Suppress("unused") +val exportAllActivitiesPatch = resourcePatch( name = "Export all activities", description = "Makes all app activities exportable.", use = false, -) -@Suppress("unused") -object ExportAllActivitiesPatch : ResourcePatch() { - private const val EXPORTED_FLAG = "android:exported" - - override fun execute(context: ResourceContext) { - context.xmlEditor["AndroidManifest.xml"].use { editor -> - val document = editor.file +) { + execute { context -> + val exportedFlag = "android:exported" + context.document["AndroidManifest.xml"].use { document -> val activities = document.getElementsByTagName("activity") for (i in 0..activities.length) { activities.item(i)?.apply { - val exportedAttribute = attributes.getNamedItem(EXPORTED_FLAG) + val exportedAttribute = attributes.getNamedItem(exportedFlag) if (exportedAttribute != null) { if (exportedAttribute.nodeValue != "true") { @@ -31,7 +26,7 @@ object ExportAllActivitiesPatch : ResourcePatch() { // Reason why the attribute is added in the case it does not exist: // https://github.com/revanced/revanced-patches/pull/1751/files#r1141481604 else { - document.createAttribute(EXPORTED_FLAG) + document.createAttribute(exportedFlag) .apply { value = "true" } .let(attributes::setNamedItem) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt new file mode 100644 index 0000000000..434b97e276 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatch.kt @@ -0,0 +1,92 @@ +package app.revanced.patches.all.misc.build + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference + +private const val BUILD_CLASS_DESCRIPTOR = "Landroid/os/Build;" + +class BuildInfo( + // The build information supported32BitAbis, supported64BitAbis, and supportedAbis are not supported for now, + // because initializing an array in transform is a bit more complex. + val board: String? = null, + val bootloader: String? = null, + val brand: String? = null, + val cpuAbi: String? = null, + val cpuAbi2: String? = null, + val device: String? = null, + val display: String? = null, + val fingerprint: String? = null, + val hardware: String? = null, + val host: String? = null, + val id: String? = null, + val manufacturer: String? = null, + val model: String? = null, + val odmSku: String? = null, + val product: String? = null, + val radio: String? = null, + val serial: String? = null, + val sku: String? = null, + val socManufacturer: String? = null, + val socModel: String? = null, + val tags: String? = null, + val time: Long? = null, + val type: String? = null, + val user: String? = null, +) + +fun baseSpoofBuildInfoPatch(buildInfoSupplier: () -> BuildInfo) = bytecodePatch { + // Lazy, so that patch options above are initialized before they are accessed. + val replacements by lazy { + with(buildInfoSupplier()) { + buildMap { + if (board != null) put("BOARD", "const-string" to "\"$board\"") + if (bootloader != null) put("BOOTLOADER", "const-string" to "\"$bootloader\"") + if (brand != null) put("BRAND", "const-string" to "\"$brand\"") + if (cpuAbi != null) put("CPU_ABI", "const-string" to "\"$cpuAbi\"") + if (cpuAbi2 != null) put("CPU_ABI2", "const-string" to "\"$cpuAbi2\"") + if (device != null) put("DEVICE", "const-string" to "\"$device\"") + if (display != null) put("DISPLAY", "const-string" to "\"$display\"") + if (fingerprint != null) put("FINGERPRINT", "const-string" to "\"$fingerprint\"") + if (hardware != null) put("HARDWARE", "const-string" to "\"$hardware\"") + if (host != null) put("HOST", "const-string" to "\"$host\"") + if (id != null) put("ID", "const-string" to "\"$id\"") + if (manufacturer != null) put("MANUFACTURER", "const-string" to "\"$manufacturer\"") + if (model != null) put("MODEL", "const-string" to "\"$model\"") + if (odmSku != null) put("ODM_SKU", "const-string" to "\"$odmSku\"") + if (product != null) put("PRODUCT", "const-string" to "\"$product\"") + if (radio != null) put("RADIO", "const-string" to "\"$radio\"") + if (serial != null) put("SERIAL", "const-string" to "\"$serial\"") + if (sku != null) put("SKU", "const-string" to "\"$sku\"") + if (socManufacturer != null) put("SOC_MANUFACTURER", "const-string" to "\"$socManufacturer\"") + if (socModel != null) put("SOC_MODEL", "const-string" to "\"$socModel\"") + if (tags != null) put("TAGS", "const-string" to "\"$tags\"") + if (time != null) put("TIME", "const-wide" to "$time") + if (type != null) put("TYPE", "const-string" to "\"$type\"") + if (user != null) put("USER", "const-string" to "\"$user\"") + } + } + } + + dependsOn( + transformInstructionsPatch( + filterMap = filterMap@{ _, _, instruction, instructionIndex -> + val reference = instruction.getReference() ?: return@filterMap null + if (reference.definingClass != BUILD_CLASS_DESCRIPTOR) return@filterMap null + + return@filterMap replacements[reference.name]?.let { instructionIndex to it } + }, + transform = { mutableMethod, entry -> + val (index, replacement) = entry + val (opcode, operand) = replacement + val register = mutableMethod.getInstruction(index).registerA + + mutableMethod.replaceInstruction(index, "$opcode v$register, $operand") + }, + ), + ) +} diff --git a/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt similarity index 57% rename from src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt index 994ddd4551..7cfd385c6b 100644 --- a/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/build/SpoofBuildInfoPatch.kt @@ -1,183 +1,214 @@ package app.revanced.patches.all.misc.build -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.longPatchOption -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.longOption +import app.revanced.patcher.patch.stringOption -@Patch( +@Suppress("unused") +val spoofBuildInfoPatch = bytecodePatch( name = "Spoof build info", description = "Spoof the information about the current build.", - use = false -) -@Suppress("unused") -class SpoofBuildInfoPatch : BaseSpoofBuildInfoPatch() { - override val board by stringPatchOption( + use = false, +) { + val board by stringOption( key = "board", default = null, title = "Board", - description = "The name of the underlying board, like \"goldfish\"." + description = "The name of the underlying board, like \"goldfish\".", ) - override val bootloader by stringPatchOption( + val bootloader by stringOption( key = "bootloader", default = null, title = "Bootloader", - description = "The system bootloader version number." + description = "The system bootloader version number.", ) - override val brand by stringPatchOption( + val brand by stringOption( key = "brand", default = null, title = "Brand", - description = "The consumer-visible brand with which the product/hardware will be associated, if any." + description = "The consumer-visible brand with which the product/hardware will be associated, if any.", ) - override val cpuAbi by stringPatchOption( + val cpuAbi by stringOption( key = "cpu-abi", default = null, title = "CPU ABI", - description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead." + description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead.", ) - override val cpuAbi2 by stringPatchOption( + val cpuAbi2 by stringOption( key = "cpu-abi-2", default = null, title = "CPU ABI 2", - description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead." + description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead.", ) - override val device by stringPatchOption( + val device by stringOption( key = "device", default = null, title = "Device", - description = "The name of the industrial design." + description = "The name of the industrial design.", ) - override val display by stringPatchOption( + val display by stringOption( key = "display", default = null, title = "Display", - description = "A build ID string meant for displaying to the user." + description = "A build ID string meant for displaying to the user.", ) - override val fingerprint by stringPatchOption( + val fingerprint by stringOption( key = "fingerprint", default = null, title = "Fingerprint", - description = "A string that uniquely identifies this build." + description = "A string that uniquely identifies this build.", ) - override val hardware by stringPatchOption( + val hardware by stringOption( key = "hardware", default = null, title = "Hardware", - description = "The name of the hardware (from the kernel command line or /proc)." + description = "The name of the hardware (from the kernel command line or /proc).", ) - override val host by stringPatchOption( + val host by stringOption( key = "host", default = null, title = "Host", - description = "The host." + description = "The host.", ) - override val id by stringPatchOption( + val id by stringOption( key = "id", default = null, title = "ID", - description = "Either a changelist number, or a label like \"M4-rc20\"." + description = "Either a changelist number, or a label like \"M4-rc20\".", ) - override val manufacturer by stringPatchOption( + val manufacturer by stringOption( key = "manufacturer", default = null, title = "Manufacturer", - description = "The manufacturer of the product/hardware." + description = "The manufacturer of the product/hardware.", ) - override val model by stringPatchOption( + val model by stringOption( key = "model", default = null, title = "Model", - description = "The end-user-visible name for the end product." + description = "The end-user-visible name for the end product.", ) - override val odmSku by stringPatchOption( + val odmSku by stringOption( key = "odm-sku", default = null, title = "ODM SKU", - description = "The SKU of the device as set by the original design manufacturer (ODM)." + description = "The SKU of the device as set by the original design manufacturer (ODM).", ) - override val product by stringPatchOption( + val product by stringOption( key = "product", default = null, title = "Product", - description = "The name of the overall product." + description = "The name of the overall product.", ) - override val radio by stringPatchOption( + val radio by stringOption( key = "radio", default = null, title = "Radio", description = "This field was deprecated in API level 15. " + - "The radio firmware version is frequently not available when this class is initialized, " + - "leading to a blank or \"unknown\" value for this string. Use getRadioVersion() instead." + "The radio firmware version is frequently not available when this class is initialized, " + + "leading to a blank or \"unknown\" value for this string. Use getRadioVersion() instead.", ) - override val serial by stringPatchOption( + val serial by stringOption( key = "serial", default = null, title = "Serial", - description = "This field was deprecated in API level 26. Use getSerial() instead." + description = "This field was deprecated in API level 26. Use getSerial() instead.", ) - override val sku by stringPatchOption( + val sku by stringOption( key = "sku", default = null, title = "SKU", - description = "The SKU of the hardware (from the kernel command line)." + description = "The SKU of the hardware (from the kernel command line).", ) - override val socManufacturer by stringPatchOption( + val socManufacturer by stringOption( key = "soc-manufacturer", default = null, title = "SOC Manufacturer", - description = "The manufacturer of the device's primary system-on-chip." + description = "The manufacturer of the device's primary system-on-chip.", ) - override val socModel by stringPatchOption( + val socModel by stringOption( key = "soc-model", default = null, title = "SOC Model", - description = "The model name of the device's primary system-on-chip." + description = "The model name of the device's primary system-on-chip.", ) - override val tags by stringPatchOption( + val tags by stringOption( key = "tags", default = null, title = "Tags", - description = "Comma-separated tags describing the build, like \"unsigned,debug\"." + description = "Comma-separated tags describing the build, like \"unsigned,debug\".", ) - override val time by longPatchOption( + val time by longOption( key = "time", default = null, title = "Time", - description = "The time at which the build was produced, given in milliseconds since the UNIX epoch." + description = "The time at which the build was produced, given in milliseconds since the UNIX epoch.", ) - override val type by stringPatchOption( + val type by stringOption( key = "type", default = null, title = "Type", - description = "The type of build, like \"user\" or \"eng\"." + description = "The type of build, like \"user\" or \"eng\".", ) - override val user by stringPatchOption( + val user by stringOption( key = "user", default = null, title = "User", - description = "The user." - ) -} \ No newline at end of file + description = "The user.", + ) + + dependsOn( + baseSpoofBuildInfoPatch { + BuildInfo( + board, + bootloader, + brand, + cpuAbi, + cpuAbi2, + device, + display, + fingerprint, + hardware, + host, + id, + manufacturer, + model, + odmSku, + product, + radio, + serial, + sku, + socManufacturer, + socModel, + tags, + time, + type, + user, + ) + }, + + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt new file mode 100644 index 0000000000..b17eea94dc --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/location/hide/HideMockLocationPatch.kt @@ -0,0 +1,50 @@ +@file:Suppress("unused") + +package app.revanced.patches.all.misc.connectivity.location.hide + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.transformation.IMethodCall +import app.revanced.patches.all.misc.transformation.fromMethodReference +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Suppress("unused") +val hideMockLocationPatch = bytecodePatch( + name = "Hide mock location", + description = "Prevents the app from knowing the device location is being mocked by a third party app.", + use = false, +) { + dependsOn( + transformInstructionsPatch( + filterMap = filter@{ _, _, instruction, instructionIndex -> + val reference = instruction.getReference() ?: return@filter null + if (fromMethodReference(reference) == null) return@filter null + + instruction to instructionIndex + }, + transform = { method, entry -> + val (instruction, index) = entry + instruction as FiveRegisterInstruction + + // Replace return value with a constant `false` boolean. + method.replaceInstruction( + index + 1, + "const/4 v${instruction.registerC}, 0x0", + ) + }, + ), + ) +} + +private enum class MethodCall( + override val definedClassName: String, + override val methodName: String, + override val methodParams: Array, + override val returnType: String, +) : IMethodCall { + IsMock("Landroid/location/Location;", "isMock", emptyArray(), "Z"), + IsFromMockProvider("Landroid/location/Location;", "isFromMockProvider", emptyArray(), "Z"), +} diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatch.kt new file mode 100644 index 0000000000..b50ccfc882 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/telephony/sim/spoof/SpoofSimCountryPatch.kt @@ -0,0 +1,105 @@ +package app.revanced.patches.all.misc.connectivity.telephony.sim.spoof + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.stringOption +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch +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.reference.MethodReference +import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil +import java.util.* + +@Suppress("unused") +val spoofSimCountryPatch = bytecodePatch( + name = "Spoof SIM country", + description = "Spoofs country information returned by the SIM card provider.", + use = false, +) { + val countries = Locale.getISOCountries().associateBy { Locale("", it).displayCountry } + + fun isoCountryPatchOption( + key: String, + title: String, + ) = stringOption( + key, + null, + countries, + title, + "ISO-3166-1 alpha-2 country code equivalent for the SIM provider's country code.", + false, + validator = { it: String? -> it == null || it.uppercase() in countries.values }, + ) + + val networkCountryIso by isoCountryPatchOption( + "networkCountryIso", + "Network ISO Country Code", + ) + + val simCountryIso by isoCountryPatchOption( + "simCountryIso", + "Sim ISO Country Code", + ) + + dependsOn( + transformInstructionsPatch( + filterMap = { _, _, instruction, instructionIndex -> + if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null + + val reference = instruction.reference as? MethodReference ?: return@transformInstructionsPatch null + + val match = MethodCall.entries.firstOrNull { search -> + MethodUtil.methodSignaturesMatch(reference, search.reference) + } ?: return@transformInstructionsPatch null + + val iso = when (match) { + MethodCall.NetworkCountryIso -> networkCountryIso + MethodCall.SimCountryIso -> simCountryIso + }?.lowercase() + + iso?.let { instructionIndex to it } + }, + transform = { mutableMethod, entry: Pair -> + transformMethodCall(entry, mutableMethod) + }, + ), + ) +} + +private fun transformMethodCall( + entry: Pair, + mutableMethod: MutableMethod, +) { + val (instructionIndex, methodCallValue) = entry + + val register = mutableMethod.getInstruction(instructionIndex + 1).registerA + + mutableMethod.replaceInstruction( + instructionIndex + 1, + "const-string v$register, \"$methodCallValue\"", + ) +} + +private enum class MethodCall( + val reference: MethodReference, +) { + NetworkCountryIso( + ImmutableMethodReference( + "Landroid/telephony/TelephonyManager;", + "getNetworkCountryIso", + emptyList(), + "Ljava/lang/String;", + ), + ), + SimCountryIso( + ImmutableMethodReference( + "Landroid/telephony/TelephonyManager;", + "getSimCountryIso", + emptyList(), + "Ljava/lang/String;", + ), + ), +} diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt new file mode 100644 index 0000000000..3e086f95c3 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWifiPatch.kt @@ -0,0 +1,224 @@ +package app.revanced.patches.all.misc.connectivity.wifi.spoof + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.transformation.IMethodCall +import app.revanced.patches.all.misc.transformation.filterMapInstruction35c +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch + +internal const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = "Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch" + +internal const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" + +@Suppress("unused") +val spoofWifiPatch = bytecodePatch( + name = "Spoof Wi-Fi connection", + description = "Spoofs an existing Wi-Fi connection.", + use = false, +) { + extendWith("extensions/all/connectivity/wifi/spoof/spoof-wifi.rve") + + dependsOn( + transformInstructionsPatch( + filterMap = { classDef, _, instruction, instructionIndex -> + filterMapInstruction35c( + EXTENSION_CLASS_DESCRIPTOR_PREFIX, + classDef, + instruction, + instructionIndex, + ) + }, + transform = { method, entry -> + val (methodType, instruction, instructionIndex) = entry + methodType.replaceInvokeVirtualWithExtension( + EXTENSION_CLASS_DESCRIPTOR, + method, + instruction, + instructionIndex, + ) + }, + ), + ) +} + +// Information about method calls we want to replace +@Suppress("unused") +private enum class MethodCall( + override val definedClassName: String, + override val methodName: String, + override val methodParams: Array, + override val returnType: String, +) : IMethodCall { + GetSystemService1( + "Landroid/content/Context;", + "getSystemService", + arrayOf("Ljava/lang/String;"), + "Ljava/lang/Object;", + ), + GetSystemService2( + "Landroid/content/Context;", + "getSystemService", + arrayOf("Ljava/lang/Class;"), + "Ljava/lang/Object;", + ), + GetActiveNetworkInfo( + "Landroid/net/ConnectivityManager;", + "getActiveNetworkInfo", + arrayOf(), + "Landroid/net/NetworkInfo;", + ), + IsConnected( + "Landroid/net/NetworkInfo;", + "isConnected", + arrayOf(), + "Z", + ), + IsConnectedOrConnecting( + "Landroid/net/NetworkInfo;", + "isConnectedOrConnecting", + arrayOf(), + "Z", + ), + IsAvailable( + "Landroid/net/NetworkInfo;", + "isAvailable", + arrayOf(), + "Z", + ), + GetState( + "Landroid/net/NetworkInfo;", + "getState", + arrayOf(), + "Landroid/net/NetworkInfo\$State;", + ), + GetDetailedState( + "Landroid/net/NetworkInfo;", + "getDetailedState", + arrayOf(), + "Landroid/net/NetworkInfo\$DetailedState;", + ), + IsActiveNetworkMetered( + "Landroid/net/ConnectivityManager;", + "isActiveNetworkMetered", + arrayOf(), + "Z", + ), + GetActiveNetwork( + "Landroid/net/ConnectivityManager;", + "getActiveNetwork", + arrayOf(), + "Landroid/net/Network;", + ), + GetNetworkInfo( + "Landroid/net/ConnectivityManager;", + "getNetworkInfo", + arrayOf("Landroid/net/Network;"), + "Landroid/net/NetworkInfo;", + ), + HasTransport( + "Landroid/net/NetworkCapabilities;", + "hasTransport", + arrayOf("I"), + "Z", + ), + HasCapability( + "Landroid/net/NetworkCapabilities;", + "hasCapability", + arrayOf("I"), + "Z", + ), + RegisterBestMatchingNetworkCallback( + "Landroid/net/ConnectivityManager;", + "registerBestMatchingNetworkCallback", + arrayOf( + "Landroid/net/NetworkRequest;", + "Landroid/net/ConnectivityManager\$NetworkCallback;", + "Landroid/os/Handler;", + ), + "V", + ), + RegisterDefaultNetworkCallback1( + "Landroid/net/ConnectivityManager;", + "registerDefaultNetworkCallback", + arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"), + "V", + ), + RegisterDefaultNetworkCallback2( + "Landroid/net/ConnectivityManager;", + "registerDefaultNetworkCallback", + arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"), + "V", + ), + RegisterNetworkCallback1( + "Landroid/net/ConnectivityManager;", + "registerNetworkCallback", + arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"), + "V", + ), + RegisterNetworkCallback2( + "Landroid/net/ConnectivityManager;", + "registerNetworkCallback", + arrayOf("Landroid/net/NetworkRequest;", "Landroid/app/PendingIntent;"), + "V", + ), + RegisterNetworkCallback3( + "Landroid/net/ConnectivityManager;", + "registerNetworkCallback", + arrayOf( + "Landroid/net/NetworkRequest;", + "Landroid/net/ConnectivityManager\$NetworkCallback;", + "Landroid/os/Handler;", + ), + "V", + ), + RequestNetwork1( + "Landroid/net/ConnectivityManager;", + "requestNetwork", + arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"), + "V", + ), + RequestNetwork2( + "Landroid/net/ConnectivityManager;", + "requestNetwork", + arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"), + "V", + ), + RequestNetwork3( + "Landroid/net/ConnectivityManager;", + "requestNetwork", + arrayOf( + "Landroid/net/NetworkRequest;", + "Landroid/net/ConnectivityManager\$NetworkCallback;", + "Landroid/os/Handler;", + ), + "V", + ), + RequestNetwork4( + "Landroid/net/ConnectivityManager;", + "requestNetwork", + arrayOf("Landroid/net/NetworkRequest;", "Landroid/app/PendingIntent;"), + "V", + ), + RequestNetwork5( + "Landroid/net/ConnectivityManager;", + "requestNetwork", + arrayOf( + "Landroid/net/NetworkRequest;", + "Landroid/net/ConnectivityManager\$NetworkCallback;", + "Landroid/os/Handler;", + "I", + ), + "V", + ), + UnregisterNetworkCallback1( + "Landroid/net/ConnectivityManager;", + "unregisterNetworkCallback", + arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"), + "V", + ), + UnregisterNetworkCallback2( + "Landroid/net/ConnectivityManager;", + "unregisterNetworkCallback", + arrayOf("Landroid/app/PendingIntent;"), + "V", + ), +} diff --git a/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt similarity index 58% rename from src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt index 4e3dedd329..97320c664d 100644 --- a/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatch.kt @@ -1,21 +1,16 @@ package app.revanced.patches.all.misc.debugging -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch import org.w3c.dom.Element -@Patch( +@Suppress("unused") +val enableAndroidDebuggingPatch = resourcePatch( name = "Enable Android debugging", description = "Enables Android debugging capabilities. This can slow down the app.", use = false, -) -@Suppress("unused") -object EnableAndroidDebuggingPatch : ResourcePatch() { - override fun execute(context: ResourceContext) { - context.xmlEditor["AndroidManifest.xml"].use { editor -> - val document = editor.file - +) { + execute { context -> + context.document["AndroidManifest.xml"].use { document -> val applicationNode = document .getElementsByTagName("application") diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatch.kt new file mode 100644 index 0000000000..7256d1edb3 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/directory/ChangeDataDirectoryLocationPatch.kt @@ -0,0 +1,58 @@ +package app.revanced.patches.all.misc.directory + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch +import app.revanced.util.getReference +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.immutable.reference.ImmutableMethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil + +@Suppress("unused") +val changeDataDirectoryLocationPatch = bytecodePatch( + name = "Change data directory location", + description = "Changes the data directory in the application from " + + "the app internal storage directory to /sdcard/android/data accessible by root-less devices." + + "Using this patch can cause unexpected issues with some apps.", + use = false, +) { + dependsOn( + transformInstructionsPatch( + filterMap = filter@{ _, _, instruction, instructionIndex -> + val reference = instruction.getReference() ?: return@filter null + + if (!MethodUtil.methodSignaturesMatch(reference, MethodCall.GetDir.reference)) { + return@filter null + } + + return@filter instructionIndex + }, + transform = { method, index -> + val getDirInstruction = method.getInstruction(index) + val contextRegister = getDirInstruction.registerC + val dataRegister = getDirInstruction.registerD + + method.replaceInstruction( + index, + "invoke-virtual { v$contextRegister, v$dataRegister }, " + + "Landroid/content/Context;->getExternalFilesDir(Ljava/lang/String;)Ljava/io/File;", + ) + }, + ), + ) +} + +private enum class MethodCall( + val reference: MethodReference, +) { + GetDir( + ImmutableMethodReference( + "Landroid/content/Context;", + "getDir", + listOf("Ljava/lang/String;", "I"), + "Ljava/io/File;", + ), + ), +} diff --git a/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt similarity index 54% rename from src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt index 7aaee12b47..a8bb80517e 100644 --- a/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt @@ -1,23 +1,22 @@ package app.revanced.patches.all.misc.hex import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.registerNewPatchOption -import app.revanced.patches.shared.misc.hex.BaseHexPatch +import app.revanced.patcher.patch.rawResourcePatch +import app.revanced.patcher.patch.stringsOption +import app.revanced.patches.shared.misc.hex.Replacement +import app.revanced.patches.shared.misc.hex.hexPatch import app.revanced.util.Utils.trimIndentMultiline -import app.revanced.patcher.patch.Patch as PatchClass -@Patch( +@Suppress("unused") +val hexPatch = rawResourcePatch( name = "Hex", description = "Replaces a hexadecimal patterns of bytes of files in an APK.", use = false, -) -@Suppress("unused") -class HexPatch : BaseHexPatch() { +) { // TODO: Instead of stringArrayOption, use a custom option type to work around // https://github.com/ReVanced/revanced-library/issues/48. // Replace the custom option type with a stringArrayOption once the issue is resolved. - private val replacementsOption by registerNewPatchOption, List>( + val replacements by stringsOption( key = "replacements", title = "Replacements", description = """ @@ -34,22 +33,24 @@ class HexPatch : BaseHexPatch() { 'aa 01 02 FF|00 00 00 00|path/to/file' """.trimIndentMultiline(), required = true, - valueType = "StringArray", ) - override val replacements - get() = replacementsOption!!.map { from -> - val (pattern, replacementPattern, targetFilePath) = try { - from.split("|", limit = 3) - } catch (e: Exception) { - throw PatchException( - "Invalid input: $from.\n" + - "Every pattern must be followed by a pipe ('|'), " + - "the replacement pattern, another pipe ('|'), " + - "and the path to the file to make the changes in relative to the APK root. ", - ) - } + dependsOn( + hexPatch { + replacements!!.map { from -> + val (pattern, replacementPattern, targetFilePath) = try { + from.split("|", limit = 3) + } catch (e: Exception) { + throw PatchException( + "Invalid input: $from.\n" + + "Every pattern must be followed by a pipe ('|'), " + + "the replacement pattern, another pipe ('|'), " + + "and the path to the file to make the changes in relative to the APK root. ", + ) + } - Replacement(pattern, replacementPattern, targetFilePath) - } + Replacement(pattern, replacementPattern, targetFilePath) + }.toSet() + }, + ) } diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt new file mode 100644 index 0000000000..abf7f002cc --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/interaction/gestures/PredictiveBackGesturePatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.all.misc.interaction.gestures + +import app.revanced.patcher.patch.resourcePatch + +@Suppress("unused") +val predictiveBackGesturePatch = resourcePatch( + name = "Predictive back gesture", + description = "Enables the predictive back gesture introduced on Android 13.", + use = false, +) { + execute { context -> + val flag = "android:enableOnBackInvokedCallback" + + context.document["AndroidManifest.xml"].use { document -> + with(document.getElementsByTagName("application").item(0)) { + if (attributes.getNamedItem(flag) != null) return@with + + document.createAttribute(flag) + .apply { value = "true" } + .let(attributes::setNamedItem) + } + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt similarity index 81% rename from src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt index dc300df4a3..26a3be2b06 100644 --- a/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch.kt @@ -1,28 +1,24 @@ package app.revanced.patches.all.misc.network -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.all.misc.debugging.EnableAndroidDebuggingPatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.debugging.enableAndroidDebuggingPatch import app.revanced.util.Utils.trimIndentMultiline import org.w3c.dom.Element import java.io.File -@Patch( +@Suppress("unused") +val overrideCertificatePinningPatch = resourcePatch( name = "Override certificate pinning", description = "Overrides certificate pinning, allowing to inspect traffic via a proxy.", - dependencies = [EnableAndroidDebuggingPatch::class], use = false, -) -@Suppress("unused") -object OverrideCertificatePinningPatch : ResourcePatch() { - override fun execute(context: ResourceContext) { +) { + dependsOn(enableAndroidDebuggingPatch) + + execute { context -> val resXmlDirectory = context.get("res/xml") // Add android:networkSecurityConfig="@xml/network_security_config" and the "networkSecurityConfig" attribute if it does not exist. - context.xmlEditor["AndroidManifest.xml"].use { editor -> - val document = editor.file - + context.document["AndroidManifest.xml"].use { document -> val applicationNode = document.getElementsByTagName("application").item(0) as Element if (!applicationNode.hasAttribute("networkSecurityConfig")) { @@ -58,4 +54,4 @@ object OverrideCertificatePinningPatch : ResourcePatch() { ) } } -} +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt new file mode 100644 index 0000000000..9565e83810 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/packagename/ChangePackageNamePatch.kt @@ -0,0 +1,62 @@ +package app.revanced.patches.all.misc.packagename + +import app.revanced.patcher.patch.Option +import app.revanced.patcher.patch.OptionException +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.patch.stringOption +import org.w3c.dom.Element + +lateinit var packageNameOption: Option + +/** + * Set the package name to use. + * If this is called multiple times, the first call will set the package name. + * + * @param fallbackPackageName The package name to use if the user has not already specified a package name. + * @return The package name that was set. + * @throws OptionException.ValueValidationException If the package name is invalid. + */ +fun setOrGetFallbackPackageName(fallbackPackageName: String): String { + val packageName = packageNameOption.value!! + + return if (packageName == packageNameOption.default) { + fallbackPackageName.also { packageNameOption.value = it } + } else { + packageName + } +} + +@Suppress("unused") +val changePackageNamePatch = resourcePatch( + name = "Change package name", + description = "Appends \".revanced\" to the package name by default. Changing the package name of the app can lead to unexpected issues.", + use = false, +) { + packageNameOption = stringOption( + key = "packageName", + default = "Default", + values = mapOf("Default" to "Default"), + title = "Package name", + description = "The name of the package to rename the app to.", + required = true, + ) { + it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) + } + + finalize { context -> + context.document["AndroidManifest.xml"].use { document -> + + val replacementPackageName = packageNameOption.value + + val manifest = document.getElementsByTagName("manifest").item(0) as Element + manifest.setAttribute( + "package", + if (replacementPackageName != packageNameOption.default) { + replacementPackageName + } else { + "${manifest.getAttribute("package")}.revanced" + }, + ) + } + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt new file mode 100644 index 0000000000..9c13b7034a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/resources/AddResourcesPatch.kt @@ -0,0 +1,397 @@ +package app.revanced.patches.all.misc.resources + +import app.revanced.patcher.patch.Patch +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.Document +import app.revanced.util.* +import app.revanced.util.resource.ArrayResource +import app.revanced.util.resource.BaseResource +import app.revanced.util.resource.StringResource +import org.w3c.dom.Node + +/** + * An identifier of an app. For example, `youtube`. + */ +private typealias AppId = String + +/** + * An identifier of a patch. For example, `ad.general.HideAdsPatch`. + */ +private typealias PatchId = String + +/** + * A set of resources of a patch. + */ +private typealias PatchResources = MutableSet + +/** + * A map of resources belonging to a patch. + */ +private typealias AppResources = MutableMap + +/** + * A map of resources belonging to an app. + */ +private typealias Resources = MutableMap + +/** + * The value of a resource. + * For example, `values` or `values-de`. + */ +private typealias Value = String + +/** + * A set of resources mapped by their value. + */ +private typealias MutableResources = MutableMap> + +/** + * A map of all resources associated by their value staged by [addResourcesPatch]. + */ +private lateinit var stagedResources: Map + +/** + * A map of all resources added to the app by [addResourcesPatch]. + */ +private val resources: MutableResources = mutableMapOf() + +/** + * Map of Crowdin locales to Android resource locale names. + * + * Fixme: Instead this patch should detect what locale regions are present in both patches and the target app, + * and automatically merge into the appropriate existing target file. + * So if a target app has only 'es', then the Crowdin file of 'es-rES' should merge into that. + * But if a target app has specific regions (such as 'pt-rBR'), + * then the Crowdin region specific file should merged into that. + */ +private val locales = mapOf( + "af-rZA" to "af", + "am-rET" to "am", + "ar-rSA" to "ar", + "as-rIN" to "as", + "az-rAZ" to "az", + "be-rBY" to "be", + "bg-rBG" to "bg", + "bn-rBD" to "bn", + "bs-rBA" to "bs", + "ca-rES" to "ca", + "cs-rCZ" to "cs", + "da-rDK" to "da", + "de-rDE" to "de", + "el-rGR" to "el", + "es-rES" to "es", + "et-rEE" to "et", + "eu-rES" to "eu", + "fa-rIR" to "fa", + "fi-rFI" to "fi", + "fil-rPH" to "tl", + "fr-rFR" to "fr", + "ga-rIE" to "ga", + "gl-rES" to "gl", + "gu-rIN" to "gu", + "hi-rIN" to "hi", + "hr-rHR" to "hr", + "hu-rHU" to "hu", + "hy-rAM" to "hy", + "in-rID" to "in", + "is-rIS" to "is", + "it-rIT" to "it", + "iw-rIL" to "iw", + "ja-rJP" to "ja", + "ka-rGE" to "ka", + "kk-rKZ" to "kk", + "km-rKH" to "km", + "kn-rIN" to "kn", + "ko-rKR" to "ko", + "ky-rKG" to "ky", + "lo-rLA" to "lo", + "lt-rLT" to "lt", + "lv-rLV" to "lv", + "mk-rMK" to "mk", + "ml-rIN" to "ml", + "mn-rMN" to "mn", + "mr-rIN" to "mr", + "ms-rMY" to "ms", + "my-rMM" to "my", + "nb-rNO" to "nb", + "ne-rIN" to "ne", + "nl-rNL" to "nl", + "or-rIN" to "or", + "pa-rIN" to "pa", + "pl-rPL" to "pl", + "pt-rBR" to "pt-rBR", + "pt-rPT" to "pt-rPT", + "ro-rRO" to "ro", + "ru-rRU" to "ru", + "si-rLK" to "si", + "sk-rSK" to "sk", + "sl-rSI" to "sl", + "sq-rAL" to "sq", + "sr-rCS" to "b+sr+Latn", + "sr-rSP" to "sr", + "sv-rSE" to "sv", + "sw-rKE" to "sw", + "ta-rIN" to "ta", + "te-rIN" to "te", + "th-rTH" to "th", + "tl-rPH" to "tl", + "tr-rTR" to "tr", + "uk-rUA" to "uk", + "ur-rIN" to "ur", + "uz-rUZ" to "uz", + "vi-rVN" to "vi", + "zh-rCN" to "zh-rCN", + "zh-rTW" to "zh-rTW", + "zu-rZA" to "zu", +) + +/** + * Adds a [BaseResource] to the map using [MutableMap.getOrPut]. + * + * @param value The value of the resource. For example, `values` or `values-de`. + * @param resource The resource to add. + * + * @return True if the resource was added, false if it already existed. + */ +fun addResource( + value: Value, + resource: BaseResource, +) = resources.getOrPut(value, ::mutableSetOf).add(resource) + +/** + * Adds a list of [BaseResource]s to the map using [MutableMap.getOrPut]. + * + * @param value The value of the resource. For example, `values` or `values-de`. + * @param resources The resources to add. + * + * @return True if the resources were added, false if they already existed. + */ +fun addResources( + value: Value, + resources: Iterable, +) = app.revanced.patches.all.misc.resources.resources.getOrPut(value, ::mutableSetOf).addAll(resources) + +/** + * Adds a [StringResource]. + * + * @param name The name of the string resource. + * @param value The value of the string resource. + * @param formatted Whether the string resource is formatted. Defaults to `true`. + * @param resourceValue The value of the resource. For example, `values` or `values-de`. + * + * @return True if the resource was added, false if it already existed. + */ +fun addResources( + name: String, + value: String, + formatted: Boolean = true, + resourceValue: Value = "values", +) = addResource(resourceValue, StringResource(name, value, formatted)) + +/** + * Adds an [ArrayResource]. + * + * @param name The name of the array resource. + * @param items The items of the array resource. + * + * @return True if the resource was added, false if it already existed. + */ +fun addResources( + name: String, + items: List, +) = addResource("values", ArrayResource(name, items)) + +/** + * Puts all resources of any [Value] staged in [stagedResources] for the [Patch] to [addResources]. + * + * @param patch The [Patch] of the patch to stage resources for. + * @param parseIds A function that parses a set of [PatchId] each mapped to an [AppId] from the given [Patch]. + * This is used to access the resources in [addResources] to stage them in [stagedResources]. + * The default implementation assumes that the [Patch] has a name and declares packages it is compatible with. + * + * @return True if any resources were added, false if none were added. + * + * @see addResourcesPatch + */ +fun addResources( + patch: Patch<*>, + parseIds: (Patch<*>) -> Map> = { + val patchId = patch.name ?: throw PatchException("Patch has no name") + val packages = patch.compatiblePackages ?: throw PatchException("Patch has no compatible packages") + + buildMap> { + packages.forEach { (appId, _) -> + getOrPut(appId) { mutableSetOf() }.add(patchId) + } + } + }, +): Boolean { + var result = false + + // Stage resources for the given patch to addResourcesPatch associated with their value. + parseIds(patch).forEach { (appId, patchIds) -> + patchIds.forEach { patchId -> + stagedResources.forEach { (value, resources) -> + resources[appId]?.get(patchId)?.let { patchResources -> + if (addResources(value, patchResources)) result = true + } + } + } + } + + return result +} + +/** + * Puts all resources for the given [appId] and [patchId] staged in [addResources] to [addResourcesPatch]. + * + * + * @return True if any resources were added, false if none were added. + * + * @see addResourcesPatch + */ +fun addResources( + appId: AppId, + patchId: String, +) = stagedResources.forEach { (value, resources) -> + resources[appId]?.get(patchId)?.let { patchResources -> + addResources(value, patchResources) + } +} + +val addResourcesPatch = resourcePatch( + description = "Add resources such as strings or arrays to the app.", +) { + /* + The strategy of this patch is to stage resources present in `/resources/addresources`. + These resources are organized by their respective value and patch. + + On addResourcesPatch#execute, all resources are staged in a temporary map. + After that, other patches that depend on addResourcesPatch can call + addResourcesPatch#invoke(Patch) to stage resources belonging to that patch + from the temporary map to addResourcesPatch. + + After all patches that depend on addResourcesPatch have been executed, + addResourcesPatch#finalize is finally called to add all staged resources to the app. + */ + execute { context -> + stagedResources = buildMap { + /** + * Puts resources under `/resources/addresources//.xml` into the map. + * + * @param sourceValue The source value of the resource. For example, `values` or `values-de-rDE`. + * @param destValue The destination value of the resource. For example, 'values' or 'values-de'. + * @param resourceKind The kind of the resource. For example, `strings` or `arrays`. + * @param transform A function that transforms the [Node]s from the XML files to a [BaseResource]. + */ + fun addResources( + sourceValue: Value, + destValue: Value = sourceValue, + resourceKind: String, + transform: (Node) -> BaseResource, + ) { + inputStreamFromBundledResource( + "addresources", + "$sourceValue/$resourceKind.xml", + )?.let { stream -> + // Add the resources associated with the given value to the map, + // instead of overwriting it. + // This covers the example case such as adding strings and arrays of the same value. + getOrPut(destValue, ::mutableMapOf).apply { + context.document[stream].use { document -> + document.getElementsByTagName("app").asSequence().forEach { app -> + val appId = app.attributes.getNamedItem("id").textContent + + getOrPut(appId, ::mutableMapOf).apply { + app.forEachChildElement { patch -> + val patchId = patch.attributes.getNamedItem("id").textContent + + getOrPut(patchId, ::mutableSetOf).apply { + patch.forEachChildElement { resourceNode -> + val resource = transform(resourceNode) + + add(resource) + } + } + } + } + } + } + } + } + } + + // Stage all resources to a temporary map. + // Staged resources consumed by addResourcesPatch#invoke(Patch) + // are later used in addResourcesPatch#finalize. + try { + val addStringResources = { source: Value, dest: Value -> + addResources(source, dest, "strings", StringResource::fromNode) + } + locales.forEach { (source, dest) -> addStringResources("values-$source", "values-$dest") } + addStringResources("values", "values") + + addResources("values", "values", "arrays", ArrayResource::fromNode) + } catch (e: Exception) { + throw PatchException("Failed to read resources", e) + } + } + } + + /** + * Adds all resources staged in [addResourcesPatch] to the app. + * This is called after all patches that depend on [addResourcesPatch] have been executed. + */ + finalize { context -> + operator fun MutableMap>.invoke( + value: Value, + resource: BaseResource, + ) { + // TODO: Fix open-closed principle violation by modifying BaseResource#serialize so that it accepts + // a Value and the map of documents. It will then get or put the document suitable for its resource type + // to serialize itself to it. + val resourceFileName = + when (resource) { + is StringResource -> "strings" + is ArrayResource -> "arrays" + else -> throw NotImplementedError("Unsupported resource type") + } + + getOrPut(resourceFileName) { + val targetFile = + context["res/$value/$resourceFileName.xml"].also { + it.parentFile?.mkdirs() + + if (it.createNewFile()) { + it.writeText("\n\n") + } + } + + context.document[targetFile.path].let { document -> + + // Save the target node here as well + // in order to avoid having to call document.getNode("resources") + // but also save the document so that it can be closed later. + document to document.getNode("resources") + } + }.let { (_, targetNode) -> + targetNode.addResource(resource) { invoke(value, it) } + } + } + + resources.forEach { (value, resources) -> + // A map of document associated by their kind (e.g. strings, arrays). + // Each document is accompanied by the target node to which resources are added. + // A map is used because Map#getOrPut allows opening a new document for the duration of a resource value. + // This is done to prevent having to open the files for every resource that is added. + // Instead, it is cached once and reused for resources of the same value. + // This map is later accessed to close all documents for the current resource value. + val documents = mutableMapOf>() + + resources.forEach { resource -> documents(value, resource) } + + documents.values.forEach { (document, _) -> document.close() } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt new file mode 100644 index 0000000000..3633541e74 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/screencapture/RemoveScreenCaptureRestrictionPatch.kt @@ -0,0 +1,83 @@ +package app.revanced.patches.all.misc.screencapture + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.transformation.IMethodCall +import app.revanced.patches.all.misc.transformation.filterMapInstruction35c +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch +import org.w3c.dom.Element + +private val removeCaptureRestrictionResourcePatch = resourcePatch( + description = "Sets allowAudioPlaybackCapture in manifest to true.", +) { + execute { context -> + context.document["AndroidManifest.xml"].use { document -> + // Get the application node. + val applicationNode = + document + .getElementsByTagName("application") + .item(0) as Element + + // Set allowAudioPlaybackCapture attribute to true. + applicationNode.setAttribute("android:allowAudioPlaybackCapture", "true") + } + } +} + +private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = + "Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch" +private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" + +@Suppress("unused") +val removeScreenCaptureRestrictionPatch = bytecodePatch( + name = "Remove screen capture restriction", + description = "Removes the restriction of capturing audio from apps that normally wouldn't allow it.", + use = false, +) { + extendWith("extensions/all/screencapture/remove-screen-capture-restriction.rve") + + dependsOn( + removeCaptureRestrictionResourcePatch, + transformInstructionsPatch( + filterMap = { classDef, _, instruction, instructionIndex -> + filterMapInstruction35c( + EXTENSION_CLASS_DESCRIPTOR_PREFIX, + classDef, + instruction, + instructionIndex, + ) + }, + transform = { mutableMethod, entry -> + val (methodType, instruction, instructionIndex) = entry + methodType.replaceInvokeVirtualWithExtension( + EXTENSION_CLASS_DESCRIPTOR, + mutableMethod, + instruction, + instructionIndex, + ) + }, + ), + ) +} + +// Information about method calls we want to replace +@Suppress("unused") +private enum class MethodCall( + override val definedClassName: String, + override val methodName: String, + override val methodParams: Array, + override val returnType: String, +) : IMethodCall { + SetAllowedCapturePolicySingle( + "Landroid/media/AudioAttributes\$Builder;", + "setAllowedCapturePolicy", + arrayOf("I"), + "Landroid/media/AudioAttributes\$Builder;", + ), + SetAllowedCapturePolicyGlobal( + "Landroid/media/AudioManager;", + "setAllowedCapturePolicy", + arrayOf("I"), + "V", + ), +} diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt new file mode 100644 index 0000000000..af689f5ed0 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatch.kt @@ -0,0 +1,97 @@ +package app.revanced.patches.all.misc.screenshot + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.transformation.IMethodCall +import app.revanced.patches.all.misc.transformation.filterMapInstruction35c +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c +import com.android.tools.smali.dexlib2.iface.reference.FieldReference + +private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = + "Lapp/revanced/extension/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch" +private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" + +@Suppress("unused") +val removeScreenshotRestrictionPatch = bytecodePatch( + name = "Remove screenshot restriction", + description = "Removes the restriction of taking screenshots in apps that normally wouldn't allow it.", + use = false, +) { + extendWith("extensions/all/screenshot/remove-screenshot-restriction.rve") + + dependsOn( + // Remove the restriction of taking screenshots. + transformInstructionsPatch( + filterMap = { classDef, _, instruction, instructionIndex -> + filterMapInstruction35c( + EXTENSION_CLASS_DESCRIPTOR_PREFIX, + classDef, + instruction, + instructionIndex, + ) + }, + transform = { mutableMethod, entry -> + val (methodType, instruction, instructionIndex) = entry + methodType.replaceInvokeVirtualWithExtension( + EXTENSION_CLASS_DESCRIPTOR, + mutableMethod, + instruction, + instructionIndex, + ) + }, + ), + // Modify layout params. + transformInstructionsPatch( + filterMap = { _, _, instruction, instructionIndex -> + if (instruction.opcode != Opcode.IPUT) { + return@transformInstructionsPatch null + } + + val instruction22c = instruction as Instruction22c + val fieldReference = instruction22c.reference as FieldReference + + if (fieldReference.definingClass != "Landroid/view/WindowManager\$LayoutParams;" || + fieldReference.name != "flags" || + fieldReference.type != "I" + ) { + return@transformInstructionsPatch null + } + + Pair(instruction22c, instructionIndex) + }, + transform = { mutableMethod, entry -> + val (instruction, index) = entry + val register = instruction.registerA + + mutableMethod.addInstructions( + index, + "and-int/lit16 v$register, v$register, -0x2001", + ) + }, + ), + ) +} + +// Information about method calls we want to replace +@Suppress("unused") +private enum class MethodCall( + override val definedClassName: String, + override val methodName: String, + override val methodParams: Array, + override val returnType: String, +) : IMethodCall { + AddFlags( + "Landroid/view/Window;", + "addFlags", + arrayOf("I"), + "V", + ), + SetFlags( + "Landroid/view/Window;", + "setFlags", + arrayOf("I", "I"), + "V", + ), +} diff --git a/src/main/kotlin/app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt similarity index 63% rename from src/main/kotlin/app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt index 6e7fbb9365..c2e39dc24a 100644 --- a/src/main/kotlin/app/revanced/patches/all/shortcut/sharetargets/RemoveShareTargetsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/shortcut/sharetargets/RemoveShareTargetsPatch.kt @@ -1,26 +1,23 @@ -package app.revanced.patches.all.shortcut.sharetargets +package app.revanced.patches.all.misc.shortcut.sharetargets -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch import app.revanced.util.asSequence import app.revanced.util.getNode import org.w3c.dom.Element import java.io.FileNotFoundException import java.util.logging.Logger -@Patch( +@Suppress("unused") +val removeShareTargetsPatch = resourcePatch( name = "Remove share targets", description = "Removes share targets like directly sharing to a frequent contact.", use = false, -) -@Suppress("unused") -object RemoveShareTargetsPatch : ResourcePatch() { - override fun execute(context: ResourceContext) { +) { + execute { context -> try { context.document["res/xml/shortcuts.xml"] } catch (_: FileNotFoundException) { - return Logger.getLogger(this::class.java.name).warning("The app has no shortcuts") + return@execute Logger.getLogger(this::class.java.name).warning("The app has no shortcuts") }.use { document -> val rootNode = document.getNode("shortcuts") as? Element ?: return@use diff --git a/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt similarity index 67% rename from src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt index 5736ff247e..0c39436d1c 100644 --- a/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/MethodCall.kt @@ -18,8 +18,8 @@ interface IMethodCall { /** * Replaces an invoke-virtual instruction with an invoke-static instruction, - * which calls a static replacement method in the respective integrations class. - * The method definition in the integrations class is expected to be the same, + * which calls a static replacement method in the respective extension class. + * The method definition in the extension class is expected to be the same, * except that the method should be static and take as a first parameter * an instance of the class, in which the original method was defined in. * @@ -27,56 +27,58 @@ interface IMethodCall { * * original method: Window#setFlags(int, int) * - * replacement method: Integrations#setFlags(Window, int, int) + * replacement method: Extension#setFlags(Window, int, int) */ - fun replaceInvokeVirtualWithIntegrations( + fun replaceInvokeVirtualWithExtension( definingClassDescriptor: String, method: MutableMethod, instruction: Instruction35c, - instructionIndex: Int + instructionIndex: Int, ) { val registers = arrayOf( instruction.registerC, instruction.registerD, instruction.registerE, instruction.registerF, - instruction.registerG + instruction.registerG, ) val argsNum = methodParams.size + 1 // + 1 for instance of definedClassName if (argsNum > registers.size) { // should never happen, but just to be sure (also for the future) a safety check throw RuntimeException( - "Not enough registers for ${definedClassName}#${methodName}: " + - "Required $argsNum registers, but only got ${registers.size}." + "Not enough registers for $definedClassName#$methodName: " + + "Required $argsNum registers, but only got ${registers.size}.", ) } - val args = registers.take(argsNum).joinToString(separator = ", ") { reg -> "v${reg}" } - val replacementMethodDefinition = - "${methodName}(${definedClassName}${methodParams.joinToString(separator = "")})${returnType}" + val args = registers.take(argsNum).joinToString(separator = ", ") { reg -> "v$reg" } + val replacementMethod = + "$methodName(${definedClassName}${methodParams.joinToString(separator = "")})$returnType" method.replaceInstruction( instructionIndex, - "invoke-static { $args }, ${definingClassDescriptor}->${replacementMethodDefinition}" + "invoke-static { $args }, $definingClassDescriptor->$replacementMethod", ) } } -inline fun fromMethodReference(methodReference: MethodReference) +inline fun fromMethodReference( + methodReference: MethodReference, +) where E : Enum, E : IMethodCall = enumValues().firstOrNull { search -> - search.definedClassName == methodReference.definingClass - && search.methodName == methodReference.name - && methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams) - && search.returnType == methodReference.returnType + search.definedClassName == methodReference.definingClass && + search.methodName == methodReference.name && + methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams) && + search.returnType == methodReference.returnType } inline fun filterMapInstruction35c( - integrationsClassDescriptorPrefix: String, + extensionClassDescriptorPrefix: String, classDef: ClassDef, instruction: Instruction, - instructionIndex: Int + instructionIndex: Int, ): Instruction35cInfo? where E : Enum, E : IMethodCall { - if (classDef.type.startsWith(integrationsClassDescriptorPrefix)) { + if (classDef.startsWith(extensionClassDescriptorPrefix)) { // avoid infinite recursion return null } diff --git a/src/main/kotlin/app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt similarity index 79% rename from src/main/kotlin/app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt index e651c4d637..48d9ffa58e 100644 --- a/src/main/kotlin/app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/transformation/TransformInstructionsPatch.kt @@ -1,24 +1,16 @@ package app.revanced.patches.all.misc.transformation -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.util.findMutableMethodOf import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.Instruction -@Suppress("MemberVisibilityCanBePrivate") -abstract class BaseTransformInstructionsPatch : BytecodePatch(emptySet()) { - abstract fun filterMap( - classDef: ClassDef, - method: Method, - instruction: Instruction, - instructionIndex: Int, - ): T? - - abstract fun transform(mutableMethod: MutableMethod, entry: T) - +fun transformInstructionsPatch( + filterMap: (ClassDef, Method, Instruction, Int) -> T?, + transform: (MutableMethod, T) -> Unit, +) = bytecodePatch { // Returns the patch indices as a Sequence, which will execute lazily. fun findPatchIndices(classDef: ClassDef, method: Method): Sequence? { return method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) -> @@ -26,7 +18,7 @@ abstract class BaseTransformInstructionsPatch : BytecodePatch(emptySet()) { } } - override fun execute(context: BytecodeContext) { + execute { context -> // Find all methods to patch buildMap { context.classes.forEach { classDef -> diff --git a/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt similarity index 69% rename from src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt rename to patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt index 5170332db7..b8bfb4432d 100644 --- a/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt @@ -1,22 +1,19 @@ package app.revanced.patches.all.misc.versioncode -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.intPatchOption +import app.revanced.patcher.patch.intOption +import app.revanced.patcher.patch.resourcePatch import app.revanced.util.getNode import org.w3c.dom.Element -@Patch( +@Suppress("unused") +val changeVersionCodePatch = resourcePatch( name = "Change version code", description = "Changes the version code of the app. By default the highest version code is set. " + "This allows older versions of an app to be installed " + "if their version code is set to the same or a higher value and can stop app stores to update the app.", use = false, -) -@Suppress("unused") -object ChangeVersionCodePatch : ResourcePatch() { - private val versionCode by intPatchOption( +) { + val versionCode by intOption( key = "versionCode", default = Int.MAX_VALUE, values = mapOf( @@ -26,11 +23,9 @@ object ChangeVersionCodePatch : ResourcePatch() { title = "Version code", description = "The version code to use", required = true, - ) { - it!! >= 1 - } + ) { versionCode -> versionCode!! >= 1 } - override fun execute(context: ResourceContext) { + execute { context -> context.document["AndroidManifest.xml"].use { document -> val manifestElement = document.getNode("manifest") as Element manifestElement.setAttribute("android:versionCode", "$versionCode") diff --git a/patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt new file mode 100644 index 0000000000..5a726b7914 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/amazon/DeepLinkingPatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.amazon + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val deepLinkingPatch = bytecodePatch( + name = "Always allow deep-linking", + description = "Open Amazon links, even if the app is not set to handle Amazon links.", +) { + compatibleWith("com.amazon.mShop.android.shopping") + + val deepLinkingMatch by deepLinkingFingerprint() + + execute { + deepLinkingMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt new file mode 100644 index 0000000000..1339149f43 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/amazon/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.amazon + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val deepLinkingFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE) + returns("Z") + parameters("L") + strings("https://www.", "android.intent.action.VIEW") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/Fingerprints.kt new file mode 100644 index 0000000000..bbe71e8c78 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/Fingerprints.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.backdrops.misc.pro + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val proUnlockFingerprint = fingerprint { + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + ) + custom { method, _ -> + method.name == "lambda\$existPurchase\$0" && + method.definingClass == "Lcom/backdrops/wallpapers/data/local/DatabaseHandlerIAB;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt b/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt new file mode 100644 index 0000000000..144f165de1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/backdrops/misc/pro/ProUnlockPatch.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.backdrops.misc.pro + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val proUnlockPatch = bytecodePatch( + name = "Pro unlock", +) { + compatibleWith("com.backdrops.wallpapers") + + val proUnlockMatch by proUnlockFingerprint() + + execute { + val registerIndex = proUnlockMatch.patternMatch!!.endIndex - 1 + + proUnlockMatch.mutableMethod.apply { + val register = getInstruction(registerIndex).registerA + addInstruction( + proUnlockMatch.patternMatch!!.endIndex, + "const/4 v$register, 0x1", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/Fingerprints.kt new file mode 100644 index 0000000000..96bbab5ccc --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/Fingerprints.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.bandcamp.limitations + +import app.revanced.patcher.fingerprint + +internal val handlePlaybackLimitsFingerprint = fingerprint { + strings("play limits processing track", "found play_count") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt new file mode 100644 index 0000000000..88302ef4c9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/bandcamp/limitations/RemovePlayLimitsPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.bandcamp.limitations + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val removePlayLimitsPatch = bytecodePatch( + name = "Remove play limits", + description = "Disables purchase nagging and playback limits of not purchased tracks.", +) { + compatibleWith("com.bandcamp.android") + + val handlePlaybackLimitsMatch by handlePlaybackLimitsFingerprint() + + execute { + handlePlaybackLimitsMatch.mutableMethod.addInstructions(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt new file mode 100644 index 0000000000..a0a79f6db9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/BypassRootChecksPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.cieid.restrictions.root + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val bypassRootChecksPatch = bytecodePatch( + name = "Bypass root checks", + description = "Removes the restriction to use the app with root permissions or on a custom ROM.", +) { + compatibleWith("it.ipzs.cieid") + + val checkRootMatch by checkRootFingerprint() + + execute { + checkRootMatch.mutableMethod.addInstruction(1, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt new file mode 100644 index 0000000000..387b0a0be8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/cieid/restrictions/root/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.cieid.restrictions.root + +import app.revanced.patcher.fingerprint + +internal val checkRootFingerprint = fingerprint { + custom { method, _ -> + method.name == "onResume" && method.definingClass == "Lit/ipzs/cieid/BaseActivity;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt new file mode 100644 index 0000000000..fa9eaf0099 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/DisableAdsPatch.kt @@ -0,0 +1,34 @@ +package app.revanced.patches.duolingo.ad + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +@Suppress("unused") +val disableAdsPatch = bytecodePatch( + "Disable ads", +) { + compatibleWith("com.duolingo") + + val initializeMonetizationDebugSettingsMatch by initializeMonetizationDebugSettingsFingerprint() + + execute { + // Couple approaches to remove ads exist: + // + // MonetizationDebugSettings has a boolean value for "disableAds". + // OnboardingState has a getter to check if the user has any "adFreeSessions". + // SharedPreferences has a debug boolean value with key "disable_ads", which maps to "DebugCategory.DISABLE_ADS". + // + // MonetizationDebugSettings seems to be the most general setting to work fine. + initializeMonetizationDebugSettingsMatch.mutableMethod.apply { + val insertIndex = initializeMonetizationDebugSettingsMatch.patternMatch!!.startIndex + val register = getInstruction(insertIndex).registerA + + addInstructions( + insertIndex, + "const/4 v$register, 0x1", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt new file mode 100644 index 0000000000..59b0644d98 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/ad/Fingerprints.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.duolingo.ad + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val initializeMonetizationDebugSettingsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters( + "Z", // disableAds + "Z", // useDebugBilling + "Z", // showManageSubscriptions + "Z", // alwaysShowSuperAds + "Lcom/duolingo/debug/FamilyQuestOverride;", + ) + opcodes(Opcode.IPUT_BOOLEAN) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt new file mode 100644 index 0000000000..609b03cf13 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.duolingo.debug + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +@Suppress("unused") +val enableDebugMenuPatch = bytecodePatch( + name = "Enable debug menu", + use = false, +) { + compatibleWith("com.duolingo"("5.158.4")) + + val initializeBuildConfigProviderMatch by initializeBuildConfigProviderFingerprint() + + execute { + initializeBuildConfigProviderMatch.mutableMethod.apply { + val insertIndex = initializeBuildConfigProviderMatch.patternMatch!!.startIndex + val register = getInstruction(insertIndex).registerA + + addInstructions( + insertIndex, + "const/4 v$register, 0x1", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt new file mode 100644 index 0000000000..543e40b434 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.duolingo.debug + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +/** + * The `BuildConfigProvider` class has two booleans: + * + * - `isChina`: (usually) compares "play" with "china"...except for builds in China + * - `isDebug`: compares "release" with "debug" <-- we want to force this to `true` + */ + +internal val initializeBuildConfigProviderFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + opcodes(Opcode.IPUT_BOOLEAN) + strings("debug", "release", "china") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt new file mode 100644 index 0000000000..7c732378f4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/Fingerprints.kt @@ -0,0 +1,47 @@ +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val baseModelMapperFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Lcom/facebook/graphql/modelutil/BaseModelWithTree;") + parameters("Ljava/lang/Class", "I", "I") + opcodes( + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_4, + Opcode.IF_EQ, + ) +} + +internal val getSponsoredDataModelTemplateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters() + opcodes( + Opcode.CONST, + Opcode.CONST, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.RETURN_OBJECT, + ) + custom { _, classDef -> + classDef.type == "Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;" + } +} + +internal val getStoryVisibilityFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Ljava/lang/String;") + opcodes( + Opcode.INSTANCE_OF, + Opcode.IF_NEZ, + Opcode.INSTANCE_OF, + Opcode.IF_NEZ, + Opcode.INSTANCE_OF, + Opcode.IF_NEZ, + Opcode.CONST, + ) + strings("This should not be called for base class object") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt new file mode 100644 index 0000000000..0c201f9fc8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch.kt @@ -0,0 +1,90 @@ +package app.revanced.patches.facebook.ads.mainfeed + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import baseModelMapperFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter +import getSponsoredDataModelTemplateFingerprint +import getStoryVisibilityFingerprint + +@Suppress("unused") +val hideSponsoredStoriesPatch = bytecodePatch( + name = "Hide 'Sponsored Stories'", +) { + compatibleWith("com.facebook.katana") + + val getStoryVisibilityMatch by getStoryVisibilityFingerprint() + val getSponsoredDataModelTemplateMatch by getSponsoredDataModelTemplateFingerprint() + val baseModelMapperMatch by baseModelMapperFingerprint() + + execute { + val sponsoredDataModelTemplateMethod = getSponsoredDataModelTemplateMatch.method + val baseModelMapperMethod = baseModelMapperMatch.method + val baseModelWithTreeType = baseModelMapperMethod.returnType + + val graphQlStoryClassDescriptor = "Lcom/facebook/graphql/model/GraphQLStory;" + + // The "SponsoredDataModelTemplate" methods has the ids in its body to extract sponsored data + // from GraphQL models, but targets the wrong derived type of "BaseModelWithTree". Since those ids + // could change in future version, we need to extract them and call the base implementation directly. + val getSponsoredDataHelperMethod = ImmutableMethod( + getStoryVisibilityMatch.classDef.type, + "getSponsoredData", + listOf(ImmutableMethodParameter(graphQlStoryClassDescriptor, null, null)), + baseModelWithTreeType, + AccessFlags.PRIVATE.value or AccessFlags.STATIC.value, + null, + null, + MutableMethodImplementation(4), + ).toMutable().apply { + // Extract the ids of the original method. These ids seem to correspond to model types for + // GraphQL data structure. They are then fed to a method of BaseModelWithTree that populate + // and cast the requested GraphQL subtype. The Ids are found in the two first "CONST" instructions. + val constInstructions = sponsoredDataModelTemplateMethod.implementation!!.instructions + .asSequence() + .filterIsInstance() + .take(2) + .toList() + + val storyTypeId = constInstructions[0].narrowLiteral + val sponsoredDataTypeId = constInstructions[1].narrowLiteral + + addInstructions( + """ + const-class v2, $baseModelWithTreeType + const v1, $storyTypeId + const v0, $sponsoredDataTypeId + invoke-virtual {p0, v2, v1, v0}, $baseModelMapperMethod + move-result-object v0 + check-cast v0, $baseModelWithTreeType + return-object v0 + """, + ) + } + + getStoryVisibilityMatch.mutableClass.methods.add(getSponsoredDataHelperMethod) + + // Check if the parameter type is GraphQLStory and if sponsoredDataModelGetter returns a non-null value. + // If so, hide the story by setting the visibility to StoryVisibility.GONE. + getStoryVisibilityMatch.mutableMethod.addInstructionsWithLabels( + getStoryVisibilityMatch.patternMatch!!.startIndex, + """ + instance-of v0, p0, $graphQlStoryClassDescriptor + if-eqz v0, :resume_normal + invoke-static {p0}, $getSponsoredDataHelperMethod + move-result-object v0 + if-eqz v0, :resume_normal + const-string v0, "GONE" + return-object v0 + :resume_normal + nop + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt new file mode 100644 index 0000000000..293d7ee82f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/Fingerprints.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.facebook.ads.story + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue + +internal val adsInsertionFingerprint = fieldFingerprint( + fieldValue = "AdBucketDataSourceUtil\$attemptAdsInsertion\$1", +) + +internal val fetchMoreAdsFingerprint = fieldFingerprint( + fieldValue = "AdBucketDataSourceUtil\$attemptFetchMoreAds\$1", +) + +internal fun fieldFingerprint(fieldValue: String) = fingerprint { + returns("V") + parameters() + custom { method, classDef -> + method.name == "run" && + classDef.fields.any any@{ field -> + if (field.name != "__redex_internal_original_name") return@any false + (field.initialValue as? StringEncodedValue)?.value == fieldValue + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt new file mode 100644 index 0000000000..7699b7be15 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/facebook/ads/story/HideStoryAdsPatch.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.facebook.ads.story + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideStoryAdsPatch = bytecodePatch( + name = "Hide story ads", + description = "Hides the ads in the Facebook app stories.", +) { + compatibleWith("com.facebook.katana") + + val fetchMoreAdsMatch by fetchMoreAdsFingerprint() + val adsInsertionMatch by adsInsertionFingerprint() + + execute { + setOf(fetchMoreAdsMatch, adsInsertionMatch).forEach { match -> + match.mutableMethod.replaceInstruction(0, "return-void") + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt new file mode 100644 index 0000000000..4bdeaf4a5b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/BootloaderDetectionPatch.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.finanzonline.detection.bootloader + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val bootloaderDetectionPatch = bytecodePatch( + name = "Remove bootloader detection", + description = "Removes the check for an unlocked bootloader.", +) { + compatibleWith("at.gv.bmf.bmf2go") + + val createKeyMatch by createKeyFingerprint() + val bootStateMatch by bootStateFingerprint() + + execute { + setOf(createKeyMatch, bootStateMatch).forEach { match -> + match.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/fingerprints/BootStateFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt similarity index 57% rename from src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/fingerprints/BootStateFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt index f76ab78aa9..4c8ee1c541 100644 --- a/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/fingerprints/BootStateFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/bootloader/Fingerprints.kt @@ -1,14 +1,14 @@ -package app.revanced.patches.finanzonline.detection.bootloader.fingerprints +package app.revanced.patches.finanzonline.detection.bootloader -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint // Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#isBootStateOk (3.0.1) -internal object BootStateFingerprint : MethodFingerprint( - "Z", - accessFlags = AccessFlags.PUBLIC.value, - opcodes = listOf( +internal val bootStateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("Z") + opcodes( Opcode.INVOKE_DIRECT, Opcode.MOVE_RESULT_OBJECT, Opcode.CONST_4, @@ -27,4 +27,11 @@ internal object BootStateFingerprint : MethodFingerprint( Opcode.MOVE, Opcode.RETURN ) -) +} + +// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#createKey (3.0.1) +internal val createKeyFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("Z") + strings("attestation", "SHA-256", "random", "EC", "AndroidKeyStore") +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/fingerprints/RootDetectionFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt similarity index 53% rename from src/main/kotlin/app/revanced/patches/finanzonline/detection/root/fingerprints/RootDetectionFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt index 50c7f76a8e..fec7f1249f 100644 --- a/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/fingerprints/RootDetectionFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/Fingerprints.kt @@ -1,16 +1,15 @@ -package app.revanced.patches.finanzonline.detection.root.fingerprints +package app.revanced.patches.finanzonline.detection.root -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 +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint // Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.RootDetection#isRooted (3.0.1) -internal object RootDetectionFingerprint : MethodFingerprint( - "L", - accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, - parameters = listOf("L"), - opcodes = listOf( +internal val rootDetectionFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("L") + parameters("L") + opcodes( Opcode.NEW_INSTANCE, Opcode.INVOKE_DIRECT, Opcode.INVOKE_VIRTUAL, @@ -19,4 +18,4 @@ internal object RootDetectionFingerprint : MethodFingerprint( Opcode.MOVE_RESULT_OBJECT, Opcode.RETURN_OBJECT ) -) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt new file mode 100644 index 0000000000..9144bf749c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/finanzonline/detection/root/RootDetectionPatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.finanzonline.detection.root + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val rootDetectionPatch = bytecodePatch( + name = "Remove root detection", + description = "Removes the check for root permissions.", +) { + compatibleWith("at.gv.bmf.bmf2go") + + val rootDetectionMatch by rootDetectionFingerprint() + + execute { + rootDetectionMatch.mutableMethod.addInstructions( + 0, + """ + sget-object v0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean; + return-object v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt new file mode 100644 index 0000000000..81cbb8c581 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/EnableCustomTabsPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.googlenews.customtabs + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val enableCustomTabsPatch = bytecodePatch( + name = "Enable CustomTabs", + description = "Enables CustomTabs to open articles in your default browser.", +) { + compatibleWith("com.google.android.apps.magazines") + + val launchCustomTabMatch by launchCustomTabFingerprint() + + execute { + launchCustomTabMatch.mutableMethod.apply { + val checkIndex = launchCustomTabMatch.patternMatch!!.endIndex + 1 + val register = getInstruction(checkIndex).registerA + + replaceInstruction(checkIndex, "const/4 v$register, 0x1") + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt new file mode 100644 index 0000000000..8880c010e9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/customtabs/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.googlenews.customtabs + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val launchCustomTabFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + opcodes( + Opcode.IPUT_OBJECT, + Opcode.CONST_4, + Opcode.IPUT, + Opcode.CONST_4, + Opcode.IPUT_BOOLEAN, + ) + custom { _, classDef -> classDef.endsWith("CustomTabsArticleLauncher;") } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/ExtensionPatch.kt new file mode 100644 index 0000000000..2818726622 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/ExtensionPatch.kt @@ -0,0 +1,6 @@ +package app.revanced.patches.googlenews.misc.extension + +import app.revanced.patches.googlenews.misc.extension.hooks.startActivityInitHook +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val extensionPatch = sharedExtensionPatch(startActivityInitHook) diff --git a/src/main/kotlin/app/revanced/patches/googlenews/misc/integrations/fingerprints/StartActivityInitFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt similarity index 71% rename from src/main/kotlin/app/revanced/patches/googlenews/misc/integrations/fingerprints/StartActivityInitFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt index b716367935..558b24eba4 100644 --- a/src/main/kotlin/app/revanced/patches/googlenews/misc/integrations/fingerprints/StartActivityInitFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/extension/hooks/StartActivityInitHook.kt @@ -1,26 +1,15 @@ -package app.revanced.patches.googlenews.misc.integrations.fingerprints +package app.revanced.patches.googlenews.misc.extension.hooks -import app.revanced.patches.googlenews.misc.integrations.fingerprints.StartActivityInitFingerprint.getApplicationContextIndex -import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint +import app.revanced.patches.shared.misc.extension.extensionHook import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference -internal object StartActivityInitFingerprint : IntegrationsFingerprint( - opcodes = listOf( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT, - Opcode.CONST_4, - Opcode.IF_EQZ, - Opcode.CONST, - Opcode.INVOKE_VIRTUAL, - Opcode.IPUT_OBJECT, - Opcode.IPUT_BOOLEAN, - Opcode.INVOKE_VIRTUAL, // Calls startActivity.getApplicationContext(). - Opcode.MOVE_RESULT_OBJECT, - ), +private var getApplicationContextIndex = -1 + +internal val startActivityInitHook = extensionHook( insertIndexResolver = { method -> getApplicationContextIndex = method.indexOfFirstInstructionOrThrow { getReference()?.name == "getApplicationContext" @@ -33,9 +22,20 @@ internal object StartActivityInitFingerprint : IntegrationsFingerprint( as OneRegisterInstruction moveResultInstruction.registerA }, - customFingerprint = { methodDef, classDef -> - methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;") - }, ) { - private var getApplicationContextIndex = -1 + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.IPUT_OBJECT, + Opcode.IPUT_BOOLEAN, + Opcode.INVOKE_VIRTUAL, // Calls startActivity.getApplicationContext(). + Opcode.MOVE_RESULT_OBJECT, + ) + custom { methodDef, classDef -> + methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;") + } } diff --git a/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt rename to patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Constants.kt diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt new file mode 100644 index 0000000000..6ddeb3e07f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.googlenews.misc.gms + +import app.revanced.patcher.fingerprint + +internal val magazinesActivityOnCreateFingerprint = fingerprint { + custom { methodDef, classDef -> + methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt new file mode 100644 index 0000000000..d03ce31e7d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlenews/misc/gms/GmsCoreSupportPatch.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.googlenews.misc.gms + +import app.revanced.patcher.patch.Option +import app.revanced.patches.googlenews.misc.extension.extensionPatch +import app.revanced.patches.googlenews.misc.gms.Constants.MAGAZINES_PACKAGE_NAME +import app.revanced.patches.googlenews.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME +import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch +import app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch + +@Suppress("unused") +val gmsCoreSupportPatch = gmsCoreSupportPatch( + fromPackageName = MAGAZINES_PACKAGE_NAME, + toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME, + mainActivityOnCreateFingerprint = magazinesActivityOnCreateFingerprint, + extensionPatch = extensionPatch, + gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch, +) { + // Remove version constraint, + // once https://github.com/ReVanced/revanced-patches/pull/3111#issuecomment-2240877277 is resolved. + compatibleWith(MAGAZINES_PACKAGE_NAME("5.108.0.644447823")) +} + +private fun gmsCoreSupportResourcePatch( + gmsCoreVendorGroupIdOption: Option, +) = gmsCoreSupportResourcePatch( + fromPackageName = MAGAZINES_PACKAGE_NAME, + toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME, + spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666", + gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption, +) diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/ExtensionPatch.kt new file mode 100644 index 0000000000..406d28fe47 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/ExtensionPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.googlephotos.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val extensionPatch = sharedExtensionPatch(homeActivityInitHook) diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/misc/integrations/fingerprints/HomeActivityInitFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Fingerprints.kt similarity index 69% rename from src/main/kotlin/app/revanced/patches/googlephotos/misc/integrations/fingerprints/HomeActivityInitFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Fingerprints.kt index 1fb7bf440f..84e6d58766 100644 --- a/src/main/kotlin/app/revanced/patches/googlephotos/misc/integrations/fingerprints/HomeActivityInitFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/extension/Fingerprints.kt @@ -1,22 +1,15 @@ -package app.revanced.patches.googlephotos.misc.integrations.fingerprints +package app.revanced.patches.googlephotos.misc.extension -import app.revanced.patches.googlephotos.misc.integrations.fingerprints.HomeActivityInitFingerprint.getApplicationContextIndex -import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint +import app.revanced.patches.shared.misc.extension.extensionHook import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference -internal object HomeActivityInitFingerprint : IntegrationsFingerprint( - opcodes = listOf( - Opcode.CONST_STRING, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IF_NEZ, - Opcode.INVOKE_VIRTUAL, // Calls getApplicationContext(). - Opcode.MOVE_RESULT_OBJECT, - ), +private var getApplicationContextIndex = -1 + +internal val homeActivityInitHook = extensionHook( insertIndexResolver = { method -> getApplicationContextIndex = method.indexOfFirstInstructionOrThrow { getReference()?.name == "getApplicationContext" @@ -29,9 +22,16 @@ internal object HomeActivityInitFingerprint : IntegrationsFingerprint( as OneRegisterInstruction moveResultInstruction.registerA }, - customFingerprint = { methodDef, classDef -> - methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;") - }, ) { - private var getApplicationContextIndex = -1 + opcodes( + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_NEZ, + Opcode.INVOKE_VIRTUAL, // Calls getApplicationContext(). + Opcode.MOVE_RESULT_OBJECT, + ) + custom { methodDef, classDef -> + methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;") + } } diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt new file mode 100644 index 0000000000..95f2a3dba7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/Fingerprints.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.googlephotos.misc.features + +import app.revanced.patcher.fingerprint + +internal val initializeFeaturesEnumFingerprint = fingerprint { + strings("com.google.android.apps.photos.NEXUS_PRELOAD") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatch.kt new file mode 100644 index 0000000000..fa7ff3c144 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofBuildInfoPatch.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.googlephotos.misc.features + +import app.revanced.patches.all.misc.build.BuildInfo +import app.revanced.patches.all.misc.build.baseSpoofBuildInfoPatch + +// Spoof build info to Google Pixel XL. +@Suppress("unused") +val spoofBuildInfoPatch = baseSpoofBuildInfoPatch { + BuildInfo( + brand = "google", + manufacturer = "Google", + device = "marlin", + product = "marlin", + model = "Pixel XL", + fingerprint = "google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys", + ) +} diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt similarity index 53% rename from src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt index 040012d458..f0aaad8905 100644 --- a/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/features/SpoofFeaturesPatch.kt @@ -1,30 +1,26 @@ -package app.revanced.patches.googlephotos.features +package app.revanced.patches.googlephotos.misc.features -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringArrayPatchOption -import app.revanced.patches.googlephotos.features.fingerprints.InitializeFeaturesEnumFingerprint +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.stringsOption import app.revanced.util.getReference -import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.StringReference -@Patch( +@Suppress("unused") +val spoofFeaturesPatch = bytecodePatch( name = "Spoof features", description = "Spoofs the device to enable Google Pixel exclusive features, including unlimited storage.", - dependencies = [SpoofBuildInfoPatch::class], - compatiblePackages = [CompatiblePackage("com.google.android.apps.photos")], -) -@Suppress("unused") -object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprint)) { - private val featuresToEnable by stringArrayPatchOption( - "featuresToEnable", - arrayOf( +) { + compatibleWith("com.google.android.apps.photos") + + dependsOn(spoofBuildInfoPatch) + + val featuresToEnable by stringsOption( + key = "featuresToEnable", + default = listOf( "com.google.android.apps.photos.NEXUS_PRELOAD", "com.google.android.apps.photos.nexus_preload", ), @@ -33,9 +29,9 @@ object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprin required = true, ) - private val featuresToDisable by stringArrayPatchOption( - "featuresToDisable", - arrayOf( + val featuresToDisable by stringsOption( + key = "featuresToDisable", + default = listOf( "com.google.android.apps.photos.PIXEL_2017_PRELOAD", "com.google.android.apps.photos.PIXEL_2018_PRELOAD", "com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD", @@ -58,29 +54,32 @@ object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprin required = true, ) - override fun execute(context: BytecodeContext) { + val initializeFeaturesEnumMatch by initializeFeaturesEnumFingerprint() + + execute { + @Suppress("NAME_SHADOWING") val featuresToEnable = featuresToEnable!!.toSet() + + @Suppress("NAME_SHADOWING") val featuresToDisable = featuresToDisable!!.toSet() - InitializeFeaturesEnumFingerprint.resultOrThrow().let { result -> - result.mutableMethod.apply { - getInstructions().filter { it.opcode == Opcode.CONST_STRING }.forEach { - val feature = it.getReference()!!.string + initializeFeaturesEnumMatch.mutableMethod.apply { + instructions.filter { it.opcode == Opcode.CONST_STRING }.forEach { + val feature = it.getReference()!!.string - val spoofedFeature = when (feature) { - in featuresToEnable -> "android.hardware.wifi" - in featuresToDisable -> "dummy" - else -> return@forEach - } + val spoofedFeature = when (feature) { + in featuresToEnable -> "android.hardware.wifi" + in featuresToDisable -> "dummy" + else -> return@forEach + } - val constStringIndex = it.location.index - val constStringRegister = (it as OneRegisterInstruction).registerA + val constStringIndex = it.location.index + val constStringRegister = (it as OneRegisterInstruction).registerA - replaceInstruction( - constStringIndex, - "const-string v$constStringRegister, \"$spoofedFeature\"", - ) - } + replaceInstruction( + constStringIndex, + "const-string v$constStringRegister, \"$spoofedFeature\"", + ) } } } diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt rename to patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Constants.kt diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt new file mode 100644 index 0000000000..f47c1a3d94 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.googlephotos.misc.gms + +import app.revanced.patcher.fingerprint + +internal val homeActivityOnCreateFingerprint = fingerprint { + custom { methodDef, classDef -> + methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt new file mode 100644 index 0000000000..3ed14a29dd --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.googlephotos.misc.gms + +import app.revanced.patcher.patch.Option +import app.revanced.patches.googlephotos.misc.extension.extensionPatch +import app.revanced.patches.googlephotos.misc.gms.Constants.PHOTOS_PACKAGE_NAME +import app.revanced.patches.googlephotos.misc.gms.Constants.REVANCED_PHOTOS_PACKAGE_NAME +import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch + +@Suppress("unused") +val gmsCoreSupportPatch = gmsCoreSupportPatch( + fromPackageName = PHOTOS_PACKAGE_NAME, + toPackageName = REVANCED_PHOTOS_PACKAGE_NAME, + mainActivityOnCreateFingerprint = homeActivityOnCreateFingerprint, + extensionPatch = extensionPatch, + gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch, +) { + compatibleWith(PHOTOS_PACKAGE_NAME) +} + +private fun gmsCoreSupportResourcePatch( + gmsCoreVendorGroupIdOption: Option, +) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch( + fromPackageName = PHOTOS_PACKAGE_NAME, + toPackageName = REVANCED_PHOTOS_PACKAGE_NAME, + spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600", + gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption, +) diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/Fingerprints.kt new file mode 100644 index 0000000000..54c20a7f8c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/Fingerprints.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.googlephotos.misc.preferences + +import app.revanced.patcher.fingerprint + +internal val backupPreferencesFingerprint = fingerprint { + returns("Lcom/google/android/apps/photos/backup/data/BackupPreferences;") + strings("backup_prefs_had_backup_only_when_charging_enabled") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt new file mode 100644 index 0000000000..91693d0475 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlephotos/misc/preferences/RestoreHiddenBackUpWhileChargingTogglePatch.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.googlephotos.misc.preferences + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch( + name = "Restore hidden 'Back up while charging' toggle", + description = "Restores a hidden toggle to only run backups when the device is charging.", +) { + compatibleWith("com.google.android.apps.photos") + + val backupPreferencesMatch by backupPreferencesFingerprint() + + execute { + // Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true. + val chargingPrefStringIndex = backupPreferencesMatch.stringMatches!!.first().index + backupPreferencesMatch.mutableMethod.apply { + // Get the register of move-result. + val resultRegister = getInstruction(chargingPrefStringIndex + 2).registerA + // Insert const after move-result to override register as true. + addInstruction(chargingPrefStringIndex + 3, "const/4 v$resultRegister, 0x1") + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/Fingerprints.kt new file mode 100644 index 0000000000..62e1e5f16b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.googlerecorder.restrictions + +import app.revanced.patcher.fingerprint + +internal val onApplicationCreateFingerprint = fingerprint { + strings("com.google.android.feature.PIXEL_2017_EXPERIENCE") + custom { method, classDef -> + if (method.name != "onCreate") return@custom false + + classDef.endsWith("RecorderApplication;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt new file mode 100644 index 0000000000..2c5a0aec15 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.googlerecorder.restrictions + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val removeDeviceRestrictionsPatch = bytecodePatch( + name = "Remove device restrictions", + description = "Removes restrictions from using the app on any device. Requires mounting patched app over original.", +) { + compatibleWith("com.google.android.apps.recorder") + + val onApplicationCreateMatch by onApplicationCreateFingerprint() + + execute { + val featureStringIndex = onApplicationCreateMatch.stringMatches!!.first().index + + onApplicationCreateMatch.mutableMethod.apply { + // Remove check for device restrictions. + removeInstructions(featureStringIndex - 2, 5) + + val featureAvailableRegister = getInstruction(featureStringIndex).registerA + + // Override "isPixelDevice()" to return true. + addInstruction(featureStringIndex, "const/4 v$featureAvailableRegister, 0x1") + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt new file mode 100644 index 0000000000..ae269f8178 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/DisableAdsPatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.hexeditor.ad + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableAdsPatch = bytecodePatch( + name = "Disable ads", +) { + compatibleWith("com.myprog.hexedit") + + val primaryAdsMatch by primaryAdsFingerprint() + + execute { + primaryAdsMatch.mutableMethod.replaceInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt new file mode 100644 index 0000000000..2fa2c5b851 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/hexeditor/ad/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.hexeditor.ad + +import app.revanced.patcher.fingerprint + +internal val primaryAdsFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("PreferencesHelper;") && method.name == "isAdsDisabled" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt new file mode 100644 index 0000000000..84db554572 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/Fingerprints.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.iconpackstudio.misc.pro + +import app.revanced.patcher.fingerprint + +internal val checkProFingerprint = fingerprint { + returns("Z") + custom { _, classDef -> classDef.endsWith("IPSPurchaseRepository;") } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt new file mode 100644 index 0000000000..daea6581c1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/iconpackstudio/misc/pro/UnlockProPatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.iconpackstudio.misc.pro + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockProPatch = bytecodePatch( + name = "Unlock pro", +) { + compatibleWith("ginlemon.iconpackstudio"("2.2 build 016")) + + val checkProMatch by checkProFingerprint() + + execute { + checkProMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/Fingerprints.kt new file mode 100644 index 0000000000..38f814f6d4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/Fingerprints.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.idaustria.detection.root + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val attestationSupportedCheckFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("V") + custom { method, classDef -> + method.name == "attestationSupportCheck" && + classDef.endsWith("/DeviceIntegrityCheck;") + } +} + +internal val bootloaderCheckFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("Z") + custom { method, classDef -> + method.name == "bootloaderCheck" && + classDef.endsWith("/DeviceIntegrityCheck;") + } +} + +internal val rootCheckFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("V") + custom { method, classDef -> + method.name == "rootCheck" && + classDef.endsWith("/DeviceIntegrityCheck;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt new file mode 100644 index 0000000000..9440e20d31 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.idaustria.detection.root + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly + +@Suppress("unused") +val rootDetectionPatch = bytecodePatch( + name = "Remove root detection", + description = "Removes the check for root permissions and unlocked bootloader.", +) { + compatibleWith("at.gv.oe.app") + + attestationSupportedCheckFingerprint() + bootloaderCheckFingerprint() + rootCheckFingerprint() + + execute { + setOf(attestationSupportedCheckFingerprint, bootloaderCheckFingerprint, rootCheckFingerprint).returnEarly(true) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt new file mode 100644 index 0000000000..61cd9605f1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/Fingerprints.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.idaustria.detection.signature + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val spoofSignatureFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE) + returns("L") + parameters("L") + custom { method, classDef -> + classDef.endsWith("/SL2Step1Task;") && method.name == "getPubKey" + } +} diff --git a/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt similarity index 65% rename from src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt rename to patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt index ae427b7441..12295f90c0 100644 --- a/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt @@ -1,23 +1,20 @@ package app.revanced.patches.idaustria.detection.signature -import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.idaustria.detection.signature.fingerprints.SpoofSignatureFingerprint +import app.revanced.patcher.patch.bytecodePatch -@Patch( +@Suppress("unused") +val spoofSignaturePatch = bytecodePatch( name = "Spoof signature", description = "Spoofs the signature of the app.", - compatiblePackages = [CompatiblePackage("at.gv.oe.app")] -) -@Suppress("unused") -object SpoofSignaturePatch : BytecodePatch( - setOf(SpoofSignatureFingerprint) ) { - private const val EXPECTED_SIGNATURE = - "OpenSSLRSAPublicKey{modulus=ac3e6fd6050aa7e0d6010ae58190404cd89a56935b44f6fee" + + compatibleWith("at.gv.oe.app") + + val spoofSignatureMatch by spoofSignatureFingerprint() + + execute { + val expectedSignature = + "OpenSSLRSAPublicKey{modulus=ac3e6fd6050aa7e0d6010ae58190404cd89a56935b44f6fee" + "067c149768320026e10b24799a1339e414605e448e3f264444a327b9ae292be2b62ad567dd1800dbed4a88f718a33dc6db6b" + "f5178aa41aa0efff8a3409f5ca95dbfccd92c7b4298966df806ea7a0204a00f0e745f6d9f13bdf24f3df715d7b62c1600906" + "15de1c8a956b9286764985a3b3c060963c435fb9481a5543aaf0671fc2dba6c5c2b17d1ef1d85137f14dc9bbdf3490288087" + @@ -29,13 +26,12 @@ object SpoofSignaturePatch : BytecodePatch( "77ef1be61b2c01ebdabddcbf53cc4b6fd9a3c445606ee77b3758162c80ad8f8137b3c6864e92db904807dcb2be9d7717dd21" + "bf42c121d620ddfb7914f7a95c713d9e1c1b7bdb4a03d618e40cf7e9e235c0b5687e03b7ab3,publicExponent=10001}" - override fun execute(context: BytecodeContext) { - SpoofSignatureFingerprint.result!!.mutableMethod.addInstructions( + spoofSignatureMatch.mutableMethod.addInstructions( 0, """ - const-string v0, "$EXPECTED_SIGNATURE" + const-string v0, "$expectedSignature" return-object v0 - """ + """, ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt new file mode 100644 index 0000000000..573bd72e35 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/Fingerprints.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.inshorts.ad + +import app.revanced.patcher.fingerprint + +internal val inshortsAdsFingerprint = fingerprint { + returns("V") + strings("GoogleAdLoader", "exception in requestAd") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt new file mode 100644 index 0000000000..3fff6a2701 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/inshorts/ad/InshortsAdsPatch.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.inshorts.ad + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideAdsPatch = bytecodePatch( + name = "Hide ads", +) { + compatibleWith("com.nis.app") + + val inshortsAdsMatch by inshortsAdsFingerprint() + + execute { + inshortsAdsMatch.mutableMethod.addInstruction( + 0, + """ + return-void + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/Fingerprints.kt new file mode 100644 index 0000000000..65d0527297 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/Fingerprints.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.instagram.ads + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val adInjectorFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE) + returns("Z") + parameters("L", "L") + strings( + "SponsoredContentController.insertItem", + "SponsoredContentController::Delivery", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt new file mode 100644 index 0000000000..1291c42127 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/ads/HideAdsPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.instagram.ads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideAdsPatch = bytecodePatch( + name = "Hide ads", + description = "Hides ads in stories, discover, profile, etc. " + + "An ad can still appear once when refreshing the home feed.", +) { + compatibleWith("com.instagram.android") + + val adInjectorMatch by adInjectorFingerprint() + + execute { + adInjectorMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x0 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt new file mode 100644 index 0000000000..30242b8d94 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.irplus.ad + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val irplusAdsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters("L", "Z") + strings("TAGGED") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt new file mode 100644 index 0000000000..a0a6e01704 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/irplus/ad/RemoveAdsPatch.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.irplus.ad + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val removeAdsPatch = bytecodePatch( + name = "Remove ads", +) { + compatibleWith("net.binarymode.android.irplus") + + val irplusAdsMatch by irplusAdsFingerprint() + + execute { + // By overwriting the second parameter of the method, + // the view which holds the advertisement is removed. + irplusAdsMatch.mutableMethod.addInstruction(0, "const/4 p2, 0x0") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt new file mode 100644 index 0000000000..79d41368e2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatch.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.lightroom.misc.login + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableMandatoryLoginPatch = bytecodePatch( + name = "Disable mandatory login", +) { + compatibleWith("com.adobe.lrmobile") + + val isLoggedInMatch by isLoggedInFingerprint() + + execute { + isLoggedInMatch.mutableMethod.apply { + val index = implementation!!.instructions.lastIndex - 1 + // Set isLoggedIn = true. + replaceInstruction(index, "const/4 v0, 0x1") + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt new file mode 100644 index 0000000000..6345541e14 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/login/Fingerprints.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.lightroom.misc.login + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val isLoggedInFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) + returns("Z") + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.IF_NE, + Opcode.CONST_4, + Opcode.GOTO + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt new file mode 100644 index 0000000000..5a00dc68c1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.lightroom.misc.premium + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val hasPurchasedFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("Z") + opcodes( + Opcode.SGET_OBJECT, + Opcode.CONST_4, + Opcode.CONST_4, + Opcode.CONST_4, + ) + strings("isPurchaseDoneRecently = true, access platform profile present? = ") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt new file mode 100644 index 0000000000..730c523d4f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.lightroom.misc.premium + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockPremiumPatch = bytecodePatch( + name = "Unlock premium", +) { + compatibleWith("com.adobe.lrmobile") + + val hasPurchasedMatch by hasPurchasedFingerprint() + + execute { + // Set hasPremium = true. + hasPurchasedMatch.mutableMethod.replaceInstruction(2, "const/4 v2, 0x1") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt new file mode 100644 index 0000000000..0dfbf5cdab --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/Fingerprints.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.memegenerator.detection.license + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val licenseValidationFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters("Landroid/content/Context;") + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_WIDE, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_WIDE, + Opcode.CMP_LONG, + Opcode.IF_GEZ, + Opcode.CONST_4, + Opcode.RETURN, + Opcode.CONST_4, + Opcode.RETURN + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt new file mode 100644 index 0000000000..6d49ef6a9b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.memegenerator.detection.license + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val licenseValidationPatch = bytecodePatch( + description = "Disables Firebase license validation.", +) { + val licenseValidationMatch by licenseValidationFingerprint() + + execute { + licenseValidationMatch.mutableMethod.replaceInstructions( + 0, + """ + const/4 p0, 0x1 + return p0 + """, + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/fingerprints/VerifySignatureFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt similarity index 54% rename from src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/fingerprints/VerifySignatureFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt index b77fd051fd..75912318ba 100644 --- a/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/fingerprints/VerifySignatureFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/Fingerprints.kt @@ -1,17 +1,14 @@ -package app.revanced.patches.memegenerator.detection.signature.fingerprints +package app.revanced.patches.memegenerator.detection.signature -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint -@FuzzyPatternScanMethod(2) -internal object VerifySignatureFingerprint : MethodFingerprint( - returnType = "Z", - accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, - parameters = listOf("Landroid/app/Activity;"), - opcodes = listOf( +internal val verifySignatureFingerprint = fingerprint(fuzzyPatternScanThreshold = 2) { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters("Landroid/app/Activity;") + opcodes( Opcode.SGET_OBJECT, Opcode.IF_NEZ, Opcode.INVOKE_STATIC, @@ -31,5 +28,5 @@ internal object VerifySignatureFingerprint : MethodFingerprint( Opcode.CONST_4, Opcode.RETURN, Opcode.ADD_INT_LIT8 - ), -) \ No newline at end of file + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt new file mode 100644 index 0000000000..6637b57e7d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/detection/signature/SignatureVerificationPatch.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.memegenerator.detection.signature + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val signatureVerificationPatch = bytecodePatch( + description = "Disables detection of incorrect signature.", +) { + val verifySignatureMatch by verifySignatureFingerprint() + + execute { + verifySignatureMatch.mutableMethod.replaceInstructions( + 0, + """ + const/4 p0, 0x1 + return p0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt new file mode 100644 index 0000000000..1f16bb10ec --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/Fingerprints.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.memegenerator.misc.pro + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val isFreeVersionFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Ljava/lang/Boolean;") + parameters("Landroid/content/Context;") + opcodes( + Opcode.SGET, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ + ) + strings("free") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt new file mode 100644 index 0000000000..fa475fbbcf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/memegenerator/misc/pro/UnlockProVersionPatch.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.memegenerator.misc.pro + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.memegenerator.detection.license.licenseValidationPatch +import app.revanced.patches.memegenerator.detection.signature.signatureVerificationPatch + +@Suppress("unused") +val unlockProVersionPatch = bytecodePatch( + name = "Unlock pro", +) { + dependsOn(signatureVerificationPatch, licenseValidationPatch) + + compatibleWith("com.zombodroid.MemeGenerator"("4.6364", "4.6370", "4.6375", "4.6377")) + + val isFreeVersionMatch by isFreeVersionFingerprint() + + execute { + isFreeVersionMatch.mutableMethod.replaceInstructions( + 0, + """ + sget-object p0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean; + return-object p0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt new file mode 100644 index 0000000000..a2fa6329c1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/Fingerprints.kt @@ -0,0 +1,36 @@ +package app.revanced.patches.messenger.inbox + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue + +internal val createInboxSubTabsFingerprint = fingerprint { + returns("V") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + opcodes( + Opcode.CONST_4, + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_VOID, + ) + custom { method, classDef -> + method.name == "run" && + classDef.fields.any any@{ field -> + if (field.name != "__redex_internal_original_name") return@any false + (field.initialValue as? StringEncodedValue)?.value == "InboxSubtabsItemSupplierImplementation\$onSubscribe\$1" + } + } +} + +internal val loadInboxAdsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("V") + strings( + "ads_load_begin", + "inbox_ads_fetch_start", + ) + custom { method, _ -> + method.definingClass == "Lcom/facebook/messaging/business/inboxads/plugins/inboxads/itemsupplier/" + + "InboxAdsItemSupplierImplementation;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt new file mode 100644 index 0000000000..ca13b71c88 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxAdsPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.messenger.inbox + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideInboxAdsPatch = bytecodePatch( + name = "Hide inbox ads", + description = "Hides ads in inbox.", +) { + compatibleWith("com.facebook.orca") + + val loadInboxAdsMatch by loadInboxAdsFingerprint() + + execute { + loadInboxAdsMatch.mutableMethod.replaceInstruction(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt new file mode 100644 index 0000000000..24d3f81920 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.messenger.inbox + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideInboxSubtabsPatch = bytecodePatch( + name = "Hide inbox subtabs", + description = "Hides Home and Channels tabs between active now tray and chats.", +) { + compatibleWith("com.facebook.orca") + + val createInboxSubTabsMatch by createInboxSubTabsFingerprint() + + execute { + createInboxSubTabsMatch.mutableMethod.replaceInstruction(2, "const/4 v0, 0x0") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt new file mode 100644 index 0000000000..3d4e223f63 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.messenger.inputfield + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val disableSwitchingEmojiToStickerPatch = bytecodePatch( + name = "Disable switching emoji to sticker", + description = "Disables switching from emoji to sticker search mode in message input field.", +) { + compatibleWith("com.facebook.orca"("439.0.0.29.119")) + + val switchMessangeInputEmojiButtonMatch by switchMessangeInputEmojiButtonFingerprint() + + execute { + val setStringIndex = switchMessangeInputEmojiButtonMatch.patternMatch!!.startIndex + 2 + + switchMessangeInputEmojiButtonMatch.mutableMethod.apply { + val targetRegister = getInstruction(setStringIndex).registerA + + replaceInstruction(setStringIndex, "const-string v$targetRegister, \"expression\"") + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt new file mode 100644 index 0000000000..651491ca61 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.messenger.inputfield + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableTypingIndicatorPatch = bytecodePatch( + name = "Disable typing indicator", + description = "Disables the indicator while typing a message.", +) { + compatibleWith("com.facebook.orca") + + val sendTypingIndicatorMatch by sendTypingIndicatorFingerprint() + + execute { + sendTypingIndicatorMatch.mutableMethod.replaceInstruction(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/Fingerprints.kt new file mode 100644 index 0000000000..0a1d181d2d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/messenger/inputfield/Fingerprints.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.messenger.inputfield + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.dexbacked.value.DexBackedStringEncodedValue + +internal val sendTypingIndicatorFingerprint = fingerprint { + returns("V") + parameters() + custom { method, classDef -> + method.name == "run" && + classDef.fields.any { + it.name == "__redex_internal_original_name" && + (it.initialValue as? DexBackedStringEncodedValue)?.value == "ConversationTypingContext\$sendActiveStateRunnable\$1" + } + } +} + +internal val switchMessangeInputEmojiButtonFingerprint = fingerprint { + returns("V") + parameters("L", "Z") + opcodes( + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.CONST_STRING, + Opcode.GOTO, + Opcode.CONST_STRING, + Opcode.GOTO, + ) + strings("afterTextChanged", "expression_search") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt new file mode 100644 index 0000000000..14105f762a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.mifitness.misc.locale + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val syncBluetoothLanguageFingerprint = fingerprint { + opcodes(Opcode.MOVE_RESULT_OBJECT) + custom { method, _ -> + method.name == "syncBluetoothLanguage" && + method.definingClass == "Lcom/xiaomi/fitness/devicesettings/DeviceSettingsSyncer;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt new file mode 100644 index 0000000000..bb038f26bc --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatch.kt @@ -0,0 +1,33 @@ +package app.revanced.patches.mifitness.misc.locale + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.mifitness.misc.login.fixLoginPatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val forceEnglishLocalePatch = bytecodePatch( + name = "Force English locale", + description = "Forces wearable devices to use the English locale.", +) { + compatibleWith("com.xiaomi.wearable") + + dependsOn(fixLoginPatch) + + val syncBluetoothLanguageMatch by syncBluetoothLanguageFingerprint() + + execute { + val resolvePhoneLocaleInstruction = syncBluetoothLanguageMatch.patternMatch!!.startIndex + + syncBluetoothLanguageMatch.mutableMethod.apply { + val registerIndexToUpdate = + getInstruction(resolvePhoneLocaleInstruction).registerA + + replaceInstruction( + resolvePhoneLocaleInstruction, + "const-string v$registerIndexToUpdate, \"en_gb\"", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt new file mode 100644 index 0000000000..e3eee24991 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.mifitness.misc.login + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val xiaomiAccountManagerConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR) + parameters("Landroid/content/Context;", "Z") + custom { method, _ -> + method.definingClass == "Lcom/xiaomi/passport/accountmanager/XiaomiAccountManager;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt new file mode 100644 index 0000000000..27c18eae06 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/mifitness/misc/login/FixLoginPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.mifitness.misc.login + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val fixLoginPatch = bytecodePatch( + name = "Fix login", + description = "Fixes login for uncertified Mi Fitness app", +) { + compatibleWith("com.xiaomi.wearable") + + val xiaomiAccountManagerConstructorMatch by xiaomiAccountManagerConstructorFingerprint() + + execute { + xiaomiAccountManagerConstructorMatch.mutableMethod.addInstruction(0, "const/16 p2, 0x0") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt new file mode 100644 index 0000000000..6ce0519ada --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/Fingerprints.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.music.ad.video + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val showVideoAdsParentFingerprint = fingerprint { + opcodes( + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IGET_OBJECT, + ) + strings("maybeRegenerateCpnAndStatsClient called unexpectedly, but no error.") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt new file mode 100644 index 0000000000..b39b2822ff --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/ad/video/HideVideoAds.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.music.ad.video + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideVideoAdsPatch = bytecodePatch( + name = "Hide music video ads", + description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.", +) { + compatibleWith("com.google.android.apps.youtube.music") + + val showVideoAdsParentMatch by showVideoAdsParentFingerprint() + + execute { context -> + val showVideoAdsMethod = context + .navigate(showVideoAdsParentMatch.mutableMethod) + .at(showVideoAdsParentMatch.patternMatch!!.startIndex + 1).mutable() + + showVideoAdsMethod.addInstruction(0, "const/4 p1, 0x0") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt new file mode 100644 index 0000000000..6d09557c82 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/EnableExclusiveAudioPlayback.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.music.audio.exclusiveaudio + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val enableExclusiveAudioPlaybackPatch = bytecodePatch( + name = "Enable exclusive audio playback", + description = "Enables the option to play audio without video.", +) { + compatibleWith("com.google.android.apps.youtube.music") + + val allowExclusiveAudioPlaybackMatch by allowExclusiveAudioPlaybackFingerprint() + + execute { + allowExclusiveAudioPlaybackMatch.mutableMethod.apply { + addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/fingerprints/AllowExclusiveAudioPlaybackFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt similarity index 55% rename from src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/fingerprints/AllowExclusiveAudioPlaybackFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt index c86f794272..02a978f0fc 100644 --- a/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/fingerprints/AllowExclusiveAudioPlaybackFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/audio/exclusiveaudio/Fingerprints.kt @@ -1,15 +1,14 @@ -package app.revanced.patches.music.audio.exclusiveaudio.fingerprints +package app.revanced.patches.music.audio.exclusiveaudio -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 +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint -internal object AllowExclusiveAudioPlaybackFingerprint: MethodFingerprint( - "Z", - AccessFlags.PUBLIC or AccessFlags.FINAL, - listOf(), - listOf( +internal val allowExclusiveAudioPlaybackFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT, Opcode.CHECK_CAST, @@ -22,4 +21,4 @@ internal object AllowExclusiveAudioPlaybackFingerprint: MethodFingerprint( Opcode.MOVE_RESULT, Opcode.RETURN ) -) \ No newline at end of file +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt new file mode 100644 index 0000000000..13820d29d8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/Fingerprints.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.music.interaction.permanentrepeat + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val repeatTrackFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "L") + opcodes( + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ + ) + strings("w_st") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt new file mode 100644 index 0000000000..31c3be9d6a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.music.interaction.permanentrepeat + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import org.stringtemplate.v4.compiler.Bytecode.instructions + +@Suppress("unused") +val permanentRepeatPatch = bytecodePatch( + name = "Permanent repeat", + description = "Permanently remember your repeating preference even if the playlist ends or another track is played.", + use = false, +) { + compatibleWith("com.google.android.apps.youtube.music") + + val repeatTrackMatch by repeatTrackFingerprint() + + execute { + val startIndex = repeatTrackMatch.patternMatch!!.endIndex + val repeatIndex = startIndex + 1 + + repeatTrackMatch.mutableMethod.apply { + addInstructionsWithLabels( + startIndex, + "goto :repeat", + ExternalLabel("repeat", instructions[repeatIndex]), + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/Fingerprints.kt new file mode 100644 index 0000000000..f2169744fe --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/Fingerprints.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.music.interaction.permanentshuffle + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val disableShuffleFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + opcodes( + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.SGET_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt new file mode 100644 index 0000000000..f4d7542164 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.music.interaction.permanentshuffle + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val permanentShufflePatch = bytecodePatch( + name = "Permanent shuffle", + description = "Permanently remember your shuffle preference " + + "even if the playlist ends or another track is played.", + use = false, +) { + compatibleWith( + "com.google.android.apps.youtube.music"( + "6.45.54", + "6.51.53", + "7.01.53", + "7.02.52", + "7.03.52", + ), + ) + + val disableShuffleMatch by disableShuffleFingerprint() + + execute { + disableShuffleMatch.mutableMethod.addInstruction(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt new file mode 100644 index 0000000000..d7f0f03deb --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.music.layout.compactheader + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val constructCategoryBarFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters("Landroid/content/Context;", "L", "L", "L") + opcodes( + Opcode.IPUT_OBJECT, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt new file mode 100644 index 0000000000..0dc7e02e4b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt @@ -0,0 +1,32 @@ +package app.revanced.patches.music.layout.compactheader + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val hideCategoryBar = bytecodePatch( + name = "Hide category bar", + description = "Hides the category bar at the top of the homepage.", + use = false, +) { + compatibleWith("com.google.android.apps.youtube.music") + + val constructCategoryBarMatch by constructCategoryBarFingerprint() + + execute { + constructCategoryBarMatch.mutableMethod.apply { + val insertIndex = constructCategoryBarMatch.patternMatch!!.startIndex + val register = getInstruction(insertIndex - 1).registerA + + addInstructions( + insertIndex, + """ + const/16 v2, 0x8 + invoke-virtual {v$register, v2}, Landroid/view/View;->setVisibility(I)V + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt new file mode 100644 index 0000000000..29558ab4be --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/Fingerprints.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.music.layout.premium + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val hideGetPremiumFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + opcodes( + Opcode.IF_NEZ, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + ) + strings("FEmusic_history", "FEmusic_offline") +} + +internal val membershipSettingsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/CharSequence;") + opcodes( + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt new file mode 100644 index 0000000000..08711e1fbd --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/premium/HideGetPremiumPatch.kt @@ -0,0 +1,48 @@ +package app.revanced.patches.music.layout.premium + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction + +@Suppress("unused") +val hideGetPremiumPatch = bytecodePatch( + name = "Hide 'Get Music Premium' label", + description = "Hides the \"Get Music Premium\" label from the account menu and settings.", +) { + compatibleWith("com.google.android.apps.youtube.music") + + val hideGetPremiumMatch by hideGetPremiumFingerprint() + val membershipSettingsMatch by membershipSettingsFingerprint() + + execute { + hideGetPremiumMatch.mutableMethod.apply { + val insertIndex = hideGetPremiumMatch.patternMatch!!.endIndex + + val setVisibilityInstruction = getInstruction(insertIndex) + val getPremiumViewRegister = setVisibilityInstruction.registerC + val visibilityRegister = setVisibilityInstruction.registerD + + replaceInstruction( + insertIndex, + "const/16 v$visibilityRegister, 0x8", + ) + + addInstruction( + insertIndex + 1, + "invoke-virtual {v$getPremiumViewRegister, v$visibilityRegister}, " + + "Landroid/view/View;->setVisibility(I)V", + ) + } + + membershipSettingsMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x0 + return-object v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/Fingerprints.kt new file mode 100644 index 0000000000..f3c96dc44a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/Fingerprints.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.music.layout.upgradebutton + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val pivotBarConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters("L", "Z") + opcodes( + Opcode.CHECK_CAST, + Opcode.INVOKE_INTERFACE, + Opcode.GOTO, + Opcode.IPUT_OBJECT, + Opcode.RETURN_VOID + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt new file mode 100644 index 0000000000..01167b6e4c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/RemoveUpgradeButtonPatch.kt @@ -0,0 +1,77 @@ +package app.revanced.patches.music.layout.upgradebutton + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.newLabel +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.toInstructions +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22t +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference + +@Suppress("unused") +val removeUpgradeButtonPatch = bytecodePatch( + name = "Remove upgrade button", + description = "Removes the upgrade tab from the pivot bar.", +) { + compatibleWith("com.google.android.apps.youtube.music") + + val pivotBarConstructorMatch by pivotBarConstructorFingerprint() + + execute { + pivotBarConstructorMatch.mutableMethod.apply { + val pivotBarElementFieldReference = + getInstruction(pivotBarConstructorMatch.patternMatch!!.endIndex - 1) + .getReference() + + val register = getInstruction(0).registerC + + // First compile all the needed instructions. + val instructionList = """ + invoke-interface { v0 }, Ljava/util/List;->size()I + move-result v1 + const/4 v2, 0x4 + invoke-interface {v0, v2}, Ljava/util/List;->remove(I)Ljava/lang/Object; + iput-object v0, v$register, $pivotBarElementFieldReference + """.toInstructions().toMutableList() + + val endIndex = pivotBarConstructorMatch.patternMatch!!.endIndex + + // Replace the instruction to retain the label at given index. + replaceInstruction( + endIndex - 1, + instructionList[0], // invoke-interface. + ) + // Do not forget to remove this instruction since we added it already. + instructionList.removeFirst() + + val exitInstruction = instructionList.last() // iput-object + addInstruction( + endIndex, + exitInstruction, + ) + // Do not forget to remove this instruction since we added it already. + instructionList.removeLast() + + // Add the necessary if statement to remove the upgrade tab button in case it exists. + instructionList.add( + 2, // if-le. + BuilderInstruction22t( + Opcode.IF_LE, + 1, + 2, + newLabel(endIndex), + ), + ) + + addInstructions( + endIndex, + instructionList, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt new file mode 100644 index 0000000000..b499cdbc75 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/BypassCertificateChecksPatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.music.misc.androidauto + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val bypassCertificateChecksPatch = bytecodePatch( + name = "Bypass certificate checks", + description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.", +) { + compatibleWith("com.google.android.apps.youtube.music") + + val checkCertificateMatch by checkCertificateFingerprint() + + execute { + checkCertificateMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt new file mode 100644 index 0000000000..957f055b68 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/androidauto/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.music.misc.androidauto + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val checkCertificateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters("Ljava/lang/String;") + strings("X509", "Failed to get certificate.") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt new file mode 100644 index 0000000000..7e791af125 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.music.misc.backgroundplayback + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val backgroundPlaybackPatch = bytecodePatch( + name = "Remove background playback restrictions", + description = "Removes restrictions on background playback, including playing kids videos in the background.", +) { + compatibleWith("com.google.android.apps.youtube.music") + + val kidsBackgroundPlaybackPolicyControllerMatch by kidsBackgroundPlaybackPolicyControllerFingerprint() + val backgroundPlaybackDisableMatch by backgroundPlaybackDisableFingerprint() + + execute { + kidsBackgroundPlaybackPolicyControllerMatch.mutableMethod.addInstruction( + 0, + "return-void", + ) + + backgroundPlaybackDisableMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt new file mode 100644 index 0000000000..e1cf24e1a2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/Fingerprints.kt @@ -0,0 +1,42 @@ +package app.revanced.patches.music.misc.backgroundplayback + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val backgroundPlaybackDisableFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters("L") + opcodes( + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.IGET, + Opcode.AND_INT_LIT16, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.IGET, + ) +} + +internal val kidsBackgroundPlaybackPolicyControllerFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("I", "L", "Z") + opcodes( + Opcode.IGET, + Opcode.IF_NE, + Opcode.IGET_OBJECT, + Opcode.IF_NE, + Opcode.IGET_BOOLEAN, + Opcode.IF_EQ, + Opcode.GOTO, + Opcode.RETURN_VOID, + Opcode.SGET_OBJECT, + Opcode.CONST_4, + Opcode.IF_NE, + Opcode.IPUT_BOOLEAN, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt new file mode 100644 index 0000000000..e6c1c69feb --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/SharedExtensionPatch.kt @@ -0,0 +1,6 @@ +package app.revanced.patches.music.misc.extension + +import app.revanced.patches.music.misc.extension.hooks.applicationInitHook +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch(applicationInitHook) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt new file mode 100644 index 0000000000..1e1a43f9aa --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.music.misc.extension.hooks + +import app.revanced.patches.shared.misc.extension.extensionHook + +internal val applicationInitHook = extensionHook { + returns("V") + parameters() + strings("activity") + custom { method, _ -> method.name == "onCreate" } +} diff --git a/src/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt rename to patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Constants.kt diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt new file mode 100644 index 0000000000..7131e143df --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.music.misc.gms + +import app.revanced.patcher.fingerprint + +internal val musicActivityOnCreateFingerprint = fingerprint { + returns("V") + parameters("Landroid/os/Bundle;") + custom { method, classDef -> + method.name == "onCreate" && classDef.endsWith("/MusicActivity;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt new file mode 100644 index 0000000000..e38ca6015c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/GmsCoreSupportPatch.kt @@ -0,0 +1,33 @@ +package app.revanced.patches.music.misc.gms + +import app.revanced.patcher.patch.Option +import app.revanced.patches.music.misc.extension.sharedExtensionPatch +import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME +import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME +import app.revanced.patches.shared.castContextFetchFingerprint +import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch +import app.revanced.patches.shared.primeMethodFingerprint + +@Suppress("unused") +val gmsCoreSupportPatch = gmsCoreSupportPatch( + fromPackageName = MUSIC_PACKAGE_NAME, + toPackageName = REVANCED_MUSIC_PACKAGE_NAME, + primeMethodFingerprint = primeMethodFingerprint, + earlyReturnFingerprints = setOf( + castContextFetchFingerprint, + ), + mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint, + extensionPatch = sharedExtensionPatch, + gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch, +) { + compatibleWith(MUSIC_PACKAGE_NAME) +} + +private fun gmsCoreSupportResourcePatch( + gmsCoreVendorGroupIdOption: Option, +) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch( + fromPackageName = MUSIC_PACKAGE_NAME, + toPackageName = REVANCED_MUSIC_PACKAGE_NAME, + gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption, + spoofedPackageSignature = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875", +) diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/fingerprints/AbstractClientIdFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/fingerprints/AbstractClientIdFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt diff --git a/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt new file mode 100644 index 0000000000..6bc4c21e5c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/Fingerprints.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.myexpenses.misc.pro + +import app.revanced.patcher.fingerprint + +internal val isEnabledFingerprint = fingerprint { + returns("Z") + strings("feature", "feature.licenceStatus") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt new file mode 100644 index 0000000000..6f3fd4a1b9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/myexpenses/misc/pro/UnlockProPatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.myexpenses.misc.pro + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockProPatch = bytecodePatch( + name = "Unlock pro", +) { + compatibleWith("org.totschnig.myexpenses") + + val isEnabledMatch by isEnabledFingerprint() + + execute { + isEnabledMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt new file mode 100644 index 0000000000..160e2db274 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/Fingerprints.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.myfitnesspal.ads + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val isPremiumUseCaseImplFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + custom { method, classDef -> + classDef.endsWith("IsPremiumUseCaseImpl;") && method.name == "doWork" + } +} + +internal val mainActivityNavigateToNativePremiumUpsellFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + custom { method, classDef -> + classDef.endsWith("MainActivity;") && method.name == "navigateToNativePremiumUpsell" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt new file mode 100644 index 0000000000..47be5c2fde --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/myfitnesspal/ads/HideAdsPatch.kt @@ -0,0 +1,33 @@ +package app.revanced.patches.myfitnesspal.ads + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideAdsPatch = bytecodePatch( + name = "Hide ads", + description = "Hides most of the ads across the app.", +) { + compatibleWith("com.myfitnesspal.android") + + val isPremiumUseCaseImplMatch by isPremiumUseCaseImplFingerprint() + val mainActivityNavigateToNativePremiumUpsellMatch by mainActivityNavigateToNativePremiumUpsellFingerprint() + + execute { + // Overwrite the premium status specifically for ads. + isPremiumUseCaseImplMatch.mutableMethod.replaceInstructions( + 0, + """ + sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean; + return-object v0 + """, + ) + + // Prevent the premium upsell dialog from showing when the main activity is launched. + // In other places that are premium-only the dialog will still show. + mainActivityNavigateToNativePremiumUpsellMatch.mutableMethod.replaceInstructions( + 0, + "return-void", + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt similarity index 62% rename from src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt index 70346ec2c2..c747f426e1 100644 --- a/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/netguard/broadcasts/removerestriction/RemoveBroadcastsRestrictionPatch.kt @@ -1,23 +1,17 @@ package app.revanced.patches.netguard.broadcasts.removerestriction -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch import org.w3c.dom.Element -@Patch( +@Suppress("unused") +val removeBroadcastsRestrictionPatch = resourcePatch( name = "Remove broadcasts restriction", description = "Enables starting/stopping NetGuard via broadcasts.", - compatiblePackages = [CompatiblePackage("eu.faircode.netguard")], - use = false, -) -@Suppress("unused") -object RemoveBroadcastsRestrictionPatch : ResourcePatch() { - override fun execute(context: ResourceContext) { - context.xmlEditor["AndroidManifest.xml"].use { editor -> - val document = editor.file +) { + compatibleWith("eu.faircode.netguard") + execute { context -> + context.document["AndroidManifest.xml"].use { document -> val applicationNode = document .getElementsByTagName("application") diff --git a/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/Fingerprints.kt new file mode 100644 index 0000000000..f4f5d48c02 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/Fingerprints.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.nfctoolsse.misc.pro + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val isLicenseRegisteredFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("Z") + strings("kLicenseCheck") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt new file mode 100644 index 0000000000..c44f911162 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.nfctoolsse.misc.pro + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockProPatch = bytecodePatch( + name = "Unlock pro", +) { + compatibleWith("com.wakdev.apps.nfctools.se") + + val isLicenseRegisteredMatch by isLicenseRegisteredFingerprint() + + execute { + isLicenseRegisteredMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/Fingerprints.kt new file mode 100644 index 0000000000..e2bffa451a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.nyx.misc.pro + +import app.revanced.patcher.fingerprint + +internal val checkProFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("BillingManager;") && method.name == "isProVersion" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt new file mode 100644 index 0000000000..a8a1f3a18f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/nyx/misc/pro/UnlockProPatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.nyx.misc.pro + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockProPatch = bytecodePatch( + name = "Unlock pro", +) { + compatibleWith("com.awedea.nyx") + + val checkProMatch by checkProFingerprint() + + execute { + checkProMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/Fingerprints.kt new file mode 100644 index 0000000000..69463c510d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.openinghours.misc.fix.crash + +import app.revanced.patcher.fingerprint + +internal val setPlaceFingerprint = fingerprint { + returns("V") + parameters("Lde/simon/openinghours/models/Place;") + custom { method, _ -> + method.name == "setPlace" && + method.definingClass == "Lde/simon/openinghours/views/custom/PlaceCard;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt new file mode 100644 index 0000000000..a24ba416fc --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt @@ -0,0 +1,107 @@ +package app.revanced.patches.openinghours.misc.fix.crash + +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.newLabel +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21t +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Suppress("unused") +val fixCrashPatch = bytecodePatch( + name = "Fix crash", +) { + compatibleWith("de.simon.openinghours"("1.0")) + + val setPlaceMatch by setPlaceFingerprint() + + execute { + val indexedInstructions = setPlaceMatch.mutableMethod.instructions.withIndex().toList() + + /** + * This function replaces all `checkNotNull` instructions in the integer interval + * from [startIndex] to [endIndex], both inclusive. In place of the `checkNotNull` + * instruction an if-null check is inserted. If the if-null check yields that + * the value is indeed null, we jump to a newly created label at `endIndex + 1`. + */ + fun avoidNullPointerException(startIndex: Int, endIndex: Int) { + val continueLabel = setPlaceMatch.mutableMethod.newLabel(endIndex + 1) + + for (index in startIndex..endIndex) { + val instruction = indexedInstructions[index].value + + if (!instruction.isCheckNotNullInstruction) { + continue + } + + val checkNotNullInstruction = instruction as FiveRegisterInstruction + val originalRegister = checkNotNullInstruction.registerC + + setPlaceMatch.mutableMethod.replaceInstruction( + index, + BuilderInstruction21t( + Opcode.IF_EQZ, + originalRegister, + continueLabel, + ), + ) + } + } + + val getOpeningHoursIndex = getIndicesOfInvoke( + indexedInstructions, + "Lde/simon/openinghours/models/Place;", + "getOpeningHours", + ) + + val setWeekDayTextIndex = getIndexOfInvoke( + indexedInstructions, + "Lde/simon/openinghours/views/custom/PlaceCard;", + "setWeekDayText", + ) + + val startCalculateStatusIndex = getIndexOfInvoke( + indexedInstructions, + "Lde/simon/openinghours/views/custom/PlaceCard;", + "startCalculateStatus", + ) + + // Replace the Intrinsics;->checkNotNull instructions with a null check + // and jump to our newly created label if it returns true. + // This avoids the NullPointerExceptions. + avoidNullPointerException(getOpeningHoursIndex[1], startCalculateStatusIndex) + avoidNullPointerException(getOpeningHoursIndex[0], setWeekDayTextIndex) + } +} + +private fun isInvokeInstruction(instruction: Instruction, className: String, methodName: String): Boolean { + val methodRef = instruction.getReference() ?: return false + return methodRef.definingClass == className && methodRef.name == methodName +} + +private fun getIndicesOfInvoke( + instructions: List>, + className: String, + methodName: String, +): List = instructions.mapNotNull { (index, instruction) -> + if (isInvokeInstruction(instruction, className, methodName)) { + index + } else { + null + } +} + +private fun getIndexOfInvoke( + instructions: List>, + className: String, + methodName: String, +): Int = instructions.first { (_, instruction) -> + isInvokeInstruction(instruction, className, methodName) +}.index + +private val Instruction.isCheckNotNullInstruction + get() = isInvokeInstruction(this, "Lkotlin/jvm/internal/Intrinsics;", "checkNotNull") diff --git a/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt similarity index 55% rename from src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt index fe01d7b59a..90c0bbb919 100644 --- a/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/Fingerprints.kt @@ -1,11 +1,12 @@ -package app.revanced.patches.photomath.detection.deviceid.fingerprints +package app.revanced.patches.photomath.detection.deviceid -import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.fingerprint -internal object GetDeviceIdFingerprint : MethodFingerprint( - returnType = "Ljava/lang/String;", - opcodes = listOf( +internal val getDeviceIdFingerprint = fingerprint { + returns("Ljava/lang/String;") + parameters() + opcodes( Opcode.SGET_OBJECT, Opcode.IGET_OBJECT, Opcode.INVOKE_STATIC, @@ -16,6 +17,5 @@ internal object GetDeviceIdFingerprint : MethodFingerprint( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT, Opcode.INVOKE_VIRTUAL, - ), - parameters = listOf() -) \ No newline at end of file + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt new file mode 100644 index 0000000000..152ee8edd4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.photomath.detection.deviceid + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch +import kotlin.random.Random + +@Suppress("unused") +val getDeviceIdPatch = bytecodePatch( + name = "Spoof device ID", + description = "Spoofs device ID to mitigate manual bans by developers.", +) { + dependsOn(signatureDetectionPatch) + + compatibleWith("com.microblink.photomath"("8.37.0")) + + val getDeviceIdMatch by getDeviceIdFingerprint() + + execute { + getDeviceIdMatch.mutableMethod.replaceInstructions( + 0, + """ + const-string v0, "${Random.nextLong().toString(16)}" + return-object v0 + """, + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt similarity index 52% rename from src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt index cd784ab20d..5d7a20783f 100644 --- a/src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/Fingerprints.kt @@ -1,13 +1,10 @@ -package app.revanced.patches.photomath.detection.signature.fingerprints +package app.revanced.patches.photomath.detection.signature -import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.fingerprint -internal object CheckSignatureFingerprint : MethodFingerprint( - strings = listOf( - "signatures", - ), - opcodes = listOf( +internal val checkSignatureFingerprint = fingerprint { + opcodes( Opcode.CONST_STRING, Opcode.INVOKE_STATIC, Opcode.INVOKE_STATIC, @@ -17,4 +14,5 @@ internal object CheckSignatureFingerprint : MethodFingerprint( Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT, ) -) + strings("signatures") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt new file mode 100644 index 0000000000..5a2a7c5da0 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/detection/signature/SignatureDetectionPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.photomath.detection.signature + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val signatureDetectionPatch = bytecodePatch( + description = "Disables detection of incorrect signature.", +) { + val checkSignatureMatch by checkSignatureFingerprint() + + execute { + val signatureCheckInstruction = checkSignatureMatch.mutableMethod.getInstruction( + checkSignatureMatch.patternMatch!!.endIndex, + ) + val checkRegister = (signatureCheckInstruction as OneRegisterInstruction).registerA + + checkSignatureMatch.mutableMethod.replaceInstruction( + signatureCheckInstruction.location.index, + "const/4 v$checkRegister, 0x1", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt new file mode 100644 index 0000000000..301f2f9a52 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/Fingerprints.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.photomath.misc.annoyances + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val hideUpdatePopupFingerprint = fingerprint { + accessFlags(AccessFlags.FINAL, AccessFlags.PUBLIC) + returns("V") + opcodes( + Opcode.CONST_HIGH16, + Opcode.INVOKE_VIRTUAL, // ViewPropertyAnimator.alpha(1.0f) + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_WIDE_16, + Opcode.INVOKE_VIRTUAL, // ViewPropertyAnimator.setDuration(1000L) + ) + custom { method, _ -> + // The popup is shown only in the main activity + method.definingClass == "Lcom/microblink/photomath/main/activity/MainActivity;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt new file mode 100644 index 0000000000..00d0caa183 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.photomath.misc.annoyances + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch + +@Suppress("unused") +val hideUpdatePopupPatch = bytecodePatch( + name = "Hide update popup", + description = "Prevents the update popup from showing up.", +) { + dependsOn(signatureDetectionPatch) + + compatibleWith("com.microblink.photomath"("8.32.0")) + + val hideUpdatePopupMatch by hideUpdatePopupFingerprint() + + execute { + hideUpdatePopupMatch.mutableMethod.addInstructions( + 2, // Insert after the null check. + "return-void", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt new file mode 100644 index 0000000000..adf0d7bd85 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/EnableBookpointPatch.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.photomath.misc.unlock.bookpoint + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val enableBookpointPatch = bytecodePatch( + description = "Enables textbook access", +) { + val isBookpointEnabledMatch by isBookpointEnabledFingerprint() + + execute { + isBookpointEnabledMatch.mutableMethod.replaceInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt new file mode 100644 index 0000000000..6722f4223e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/bookpoint/Fingerprints.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.photomath.misc.unlock.bookpoint + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val isBookpointEnabledFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + strings( + "NoGeoData", + "NoCountryInGeo", + "RemoteConfig", + "GeoRCMismatch" + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt new file mode 100644 index 0000000000..f6c282cbd4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/Fingerprints.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.photomath.misc.unlock.plus + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val isPlusUnlockedFingerprint = fingerprint{ + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + strings("genius") + custom { _, classDef -> + classDef.endsWith("/User;") + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt new file mode 100644 index 0000000000..f62533e0c2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.photomath.misc.unlock.plus + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.photomath.detection.signature.signatureDetectionPatch +import app.revanced.patches.photomath.misc.unlock.bookpoint.enableBookpointPatch + +@Suppress("unused") +val unlockPlusPatch = bytecodePatch( + name = "Unlock plus", +) { + dependsOn(signatureDetectionPatch, enableBookpointPatch) + + compatibleWith("com.microblink.photomath"("8.37.0")) + + val isPlusUnlockedMatch by isPlusUnlockedFingerprint() + + execute { + isPlusUnlockedMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt new file mode 100644 index 0000000000..8c2d579ef5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.piccomafr.misc + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val getAndroidIdFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String;") + parameters("Landroid/content/Context;") + strings("context", "android_id") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt new file mode 100644 index 0000000000..6d9e4dc5ca --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt @@ -0,0 +1,52 @@ +package app.revanced.patches.piccomafr.misc + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.stringOption + +@Suppress("unused") +val spoofAndroidDeviceIdPatch = bytecodePatch( + name = "Spoof Android device ID", + description = "Spoofs the Android device ID used by the app for account authentication." + + "This can be used to copy the account to another device.", + use = false, +) { + compatibleWith( + "com.piccomaeurope.fr"( + "6.4.0", + "6.4.1", + "6.4.2", + "6.4.3", + "6.4.4", + "6.4.5", + "6.5.0", + "6.5.1", + "6.5.2", + "6.5.3", + "6.5.4", + "6.6.0", + "6.6.1", + "6.6.2", + ), + ) + + val getAndroidIDMatch by getAndroidIdFingerprint() + + val androidDeviceId by stringOption( + key = "android-device-id", + default = "0011223344556677", + title = "Android device ID", + description = "The Android device ID to spoof to.", + required = true, + ) { it!!.matches("[A-Fa-f0-9]{16}".toRegex()) } + + execute { + getAndroidIDMatch.mutableMethod.addInstructions( + 0, + """ + const-string v0, "$androidDeviceId" + return-object v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt new file mode 100644 index 0000000000..cf4ba8b086 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt @@ -0,0 +1,71 @@ +package app.revanced.patches.piccomafr.tracking + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.StringReference + +@Suppress("unused") +val disableTrackingPatch = bytecodePatch( + name = "Disable tracking", + description = "Disables tracking by replacing tracking URLs with example.com.", +) { + compatibleWith( + "com.piccomaeurope.fr"( + "6.4.0", + "6.4.1", + "6.4.2", + "6.4.3", + "6.4.4", + "6.4.5", + "6.5.0", + "6.5.1", + "6.5.2", + "6.5.3", + "6.5.4", + "6.6.0", + "6.6.1", + "6.6.2", + ), + ) + + val facebookSDKMatch by facebookSDKFingerprint() + val firebaseInstallMatch by firebaseInstallFingerprint() + val appMeasurementMatch by appMeasurementFingerprint() + + execute { + facebookSDKMatch.mutableMethod.apply { + instructions.filter { instruction -> + instruction.opcode == Opcode.CONST_STRING + }.forEach { instruction -> + instruction as OneRegisterInstruction + + replaceInstruction( + instruction.location.index, + "const-string v${instruction.registerA}, \"example.com\"", + ) + } + } + + firebaseInstallMatch.mutableMethod.apply { + instructions.filter { + it.opcode == Opcode.CONST_STRING + }.filter { + it.getReference()?.string == "firebaseinstallations.googleapis.com" + }.forEach { instruction -> + instruction as OneRegisterInstruction + + replaceInstruction( + instruction.location.index, + "const-string v${instruction.registerA}, \"example.com\"", + ) + } + } + + appMeasurementMatch.mutableMethod.addInstruction(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt new file mode 100644 index 0000000000..794f21bcb6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/piccomafr/tracking/Fingerprints.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.piccomafr.tracking + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val appMeasurementFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + strings("config/app/", "Fetching remote configuration") +} + +internal val facebookSDKFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + returns("V") + strings("instagram.com", "facebook.com") +} + +internal val firebaseInstallFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE) + strings( + "https://%s/%s/%s", + "firebaseinstallations.googleapis.com", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt new file mode 100644 index 0000000000..3e2addaa06 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.pixiv.ads + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val shouldShowAdsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + custom { methodDef, classDef -> + classDef.type.endsWith("AdUtils;") && methodDef.name == "shouldShowAds" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt new file mode 100644 index 0000000000..584d2f48a1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/pixiv/ads/HideAdsPatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.pixiv.ads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideAdsPatch = bytecodePatch( + name = "Hide ads", +) { + compatibleWith("jp.pxv.android") + + val shouldShowAdsMatch by shouldShowAdsFingerprint() + + execute { + shouldShowAdsMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x0 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt new file mode 100644 index 0000000000..a4d2c9e221 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.rar.misc.annoyances.purchasereminder + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val showReminderFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("V") + custom { method, _ -> + method.definingClass.endsWith("AdsNotify;") && method.name == "show" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt new file mode 100644 index 0000000000..32957d5bd6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatch.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.rar.misc.annoyances.purchasereminder + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hidePurchaseReminderPatch = bytecodePatch( + name = "Hide purchase reminder", + description = "Hides the popup that reminds you to purchase the app.", + +) { + compatibleWith("com.rarlab.rar") + + val showReminderMatch by showReminderFingerprint() + + execute { + showReminderMatch.mutableMethod.addInstruction(0, "return-void") + } +} diff --git a/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt similarity index 65% rename from src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt index 3118031951..f3f6d66696 100644 --- a/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/banner/HideBannerPatch.kt @@ -1,20 +1,18 @@ package app.revanced.patches.reddit.ad.banner -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch // Note that for now, this patch and anything using it will only work on // Reddit 2024.17.0 or older. Newer versions will crash during patching. // See https://github.com/ReVanced/revanced-patches/issues/3099 -@Patch(description = "Hides banner ads from comments on subreddits.") -object HideBannerPatch : ResourcePatch() { - private const val RESOURCE_FILE_PATH = "res/layout/merge_listheader_link_detail.xml" - - override fun execute(context: ResourceContext) { - context.xmlEditor[RESOURCE_FILE_PATH].use { editor -> - val document = editor.file +@Suppress("unused") +val hideBannerPatch = resourcePatch( + description = "Hides banner ads from comments on subreddits.", +) { + execute { context -> + val resourceFilePath = "res/layout/merge_listheader_link_detail.xml" + context.document[resourceFilePath].use { document -> document.getElementsByTagName("merge").item(0).childNodes.apply { val attributes = arrayOf("height", "width") diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt new file mode 100644 index 0000000000..c99df5707a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/Fingerprints.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.reddit.ad.comments + +import app.revanced.patcher.fingerprint + +internal val hideCommentAdsFingerprint = fingerprint { + strings( + "link", + // CommentPageRepository is not returning a link object + "is not returning a link object" + ) + custom { _, classDef -> + classDef.sourceFile == "PostDetailPresenter.kt" + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt new file mode 100644 index 0000000000..c556e76a2a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/comments/HideCommentAdsPatch.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.reddit.ad.comments + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideCommentAdsPatch = bytecodePatch( + description = "Removes ads in the comments.", +) { + val hideCommentAdsMatch by hideCommentAdsFingerprint() + + execute { + hideCommentAdsMatch.mutableMethod.addInstructions( + 0, + """ + new-instance v0, Ljava/lang/Object; + invoke-direct {v0}, Ljava/lang/Object;->()V + return-object v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt new file mode 100644 index 0000000000..e7dd789123 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.reddit.ad.general + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val adPostFingerprint = fingerprint { + returns("V") + // "children" are present throughout multiple versions + strings("children") + custom { _, classDef -> classDef.endsWith("Listing;") } +} + +internal val newAdPostFingerprint = fingerprint { + opcodes(Opcode.INVOKE_VIRTUAL) + strings("chain", "feedElement") + custom { _, classDef -> classDef.sourceFile == "AdElementConverter.kt" } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt new file mode 100644 index 0000000000..fae1100421 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/ad/general/HideAdsPatch.kt @@ -0,0 +1,81 @@ +package app.revanced.patches.reddit.ad.general + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.reddit.ad.banner.hideBannerPatch +import app.revanced.patches.reddit.ad.comments.hideCommentAdsPatch +import app.revanced.patches.reddit.misc.extension.sharedExtensionPatch +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Suppress("unused") +val hideAdsPatch = bytecodePatch( + name = "Hide ads", +) { + dependsOn(hideBannerPatch, hideCommentAdsPatch, sharedExtensionPatch) + + // Note that for now, this patch and anything using it will only work on + // Reddit 2024.17.0 or older. Newer versions will crash during patching. + // See https://github.com/ReVanced/revanced-patches/issues/3099 + // and https://github.com/iBotPeaches/Apktool/issues/3534. + // This constraint is necessary due to dependency on hideBannerPatch. + compatibleWith("com.reddit.frontpage"("2024.17.0")) + + val adPostMatch by adPostFingerprint() + val newAdPostMatch by newAdPostFingerprint() + + execute { + // region Filter promoted ads (does not work in popular or latest feed) + + val filterMethodDescriptor = + "Lapp/revanced/extension/reddit/patches/FilterPromotedLinksPatch;" + + "->filterChildren(Ljava/lang/Iterable;)Ljava/util/List;" + + adPostMatch.mutableMethod.apply { + val setPostsListChildren = implementation!!.instructions.first { instruction -> + if (instruction.opcode != Opcode.IPUT_OBJECT) return@first false + + val reference = (instruction as ReferenceInstruction).reference as FieldReference + reference.name == "children" + } + + val castedInstruction = setPostsListChildren as Instruction22c + val itemsRegister = castedInstruction.registerA + val listInstanceRegister = castedInstruction.registerB + + // postsList.children = filterChildren(postListItems) + removeInstruction(setPostsListChildren.location.index) + addInstructions( + setPostsListChildren.location.index, + """ + invoke-static {v$itemsRegister}, $filterMethodDescriptor + move-result-object v0 + iput-object v0, v$listInstanceRegister, ${castedInstruction.reference} + """, + ) + } + + // endregion + + // region Remove ads from popular and latest feed + + // The new feeds work by inserting posts into lists. + // AdElementConverter is conveniently responsible for inserting all feed ads. + // By removing the appending instruction no ad posts gets appended to the feed. + val index = newAdPostMatch.method.implementation!!.instructions.indexOfFirst { + if (it.opcode != Opcode.INVOKE_VIRTUAL) return@indexOfFirst false + + val reference = (it as ReferenceInstruction).reference as MethodReference + + reference.name == "add" && reference.definingClass == "Ljava/util/ArrayList;" + } + + newAdPostMatch.mutableMethod.removeInstruction(index) + } + + // endregion +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt new file mode 100644 index 0000000000..ccc0346a66 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/FixSLinksPatch.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.reddit.customclients + +import app.revanced.patcher.patch.BytecodePatchBuilder +import app.revanced.patcher.patch.Patch +import app.revanced.patcher.patch.bytecodePatch + +const val RESOLVE_S_LINK_METHOD = "patchResolveSLink(Ljava/lang/String;)Z" +const val SET_ACCESS_TOKEN_METHOD = "patchSetAccessToken(Ljava/lang/String;)V" + +fun fixSLinksPatch( + extensionPatch: Patch<*>, + block: BytecodePatchBuilder.() -> Unit = {}, +) = bytecodePatch(name = "Fix /s/ links") { + dependsOn(extensionPatch) + + block() +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt new file mode 100644 index 0000000000..b21b36a0d7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/SpoofClientPatch.kt @@ -0,0 +1,34 @@ +package app.revanced.patches.reddit.customclients + +import app.revanced.patcher.patch.BytecodePatchBuilder +import app.revanced.patcher.patch.Option +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.stringOption + +/** + * Base class for patches that spoof the Reddit client. + * + * @param redirectUri The redirect URI of the Reddit OAuth client. + * @param block The patch block. It is called with the client ID option. + */ +fun spoofClientPatch( + redirectUri: String, + block: BytecodePatchBuilder.(Option) -> Unit = {}, +) = bytecodePatch( + name = "Spoof client", + description = "Restores functionality of the app by using custom client ID.", +) { + block( + stringOption( + "client-id", + null, + null, + "OAuth client ID", + "The Reddit OAuth client ID. " + + "You can get your client ID from https://www.reddit.com/prefs/apps. " + + "The application type has to be \"Installed app\" " + + "and the redirect URI has to be set to \"$redirectUri\".", + true, + ), + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt new file mode 100644 index 0000000000..bb87c21148 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/Fingerprints.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.reddit.customclients.baconreader.api + +import app.revanced.patcher.fingerprint + +internal val getAuthorizationUrlFingerprint = fingerprint { + strings("client_id=zACVn0dSFGdWqQ") +} +internal val getClientIdFingerprint = fingerprint { + strings("client_id=zACVn0dSFGdWqQ") + custom { method, classDef -> + if (!classDef.endsWith("RedditOAuth;")) return@custom false + + method.name == "getAuthorizeUrl" + } +} + +internal val requestTokenFingerprint = fingerprint { + strings("zACVn0dSFGdWqQ", "kDm2tYpu9DqyWFFyPlNcXGEni4k") // App ID and secret. +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt new file mode 100644 index 0000000000..649dffc16f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/baconreader/api/SpoofClientPatch.kt @@ -0,0 +1,40 @@ +package app.revanced.patches.reddit.customclients.baconreader.api + +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patches.reddit.customclients.spoofClientPatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val spoofClientPatch = spoofClientPatch(redirectUri = "http://baconreader.com/auth") { clientIdOption -> + compatibleWith( + "com.onelouder.baconreader", + "com.onelouder.baconreader.premium", + ) + + val getAuthorizationUrlMatch by getAuthorizationUrlFingerprint() + val requestTokenMatch by requestTokenFingerprint() + + val clientId by clientIdOption + + execute { + fun Match.patch(replacementString: String) { + val clientIdIndex = stringMatches!!.first().index + + mutableMethod.apply { + val clientIdRegister = getInstruction(clientIdIndex).registerA + replaceInstruction( + clientIdIndex, + "const-string v$clientIdRegister, \"$replacementString\"", + ) + } + } + + // Patch client id in authorization url. + getAuthorizationUrlMatch.patch("client_id=$clientId") + + // Patch client id for access token request. + requestTokenMatch.patch(clientId!!) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt new file mode 100644 index 0000000000..fefe25160e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatch.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.ads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableAdsPatch = bytecodePatch( + name = "Disable ads", +) { + compatibleWith("com.rubenmayayo.reddit") + + val maxMediationMatch by maxMediationFingerprint() + val admobMediationMatch by admobMediationFingerprint() + + execute { + arrayOf(maxMediationMatch, admobMediationMatch).forEach { + it.mutableMethod.addInstructions(0, "return-void") + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt new file mode 100644 index 0000000000..618e2f1456 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/ads/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.ads + +import app.revanced.patcher.fingerprint + +internal val maxMediationFingerprint = fingerprint { + strings("MaxMediation: Attempting to initialize SDK") +} + +internal val admobMediationFingerprint = fingerprint { + strings("AdmobMediation: Attempting to initialize SDK") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt new file mode 100644 index 0000000000..cc06fd3968 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/Fingerprints.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.api + +import app.revanced.patcher.fingerprint + +internal val buildUserAgentFingerprint = fingerprint { + strings("%s:%s:%s (by /u/%s)") +} + +internal val getClientIdFingerprint = fingerprint { + custom { method, classDef -> + if (!classDef.endsWith("Credentials;")) return@custom false + + method.name == "getClientId" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt new file mode 100644 index 0000000000..fb4df92f27 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/api/SpoofClientPatch.kt @@ -0,0 +1,41 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.api + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patches.reddit.customclients.spoofClientPatch + +@Suppress("unused") +val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption -> + compatibleWith("com.rubenmayayo.reddit") + + val getClientIdMatch by getClientIdFingerprint() + val buildUserAgentMatch by buildUserAgentFingerprint() + + val clientId by clientIdOption + + execute { + // region Patch client id. + + getClientIdMatch.mutableMethod.addInstructions( + 0, + """ + const-string v0, "$clientId" + return-object v0 + """, + ) + + // endregion + + // region Patch user agent. + + // Use a random number as the platform in the user agent string. + val platformName = (0..100000).random() + val platformParameter = 0 + + buildUserAgentMatch.mutableMethod.addInstructions( + 0, + "const-string p$platformParameter, \"$platformName\"", + ) + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt new file mode 100644 index 0000000000..a2b1530b8b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/Fingerprints.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads + +import app.revanced.patcher.fingerprint + +internal val downloadAudioFingerprint = fingerprint { + strings("/DASH_audio.mp4", "/audio") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt new file mode 100644 index 0000000000..31975f44b9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt @@ -0,0 +1,32 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val fixAudioMissingInDownloadsPatch = bytecodePatch( + name = "Fix missing audio in video downloads", + description = "Fixes audio missing in videos downloaded from v.redd.it.", +) { + compatibleWith("com.rubenmayayo.reddit") + + val downloadAudioMatch by downloadAudioFingerprint() + + execute { + val endpointReplacements = mapOf( + "/DASH_audio.mp4" to "/DASH_AUDIO_128.mp4", + "/audio" to "/DASH_AUDIO_64.mp4", + ) + + downloadAudioMatch.stringMatches!!.forEach { match -> + downloadAudioMatch.mutableMethod.apply { + val replacement = endpointReplacements[match.string] + val register = getInstruction(match.index).registerA + + replaceInstruction(match.index, "const-string v$register, \"$replacement\"") + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt new file mode 100644 index 0000000000..665dba5a40 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/Fingerprints.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val getOAuthAccessTokenFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("Ljava/lang/String") + strings("access_token") + custom { method, _ -> method.definingClass == "Lnet/dean/jraw/http/oauth/OAuthData;" } +} + +internal val handleNavigationFingerprint = fingerprint { + strings( + "android.intent.action.SEARCH", + "subscription", + "sort", + "period", + "boostforreddit.com/themes", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt new file mode 100644 index 0000000000..b35a853200 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt @@ -0,0 +1,53 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.reddit.customclients.RESOLVE_S_LINK_METHOD +import app.revanced.patches.reddit.customclients.SET_ACCESS_TOKEN_METHOD +import app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.sharedExtensionPatch +import app.revanced.patches.reddit.customclients.fixSLinksPatch + +const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/boostforreddit/FixSLinksPatch;" + +@Suppress("unused") +val fixSlinksPatch = fixSLinksPatch( + extensionPatch = sharedExtensionPatch, +) { + compatibleWith("com.rubenmayayo.reddit") + + val handleNavigationMatch by handleNavigationFingerprint() + val setAccessTokenMatch by getOAuthAccessTokenFingerprint() + + execute { + // region Patch navigation handler. + + handleNavigationMatch.mutableMethod.apply { + val urlRegister = "p1" + val tempRegister = "v1" + + addInstructionsWithLabels( + 0, + """ + invoke-static { $urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->$RESOLVE_S_LINK_METHOD + move-result $tempRegister + if-eqz $tempRegister, :continue + return $tempRegister + """, + ExternalLabel("continue", getInstruction(0)), + ) + } + + // endregion + + // region Patch set access token. + + setAccessTokenMatch.mutableMethod.addInstruction( + 3, + "invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->$SET_ACCESS_TOKEN_METHOD", + ) + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatch.kt new file mode 100644 index 0000000000..3d92d142b9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/SharedExtensionPatch.kt @@ -0,0 +1,6 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.misc.extension + +import app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.hooks.initHook +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch(initHook) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt new file mode 100644 index 0000000000..1e7e4e2e83 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/extension/hooks/InitHook.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.hooks + +import app.revanced.patches.shared.misc.extension.extensionHook + +internal val initHook = extensionHook( + insertIndexResolver = { 1 }, +) { + custom { method, _ -> + method.definingClass == "Lcom/rubenmayayo/reddit/MyApplication;" && method.name == "onCreate" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/Fingerprints.kt new file mode 100644 index 0000000000..4bce1362c2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/Fingerprints.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.reddit.customclients.infinityforreddit.api + +import app.revanced.patcher.fingerprint + +internal val apiUtilsFingerprint = fingerprint { + strings("native-lib") +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt similarity index 60% rename from src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt index 0565bfefa7..3ec7d8c699 100644 --- a/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch.kt @@ -1,24 +1,22 @@ package app.revanced.patches.reddit.customclients.infinityforreddit.api -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprintResult import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.smali.toInstructions -import app.revanced.patches.reddit.customclients.BaseSpoofClientPatch -import app.revanced.patches.reddit.customclients.infinityforreddit.api.fingerprints.APIUtilsFingerprint +import app.revanced.patches.reddit.customclients.spoofClientPatch import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation @Suppress("unused") -object SpoofClientPatch : BaseSpoofClientPatch( - redirectUri = "infinity://localhost", - clientIdFingerprints = setOf(APIUtilsFingerprint), - compatiblePackages = setOf(CompatiblePackage("ml.docilealligator.infinityforreddit")) -) { - override fun Set.patchClientId(context: BytecodeContext) { - first().mutableClass.methods.apply { +val spoofClientPatch = spoofClientPatch(redirectUri = "infinity://localhost") { clientIdOption -> + compatibleWith("ml.docilealligator.infinityforreddit") + + val apiUtilsMatch by apiUtilsFingerprint() + + val clientId by clientIdOption + + execute { + apiUtilsMatch.mutableClass.methods.apply { val getClientIdMethod = single { it.name == "getId" }.also(::remove) val newGetClientIdMethod = ImmutableMethod( @@ -26,7 +24,7 @@ object SpoofClientPatch : BaseSpoofClientPatch( getClientIdMethod.name, null, getClientIdMethod.returnType, - AccessFlags.PUBLIC or AccessFlags.STATIC, + AccessFlags.PUBLIC.value or AccessFlags.STATIC.value, null, null, ImmutableMethodImplementation( diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt new file mode 100644 index 0000000000..36fe062796 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/Fingerprints.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.reddit.customclients.infinityforreddit.subscription + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal + +internal val billingClientOnServiceConnectedFingerprint = fingerprint { + strings("Billing service connected") +} + +internal val startSubscriptionActivityFingerprint = fingerprint { + literal { + // Intent start flag only used in the subscription activity + 0x10008000 + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt new file mode 100644 index 0000000000..1c427c967d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/infinityforreddit/subscription/UnlockSubscriptionPatch.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.reddit.customclients.infinityforreddit.subscription + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.reddit.customclients.infinityforreddit.api.spoofClientPatch +import app.revanced.util.returnEarly + +@Suppress("unused") +val unlockSubscriptionPatch = bytecodePatch( + name = "Unlock subscription", + description = "Unlocks the subscription feature but requires a custom client ID.", +) { + dependsOn(spoofClientPatch) + + compatibleWith("ml.docilealligator.infinityforreddit") + + startSubscriptionActivityFingerprint() + billingClientOnServiceConnectedFingerprint() + + execute { + setOf(startSubscriptionActivityFingerprint, billingClientOnServiceConnectedFingerprint).returnEarly() + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt new file mode 100644 index 0000000000..c6498ee00f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/DisableAdsPatch.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.reddit.customclients.joeyforreddit.ads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy.disablePiracyDetectionPatch + +@Suppress("unused") +val disableAdsPatch = bytecodePatch( + name = "Disable ads", +) { + dependsOn(disablePiracyDetectionPatch) + + compatibleWith("o.o.joey") + + val isAdFreeUserMatch by isAdFreeUserFingerprint() + + execute { + isAdFreeUserMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt new file mode 100644 index 0000000000..465faf1200 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/ads/Fingerprints.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.reddit.customclients.joeyforreddit.ads + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val isAdFreeUserFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("Z") + strings("AD_FREE_USER") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt new file mode 100644 index 0000000000..e6c591748c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/Fingerprints.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.reddit.customclients.joeyforreddit.api + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val authUtilityUserAgentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Ljava/lang/String;") + opcodes(Opcode.APUT_OBJECT) + custom { method, classDef -> + classDef.sourceFile == "AuthUtility.java" + } +} + +internal val getClientIdFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("L") + opcodes( + Opcode.CONST, // R.string.valuable_cid + Opcode.INVOKE_STATIC, // StringMaster.decrypt + Opcode.MOVE_RESULT_OBJECT, + Opcode.RETURN_OBJECT + ) + custom { _, classDef -> + classDef.sourceFile == "AuthUtility.java" + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt new file mode 100644 index 0000000000..ad3fc0cb16 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/api/SpoofClientPatch.kt @@ -0,0 +1,52 @@ +package app.revanced.patches.reddit.customclients.joeyforreddit.api + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy.disablePiracyDetectionPatch +import app.revanced.patches.reddit.customclients.spoofClientPatch + +@Suppress("unused") +val spoofClientPatch = spoofClientPatch(redirectUri = "https://127.0.0.1:65023/authorize_callback") { clientIdOption -> + dependsOn(disablePiracyDetectionPatch) + + compatibleWith( + "o.o.joey", + "o.o.joey.pro", + "o.o.joey.dev", + ) + + val getClientIdMatch by getClientIdFingerprint() + val authUtilityUserAgentMatch by authUtilityUserAgentFingerprint() + + val clientId by clientIdOption + + execute { + // region Patch client id. + + getClientIdMatch.mutableMethod.addInstructions( + 0, + """ + const-string v0, "$clientId" + return-object v0 + """, + ) + + // endregion + + // region Patch user agent. + + // Use a random user agent. + val randomName = (0..100000).random() + val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)" + + authUtilityUserAgentMatch.mutableMethod.replaceInstructions( + 0, + """ + const-string v0, "$userAgent" + return-object v0 + """, + ) + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt new file mode 100644 index 0000000000..32a676f9d5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/DisablePiracyDetectionPatch.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disablePiracyDetectionPatch = bytecodePatch { + val piracyDetectionMatch by piracyDetectionFingerprint() + + execute { + piracyDetectionMatch.mutableMethod.addInstruction(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt new file mode 100644 index 0000000000..76343a5309 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/joeyforreddit/detection/piracy/Fingerprints.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.reddit.customclients.joeyforreddit.detection.piracy + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val piracyDetectionFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) + returns("V") + opcodes( + Opcode.NEW_INSTANCE, + Opcode.CONST_16, + Opcode.CONST_WIDE_16, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_VOID + ) + custom { _, classDef -> + classDef.endsWith("ProcessLifeCyleListener;") + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt new file mode 100644 index 0000000000..14d0f4766d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/Fingerprints.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.reddit.customclients.redditisfun.api + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +fun baseClientIdFingerprint(string: String) = fingerprint { + strings("yyOCBp.RHJhDKd", string) +} + +internal val basicAuthorizationFingerprint = baseClientIdFingerprint( + string = "fJOxVwBUyo*=f:.patchClientId(context: BytecodeContext) { +val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { clientIdOption -> + compatibleWith( + "com.andrewshu.android.reddit", + "com.andrewshu.android.redditdonation", + ) + + val buildAuthorizationStringMatch by buildAuthorizationStringFingerprint() + val basicAuthorizationMatch by basicAuthorizationFingerprint() + val getUserAgentMatch by getUserAgentFingerprint() + + val clientId by clientIdOption + + execute { + // region Patch client id. + /** * Replaces a one register instruction with a const-string instruction * at the index returned by [getReplacementIndex]. * * @param string The string to replace the instruction with. * @param getReplacementIndex A function that returns the index of the instruction to replace - * using the [StringMatch] list from the [MethodFingerprintResult]. + * using the [Match.StringMatch] list from the [Match]. */ - fun MethodFingerprintResult.replaceWith( + fun Match.replaceWith( string: String, - getReplacementIndex: List.() -> Int, + getReplacementIndex: List.() -> Int, ) = mutableMethod.apply { - val replacementIndex = scanResult.stringsScanResult!!.matches.getReplacementIndex() + val replacementIndex = stringMatches!!.getReplacementIndex() val clientIdRegister = getInstruction(replacementIndex).registerA replaceInstruction(replacementIndex, "const-string v$clientIdRegister, \"$string\"") } // Patch OAuth authorization. - first().replaceWith(clientId!!) { first().index + 4 } + buildAuthorizationStringMatch.replaceWith(clientId!!) { first().index + 4 } // Path basic authorization. - last().replaceWith("$clientId:") { last().index + 7 } - } + basicAuthorizationMatch.replaceWith("$clientId:") { last().index + 7 } + + // endregion + + // region Patch user agent. - override fun Set.patchUserAgent(context: BytecodeContext) { // Use a random user agent. val randomName = (0..100000).random() val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)" - first().mutableMethod.addInstructions( + getUserAgentMatch.mutableMethod.addInstructions( 0, """ const-string v0, "$userAgent" return-object v0 """, ) - } - override fun Set.patchMiscellaneous(context: BytecodeContext) { + // endregion + + // region Patch miscellaneous. + // Reddit messed up and does not append a redirect uri to the authorization url to old.reddit.com/login. // Replace old.reddit.com with ssl.reddit.com to fix this. - BuildAuthorizationStringFingerprint.result!!.mutableMethod.apply { + buildAuthorizationStringMatch.mutableMethod.apply { val index = indexOfFirstInstructionOrThrow { getReference()?.contains("old.reddit.com") == true } @@ -78,5 +83,7 @@ object SpoofClientPatch : BaseSpoofClientPatch( "const-string v$targetRegister, \"https://ssl.reddit.com/api/v1/authorize.compact\"", ) } + + // endregion } } diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt new file mode 100644 index 0000000000..263602f730 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/Fingerprints.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.reddit.customclients.relayforreddit.api + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal fun baseClientIdFingerprint(string: String) = fingerprint { + strings("dj-xCIZQYiLbEg", string) +} + +internal val getLoggedInBearerTokenFingerprint = baseClientIdFingerprint("authorization_code") + +internal val getLoggedOutBearerTokenFingerprint = baseClientIdFingerprint("https://oauth.reddit.com/grants/installed_client") + +internal val getRefreshTokenFingerprint = baseClientIdFingerprint("refresh_token") + +internal val loginActivityClientIdFingerprint = baseClientIdFingerprint("&duration=permanent") + +internal val redditCheckDisableAPIFingerprint = fingerprint { + opcodes(Opcode.IF_EQZ) + strings("Reddit Disabled") +} + +internal val setRemoteConfigFingerprint = fingerprint { + parameters("Lcom/google/firebase/remoteconfig/FirebaseRemoteConfig;") + strings("reddit_oauth_url") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt new file mode 100644 index 0000000000..3313c7ff7a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/relayforreddit/api/SpoofClientPatch.kt @@ -0,0 +1,65 @@ +package app.revanced.patches.reddit.customclients.relayforreddit.api + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patches.reddit.customclients.spoofClientPatch +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction10t +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21t +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val spoofClientPatch = spoofClientPatch(redirectUri = "dbrady://relay") { + compatibleWith( + "free.reddit.news", + "reddit.news", + ) + + val loginActivityClientIdMatch by loginActivityClientIdFingerprint() + val getLoggedInBearerTokenMatch by getLoggedInBearerTokenFingerprint() + val getLoggedOutBearerTokenMatch by getLoggedOutBearerTokenFingerprint() + val getRefreshTokenMatch by getRefreshTokenFingerprint() + val setRemoteConfigMatch by setRemoteConfigFingerprint() + val redditCheckDisableAPIMatch by redditCheckDisableAPIFingerprint() + + val clientId by it + + execute { + // region Patch client id. + + setOf( + loginActivityClientIdMatch, + getLoggedInBearerTokenMatch, + getLoggedOutBearerTokenMatch, + getRefreshTokenMatch, + ).forEach { match -> + val clientIdIndex = match.stringMatches!!.first().index + match.mutableMethod.apply { + val clientIdRegister = getInstruction(clientIdIndex).registerA + + match.mutableMethod.replaceInstruction( + clientIdIndex, + "const-string v$clientIdRegister, \"$clientId\"", + ) + } + } + + // endregion + + // region Patch miscellaneous. + + // Do not load remote config which disables OAuth login remotely. + setRemoteConfigMatch.mutableMethod.addInstructions(0, "return-void") + + // Prevent OAuth login being disabled remotely. + val checkIsOAuthRequestIndex = redditCheckDisableAPIMatch.patternMatch!!.startIndex + + redditCheckDisableAPIMatch.mutableMethod.apply { + val returnNextChain = getInstruction(checkIsOAuthRequestIndex).target + replaceInstruction(checkIsOAuthRequestIndex, BuilderInstruction10t(Opcode.GOTO, returnNextChain)) + } + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt new file mode 100644 index 0000000000..4ff8be4615 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.reddit.customclients.slide.api + +import app.revanced.patcher.fingerprint + +internal val getClientIdFingerprint = fingerprint { + custom { method, classDef -> + if (!classDef.endsWith("Credentials;")) return@custom false + + method.name == "getClientId" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt new file mode 100644 index 0000000000..bfd6870960 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/slide/api/SpoofClientPatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.reddit.customclients.slide.api + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patches.reddit.customclients.spoofClientPatch + +@Suppress("unused") +val spoofClientPatch = spoofClientPatch(redirectUri = "http://www.ccrama.me") { clientIdOption -> + compatibleWith("me.ccrama.redditslide") + + val getClientIdMatch by getClientIdFingerprint() + + val clientId by clientIdOption + + execute { + getClientIdMatch.mutableMethod.addInstructions( + 0, + """ + const-string v0, "$clientId" + return-object v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt new file mode 100644 index 0000000000..d9b5b9fd41 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/DisableAdsPatch.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.reddit.customclients.sync.ads + +import app.revanced.patcher.patch.BytecodePatchBuilder +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly + +fun disableAdsPatch(block: BytecodePatchBuilder.() -> Unit = {}) = bytecodePatch( + name = "Disable ads", +) { + isAdsEnabledFingerprint() + + execute { + isAdsEnabledFingerprint.returnEarly() + } + + block() +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt new file mode 100644 index 0000000000..e055493bdd --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/ads/Fingerprints.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.reddit.customclients.sync.ads + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val isAdsEnabledFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + strings("SyncIapHelper") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt new file mode 100644 index 0000000000..e87e2e7b40 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/DisablePiracyDetectionPatch.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.reddit.customclients.sync.detection.piracy + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disablePiracyDetectionPatch = bytecodePatch( + description = "Disables detection of modified versions.", +) { + val piracyDetectionMatch by piracyDetectionFingerprint() + + execute { + // Do not throw an error if the fingerprint is not resolved. + // This is fine because new versions of the target app do not need this patch. + piracyDetectionMatch.mutableMethod.addInstruction(0, "return-void") + } +} diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/fingerprints/PiracyDetectionFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt similarity index 57% rename from src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/fingerprints/PiracyDetectionFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt index f7cbc87716..7dc955912d 100644 --- a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/detection/piracy/fingerprints/PiracyDetectionFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/detection/piracy/Fingerprints.kt @@ -1,22 +1,21 @@ -package app.revanced.patches.reddit.customclients.syncforreddit.detection.piracy.fingerprints +package app.revanced.patches.reddit.customclients.sync.detection.piracy -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 import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint -internal object PiracyDetectionFingerprint : MethodFingerprint( - returnType = "V", - accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, - opcodes = listOf( +internal val piracyDetectionFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + opcodes( Opcode.NEW_INSTANCE, Opcode.INVOKE_DIRECT, Opcode.NEW_INSTANCE, Opcode.INVOKE_DIRECT, - Opcode.INVOKE_VIRTUAL - ), - customFingerprint = { method, _ -> + Opcode.INVOKE_VIRTUAL, + ) + custom { method, _ -> method.implementation?.instructions?.any { if (it.opcode != Opcode.NEW_INSTANCE) return@any false @@ -25,4 +24,4 @@ internal object PiracyDetectionFingerprint : MethodFingerprint( reference.toString() == "Lcom/github/javiersantos/piracychecker/PiracyChecker;" } ?: false } -) \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatch.kt new file mode 100644 index 0000000000..ca74997aa8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforlemmy/ads/DisableAdsPatch.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.reddit.customclients.sync.syncforlemmy.ads + +import app.revanced.patches.reddit.customclients.sync.ads.disableAdsPatch +import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch + +@Suppress("unused") +val disableAdsPatch = disableAdsPatch { + dependsOn(disablePiracyDetectionPatch) + + compatibleWith("com.laurencedawson.reddit_sync") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatch.kt new file mode 100644 index 0000000000..e50158cdd6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/ads/DisableAdsPatch.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.ads + +import app.revanced.patches.reddit.customclients.sync.ads.disableAdsPatch + +@Suppress("unused") +val disableAdsPatch = disableAdsPatch { + compatibleWith("io.syncapps.lemmy_sync") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt new file mode 100644 index 0000000000..85d51573cb --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/DisableSyncForLemmyBottomSheetPatch.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.annoyances.startup + +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableSyncForLemmyBottomSheetPatch = bytecodePatch( + name = "Disable Sync for Lemmy bottom sheet", + description = "Disables the bottom sheet at the startup that asks you to signup to \"Sync for Lemmy\".", +) { + compatibleWith( + "com.laurencedawson.reddit_sync"("v23.06.30-13:39"), + "com.laurencedawson.reddit_sync.pro"(), // Version unknown. + "com.laurencedawson.reddit_sync.dev"(), // Version unknown. + ) + + val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint() + + execute { + mainActivityOnCreateMatch.mutableMethod.apply { + val showBottomSheetIndex = implementation!!.instructions.lastIndex - 1 + + removeInstruction(showBottomSheetIndex) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt new file mode 100644 index 0000000000..21c788a89c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/annoyances/startup/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.annoyances.startup + +import app.revanced.patcher.fingerprint + +internal val mainActivityOnCreateFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("MainActivity;") && method.name == "onCreate" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt new file mode 100644 index 0000000000..c7902b1f4d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/Fingerprints.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.api + +import app.revanced.patcher.fingerprint + +internal val getAuthorizationStringFingerprint = fingerprint { + strings("authorize.compact?client_id") +} + +internal val getBearerTokenFingerprint = fingerprint { + strings("Basic") +} + +internal val getUserAgentFingerprint = fingerprint { + strings("android:com.laurencedawson.reddit_sync") +} + +internal val imgurImageAPIFingerprint = fingerprint { + strings("https://imgur-apiv3.p.rapidapi.com/3/image") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt new file mode 100644 index 0000000000..1d9aaa5a8b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/api/SpoofClientPatch.kt @@ -0,0 +1,94 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.api + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patches.reddit.customclients.spoofClientPatch +import app.revanced.patches.reddit.customclients.sync.detection.piracy.disablePiracyDetectionPatch +import app.revanced.util.matchOrThrow +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.reference.StringReference +import java.util.* + +@Suppress("unused") +val spoofClientPatch = spoofClientPatch( + redirectUri = "http://redditsync/auth", +) { clientIdOption -> + dependsOn(disablePiracyDetectionPatch) + + compatibleWith( + "com.laurencedawson.reddit_sync", + "com.laurencedawson.reddit_sync.pro", + "com.laurencedawson.reddit_sync.dev", + ) + + val imgurImageAPIMatch by imgurImageAPIFingerprint() + val getAuthorizationStringMatch by getAuthorizationStringFingerprint() + val getUserAgentMatch by getUserAgentFingerprint() + + val clientId by clientIdOption + + execute { context -> + // region Patch client id. + + getBearerTokenFingerprint.apply { + match(context, getAuthorizationStringMatch.classDef) + }.matchOrThrow.mutableMethod.apply { + val auth = Base64.getEncoder().encodeToString("$clientId:".toByteArray(Charsets.UTF_8)) + addInstructions( + 0, + """ + const-string v0, "Basic $auth" + return-object v0 + """, + ) + val occurrenceIndex = + getAuthorizationStringMatch.stringMatches!!.first().index + + getAuthorizationStringMatch.mutableMethod.apply { + val authorizationStringInstruction = getInstruction(occurrenceIndex) + val targetRegister = (authorizationStringInstruction as OneRegisterInstruction).registerA + val reference = authorizationStringInstruction.reference as StringReference + + val newAuthorizationUrl = reference.string.replace( + "client_id=.*?&".toRegex(), + "client_id=$clientId&", + ) + + replaceInstruction( + occurrenceIndex, + "const-string v$targetRegister, \"$newAuthorizationUrl\"", + ) + } + } + + // endregion + + // region Patch user agent. + + // Use a random user agent. + val randomName = (0..100000).random() + val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)" + + imgurImageAPIMatch.mutableMethod.replaceInstruction( + 0, + """ + const-string v0, "$userAgent" + return-object v0 + """, + ) + + // endregion + + // region Patch Imgur API URL. + + val apiUrlIndex = getUserAgentMatch.stringMatches!!.first().index + getUserAgentMatch.mutableMethod.replaceInstruction( + apiUrlIndex, + "const-string v1, \"https://api.imgur.com/3/image\"", + ) + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatch.kt new file mode 100644 index 0000000000..67f02676f3 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/SharedExtensionPatch.kt @@ -0,0 +1,6 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.extension + +import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.hooks.initHook +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch(initHook) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt new file mode 100644 index 0000000000..00244b4df7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/extension/hooks/InitHook.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.hooks + +import app.revanced.patches.shared.misc.extension.extensionHook + +internal val initHook = extensionHook( + insertIndexResolver = { 1 }, // Insert after call to super class. +) { + custom { method, classDef -> + method.name == "onCreate" && classDef.type == "Lcom/laurencedawson/reddit_sync/RedditApplication;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt new file mode 100644 index 0000000000..f7287fcc39 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/Fingerprints.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.slink + +import app.revanced.patcher.fingerprint + +internal val linkHelperOpenLinkFingerprint = fingerprint { + strings("Link title: ") +} + +internal val setAuthorizationHeaderFingerprint = fingerprint { + returns("Ljava/util/HashMap;") + strings("Authorization", "bearer ") + custom { method, _ -> method.definingClass == "Lcom/laurencedawson/reddit_sync/singleton/a;" } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt new file mode 100644 index 0000000000..80da3f6bcf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatch.kt @@ -0,0 +1,57 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.slink + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.reddit.customclients.RESOLVE_S_LINK_METHOD +import app.revanced.patches.reddit.customclients.SET_ACCESS_TOKEN_METHOD +import app.revanced.patches.reddit.customclients.fixSLinksPatch +import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.sharedExtensionPatch + +const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/syncforreddit/FixSLinksPatch;" + +@Suppress("unused") +val fixSLinksPatch = fixSLinksPatch( + extensionPatch = sharedExtensionPatch, +) { + compatibleWith( + "com.laurencedawson.reddit_sync", + "com.laurencedawson.reddit_sync.pro", + "com.laurencedawson.reddit_sync.dev", + ) + + val handleNavigationMatch by linkHelperOpenLinkFingerprint() + val setAccessTokenMatch by setAuthorizationHeaderFingerprint() + + execute { + // region Patch navigation handler. + + handleNavigationMatch.mutableMethod.apply { + val urlRegister = "p3" + val tempRegister = "v2" + + addInstructionsWithLabels( + 0, + """ + invoke-static { $urlRegister }, $EXTENSION_CLASS_DESCRIPTOR->$RESOLVE_S_LINK_METHOD + move-result $tempRegister + if-eqz $tempRegister, :continue + return $tempRegister + """, + ExternalLabel("continue", getInstruction(0)), + ) + } + + // endregion + + // region Patch set access token. + + setAccessTokenMatch.mutableMethod.addInstruction( + 0, + "invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->$SET_ACCESS_TOKEN_METHOD", + ) + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt new file mode 100644 index 0000000000..4bac74de77 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/Fingerprints.kt @@ -0,0 +1,36 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.user + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal fun userEndpointFingerprint(source: String, accessFlags: Set? = null) = fingerprint { + strings("u/") + custom { _, classDef -> classDef.sourceFile == source } + accessFlags(*accessFlags?.toTypedArray() ?: return@fingerprint) +} + +internal val oAuthFriendRequestFingerprint = userEndpointFingerprint( + "OAuthFriendRequest.java", +) + +internal val oAuthUnfriendRequestFingerprint = userEndpointFingerprint( + "OAuthUnfriendRequest.java", +) + +internal val oAuthUserIdRequestFingerprint = userEndpointFingerprint( + "OAuthUserIdRequest.java", +) + +internal val oAuthUserInfoRequestFingerprint = userEndpointFingerprint( + "OAuthUserInfoRequest.java", +) + +internal val oAuthSubredditInfoRequestConstructorFingerprint = userEndpointFingerprint( + "OAuthSubredditInfoRequest.java", + setOf(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR), +) + +internal val oAuthSubredditInfoRequestHelperFingerprint = userEndpointFingerprint( + "OAuthSubredditInfoRequest.java", + setOf(AccessFlags.PRIVATE, AccessFlags.STATIC), +) diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt new file mode 100644 index 0000000000..a53007045e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/user/UseUserEndpointPatch.kt @@ -0,0 +1,51 @@ +package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.user + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.StringReference + +@Suppress("unused") +val useUserEndpointPatch = bytecodePatch( + name = "Use /user/ endpoint", + description = "Replaces the deprecated endpoint for viewing user profiles /u with /user, that used to fix a bug.", + use = false, + +) { + compatibleWith( + "com.laurencedawson.reddit_sync", + "com.laurencedawson.reddit_sync.pro", + "com.laurencedawson.reddit_sync.dev", + ) + + val oAuthFriendRequestMatch by oAuthFriendRequestFingerprint() + val oAuthSubredditInfoRequestConstructorMatch by oAuthSubredditInfoRequestConstructorFingerprint() + val oAuthSubredditInfoRequestHelperMatch by oAuthSubredditInfoRequestHelperFingerprint() + val oAuthUnfriendRequestMatch by oAuthUnfriendRequestFingerprint() + val oAuthUserIdRequestMatch by oAuthUserIdRequestFingerprint() + val oAuthUserInfoRequestMatch by oAuthUserInfoRequestFingerprint() + + execute { + arrayOf( + oAuthFriendRequestMatch, + oAuthSubredditInfoRequestConstructorMatch, + oAuthSubredditInfoRequestHelperMatch, + oAuthUnfriendRequestMatch, + oAuthUserIdRequestMatch, + oAuthUserInfoRequestMatch, + ).map { it.stringMatches!!.first().index to it.mutableMethod }.forEach { (userPathStringIndex, method) -> + val userPathStringInstruction = method.getInstruction(userPathStringIndex) + + val userPathStringRegister = userPathStringInstruction.registerA + val fixedUserPathString = userPathStringInstruction.getReference()!! + .string.replace("u/", "user/") + + method.replaceInstruction( + userPathStringIndex, + "const-string v$userPathStringRegister, \"${fixedUserPathString}\"", + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/fingerprints/ParseRedditVideoNetworkResponseFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/Fingerprints.kt similarity index 53% rename from src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/fingerprints/ParseRedditVideoNetworkResponseFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/Fingerprints.kt index 1a06cd54d6..3123563ee8 100644 --- a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/fingerprints/ParseRedditVideoNetworkResponseFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/Fingerprints.kt @@ -1,16 +1,16 @@ -package app.revanced.patches.reddit.customclients.syncforreddit.fix.video.fingerprints +package app.revanced.patches.reddit.customclients.syncforreddit.fix.video -import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patcher.fingerprint import com.android.tools.smali.dexlib2.Opcode -internal object ParseRedditVideoNetworkResponseFingerprint : MethodFingerprint( - opcodes = listOf( +internal val parseRedditVideoNetworkResponseFingerprint = fingerprint { + opcodes( Opcode.NEW_INSTANCE, Opcode.IGET_OBJECT, Opcode.INVOKE_DIRECT, - Opcode.CONST_WIDE_32 - ), - customFingerprint = { methodDef, classDef -> + Opcode.CONST_WIDE_32, + ) + custom { methodDef, classDef -> classDef.sourceFile == "RedditVideoRequest.java" && methodDef.name == "parseNetworkResponse" } -) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt new file mode 100644 index 0000000000..a43e13fe68 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch.kt @@ -0,0 +1,57 @@ +package app.revanced.patches.reddit.customclients.syncforreddit.fix.video + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.sharedExtensionPatch +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/syncforreddit/FixRedditVideoDownloadPatch;" +private const val GET_LINKS_METHOD = "getLinks([B)[Ljava/lang/String;" + +@Suppress("unused") +val fixVideoDownloadsPatch = bytecodePatch( + name = "Fix video downloads", + description = "Fixes a bug in Sync's MPD parser resulting in only the audio-track being saved.", +) { + dependsOn(sharedExtensionPatch) + + compatibleWith( + "com.laurencedawson.reddit_sync", + "com.laurencedawson.reddit_sync.pro", + "com.laurencedawson.reddit_sync.dev", + ) + + val parseRedditVideoNetworkResponseMatch by parseRedditVideoNetworkResponseFingerprint() + + execute { + val scanResult = parseRedditVideoNetworkResponseMatch.patternMatch!! + val newInstanceIndex = scanResult.startIndex + val invokeDirectIndex = scanResult.endIndex - 1 + + val buildResponseInstruction = parseRedditVideoNetworkResponseMatch.mutableMethod.getInstruction(invokeDirectIndex) + + parseRedditVideoNetworkResponseMatch.mutableMethod.addInstructions( + newInstanceIndex + 1, + """ + # Get byte array from response. + iget-object v2, p1, Lcom/android/volley/NetworkResponse;->data:[B + + # Parse the videoUrl and audioUrl from the byte array. + invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->$GET_LINKS_METHOD + move-result-object v2 + + # Get videoUrl (Index 0). + const/4 v5, 0x0 + aget-object v${buildResponseInstruction.registerE}, v2, v5 + + # Get audioUrl (Index 1). + const/4 v6, 0x1 + aget-object v${buildResponseInstruction.registerF}, v2, v6 + + # Register E and F are used to build the response. + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt new file mode 100644 index 0000000000..116205a34a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.reddit.layout.disablescreenshotpopup + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableScreenshotPopupPatch = bytecodePatch( + name = "Disable screenshot popup", + description = "Disables the popup that shows up when taking a screenshot.", +) { + compatibleWith("com.reddit.frontpage") + + val disableScreenshotPopupMatch by disableScreenshotPopupFingerprint() + + execute { + disableScreenshotPopupMatch.mutableMethod.addInstruction(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt new file mode 100644 index 0000000000..09fe16247e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/disablescreenshotpopup/Fingerprints.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.reddit.layout.disablescreenshotpopup + +import app.revanced.patcher.fingerprint + +internal val disableScreenshotPopupFingerprint = fingerprint { + returns("V") + parameters("Landroidx/compose/runtime/", "I") + custom { method, classDef -> + if (!classDef.endsWith("\$ScreenshotTakenBannerKt\$lambda-1\$1;")) { + return@custom false + } + + method.name == "invoke" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt new file mode 100644 index 0000000000..2eac1cbe26 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/Fingerprints.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.reddit.layout.premiumicon + +import app.revanced.patcher.fingerprint + +internal val hasPremiumIconAccessFingerprint = fingerprint { + returns("Z") + custom { method, classDef -> + classDef.endsWith("MyAccount;") && method.name == "isPremiumSubscriber" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt new file mode 100644 index 0000000000..8a3a0f810b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/layout/premiumicon/UnlockPremiumIconPatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.reddit.layout.premiumicon + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockPremiumIconPatch = bytecodePatch( + name = "Unlock premium Reddit icons", + description = "Unlocks the premium Reddit icons.", +) { + compatibleWith("com.reddit.frontpage") + + val hasPremiumIconAccessMatch by hasPremiumIconAccessFingerprint() + + execute { + hasPremiumIconAccessMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/extension/ExtensionPatch.kt new file mode 100644 index 0000000000..db449a93ed --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/extension/ExtensionPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.reddit.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch() diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/Fingerprints.kt new file mode 100644 index 0000000000..3381fd2bb2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.reddit.misc.tracking.url + +import app.revanced.patcher.fingerprint + +internal val shareLinkFormatterFingerprint = fingerprint { + custom { _, classDef -> + classDef.startsWith("Lcom/reddit/sharing/") && classDef.sourceFile == "UrlUtil.kt" + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt new file mode 100644 index 0000000000..fb14ca80cf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQueryPatch.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.reddit.misc.tracking.url + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val sanitizeUrlQueryPatch = bytecodePatch( + name = "Sanitize sharing links", + description = "Removes (tracking) query parameters from the URLs when sharing links.", +) { + compatibleWith("com.reddit.frontpage") + + val shareLinkFormatterMatch by shareLinkFormatterFingerprint() + + execute { + shareLinkFormatterMatch.mutableMethod.addInstructions( + 0, + "return-object p0", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt new file mode 100644 index 0000000000..f7efe3103f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.serviceportalbund.detection.root + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val rootDetectionFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("V") + custom { _, classDef -> + classDef.endsWith("/DeviceIntegrityCheck;") + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt new file mode 100644 index 0000000000..40723ca276 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.serviceportalbund.detection.root + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val rootDetectionPatch = bytecodePatch( + name = "Remove root detection", + description = "Removes the check for root permissions and unlocked bootloader.", +) { + compatibleWith("at.gv.bka.serviceportal") + + val rootDetectionMatch by rootDetectionFingerprint() + + execute { + rootDetectionMatch.mutableMethod.addInstruction(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt new file mode 100644 index 0000000000..df927dd4a7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.shared + +import app.revanced.patcher.fingerprint + +internal val castContextFetchFingerprint = fingerprint { + strings("Error fetching CastContext.") +} + +internal val primeMethodFingerprint = fingerprint { + strings("com.google.android.GoogleCamera", "com.android.vending") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt new file mode 100644 index 0000000000..8c3066c918 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentPatch.kt @@ -0,0 +1,111 @@ +package app.revanced.patches.shared.misc.checks + +import android.os.Build.* +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.Patch +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue +import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableLongEncodedValue +import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableStringEncodedValue +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import com.android.tools.smali.dexlib2.immutable.value.ImmutableLongEncodedValue +import com.android.tools.smali.dexlib2.immutable.value.ImmutableStringEncodedValue +import java.nio.charset.StandardCharsets +import java.security.MessageDigest +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/shared/checks/CheckEnvironmentPatch;" + +fun checkEnvironmentPatch( + mainActivityOnCreateFingerprint: Fingerprint, + extensionPatch: Patch<*>, + vararg compatiblePackages: String, +) = bytecodePatch( + description = "Checks, if the application was patched by, otherwise warns the user.", +) { + compatibleWith(*compatiblePackages) + + dependsOn( + extensionPatch, + addResourcesPatch, + ) + + val patchInfoMatch by patchInfoFingerprint() + val patchInfoBuildMatch by patchInfoBuildFingerprint() + val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint() + + execute { + addResources("shared", "misc.checks.checkEnvironmentPatch") + + fun setPatchInfo() { + patchInfoMatch.setClassFields( + "PATCH_TIME" to System.currentTimeMillis().encoded, + ) + + fun setBuildInfo() { + patchInfoBuildMatch.setClassFields( + "PATCH_BOARD" to BOARD.encodedAndHashed, + "PATCH_BOOTLOADER" to BOOTLOADER.encodedAndHashed, + "PATCH_BRAND" to BRAND.encodedAndHashed, + "PATCH_CPU_ABI" to CPU_ABI.encodedAndHashed, + "PATCH_CPU_ABI2" to CPU_ABI2.encodedAndHashed, + "PATCH_DEVICE" to DEVICE.encodedAndHashed, + "PATCH_DISPLAY" to DISPLAY.encodedAndHashed, + "PATCH_FINGERPRINT" to FINGERPRINT.encodedAndHashed, + "PATCH_HARDWARE" to HARDWARE.encodedAndHashed, + "PATCH_HOST" to HOST.encodedAndHashed, + "PATCH_ID" to ID.encodedAndHashed, + "PATCH_MANUFACTURER" to MANUFACTURER.encodedAndHashed, + "PATCH_MODEL" to MODEL.encodedAndHashed, + "PATCH_PRODUCT" to PRODUCT.encodedAndHashed, + "PATCH_RADIO" to RADIO.encodedAndHashed, + "PATCH_TAGS" to TAGS.encodedAndHashed, + "PATCH_TYPE" to TYPE.encodedAndHashed, + "PATCH_USER" to USER.encodedAndHashed, + ) + } + + try { + Class.forName("android.os.Build") + // This only works on Android, + // because it uses Android APIs. + setBuildInfo() + } catch (_: ClassNotFoundException) { + } + } + + fun invokeCheck() = mainActivityOnCreateMatch.mutableMethod?.addInstructions( + 0, + "invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V", + ) + + setPatchInfo() + invokeCheck() + } +} + +@OptIn(ExperimentalEncodingApi::class) +private val String.encodedAndHashed + get() = MutableStringEncodedValue( + ImmutableStringEncodedValue( + Base64.encode( + MessageDigest.getInstance("SHA-1") + .digest(this.toByteArray(StandardCharsets.UTF_8)), + ), + ), + ) + +private val Long.encoded get() = MutableLongEncodedValue(ImmutableLongEncodedValue(this)) + +private fun Match.setClassFields(vararg fieldNameValues: Pair) { + val fieldNameValueMap = mapOf(*fieldNameValues) + + mutableClass.fields.forEach { field -> + field.initialValue = fieldNameValueMap[field.name] ?: return@forEach + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt new file mode 100644 index 0000000000..0eabd2f547 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/checks/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.shared.misc.checks + +import app.revanced.patcher.fingerprint + +internal val patchInfoFingerprint = fingerprint { + custom { _, classDef -> classDef.type == "Lapp/revanced/extension/shared/checks/PatchInfo;" } +} + +internal val patchInfoBuildFingerprint = fingerprint { + custom { _, classDef -> classDef.type == "Lapp/revanced/extension/shared/checks/PatchInfo\$Build;" } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt new file mode 100644 index 0000000000..58cc5082f1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/Fingerprints.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.shared.misc.extension + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val revancedUtilsPatchesVersionFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Ljava/lang/String;") + parameters() + custom { method, _ -> + method.name == "getPatchesReleaseVersion" && method.definingClass == EXTENSION_CLASS_DESCRIPTOR + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt new file mode 100644 index 0000000000..3d7a1e4443 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/extension/SharedExtensionPatch.kt @@ -0,0 +1,100 @@ +package app.revanced.patches.shared.misc.extension + +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.FingerprintBuilder +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.fingerprint +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.exception +import com.android.tools.smali.dexlib2.iface.Method +import java.net.URLDecoder +import java.util.jar.JarFile + +internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/Utils;" + +fun sharedExtensionPatch( + vararg hooks: ExtensionHook, +) = bytecodePatch { + extendWith("extensions/shared.rve") + + val revancedUtilsPatchesVersionMatch by revancedUtilsPatchesVersionFingerprint() + hooks.forEach { it.fingerprint() } + + execute { context -> + if (context.classBy { EXTENSION_CLASS_DESCRIPTOR in it.type } == null) { + throw PatchException( + "Shared extension has not been merged yet. This patch can not succeed without merging it.", + ) + } + + hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) } + + // Modify Utils method to include the patches release version. + revancedUtilsPatchesVersionMatch.mutableMethod.apply { + /** + * @return The file path for the jar this classfile is contained inside. + */ + fun getCurrentJarFilePath(): String { + val className = object {}::class.java.enclosingClass.name.replace('.', '/') + ".class" + val classUrl = object {}::class.java.classLoader.getResource(className) + if (classUrl != null) { + val urlString = classUrl.toString() + + if (urlString.startsWith("jar:file:")) { + val end = urlString.lastIndexOf('!') + + return URLDecoder.decode(urlString.substring("jar:file:".length, end), "UTF-8") + } + } + throw IllegalStateException("Not running from inside a JAR file.") + } + + /** + * @return The value for the manifest entry, + * or "Unknown" if the entry does not exist or is blank. + */ + @Suppress("SameParameterValue") + fun getPatchesManifestEntry(attributeKey: String) = JarFile(getCurrentJarFilePath()).use { jarFile -> + jarFile.manifest.mainAttributes.entries.firstOrNull { it.key.toString() == attributeKey }?.value?.toString() + ?: "Unknown" + } + + val manifestValue = getPatchesManifestEntry("Version") + + addInstructions( + 0, + """ + const-string v0, "$manifestValue" + return-object v0 + """, + ) + } + } +} + +class ExtensionHook internal constructor( + val fingerprint: Fingerprint, + private val insertIndexResolver: ((Method) -> Int), + private val contextRegisterResolver: (Method) -> Int, +) { + operator fun invoke(extensionClassDescriptor: String) { + fingerprint.match?.mutableMethod?.let { method -> + val insertIndex = insertIndexResolver(method) + val contextRegister = contextRegisterResolver(method) + + method.addInstruction( + insertIndex, + "invoke-static/range { v$contextRegister .. v$contextRegister }, " + + "$extensionClassDescriptor->setContext(Landroid/content/Context;)V", + ) + } ?: throw fingerprint.exception + } +} + +fun extensionHook( + insertIndexResolver: ((Method) -> Int) = { 0 }, + contextRegisterResolver: (Method) -> Int = { it.implementation!!.registerCount - 1 }, + fingerprintBuilderBlock: FingerprintBuilder.() -> Unit, +) = ExtensionHook(fingerprint(block = fingerprintBuilderBlock), insertIndexResolver, contextRegisterResolver) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt new file mode 100644 index 0000000000..80d041ada8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/Fingerprints.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.shared.misc.fix.verticalscroll + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val canScrollVerticallyFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + opcodes( + Opcode.MOVE_RESULT, + Opcode.RETURN, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT + ) + custom { _, classDef -> classDef.endsWith("SwipeRefreshLayout;") } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt new file mode 100644 index 0000000000..2ebb6a2344 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/fix/verticalscroll/VerticalScrollPatch.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.shared.misc.fix.verticalscroll + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val verticalScrollPatch = bytecodePatch( + description = "Fixes issues with refreshing the feed when the first component is of type EmptyComponent.", +) { + val canScrollVerticallyMatch by canScrollVerticallyFingerprint() + + execute { + canScrollVerticallyMatch.mutableMethod.apply { + val moveResultIndex = canScrollVerticallyMatch.patternMatch!!.endIndex + val moveResultRegister = getInstruction(moveResultIndex).registerA + + val insertIndex = moveResultIndex + 1 + addInstruction( + insertIndex, + "const/4 v$moveResultRegister, 0x0", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt new file mode 100644 index 0000000000..b5f613d541 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.shared.misc.gms + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +const val GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME = "getGmsCoreVendorGroupId" + +internal val gmsCoreSupportFingerprint = fingerprint { + custom { _, classDef -> + classDef.endsWith("GmsCoreSupport;") + } +} + +internal val googlePlayUtilityFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("I") + parameters("L", "I") + strings( + "This should never happen.", + "MetadataValueReader", + "com.google.android.gms", + ) +} + +internal val serviceCheckFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("V") + parameters("L", "I") + strings("Google Play Services not available") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt new file mode 100644 index 0000000000..c338385bfc --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt @@ -0,0 +1,614 @@ +package app.revanced.patches.shared.misc.gms + +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.* +import app.revanced.patches.all.misc.packagename.changePackageNamePatch +import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.gms.Constants.ACTIONS +import app.revanced.patches.shared.misc.gms.Constants.AUTHORITIES +import app.revanced.patches.shared.misc.gms.Constants.PERMISSIONS +import app.revanced.util.exception +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import app.revanced.util.returnEarly +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.iface.reference.StringReference +import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference +import com.android.tools.smali.dexlib2.util.MethodUtil +import org.w3c.dom.Element +import org.w3c.dom.Node + +private const val PACKAGE_NAME_REGEX_PATTERN = "^[a-z]\\w*(\\.[a-z]\\w*)+\$" + +/** + * A patch that allows patched Google apps to run without root and under a different package name + * by using GmsCore instead of Google Play Services. + * + * @param fromPackageName The package name of the original app. + * @param toPackageName The package name to fall back to if no custom package name is specified in patch options. + * @param primeMethodFingerprint The fingerprint of the "prime" method that needs to be patched. + * @param earlyReturnFingerprints The fingerprints of methods that need to be returned early. + * @param mainActivityOnCreateFingerprint The fingerprint of the main activity onCreate method. + * @param extensionPatch The patch responsible for the extension. + * @param gmsCoreSupportResourcePatchFactory The factory for the corresponding resource patch + * that is used to patch the resources. + * @param executeBlock The additional execution block of the patch. + * @param block The additional block to build the patch. + */ +fun gmsCoreSupportPatch( + fromPackageName: String, + toPackageName: String, + primeMethodFingerprint: Fingerprint? = null, + earlyReturnFingerprints: Set = setOf(), + mainActivityOnCreateFingerprint: Fingerprint, + extensionPatch: Patch<*>, + gmsCoreSupportResourcePatchFactory: (gmsCoreVendorGroupIdOption: Option) -> Patch<*>, + executeBlock: Patch.(BytecodePatchContext) -> Unit = {}, + block: BytecodePatchBuilder.() -> Unit = {}, +) = bytecodePatch( + name = "GmsCore support", + description = "Allows patched Google apps to run without root and under a different package name " + + "by using GmsCore instead of Google Play Services.", +) { + val gmsCoreVendorGroupIdOption = stringOption( + key = "gmsCoreVendorGroupId", + default = "app.revanced", + values = + mapOf( + "ReVanced" to "app.revanced", + ), + title = "GmsCore vendor group ID", + description = "The vendor's group ID for GmsCore.", + required = true, + ) { it!!.matches(Regex(PACKAGE_NAME_REGEX_PATTERN)) } + + dependsOn( + changePackageNamePatch, + gmsCoreSupportResourcePatchFactory(gmsCoreVendorGroupIdOption), + extensionPatch, + ) + + val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption + + val gmsCoreSupportMatch by gmsCoreSupportFingerprint() + val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint() + primeMethodFingerprint?.invoke() + googlePlayUtilityFingerprint() + serviceCheckFingerprint() + earlyReturnFingerprints.forEach { it() } + + execute { context -> + fun transformStringReferences(transform: (str: String) -> String?) = context.classes.forEach { + val mutableClass by lazy { + context.proxy(it).mutableClass + } + + it.methods.forEach classLoop@{ method -> + val implementation = method.implementation ?: return@classLoop + + val mutableMethod by lazy { + mutableClass.methods.first { MethodUtil.methodSignaturesMatch(it, method) } + } + + implementation.instructions.forEachIndexed insnLoop@{ index, instruction -> + val string = ((instruction as? Instruction21c)?.reference as? StringReference)?.string + ?: return@insnLoop + + // Apply transformation. + val transformedString = transform(string) ?: return@insnLoop + + mutableMethod.replaceInstruction( + index, + BuilderInstruction21c( + Opcode.CONST_STRING, + instruction.registerA, + ImmutableStringReference(transformedString), + ), + ) + } + } + } + + // region Collection of transformations that are applied to all strings. + + fun commonTransform(referencedString: String): String? = + when (referencedString) { + "com.google", + "com.google.android.gms", + in PERMISSIONS, + in ACTIONS, + in AUTHORITIES, + -> referencedString.replace("com.google", gmsCoreVendorGroupId!!) + + // No vendor prefix for whatever reason... + "subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds" + else -> null + } + + fun contentUrisTransform(str: String): String? { + // only when content:// uri + if (str.startsWith("content://")) { + // check if matches any authority + for (authority in AUTHORITIES) { + val uriPrefix = "content://$authority" + if (str.startsWith(uriPrefix)) { + return str.replace( + uriPrefix, + "content://${authority.replace("com.google", gmsCoreVendorGroupId!!)}", + ) + } + } + + // gms also has a 'subscribedfeeds' authority, check for that one too + val subFeedsUriPrefix = "content://subscribedfeeds" + if (str.startsWith(subFeedsUriPrefix)) { + return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendorGroupId.subscribedfeeds") + } + } + + return null + } + + fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? = { string -> + when (string) { + "$fromPackageName.SuggestionsProvider", + "$fromPackageName.fileprovider", + -> string.replace(fromPackageName, toPackageName) + + else -> null + } + } + + fun transformPrimeMethod(packageName: String) { + primeMethodFingerprint!!.match?.mutableMethod?.apply { + var register = 2 + + val index = instructions.indexOfFirst { + if (it.getReference()?.string != fromPackageName) return@indexOfFirst false + + register = (it as OneRegisterInstruction).registerA + return@indexOfFirst true + } + + replaceInstruction(index, "const-string v$register, \"$packageName\"") + } ?: throw primeMethodFingerprint.exception + } + + // endregion + + val packageName = setOrGetFallbackPackageName(toPackageName) + + // Transform all strings using all provided transforms, first match wins. + val transformations = arrayOf( + ::commonTransform, + ::contentUrisTransform, + packageNameTransform(fromPackageName, packageName), + ) + transformStringReferences transform@{ string -> + transformations.forEach { transform -> + transform(string)?.let { transformedString -> return@transform transformedString } + } + + return@transform null + } + + // Specific method that needs to be patched. + primeMethodFingerprint?.let { transformPrimeMethod(packageName) } + + // Return these methods early to prevent the app from crashing. + earlyReturnFingerprints.returnEarly() + serviceCheckFingerprint.returnEarly() + + // Google Play Utility is not present in all apps, so we need to check if it's present. + if (googlePlayUtilityFingerprint.match != null) { + googlePlayUtilityFingerprint.returnEarly() + } + + // Verify GmsCore is installed and whitelisted for power optimizations and background usage. + mainActivityOnCreateMatch.mutableMethod.apply { + // Temporary fix for patches with an extension patch that hook the onCreate method as well. + val setContextIndex = indexOfFirstInstruction { + val reference = getReference() ?: return@indexOfFirstInstruction false + + reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V" + } + + // Add after setContext call, because this patch needs the context. + addInstructions( + if (setContextIndex < 0) 0 else setContextIndex + 1, + "invoke-static/range { p0 .. p0 }, Lapp/revanced/extension/shared/GmsCoreSupport;->" + + "checkGmsCore(Landroid/app/Activity;)V", + ) + } + + // Change the vendor of GmsCore in the extension. + gmsCoreSupportMatch.mutableClass.methods + .single { it.name == GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME } + .replaceInstruction(0, "const-string v0, \"$gmsCoreVendorGroupId\"") + + executeBlock(context) + } + + block() +} + +/** + * A collection of permissions, intents and content provider authorities + * that are present in GmsCore which need to be transformed. + */ +private object Constants { + /** + * All permissions. + */ + val PERMISSIONS = setOf( + "com.google.android.c2dm.permission.RECEIVE", + "com.google.android.c2dm.permission.SEND", + "com.google.android.gms.auth.api.phone.permission.SEND", + "com.google.android.gms.permission.AD_ID", + "com.google.android.gms.permission.AD_ID_NOTIFICATION", + "com.google.android.gms.permission.CAR_FUEL", + "com.google.android.gms.permission.CAR_INFORMATION", + "com.google.android.gms.permission.CAR_MILEAGE", + "com.google.android.gms.permission.CAR_SPEED", + "com.google.android.gms.permission.CAR_VENDOR_EXTENSION", + "com.google.android.googleapps.permission.GOOGLE_AUTH", + "com.google.android.googleapps.permission.GOOGLE_AUTH.cp", + "com.google.android.googleapps.permission.GOOGLE_AUTH.local", + "com.google.android.googleapps.permission.GOOGLE_AUTH.mail", + "com.google.android.googleapps.permission.GOOGLE_AUTH.writely", + "com.google.android.gtalkservice.permission.GTALK_SERVICE", + "com.google.android.providers.gsf.permission.READ_GSERVICES", + ) + + /** + * All intent actions. + */ + val ACTIONS = setOf( + "com.google.android.c2dm.intent.RECEIVE", + "com.google.android.c2dm.intent.REGISTER", + "com.google.android.c2dm.intent.REGISTRATION", + "com.google.android.c2dm.intent.UNREGISTER", + "com.google.android.contextmanager.service.ContextManagerService.START", + "com.google.android.gcm.intent.SEND", + "com.google.android.gms.accounts.ACCOUNT_SERVICE", + "com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS", + "com.google.android.gms.accountsettings.action.BROWSE_SETTINGS", + "com.google.android.gms.accountsettings.action.VIEW_SETTINGS", + "com.google.android.gms.accountsettings.MY_ACCOUNT", + "com.google.android.gms.accountsettings.PRIVACY_SETTINGS", + "com.google.android.gms.accountsettings.SECURITY_SETTINGS", + "com.google.android.gms.ads.gservice.START", + "com.google.android.gms.ads.identifier.service.EVENT_ATTESTATION", + "com.google.android.gms.ads.service.CACHE", + "com.google.android.gms.ads.service.CONSENT_LOOKUP", + "com.google.android.gms.ads.service.HTTP", + "com.google.android.gms.analytics.service.START", + "com.google.android.gms.app.settings.GoogleSettingsLink", + "com.google.android.gms.appstate.service.START", + "com.google.android.gms.appusage.service.START", + "com.google.android.gms.asterism.service.START", + "com.google.android.gms.audiomodem.service.AudioModemService.START", + "com.google.android.gms.audit.service.START", + "com.google.android.gms.auth.account.authapi.START", + "com.google.android.gms.auth.account.authenticator.auto.service.START", + "com.google.android.gms.auth.account.authenticator.chromeos.START", + "com.google.android.gms.auth.account.authenticator.tv.service.START", + "com.google.android.gms.auth.account.data.service.START", + "com.google.android.gms.auth.api.credentials.PICKER", + "com.google.android.gms.auth.api.credentials.service.START", + "com.google.android.gms.auth.api.identity.service.authorization.START", + "com.google.android.gms.auth.api.identity.service.credentialsaving.START", + "com.google.android.gms.auth.api.identity.service.signin.START", + "com.google.android.gms.auth.api.phone.service.InternalService.START", + "com.google.android.gms.auth.api.signin.service.START", + "com.google.android.gms.auth.be.appcert.AppCertService", + "com.google.android.gms.auth.blockstore.service.START", + "com.google.android.gms.auth.config.service.START", + "com.google.android.gms.auth.cryptauth.cryptauthservice.START", + "com.google.android.gms.auth.GOOGLE_SIGN_IN", + "com.google.android.gms.auth.login.LOGIN", + "com.google.android.gms.auth.proximity.devicesyncservice.START", + "com.google.android.gms.auth.proximity.securechannelservice.START", + "com.google.android.gms.auth.proximity.START", + "com.google.android.gms.auth.service.START", + "com.google.android.gms.backup.ACTION_BACKUP_SETTINGS", + "com.google.android.gms.backup.G1_BACKUP", + "com.google.android.gms.backup.G1_RESTORE", + "com.google.android.gms.backup.GMS_MODULE_RESTORE", + "com.google.android.gms.beacon.internal.IBleService.START", + "com.google.android.gms.car.service.START", + "com.google.android.gms.carrierauth.service.START", + "com.google.android.gms.cast.firstparty.START", + "com.google.android.gms.cast.remote_display.service.START", + "com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE", + "com.google.android.gms.cast_mirroring.service.START", + "com.google.android.gms.checkin.BIND_TO_SERVICE", + "com.google.android.gms.chromesync.service.START", + "com.google.android.gms.clearcut.service.START", + "com.google.android.gms.common.account.CHOOSE_ACCOUNT", + "com.google.android.gms.common.download.START", + "com.google.android.gms.common.service.START", + "com.google.android.gms.common.telemetry.service.START", + "com.google.android.gms.config.START", + "com.google.android.gms.constellation.service.START", + "com.google.android.gms.credential.manager.service.firstparty.START", + "com.google.android.gms.deviceconnection.service.START", + "com.google.android.gms.drive.ApiService.RESET_AFTER_BOOT", + "com.google.android.gms.drive.ApiService.START", + "com.google.android.gms.drive.ApiService.STOP", + "com.google.android.gms.droidguard.service.INIT", + "com.google.android.gms.droidguard.service.PING", + "com.google.android.gms.droidguard.service.START", + "com.google.android.gms.enterprise.loader.service.START", + "com.google.android.gms.facs.cache.service.START", + "com.google.android.gms.facs.internal.service.START", + "com.google.android.gms.feedback.internal.IFeedbackService", + "com.google.android.gms.fido.credentialstore.internal_service.START", + "com.google.android.gms.fido.fido2.privileged.START", + "com.google.android.gms.fido.fido2.regular.START", + "com.google.android.gms.fido.fido2.zeroparty.START", + "com.google.android.gms.fido.sourcedevice.service.START", + "com.google.android.gms.fido.targetdevice.internal_service.START", + "com.google.android.gms.fido.u2f.privileged.START", + "com.google.android.gms.fido.u2f.thirdparty.START", + "com.google.android.gms.fido.u2f.zeroparty.START", + "com.google.android.gms.fitness.BleApi", + "com.google.android.gms.fitness.ConfigApi", + "com.google.android.gms.fitness.GoalsApi", + "com.google.android.gms.fitness.GoogleFitnessService.START", + "com.google.android.gms.fitness.HistoryApi", + "com.google.android.gms.fitness.InternalApi", + "com.google.android.gms.fitness.RecordingApi", + "com.google.android.gms.fitness.SensorsApi", + "com.google.android.gms.fitness.SessionsApi", + "com.google.android.gms.fonts.service.START", + "com.google.android.gms.freighter.service.START", + "com.google.android.gms.games.internal.connect.service.START", + "com.google.android.gms.games.PLAY_GAMES_UPGRADE", + "com.google.android.gms.games.service.START", + "com.google.android.gms.gass.START", + "com.google.android.gms.gmscompliance.service.START", + "com.google.android.gms.googlehelp.HELP", + "com.google.android.gms.googlehelp.service.GoogleHelpService.START", + "com.google.android.gms.growth.service.START", + "com.google.android.gms.herrevad.services.LightweightNetworkQualityAndroidService.START", + "com.google.android.gms.icing.INDEX_SERVICE", + "com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE", + "com.google.android.gms.identity.service.BIND", + "com.google.android.gms.inappreach.service.START", + "com.google.android.gms.instantapps.START", + "com.google.android.gms.kids.service.START", + "com.google.android.gms.languageprofile.service.START", + "com.google.android.gms.learning.internal.dynamitesupport.START", + "com.google.android.gms.learning.intservice.START", + "com.google.android.gms.learning.predictor.START", + "com.google.android.gms.learning.trainer.START", + "com.google.android.gms.learning.training.background.START", + "com.google.android.gms.location.places.GeoDataApi", + "com.google.android.gms.location.places.PlaceDetectionApi", + "com.google.android.gms.location.places.PlacesApi", + "com.google.android.gms.location.reporting.service.START", + "com.google.android.gms.location.settings.LOCATION_HISTORY", + "com.google.android.gms.location.settings.LOCATION_REPORTING_SETTINGS", + "com.google.android.gms.locationsharing.api.START", + "com.google.android.gms.locationsharingreporter.service.START", + "com.google.android.gms.lockbox.service.START", + "com.google.android.gms.matchstick.lighter.service.START", + "com.google.android.gms.mdm.services.DeviceManagerApiService.START", + "com.google.android.gms.mdm.services.START", + "com.google.android.gms.mdns.service.START", + "com.google.android.gms.measurement.START", + "com.google.android.gms.nearby.bootstrap.service.NearbyBootstrapService.START", + "com.google.android.gms.nearby.connection.service.START", + "com.google.android.gms.nearby.fastpair.START", + "com.google.android.gms.nearby.messages.service.NearbyMessagesService.START", + "com.google.android.gms.nearby.sharing.service.NearbySharingService.START", + "com.google.android.gms.nearby.sharing.START_SERVICE", + "com.google.android.gms.notifications.service.START", + "com.google.android.gms.ocr.service.internal.START", + "com.google.android.gms.ocr.service.START", + "com.google.android.gms.oss.licenses.service.START", + "com.google.android.gms.payse.service.BIND", + "com.google.android.gms.people.contactssync.service.START", + "com.google.android.gms.people.service.START", + "com.google.android.gms.phenotype.service.START", + "com.google.android.gms.photos.autobackup.service.START", + "com.google.android.gms.playlog.service.START", + "com.google.android.gms.plus.service.default.INTENT", + "com.google.android.gms.plus.service.image.INTENT", + "com.google.android.gms.plus.service.internal.START", + "com.google.android.gms.plus.service.START", + "com.google.android.gms.potokens.service.START", + "com.google.android.gms.pseudonymous.service.START", + "com.google.android.gms.rcs.START", + "com.google.android.gms.reminders.service.START", + "com.google.android.gms.romanesco.MODULE_BACKUP_AGENT", + "com.google.android.gms.romanesco.service.START", + "com.google.android.gms.safetynet.service.START", + "com.google.android.gms.scheduler.ACTION_PROXY_SCHEDULE", + "com.google.android.gms.search.service.SEARCH_AUTH_START", + "com.google.android.gms.semanticlocation.service.START_ODLH", + "com.google.android.gms.sesame.service.BIND", + "com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS", + "com.google.android.gms.setup.auth.SecondDeviceAuth.START", + "com.google.android.gms.signin.service.START", + "com.google.android.gms.smartdevice.d2d.SourceDeviceService.START", + "com.google.android.gms.smartdevice.d2d.TargetDeviceService.START", + "com.google.android.gms.smartdevice.directtransfer.SourceDirectTransferService.START", + "com.google.android.gms.smartdevice.directtransfer.TargetDirectTransferService.START", + "com.google.android.gms.smartdevice.postsetup.PostSetupService.START", + "com.google.android.gms.smartdevice.setup.accounts.AccountsService.START", + "com.google.android.gms.smartdevice.wifi.START_WIFI_HELPER_SERVICE", + "com.google.android.gms.social.location.activity.service.START", + "com.google.android.gms.speech.service.START", + "com.google.android.gms.statementservice.EXECUTE", + "com.google.android.gms.stats.ACTION_UPLOAD_DROPBOX_ENTRIES", + "com.google.android.gms.tapandpay.service.BIND", + "com.google.android.gms.telephonyspam.service.START", + "com.google.android.gms.testsupport.service.START", + "com.google.android.gms.thunderbird.service.START", + "com.google.android.gms.trustagent.BridgeApi.START", + "com.google.android.gms.trustagent.StateApi.START", + "com.google.android.gms.trustagent.trustlet.trustletmanagerservice.BIND", + "com.google.android.gms.trustlet.bluetooth.service.BIND", + "com.google.android.gms.trustlet.connectionlessble.service.BIND", + "com.google.android.gms.trustlet.face.service.BIND", + "com.google.android.gms.trustlet.nfc.service.BIND", + "com.google.android.gms.trustlet.onbody.service.BIND", + "com.google.android.gms.trustlet.place.service.BIND", + "com.google.android.gms.trustlet.voiceunlock.service.BIND", + "com.google.android.gms.udc.service.START", + "com.google.android.gms.update.START_API_SERVICE", + "com.google.android.gms.update.START_SERVICE", + "com.google.android.gms.update.START_SINGLE_USER_API_SERVICE", + "com.google.android.gms.update.START_TV_API_SERVICE", + "com.google.android.gms.usagereporting.service.START", + "com.google.android.gms.userlocation.service.START", + "com.google.android.gms.vehicle.cabin.service.START", + "com.google.android.gms.vehicle.climate.service.START", + "com.google.android.gms.vehicle.info.service.START", + "com.google.android.gms.wallet.service.BIND", + "com.google.android.gms.walletp2p.service.firstparty.BIND", + "com.google.android.gms.walletp2p.service.zeroparty.BIND", + "com.google.android.gms.wearable.BIND", + "com.google.android.gms.wearable.BIND_LISTENER", + "com.google.android.gms.wearable.DATA_CHANGED", + "com.google.android.gms.wearable.MESSAGE_RECEIVED", + "com.google.android.gms.wearable.NODE_CHANGED", + "com.google.android.gsf.action.GET_GLS", + "com.google.android.location.settings.LOCATION_REPORTING_SETTINGS", + "com.google.android.mdd.service.START", + "com.google.android.mdh.service.listener.START", + "com.google.android.mdh.service.START", + "com.google.android.mobstore.service.START", + "com.google.firebase.auth.api.gms.service.START", + "com.google.firebase.dynamiclinks.service.START", + "com.google.iid.TOKEN_REQUEST", + "com.google.android.gms.location.places.ui.PICK_PLACE", + ) + + /** + * All content provider authorities. + */ + val AUTHORITIES = setOf( + "com.google.android.gms.auth.accounts", + "com.google.android.gms.chimera", + "com.google.android.gms.fonts", + "com.google.android.gms.phenotype", + "com.google.android.gsf.gservices", + "com.google.settings", + ) +} + +/** + * Abstract resource patch that allows Google apps to run without root and under a different package name + * by using GmsCore instead of Google Play Services. + * + * @param fromPackageName The package name of the original app. + * @param toPackageName The package name to fall back to if no custom package name is specified in patch options. + * @param spoofedPackageSignature The signature of the package to spoof to. + * @param gmsCoreVendorGroupIdOption The option to get the vendor group ID of GmsCore. + * @param executeBlock The additional execution block of the patch. + * @param block The additional block to build the patch. + */ +fun gmsCoreSupportResourcePatch( + fromPackageName: String, + toPackageName: String, + spoofedPackageSignature: String, + gmsCoreVendorGroupIdOption: Option, + executeBlock: Patch.(ResourcePatchContext) -> Unit = {}, + block: ResourcePatchBuilder.() -> Unit = {}, +) = resourcePatch { + dependsOn( + changePackageNamePatch, + addResourcesPatch, + ) + + val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption + + execute { context -> + addResources("shared", "misc.gms.gmsCoreSupportResourcePatch") + + /** + * Add metadata to manifest to support spoofing the package name and signature of GmsCore. + */ + fun addSpoofingMetadata() { + fun Node.adoptChild( + tagName: String, + block: Element.() -> Unit, + ) { + val child = ownerDocument.createElement(tagName) + child.block() + appendChild(child) + } + + context.document["AndroidManifest.xml"].use { document -> + val applicationNode = + document + .getElementsByTagName("application") + .item(0) + + // Spoof package name and signature. + applicationNode.adoptChild("meta-data") { + setAttribute("android:name", "$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_NAME") + setAttribute("android:value", fromPackageName) + } + + applicationNode.adoptChild("meta-data") { + setAttribute("android:name", "$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_SIGNATURE") + setAttribute("android:value", spoofedPackageSignature) + } + + // GmsCore presence detection in extension. + applicationNode.adoptChild("meta-data") { + // TODO: The name of this metadata should be dynamic. + setAttribute("android:name", "app.revanced.MICROG_PACKAGE_NAME") + setAttribute("android:value", "$gmsCoreVendorGroupId.android.gms") + } + } + } + + /** + * Patch the manifest to support GmsCore. + */ + fun patchManifest() { + val packageName = setOrGetFallbackPackageName(toPackageName) + + val transformations = mapOf( + "package=\"$fromPackageName" to "package=\"$packageName", + "android:authorities=\"$fromPackageName" to "android:authorities=\"$packageName", + "$fromPackageName.permission.C2D_MESSAGE" to "$packageName.permission.C2D_MESSAGE", + "$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" to "$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION", + "com.google.android.c2dm" to "$gmsCoreVendorGroupId.android.c2dm", + "com.google.android.libraries.photos.api.mars" to "$gmsCoreVendorGroupId.android.apps.photos.api.mars", + "" to "", + ) + + val manifest = context["AndroidManifest.xml"] + manifest.writeText( + transformations.entries.fold(manifest.readText()) { acc, (from, to) -> + acc.replace( + from, + to, + ) + }, + ) + } + + patchManifest() + addSpoofingMetadata() + + executeBlock(context) + } + + block() +} diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatch.kt new file mode 100644 index 0000000000..e372d926c6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/hex/HexPatch.kt @@ -0,0 +1,123 @@ +package app.revanced.patches.shared.misc.hex + +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.rawResourcePatch +import kotlin.math.max + +// The replacements being passed using a function is intended. +// Previously the replacements were a property of the patch. Getter were being delegated to that property. +// This late evaluation was being leveraged in app.revanced.patches.all.misc.hex.HexPatch. +// Without the function, the replacements would be evaluated at the time of patch creation. +// This isn't possible because the delegated property is not accessible at that time. +fun hexPatch(replacementsSupplier: () -> Set) = rawResourcePatch { + execute { context -> + replacementsSupplier().groupBy { it.targetFilePath }.forEach { (targetFilePath, replacements) -> + val targetFile = try { + context[targetFilePath, true] + } catch (e: Exception) { + throw PatchException("Could not find target file: $targetFilePath") + } + + // TODO: Use a file channel to read and write the file instead of reading the whole file into memory, + // in order to reduce memory usage. + val targetFileBytes = targetFile.readBytes() + + replacements.forEach { replacement -> + replacement.replacePattern(targetFileBytes) + } + + targetFile.writeBytes(targetFileBytes) + } + } +} + +/** + * Represents a pattern to search for and its replacement pattern. + * + * @property pattern The pattern to search for. + * @property replacementPattern The pattern to replace the [pattern] with. + * @property targetFilePath The path to the file to make the changes in relative to the APK root. + */ +class Replacement( + private val pattern: String, + replacementPattern: String, + internal val targetFilePath: String, +) { + private val patternBytes = pattern.toByteArrayPattern() + private val replacementPattern = replacementPattern.toByteArrayPattern() + + init { + if (this.patternBytes.size != this.replacementPattern.size) { + throw PatchException("Pattern and replacement pattern must have the same length: $pattern") + } + } + + /** + * Replaces the [patternBytes] with the [replacementPattern] in the [targetFileBytes]. + * + * @param targetFileBytes The bytes of the file to make the changes in. + */ + fun replacePattern(targetFileBytes: ByteArray) { + val startIndex = indexOfPatternIn(targetFileBytes) + + if (startIndex == -1) { + throw PatchException("Pattern not found in target file: $pattern") + } + + replacementPattern.copyInto(targetFileBytes, startIndex) + } + + // TODO: Allow searching in a file channel instead of a byte array to reduce memory usage. + /** + * Returns the index of the first occurrence of [patternBytes] in the haystack + * using the Boyer-Moore algorithm. + * + * @param haystack The array to search in. + * + * @return The index of the first occurrence of the [patternBytes] in the haystack or -1 + * if the [patternBytes] is not found. + */ + private fun indexOfPatternIn(haystack: ByteArray): Int { + val needle = patternBytes + + val haystackLength = haystack.size - 1 + val needleLength = needle.size - 1 + val right = IntArray(256) { -1 } + + for (i in 0 until needleLength) right[needle[i].toInt().and(0xFF)] = i + + var skip: Int + for (i in 0..haystackLength - needleLength) { + skip = 0 + + for (j in needleLength - 1 downTo 0) { + if (needle[j] != haystack[i + j]) { + skip = max(1, j - right[haystack[i + j].toInt().and(0xFF)]) + + break + } + } + + if (skip == 0) return i + } + return -1 + } + + companion object { + /** + * Convert a string representing a pattern of hexadecimal bytes to a byte array. + * + * @return The byte array representing the pattern. + * @throws PatchException If the pattern is invalid. + */ + private fun String.toByteArrayPattern() = try { + split(" ").map { it.toInt(16).toByte() }.toByteArray() + } catch (e: NumberFormatException) { + throw PatchException( + "Could not parse pattern: $this. A pattern is a sequence of case insensitive strings " + + "representing hexadecimal bytes separated by spaces", + e, + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt similarity index 54% rename from src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt index 2c165e1f1c..ef198b4917 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt @@ -1,31 +1,33 @@ package app.revanced.patches.shared.misc.mapping -import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.patch.ResourcePatch +import app.revanced.patcher.patch.resourcePatch import org.w3c.dom.Element import java.util.* import java.util.concurrent.Executors import java.util.concurrent.TimeUnit -object ResourceMappingPatch : ResourcePatch() { - private val resourceMappings = Collections.synchronizedList(mutableListOf()) +// TODO: Probably renaming the patch/this is a good idea. +lateinit var resourceMappings: List + private set - private val THREAD_COUNT = Runtime.getRuntime().availableProcessors() - private val threadPoolExecutor = Executors.newFixedThreadPool(THREAD_COUNT) +val resourceMappingPatch = resourcePatch { + val threadCount = Runtime.getRuntime().availableProcessors() + val threadPoolExecutor = Executors.newFixedThreadPool(threadCount) - override fun execute(context: ResourceContext) { - // sSve the file in memory to concurrently read from it. - val resourceXmlFile = context.get("res/values/public.xml").readBytes() + val resourceMappings = Collections.synchronizedList(mutableListOf()) - for (threadIndex in 0 until THREAD_COUNT) { + execute { context -> + // Save the file in memory to concurrently read from it. + val resourceXmlFile = context["res/values/public.xml"].readBytes() + + for (threadIndex in 0 until threadCount) { threadPoolExecutor.execute thread@{ - context.xmlEditor[resourceXmlFile.inputStream()].use { editor -> - val document = editor.file + context.document[resourceXmlFile.inputStream()].use { document -> val resources = document.documentElement.childNodes val resourcesLength = resources.length - val jobSize = resourcesLength / THREAD_COUNT + val jobSize = resourcesLength / threadCount val batchStart = jobSize * threadIndex val batchEnd = jobSize * (threadIndex + 1) @@ -50,12 +52,13 @@ object ResourceMappingPatch : ResourcePatch() { } threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS) + + app.revanced.patches.shared.misc.mapping.resourceMappings = resourceMappings } +} - operator fun get(type: String, name: String) = - resourceMappings.firstOrNull { - it.type == type && it.name == name - }?.id ?: throw PatchException("Could not find resource type: $type name: $name") +operator fun List.get(type: String, name: String) = resourceMappings.firstOrNull { + it.type == type && it.name == name +}?.id ?: throw PatchException("Could not find resource type: $type name: $name") - data class ResourceElement(val type: String, val name: String, val id: Long) -} +data class ResourceElement internal constructor(val type: String, val name: String, val id: Long) diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt similarity index 57% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt index 1570cc3dc0..ea14b4a7b1 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/settings/BaseSettingsResourcePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt @@ -1,9 +1,9 @@ package app.revanced.patches.shared.misc.settings -import app.revanced.patcher.PatchClass -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patches.all.misc.resources.AddResourcesPatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResource +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.BasePreference import app.revanced.patches.shared.misc.settings.preference.IntentPreference import app.revanced.util.ResourceGroup @@ -11,42 +11,35 @@ import app.revanced.util.copyResources import app.revanced.util.getNode import app.revanced.util.insertFirst import org.w3c.dom.Node -import java.io.Closeable /** * A resource patch that adds settings to a settings fragment. * * @param rootPreference A pair of an intent preference and the name of the fragment file to add it to. * If null, no preference will be added. - * @param dependencies Additional dependencies of this patch. + * @param preferences A set of preferences to add to the ReVanced fragment. */ -abstract class BaseSettingsResourcePatch( - private val rootPreference: Pair? = null, - dependencies: Set = emptySet(), -) : ResourcePatch( - dependencies = setOf(AddResourcesPatch::class) + dependencies, -), - MutableSet by mutableSetOf(), - Closeable { - private lateinit var context: ResourceContext +fun settingsPatch( + rootPreference: Pair? = null, + preferences: Set, +) = resourcePatch { + dependsOn(addResourcesPatch) - override fun execute(context: ResourceContext) { + execute { context -> context.copyResources( "settings", ResourceGroup("xml", "revanced_prefs.xml"), ) - this.context = context - - AddResourcesPatch(BaseSettingsResourcePatch::class) + addResources("shared", "misc.settings.settingsResourcePatch") } - override fun close() { + finalize { context -> fun Node.addPreference(preference: BasePreference, prepend: Boolean = false) { preference.serialize(ownerDocument) { resource -> // TODO: Currently, resources can only be added to "values", which may not be the correct place. // It may be necessary to ask for the desired resourceValue in the future. - AddResourcesPatch("values", resource) + addResource("values", resource) }.let { preferenceNode -> insertFirst(preferenceNode) } @@ -54,19 +47,15 @@ abstract class BaseSettingsResourcePatch( // Add the root preference to an existing fragment if needed. rootPreference?.let { (intentPreference, fragment) -> - context.xmlEditor["res/xml/$fragment.xml"].use { editor -> - val document = editor.file - + context.document["res/xml/$fragment.xml"].use { document -> document.getNode("PreferenceScreen").addPreference(intentPreference, true) } } // Add all preferences to the ReVanced fragment. - context.xmlEditor["res/xml/revanced_prefs.xml"].use { editor -> - val document = editor.file - + context.document["res/xml/revanced_prefs.xml"].use { document -> val revancedPreferenceScreenNode = document.getNode("PreferenceScreen") - forEach { revancedPreferenceScreenNode.addPreference(it) } + preferences.forEach { revancedPreferenceScreenNode.addPreference(it) } } } } diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt similarity index 92% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt index 381a2cf3d2..648f5eefde 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt @@ -1,6 +1,6 @@ package app.revanced.patches.shared.misc.settings.preference -import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting import java.io.Closeable abstract class BasePreferenceScreen( @@ -18,7 +18,7 @@ abstract class BasePreferenceScreen( /** * Finalize and insert root preference into resource patch */ - abstract fun commit(screen: PreferenceScreen) + abstract fun commit(screen: PreferenceScreenPreference) open inner class Screen( key: String? = null, @@ -29,13 +29,13 @@ abstract class BasePreferenceScreen( private val sorting: Sorting = Sorting.BY_TITLE, ) : BasePreferenceCollection(key, titleKey, preferences) { - override fun transform(): PreferenceScreen { - return PreferenceScreen( + override fun transform(): PreferenceScreenPreference { + return PreferenceScreenPreference( key, titleKey, summaryKey, sorting, - // Screens and preferences are sorted at runtime by integrations code, + // Screens and preferences are sorted at runtime by extension code, // so title sorting uses the localized language in use. preferences = preferences + categories.map { it.transform() }, ) diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/InputType.kt diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt similarity index 70% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt index 787115dba3..d4ecaae7eb 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt @@ -6,7 +6,7 @@ import org.w3c.dom.Document /** * A non-interactive preference. * - * Typically used to present static text, but also used for custom integration code that responds to taps. + * Typically used to present static text, but also used for custom extension code that responds to taps. * * @param key The preference key. * @param summaryKey The preference summary key. @@ -19,17 +19,8 @@ class NonInteractivePreference( titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", tag: String = "Preference", - val selectable: Boolean = false + val selectable: Boolean = false, ) : BasePreference(key, titleKey, summaryKey, tag) { - - @Deprecated("Here only for binary compatibility, and should be removed after the next major version update.") - constructor( - key: String, - summaryKey: String? = "${key}_summary", - tag: String = "Preference", - selectable: Boolean = false - ) : this(key, "${key}_title", summaryKey, tag, selectable) - override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { setAttribute("android:selectable", selectable.toString()) diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreen.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt similarity index 95% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreen.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt index 049966a2c8..2b21a8413d 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreen.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt @@ -15,7 +15,7 @@ import org.w3c.dom.Document * @param preferences The preferences in this screen. */ @Suppress("MemberVisibilityCanBePrivate") -open class PreferenceScreen( +open class PreferenceScreenPreference( key: String? = null, titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", @@ -26,7 +26,7 @@ open class PreferenceScreen( // an extra bundle parameter can be added to the preferences XML declaration. // This would require bundling and referencing an additional XML file // or adding new attributes to the attrs.xml file. - // Since the key value is not currently used by integrations, + // Since the key value is not currently used by the extensions, // for now it's much simpler to modify the key to include the sort parameter. ) : BasePreference(if (sorting == Sorting.UNSORTED) key else (key + sorting.keySuffix), titleKey, summaryKey, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SummaryType.kt diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt similarity index 90% rename from src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt rename to patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt index d329a91778..81de615697 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt @@ -17,7 +17,7 @@ class TextPreference( key: String? = null, titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", - tag: String = "app.revanced.integrations.shared.settings.preference.ResettableEditTextPreference", + tag: String = "app.revanced.extension.shared.settings.preference.ResettableEditTextPreference", val inputType: InputType = InputType.TEXT ) : BasePreference(key, titleKey, summaryKey, tag) { diff --git a/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt new file mode 100644 index 0000000000..0e81d7e592 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/Fingerprints.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.solidexplorer2.functionality.filesize + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val onReadyFingerprint = fingerprint { + opcodes( + Opcode.CONST_WIDE_32, // Constant storing the 2MB limit + Opcode.CMP_LONG, + Opcode.IF_LEZ, + ) + custom { method, _ -> + method.name == "onReady" && method.definingClass == "Lpl/solidexplorer/plugins/texteditor/TextEditor;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt new file mode 100644 index 0000000000..39a8ae64f1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.solidexplorer2.functionality.filesize + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction + +@Suppress("unused") +val removeFileSizeLimitPatch = bytecodePatch( + name = "Remove file size limit", + description = "Allows opening files larger than 2 MB in the text editor.", +) { + compatibleWith("pl.solidexplorer2") + + val onReadyMatch by onReadyFingerprint() + + execute { + onReadyMatch.mutableMethod.apply { + val cmpIndex = onReadyMatch.patternMatch!!.startIndex + 1 + val cmpResultRegister = getInstruction(cmpIndex).registerA + + replaceInstruction(cmpIndex, "const/4 v$cmpResultRegister, 0x0") + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt similarity index 60% rename from src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt index c32a7b0971..d93b31af47 100644 --- a/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/BadgeTabPatch.kt @@ -1,25 +1,24 @@ package app.revanced.patches.songpal.badge -import app.revanced.util.exception -import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.songpal.badge.fingerprints.CreateTabsFingerprint +import app.revanced.patcher.patch.bytecodePatch -@Patch( +internal const val ACTIVITY_TAB_DESCRIPTOR = "Ljp/co/sony/vim/framework/ui/yourheadphones/YhContract\$Tab;" + +@Suppress("unused") +val badgeTabPatch = bytecodePatch( name = "Remove badge tab", description = "Removes the badge tab from the activity tab.", - compatiblePackages = [CompatiblePackage("com.sony.songpal.mdr")] -) -object BadgeTabPatch : BytecodePatch(setOf(CreateTabsFingerprint)) { - const val ACTIVITY_TAB_DESCRIPTOR = "Ljp/co/sony/vim/framework/ui/yourheadphones/YhContract\$Tab;" - private val arrayTabs = listOf("Log", "HealthCare") - - override fun execute(context: BytecodeContext) { - CreateTabsFingerprint.result?.mutableMethod?.apply { +) { + compatibleWith("com.sony.songpal.mdr") + + val createTabsMatch by createTabsFingerprint() + + val arrayTabs = listOf("Log", "HealthCare") + + execute { + createTabsMatch.mutableMethod.apply { removeInstructions(0, 2) val arrayRegister = 0 @@ -35,7 +34,7 @@ object BadgeTabPatch : BytecodePatch(setOf(CreateTabsFingerprint)) { const/4 v$indexRegister, $index sget-object v$arrayItemRegister, $ACTIVITY_TAB_DESCRIPTOR->$tab:$ACTIVITY_TAB_DESCRIPTOR aput-object v$arrayItemRegister, v$arrayRegister, v$indexRegister - """ + """, ) } @@ -47,9 +46,8 @@ object BadgeTabPatch : BytecodePatch(setOf(CreateTabsFingerprint)) { """ const/4 v$arrayRegister, ${arrayTabs.size} new-array v$arrayRegister, v$arrayRegister, [$ACTIVITY_TAB_DESCRIPTOR - """ + """, ) - - } ?: throw CreateTabsFingerprint.exception + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt new file mode 100644 index 0000000000..5d7498c5f3 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/Fingerprints.kt @@ -0,0 +1,54 @@ +package app.revanced.patches.songpal.badge + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference + +// Located @ ub.i0.h#p (9.5.0) +internal val createTabsFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE) + returns("Ljava/util/List;") + custom { method, _ -> + method.implementation?.instructions?.any { instruction -> + if (instruction.opcode != Opcode.INVOKE_STATIC) return@any false + + val reference = (instruction as ReferenceInstruction).reference as MethodReference + + if (reference.parameterTypes.isNotEmpty()) return@any false + if (reference.definingClass != ACTIVITY_TAB_DESCRIPTOR) return@any false + if (reference.returnType != "[${ACTIVITY_TAB_DESCRIPTOR}") return@any false + true + } ?: false + } +} + +// Located @ com.sony.songpal.mdr.vim.activity.MdrRemoteBaseActivity.e#run (9.5.0) +internal val showNotificationFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("V") + custom { method, _ -> + method.implementation?.instructions?.any { instruction -> + if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@any false + + with(expectedReference) { + val currentReference = (instruction as ReferenceInstruction).reference as MethodReference + currentReference.let { + if (it.definingClass != definingClass) return@any false + if (it.parameterTypes != parameterTypes) return@any false + if (it.returnType != returnType) return@any false + } + } + true + } ?: false + } +} + +internal val expectedReference = ImmutableMethodReference( + "Lcom/google/android/material/bottomnavigation/BottomNavigationView;", + "getOrCreateBadge", // Non-obfuscated placeholder method name. + listOf("I"), + "Lcom/google/android/material/badge/BadgeDrawable;", +) diff --git a/patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt new file mode 100644 index 0000000000..4f59c5f6ae --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/songpal/badge/RemoveNotificationBadgePatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.songpal.badge + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val removeNotificationBadgePatch = bytecodePatch( + name = "Remove notification badge", + description = "Removes the red notification badge from the activity tab.", +) { + compatibleWith("com.sony.songpal.mdr"("10.1.0")) + + val showNotificationMatch by showNotificationFingerprint() + + execute { + showNotificationMatch.mutableMethod.addInstructions(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt new file mode 100644 index 0000000000..28780ea578 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/Fingerprints.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.soundcloud.ad + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val interceptFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("L") + parameters("L") + opcodes( + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT + ) + strings("SC-Mob-UserPlan", "Configuration") +} + +internal val userConsumerPlanConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters( + "Ljava/lang/String;", + "Z", + "Ljava/lang/String;", + "Ljava/util/List;", + "Ljava/lang/String;", + "Ljava/lang/String;", + ) +} diff --git a/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt similarity index 66% rename from src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt index ea94b3f2a5..19d692863f 100644 --- a/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt @@ -1,33 +1,29 @@ package app.revanced.patches.soundcloud.ad -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.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.soundcloud.ad.fingerprints.InterceptFingerprint -import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint -import app.revanced.patches.soundcloud.ad.fingerprints.UserConsumerPlanConstructorFingerprint -import app.revanced.util.resultOrThrow +import app.revanced.patches.soundcloud.shared.featureConstructorFingerprint -@Patch( - name = "Hide ads", - compatiblePackages = [CompatiblePackage("com.soundcloud.android")], -) @Suppress("unused") -object HideAdsPatch : BytecodePatch( - setOf(FeatureConstructorFingerprint, UserConsumerPlanConstructorFingerprint, InterceptFingerprint), +val hideAdsPatch = bytecodePatch( + name = "Hide ads", ) { - override fun execute(context: BytecodeContext) { + compatibleWith("com.soundcloud.android") + + val featureConstructorMatch by featureConstructorFingerprint() + val userConsumerPlanConstructorMatch by userConsumerPlanConstructorFingerprint() + val interceptMatch by interceptFingerprint() + + execute { // Enable a preset feature to disable audio ads by modifying the JSON server response. // This method is the constructor of a class representing a "Feature" object parsed from JSON data. // p1 is the name of the feature. // p2 is true if the feature is enabled, false otherwise. - FeatureConstructorFingerprint.resultOrThrow().mutableMethod.apply { + featureConstructorMatch.mutableMethod.apply { val afterCheckNotNullIndex = 2 addInstructionsWithLabels( afterCheckNotNullIndex, @@ -49,7 +45,7 @@ object HideAdsPatch : BytecodePatch( // p4 is the "consumerPlanUpsells" value, a list of plans to try to sell to the user. // p5 is the "currentConsumerPlan" value, the type of plan currently subscribed to. // p6 is the "currentConsumerPlanTitle" value, the name of the plan currently subscribed to, shown to the user. - UserConsumerPlanConstructorFingerprint.resultOrThrow().mutableMethod.addInstructions( + userConsumerPlanConstructorMatch.mutableMethod.addInstructions( 0, """ const-string p1, "high_tier" @@ -61,12 +57,10 @@ object HideAdsPatch : BytecodePatch( ) // Prevent verification of an HTTP header containing the user's current plan, which would contradict the previous patch. - InterceptFingerprint.resultOrThrow().let { result -> - val conditionIndex = result.scanResult.patternScanResult!!.endIndex + 1 - result.mutableMethod.addInstruction( - conditionIndex, - "return-object p1", - ) - } + val conditionIndex = interceptMatch.patternMatch!!.endIndex + 1 + interceptMatch.mutableMethod.addInstruction( + conditionIndex, + "return-object p1", + ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt new file mode 100644 index 0000000000..e413338792 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/DisableTelemetryPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.soundcloud.analytics + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +val disableTelemetryPatch = bytecodePatch( + name = "Disable telemetry", + description = "Disables SoundCloud's telemetry system.", +) { + compatibleWith("com.soundcloud.android") + + val createTrackingApiMatch by createTrackingApiFingerprint() + + execute { + // Empty the "backend" argument to abort the initializer. + createTrackingApiMatch.mutableMethod.addInstruction(0, "const-string p1, \"\"") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt new file mode 100644 index 0000000000..2954b4d99c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/analytics/Fingerprints.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.soundcloud.analytics + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val createTrackingApiFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("L") + custom { methodDef, _ -> + methodDef.name == "create" + } + strings("backend", "boogaloo") +} diff --git a/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt similarity index 69% rename from src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt index 08446f9861..89a2ff5fc8 100644 --- a/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch.kt @@ -1,41 +1,34 @@ package app.revanced.patches.soundcloud.offlinesync -import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.soundcloud.offlinesync.fingerprints.DownloadOperationsHeaderVerificationFingerprint -import app.revanced.patches.soundcloud.offlinesync.fingerprints.DownloadOperationsURLBuilderFingerprint -import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint +import app.revanced.patches.soundcloud.shared.featureConstructorFingerprint import app.revanced.util.getReference -import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference -@Patch( - name = "Enable offline sync", - compatiblePackages = [CompatiblePackage("com.soundcloud.android")], -) @Suppress("unused") -object EnableOfflineSyncPatch : BytecodePatch( - setOf( - FeatureConstructorFingerprint, DownloadOperationsURLBuilderFingerprint, - DownloadOperationsHeaderVerificationFingerprint - ), +val enableOfflineSync = bytecodePatch( + name = "Enable offline sync", ) { - override fun execute(context: BytecodeContext) { + compatibleWith("com.soundcloud.android") + + val featureConstructorMatch by featureConstructorFingerprint() + val downloadOperationsURLBuilderMatch by downloadOperationsURLBuilderFingerprint() + val downloadOperationsHeaderVerificationMatch by downloadOperationsHeaderVerificationFingerprint() + + execute { // Enable the feature to allow offline track syncing by modifying the JSON server response. // This method is the constructor of a class representing a "Feature" object parsed from JSON data. // p1 is the name of the feature. // p2 is true if the feature is enabled, false otherwise. - FeatureConstructorFingerprint.resultOrThrow().mutableMethod.apply { + featureConstructorMatch.mutableMethod.apply { val afterCheckNotNullIndex = 2 addInstructionsWithLabels( @@ -53,7 +46,7 @@ object EnableOfflineSyncPatch : BytecodePatch( // Patch the URL builder to use the HTTPS_STREAM endpoint // instead of the offline sync endpoint to downloading the track. - DownloadOperationsURLBuilderFingerprint.resultOrThrow().mutableMethod.apply { + downloadOperationsURLBuilderMatch.mutableMethod.apply { val getEndpointsEnumFieldIndex = 1 val getEndpointsEnumFieldInstruction = getInstruction(getEndpointsEnumFieldIndex) @@ -62,16 +55,16 @@ object EnableOfflineSyncPatch : BytecodePatch( replaceInstruction( getEndpointsEnumFieldIndex, - "sget-object v$targetRegister, $endpointsType->HTTPS_STREAM:$endpointsType" + "sget-object v$targetRegister, $endpointsType->HTTPS_STREAM:$endpointsType", ) } // The HTTPS_STREAM endpoint does not return the necessary headers for offline sync. // Mock the headers to prevent the app from crashing by setting them to empty strings. // The headers are all cosmetic and do not affect the functionality of the app. - DownloadOperationsHeaderVerificationFingerprint.resultOrThrow().mutableMethod.apply { + downloadOperationsHeaderVerificationMatch.mutableMethod.apply { // The first three null checks need to be patched. - getInstructions().asSequence().filter { + instructions.asSequence().filter { it.opcode == Opcode.IF_EQZ }.take(3).toList().map { it.location.index }.asReversed().forEach { nullCheckIndex -> val headerStringRegister = getInstruction(nullCheckIndex).registerA diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt new file mode 100644 index 0000000000..688fe36044 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/offlinesync/Fingerprints.kt @@ -0,0 +1,29 @@ +package app.revanced.patches.soundcloud.offlinesync + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val downloadOperationsURLBuilderFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String") + parameters("L", "L") + opcodes( + Opcode.IGET_OBJECT, + Opcode.SGET_OBJECT, + Opcode.FILLED_NEW_ARRAY, + ) +} + +internal val downloadOperationsHeaderVerificationFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "L") + opcodes( + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + ) + strings("X-SC-Mime-Type", "X-SC-Preset", "X-SC-Quality") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt new file mode 100644 index 0000000000..3a50ae407e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/soundcloud/shared/Fingerprints.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.soundcloud.shared + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val featureConstructorFingerprint = fingerprint { + returns("V") + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters("Ljava/lang/String;", "Z", "Ljava/util/List;") + opcodes( + Opcode.SGET_OBJECT, + Opcode.CHECK_CAST, + Opcode.INVOKE_VIRTUAL + ) +} diff --git a/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt similarity index 75% rename from src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt rename to patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt index 9418c9f76a..30d8a0bb43 100644 --- a/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt @@ -1,20 +1,19 @@ +@file:Suppress("NAME_SHADOWING") + package app.revanced.patches.spotify.layout.theme -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.patch.stringOption import org.w3c.dom.Element -@Patch( +@Suppress("unused") +val customThemePatch = resourcePatch( name = "Custom theme", description = "Applies a custom theme.", - compatiblePackages = [CompatiblePackage("com.spotify.music")], -) -@Suppress("unused") -object CustomThemePatch : ResourcePatch() { - private var backgroundColor by stringPatchOption( +) { + compatibleWith("com.spotify.music") + + val backgroundColor by stringOption( key = "backgroundColor", default = "@android:color/black", title = "Primary background color", @@ -22,7 +21,7 @@ object CustomThemePatch : ResourcePatch() { required = true, ) - private var backgroundColorSecondary by stringPatchOption( + val backgroundColorSecondary by stringOption( key = "backgroundColorSecondary", default = "#ff282828", title = "Secondary background color", @@ -30,7 +29,7 @@ object CustomThemePatch : ResourcePatch() { required = true, ) - private var accentColor by stringPatchOption( + val accentColor by stringOption( key = "accentColor", default = "#ff1ed760", title = "Accent color", @@ -38,7 +37,7 @@ object CustomThemePatch : ResourcePatch() { required = true, ) - private var accentColorPressed by stringPatchOption( + val accentColorPressed by stringOption( key = "accentColorPressed", default = "#ff169c46", title = "Pressed dark theme accent color", @@ -48,15 +47,13 @@ object CustomThemePatch : ResourcePatch() { required = true, ) - override fun execute(context: ResourceContext) { + execute { context -> val backgroundColor = backgroundColor!! val backgroundColorSecondary = backgroundColorSecondary!! val accentColor = accentColor!! val accentColorPressed = accentColorPressed!! - context.xmlEditor["res/values/colors.xml"].use { editor -> - val document = editor.file - + context.document["res/values/colors.xml"].use { document -> val resourcesNode = document.getElementsByTagName("resources").item(0) as Element for (i in 0 until resourcesNode.childNodes.length) { diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/Fingerprints.kt new file mode 100644 index 0000000000..365235bee0 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/Fingerprints.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.spotify.lite.ondemand + +import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.fingerprint + +internal val onDemandFingerprint = fingerprint(fuzzyPatternScanThreshold = 2) { + returns("L") + parameters() + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_EQZ, + Opcode.SGET_OBJECT, + Opcode.GOTO, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IPUT, + Opcode.RETURN_OBJECT, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt new file mode 100644 index 0000000000..d8a8940d26 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/lite/ondemand/OnDemandPatch.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.spotify.lite.ondemand + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val onDemandPatch = bytecodePatch( + name = "Enable on demand", + description = "Enables listening to songs on-demand, allowing to play any song from playlists, albums or artists without limitations. This does not remove ads.", +) { + compatibleWith("com.spotify.lite") + + val onDemandMatch by onDemandFingerprint() + + execute { + // Spoof a premium account + onDemandMatch.mutableMethod.addInstruction( + onDemandMatch.patternMatch!!.endIndex - 1, + "const/4 v0, 0x2", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/Fingerprints.kt new file mode 100644 index 0000000000..761e322060 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.spotify.navbar + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags + +internal val addNavBarItemFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + literal { showBottomNavigationItemsTextId } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt new file mode 100644 index 0000000000..857842fc62 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/navbar/PremiumNavbarTabPatch.kt @@ -0,0 +1,52 @@ +package app.revanced.patches.spotify.navbar + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings + +internal var showBottomNavigationItemsTextId = -1L + private set +internal var premiumTabId = -1L + private set + +private val premiumNavbarTabResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + execute { + premiumTabId = resourceMappings["id", "premium_tab"] + + showBottomNavigationItemsTextId = resourceMappings[ + "bool", + "show_bottom_navigation_items_text", + ] + } +} + +@Suppress("unused") +val premiumNavbarTabPatch = bytecodePatch( + name = "Premium navbar tab", + description = "Hides the premium tab from the navigation bar.", +) { + dependsOn(premiumNavbarTabResourcePatch) + + compatibleWith("com.spotify.music") + + val addNavbarItemMatch by addNavBarItemFingerprint() + + // If the navigation bar item is the premium tab, do not add it. + execute { + addNavbarItemMatch.mutableMethod.addInstructions( + 0, + """ + const v1, $premiumTabId + if-ne p5, v1, :continue + return-void + :continue + nop + """, + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt similarity index 59% rename from src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt index 6b493c4b15..d7e3a30881 100644 --- a/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideOffersTabPatch.kt @@ -1,19 +1,16 @@ package app.revanced.patches.stocard.layout -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch import app.revanced.util.childElementsSequence import app.revanced.util.getNode -@Patch( - name = "Hide offers tab", - compatiblePackages = [CompatiblePackage("de.stocard.stocard")], -) @Suppress("unused") -object HideOffersTabPatch : ResourcePatch() { - override fun execute(context: ResourceContext) { +val hideOffersTabPatch = resourcePatch( + name = "Hide offers tab", +) { + compatibleWith("de.stocard.stocard") + + execute { context -> context.document["res/menu/bottom_navigation_menu.xml"].use { document -> document.getNode("menu").apply { removeChild( diff --git a/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt similarity index 58% rename from src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt index eecdeb38d8..80894391e3 100644 --- a/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/stocard/layout/HideStoryBubblesPatch.kt @@ -1,18 +1,15 @@ package app.revanced.patches.stocard.layout -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch import app.revanced.util.getNode -@Patch( - name = "Hide story bubbles", - compatiblePackages = [CompatiblePackage("de.stocard.stocard")], -) @Suppress("unused") -object HideStoryBubblesPatch : ResourcePatch() { - override fun execute(context: ResourceContext) { +val hideStoryBubblesPatch = resourcePatch( + name = "Hide story bubbles", +) { + compatibleWith("de.stocard.stocard") + + execute { context -> context.document["res/layout/rv_story_bubbles_list.xml"].use { document -> document.getNode("androidx.recyclerview.widget.RecyclerView").apply { arrayOf( diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt new file mode 100644 index 0000000000..0458f45d3e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.strava.subscription + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val getSubscribedFingerprint = fingerprint { + opcodes(Opcode.IGET_BOOLEAN) + custom { method, classDef -> + classDef.endsWith("/SubscriptionDetailResponse;") && method.name == "getSubscribed" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt new file mode 100644 index 0000000000..0d96767a2b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.strava.subscription +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockSubscriptionPatch = bytecodePatch( + name = "Unlock subscription features", + description = "Unlocks \"Routes\", \"Matched Runs\" and \"Segment Efforts\".", +) { + compatibleWith("com.strava") + + val getSubscribedMatch by getSubscribedFingerprint() + + execute { + getSubscribedMatch.mutableMethod.replaceInstruction( + getSubscribedMatch.patternMatch!!.startIndex, + "const/4 v0, 0x1", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt new file mode 100644 index 0000000000..c1af248250 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt @@ -0,0 +1,69 @@ +package app.revanced.patches.strava.upselling + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod + +@Suppress("unused") +val disableSubscriptionSuggestionsPatch = bytecodePatch( + name = "Disable subscription suggestions", +) { + compatibleWith("com.strava"("320.12")) + + val getModulesMatch by getModulesFingerprint() + + execute { + val helperMethodName = "getModulesIfNotUpselling" + val pageSuffix = "_upsell" + val label = "original" + + val className = getModulesMatch.classDef.type + val originalMethod = getModulesMatch.mutableMethod + val returnType = originalMethod.returnType + + getModulesMatch.mutableClass.methods.add( + ImmutableMethod( + className, + helperMethodName, + emptyList(), + returnType, + AccessFlags.PRIVATE.value, + null, + null, + MutableMethodImplementation(3), + ).toMutable().apply { + addInstructions( + """ + iget-object v0, p0, $className->page:Ljava/lang/String; + const-string v1, "$pageSuffix" + invoke-virtual {v0, v1}, Ljava/lang/String;->endsWith(Ljava/lang/String;)Z + move-result v0 + if-eqz v0, :$label + invoke-static {}, Ljava/util/Collections;->emptyList()Ljava/util/List; + move-result-object v0 + return-object v0 + :$label + iget-object v0, p0, $className->modules:Ljava/util/List; + return-object v0 + """, + ) + }, + ) + + val getModulesIndex = getModulesMatch.patternMatch!!.startIndex + with(originalMethod) { + removeInstruction(getModulesIndex) + addInstructions( + getModulesIndex, + """ + invoke-direct {p0}, $className->$helperMethodName()$returnType + move-result-object v0 + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/Fingerprints.kt new file mode 100644 index 0000000000..1204a36f21 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.strava.upselling + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val getModulesFingerprint = fingerprint { + opcodes(Opcode.IGET_OBJECT) + custom { method, classDef -> + classDef.endsWith("/GenericLayoutEntry;") && method.name == "getModules" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt new file mode 100644 index 0000000000..0efa845fbe --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.swissid.integritycheck + +import app.revanced.patcher.fingerprint + +internal val checkIntegrityFingerprint = fingerprint { + returns("V") + parameters("Lcom/swisssign/deviceintegrity/model/DeviceIntegrityResult;") + strings("it", "result") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt new file mode 100644 index 0000000000..82714c651a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/swissid/integritycheck/RemoveGooglePlayIntegrityCheckPatch.kt @@ -0,0 +1,32 @@ +package app.revanced.patches.swissid.integritycheck + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +private const val RESULT_METHOD_REFERENCE = " Lcom/swisssign/deviceintegrity/DeviceintegrityPlugin\$onMethodCall\$1;->" + + "\$result:Lio/flutter/plugin/common/MethodChannel\$Result;" +private const val SUCCESS_METHOD_REFERENCE = + "Lio/flutter/plugin/common/MethodChannel\$Result;->success(Ljava/lang/Object;)V" + +@Suppress("unused") +val removeGooglePlayIntegrityCheckPatch = bytecodePatch( + name = "Remove Google Play Integrity check", + description = "Removes the Google Play Integrity check. With this it's possible to use SwissID on custom ROMS." + + "If the device is rooted, root permissions must be hidden from the app.", +) { + compatibleWith("com.swisssign.swissid.mobile") + + val checkIntegrityMatch by checkIntegrityFingerprint() + + execute { + checkIntegrityMatch.mutableMethod.addInstructions( + 0, + """ + iget-object p1, p0, $RESULT_METHOD_REFERENCE + const-string v0, "VALID" + invoke-interface {p1, v0}, $SUCCESS_METHOD_REFERENCE + return-void + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt new file mode 100644 index 0000000000..4bd688de49 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/Fingerprints.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.ticktick.misc.themeunlock + +import app.revanced.patcher.fingerprint + +internal val checkLockedThemesFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("Theme;") && method.name == "isLockedTheme" + } +} + +internal val setThemeFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("ThemePreviewActivity;") && method.name == "lambda\$updateUserBtn\$1" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt new file mode 100644 index 0000000000..5e2aa37d80 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/ticktick/misc/themeunlock/UnlockThemePatch.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.ticktick.misc.themeunlock + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockProPatch = bytecodePatch( + name = "Unlock themes", + description = "Unlocks all themes that are inaccessible until a certain level is reached.", +) { + compatibleWith("com.ticktick.task") + + val checkLockedThemesMatch by checkLockedThemesFingerprint() + val setThemeMatch by setThemeFingerprint() + + execute { + checkLockedThemesMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x0 + return v0 + """, + ) + + setThemeMatch.mutableMethod.removeInstructions(0, 10) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt new file mode 100644 index 0000000000..7301c58ba7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt @@ -0,0 +1,48 @@ +package app.revanced.patches.tiktok.feedfilter + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch +import app.revanced.patches.tiktok.misc.settings.settingsPatch +import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Suppress("unused") +val feedFilterPatch = bytecodePatch( + name = "Feed filter", + description = "Removes ads, livestreams, stories, image videos " + + "and videos with a specific amount of views or likes from the feed.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + ) + + compatibleWith( + "com.ss.android.ugc.trill"("36.5.4"), + "com.zhiliaoapp.musically"("36.5.4"), + ) + + val feedApiServiceLIZMatch by feedApiServiceLIZFingerprint() + val settingsStatusLoadMatch by settingsStatusLoadFingerprint() + + execute { + feedApiServiceLIZMatch.mutableMethod.apply { + val returnFeedItemInstruction = instructions.first { it.opcode == Opcode.RETURN_OBJECT } + val feedItemsRegister = (returnFeedItemInstruction as OneRegisterInstruction).registerA + + addInstruction( + returnFeedItemInstruction.location.index, + "invoke-static { v$feedItemsRegister }, " + + "Lapp/revanced/extension/tiktok/feedfilter/FeedItemsFilter;->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V", + ) + } + + settingsStatusLoadMatch.mutableMethod.addInstruction( + 0, + "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableFeedFilter()V", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt new file mode 100644 index 0000000000..4f899661eb --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.tiktok.feedfilter + +import app.revanced.patcher.fingerprint + +internal val feedApiServiceLIZFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt new file mode 100644 index 0000000000..eb28683743 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.tiktok.interaction.cleardisplay + +import app.revanced.patcher.fingerprint + +internal val onClearDisplayEventFingerprint = fingerprint { + custom { method, classDef -> + // Internally the feature is called "Clear mode". + classDef.endsWith("/ClearModePanelComponent;") && method.name == "onClearModeEvent" + } +} diff --git a/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt similarity index 61% rename from src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt index b1522e880c..73374b5137 100644 --- a/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt @@ -1,37 +1,30 @@ package app.revanced.patches.tiktok.interaction.cleardisplay -import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnClearDisplayEventFingerprint -import app.revanced.patches.tiktok.shared.fingerprints.OnRenderFirstFrameFingerprint -import app.revanced.util.exception +import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction -@Patch( +@Suppress("unused") +val rememberClearDisplayPatch = bytecodePatch( name = "Remember clear display", description = "Remembers the clear display configurations in between videos.", - compatiblePackages = [ - CompatiblePackage("com.ss.android.ugc.trill", ["36.5.4"]), - CompatiblePackage("com.zhiliaoapp.musically", ["36.5.4"]), - ], -) -@Suppress("unused") -object RememberClearDisplayPatch : BytecodePatch( - setOf( - OnClearDisplayEventFingerprint, - OnRenderFirstFrameFingerprint, - ), ) { - override fun execute(context: BytecodeContext) { - OnClearDisplayEventFingerprint.result?.mutableMethod?.let { + compatibleWith( + "com.ss.android.ugc.trill"("36.5.4"), + "com.zhiliaoapp.musically"("36.5.4"), + ) + + val onClearDisplayEventMatch by onClearDisplayEventFingerprint() + val onRenderFirstFrameMatch by onRenderFirstFrameFingerprint() + + execute { + onClearDisplayEventMatch.mutableMethod.let { // region Hook the "Clear display" configuration save event to remember the state of clear display. val isEnabledIndex = it.indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1 @@ -40,7 +33,7 @@ object RememberClearDisplayPatch : BytecodePatch( it.addInstructions( isEnabledIndex, "invoke-static { v$isEnabledRegister }, " + - "Lapp/revanced/integrations/tiktok/cleardisplay/RememberClearDisplayPatch;->rememberClearDisplayState(Z)V", + "Lapp/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch;->rememberClearDisplayState(Z)V", ) // endregion @@ -48,10 +41,9 @@ object RememberClearDisplayPatch : BytecodePatch( // region Override the "Clear display" configuration load event to load the state of clear display. val clearDisplayEventClass = it.parameters[0].type - OnRenderFirstFrameFingerprint.result?.mutableMethod?.apply { - addInstructionsWithLabels( - 0, - """ + onRenderFirstFrameMatch.mutableMethod.addInstructionsWithLabels( + 0, + """ # Create a new clearDisplayEvent and post it to the EventBus (https://github.com/greenrobot/EventBus) # Clear display type such as 0 = LONG_PRESS, 1 = SCREEN_RECORD etc. @@ -64,7 +56,7 @@ object RememberClearDisplayPatch : BytecodePatch( const-string v3, "long_press" # The state of clear display. - invoke-static { }, Lapp/revanced/integrations/tiktok/cleardisplay/RememberClearDisplayPatch;->getClearDisplayState()Z + invoke-static { }, Lapp/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch;->getClearDisplayState()Z move-result v4 if-eqz v4, :clear_display_disabled @@ -72,11 +64,10 @@ object RememberClearDisplayPatch : BytecodePatch( invoke-direct { v0, v1, v2, v3, v4 }, $clearDisplayEventClass->(ILjava/lang/String;Ljava/lang/String;Z)V invoke-virtual { v0 }, $clearDisplayEventClass->post()Lcom/ss/android/ugc/governance/eventbus/IEvent; """, - ExternalLabel("clear_display_disabled", getInstruction(0)), - ) - } ?: throw OnRenderFirstFrameFingerprint.exception + ExternalLabel("clear_display_disabled", onRenderFirstFrameMatch.mutableMethod.getInstruction(0)), + ) // endregion - } ?: throw OnClearDisplayEventFingerprint.exception + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt new file mode 100644 index 0000000000..94afc211ff --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt @@ -0,0 +1,98 @@ +package app.revanced.patches.tiktok.interaction.downloads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch +import app.revanced.patches.tiktok.misc.settings.settingsPatch +import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Suppress("unused") +val downloadsPatch = bytecodePatch( + name = "Downloads", + description = "Removes download restrictions and changes the default path to download to.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + ) + + compatibleWith( + "com.ss.android.ugc.trill"("36.5.4"), + "com.zhiliaoapp.musically"("36.5.4"), + ) + + val aclCommonShareMatch by aclCommonShareFingerprint() + val aclCommonShare2Match by aclCommonShare2Fingerprint() + val aclCommonShare3Match by aclCommonShare3Fingerprint() + val downloadUriMatch by downloadUriFingerprint() + val settingsStatusLoadMatch by settingsStatusLoadFingerprint() + + execute { context -> + aclCommonShareMatch.mutableMethod.replaceInstructions( + 0, + """ + const/4 v0, 0x0 + return v0 + """, + ) + + aclCommonShare2Match.mutableMethod.replaceInstructions( + 0, + """ + const/4 v0, 0x2 + return v0 + """, + ) + + // Download videos without watermark. + aclCommonShare3Match.mutableMethod.addInstructionsWithLabels( + 0, + """ + invoke-static {}, Lapp/revanced/extension/tiktok/download/DownloadsPatch;->shouldRemoveWatermark()Z + move-result v0 + if-eqz v0, :noremovewatermark + const/4 v0, 0x1 + return v0 + :noremovewatermark + nop + """, + ) + + // Change the download path patch. + downloadUriMatch.mutableMethod.apply { + val firstIndex = indexOfFirstInstructionOrThrow { + getReference()?.name == "" + } + val secondIndex = indexOfFirstInstructionOrThrow { + getReference()?.returnType?.contains("Uri") == true + } + + addInstructions( + secondIndex, + """ + invoke-static {}, Lapp/revanced/extension/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String; + move-result-object v0 + """, + ) + + addInstructions( + firstIndex, + """ + invoke-static {}, Lapp/revanced/extension/tiktok/download/DownloadsPatch;->getDownloadPath()Ljava/lang/String; + move-result-object v0 + """, + ) + } + + settingsStatusLoadMatch.mutableMethod.addInstruction( + 0, + "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableDownload()V", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt new file mode 100644 index 0000000000..f58141fac8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt @@ -0,0 +1,47 @@ +package app.revanced.patches.tiktok.interaction.downloads + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val aclCommonShareFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("I") + custom { method, classDef -> + classDef.endsWith("/ACLCommonShare;") && + method.name == "getCode" + } +} + +internal val aclCommonShare2Fingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("I") + custom { method, classDef -> + classDef.endsWith("/ACLCommonShare;") && + method.name == "getShowType" + } +} + +internal val aclCommonShare3Fingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("I") + custom { method, classDef -> + classDef.endsWith("/ACLCommonShare;") && + method.name == "getTranscode" + } +} + +internal val downloadUriFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Landroid/net/Uri;") + parameters( + "Landroid/content/Context;", + "Ljava/lang/String;" + ) + strings( + "/", + "/Camera", + "/Camera/", + "video/mp4" + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt new file mode 100644 index 0000000000..ce372ea421 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.tiktok.interaction.seekbar + +import app.revanced.patcher.fingerprint + +internal val setSeekBarShowTypeFingerprint = fingerprint { + strings("seekbar show type change, change to:") +} + +internal val shouldShowSeekBarFingerprint = fingerprint { + strings("can not show seekbar, state: 1, not in resume") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt new file mode 100644 index 0000000000..5cb6db64c6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/seekbar/ShowSeekbarPatch.kt @@ -0,0 +1,38 @@ +package app.revanced.patches.tiktok.interaction.seekbar + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val showSeekbarPatch = bytecodePatch( + name = "Show seekbar", + description = "Shows progress bar for all video.", +) { + compatibleWith( + "com.ss.android.ugc.trill", + "com.zhiliaoapp.musically", + ) + + val shouldShowSeekBarMatch by shouldShowSeekBarFingerprint() + val setSeekBarShowTypeMatch by setSeekBarShowTypeFingerprint() + + execute { + shouldShowSeekBarMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + setSeekBarShowTypeMatch.mutableMethod.apply { + val typeRegister = implementation!!.registerCount - 1 + + addInstructions( + 0, + """ + const/16 v$typeRegister, 0x64 + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt new file mode 100644 index 0000000000..221036bb96 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.tiktok.interaction.speed + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val getSpeedFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/BaseListFragmentPanel;") && method.name == "onFeedSpeedSelectedEvent" + } +} + +internal val setSpeedFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("V") + parameters("Ljava/lang/String;", "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", "F") + strings("enterFrom") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt new file mode 100644 index 0000000000..81153a1dc4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt @@ -0,0 +1,74 @@ +package app.revanced.patches.tiktok.interaction.speed + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tiktok.shared.getEnterFromFingerprint +import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Suppress("unused") +val playbackSpeedPatch = bytecodePatch( + name = "Playback speed", + description = "Enables the playback speed option for all videos and " + + "retains the speed configurations in between videos.", +) { + compatibleWith( + "com.ss.android.ugc.trill"("36.5.4"), + "com.zhiliaoapp.musically"("36.5.4"), + ) + + val getSpeedMatch by getSpeedFingerprint() + val onRenderFirstFrameMatch by onRenderFirstFrameFingerprint() + val setSpeedMatch by setSpeedFingerprint() + val getEnterFromMatch by getEnterFromFingerprint() + + execute { + setSpeedMatch.let { onVideoSwiped -> + getSpeedMatch.mutableMethod.apply { + val injectIndex = indexOfFirstInstructionOrThrow { getReference()?.returnType == "F" } + 2 + val register = getInstruction(injectIndex - 1).registerA + + addInstruction( + injectIndex, + "invoke-static { v$register }," + + " Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V", + ) + } + + // By default, the playback speed will reset to 1.0 at the start of each video. + // Instead, override it with the desired playback speed. + onRenderFirstFrameMatch.mutableMethod.addInstructions( + 0, + """ + # Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method. + const/4 v0, 0x1 + invoke-virtual { p0, v0 }, ${getEnterFromMatch.method} + 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/extension/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F + move-result v2 + invoke-static { v0, v1, v2 }, ${onVideoSwiped.method} + """, + ) + + // Force enable the playback speed option for all videos. + onVideoSwiped.mutableClass.methods.find { method -> method.returnType == "Z" }?.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/ExtensionPatch.kt new file mode 100644 index 0000000000..b714b9d1c2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/ExtensionPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.tiktok.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch(initHook) diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt new file mode 100644 index 0000000000..24a49dd352 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/extension/Hooks.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.tiktok.misc.extension + +import app.revanced.patches.shared.misc.extension.extensionHook +import com.android.tools.smali.dexlib2.AccessFlags + +internal val initHook = extensionHook( + insertIndexResolver = { 1 }, // Insert after call to super class. +) { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + custom { method, classDef -> + classDef.endsWith("/AwemeHostApplication;") && + method.name == "" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt new file mode 100644 index 0000000000..1b2f0a0b0a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/DisableLoginRequirementPatch.kt @@ -0,0 +1,32 @@ +package app.revanced.patches.tiktok.misc.login.disablerequirement + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableLoginRequirementPatch = bytecodePatch( + name = "Disable login requirement", +) { + compatibleWith( + "com.ss.android.ugc.trill", + "com.zhiliaoapp.musically", + ) + + val mandatoryLoginServiceMatch by mandatoryLoginServiceFingerprint() + val mandatoryLoginService2Match by mandatoryLoginService2Fingerprint() + + execute { + listOf( + mandatoryLoginServiceMatch.mutableMethod, + mandatoryLoginService2Match.mutableMethod, + ).forEach { method -> + method.addInstructions( + 0, + """ + const/4 v0, 0x0 + return v0 + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt new file mode 100644 index 0000000000..929ef86729 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/disablerequirement/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.tiktok.misc.login.disablerequirement + +import app.revanced.patcher.fingerprint + +internal val mandatoryLoginServiceFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/MandatoryLoginService;") && + method.name == "enableForcedLogin" + } +} + +internal val mandatoryLoginService2Fingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/MandatoryLoginService;") && + method.name == "shouldShowForcedLogin" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt new file mode 100644 index 0000000000..a40f5251f5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/Fingerprints.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.tiktok.misc.login.fixgoogle + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val googleAuthAvailableFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + custom { method, _ -> + method.definingClass == "Lcom/bytedance/lobby/google/GoogleAuth;" + } +} + +internal val googleOneTapAuthAvailableFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + custom { method, _ -> + method.definingClass == "Lcom/bytedance/lobby/google/GoogleOneTapAuth;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt new file mode 100644 index 0000000000..214868d04e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLoginPatch.kt @@ -0,0 +1,33 @@ +package app.revanced.patches.tiktok.misc.login.fixgoogle + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val fixGoogleLoginPatch = bytecodePatch( + name = "Fix Google login", + description = "Allows logging in with a Google account.", +) { + compatibleWith( + "com.ss.android.ugc.trill", + "com.zhiliaoapp.musically", + ) + + val googleOneTapAuthAvailableMatch by googleOneTapAuthAvailableFingerprint() + val googleAuthAvailableMatch by googleAuthAvailableFingerprint() + + execute { + listOf( + googleOneTapAuthAvailableMatch.mutableMethod, + googleAuthAvailableMatch.mutableMethod, + ).forEach { method -> + method.addInstructions( + 0, + """ + const/4 v0, 0x0 + return v0 + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt new file mode 100644 index 0000000000..d1c4d6de68 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/Fingerprints.kt @@ -0,0 +1,35 @@ +package app.revanced.patches.tiktok.misc.settings + +import app.revanced.patcher.fingerprint + +internal val addSettingsEntryFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/SettingNewVersionFragment;") && + method.name == "initUnitManger" + } +} + +internal val adPersonalizationActivityOnCreateFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/AdPersonalizationActivity;") && + method.name == "onCreate" + } +} + +internal val settingsEntryFingerprint = fingerprint { + strings("pls pass item or extends the EventUnit") +} + +internal val settingsEntryInfoFingerprint = fingerprint { + strings( + "ExposeItem(title=", + ", icon=", + ) +} + +internal val settingsStatusLoadFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("Lapp/revanced/extension/tiktok/settings/SettingsStatus;") && + method.name == "load" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt new file mode 100644 index 0000000000..79ee387115 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/settings/SettingsPatch.kt @@ -0,0 +1,102 @@ +package app.revanced.patches.tiktok.misc.settings + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c +import com.android.tools.smali.dexlib2.iface.reference.FieldReference + +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/tiktok/settings/AdPersonalizationActivityHook;" + +@Suppress("unused") +val settingsPatch = bytecodePatch( + name = "Settings", + description = "Adds ReVanced settings to TikTok.", +) { + dependsOn(sharedExtensionPatch) + + compatibleWith( + "com.ss.android.ugc.trill"("36.5.4"), + "com.zhiliaoapp.musically"("36.5.4"), + ) + + val adPersonalizationActivityOnCreateMatch by adPersonalizationActivityOnCreateFingerprint() + val addSettingsEntryMatch by addSettingsEntryFingerprint() + val settingsEntryMatch by settingsEntryFingerprint() + val settingsEntryInfoMatch by settingsEntryInfoFingerprint() + + execute { + val initializeSettingsMethodDescriptor = + "$EXTENSION_CLASS_DESCRIPTOR->initialize(" + + "Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;" + + ")Z" + + val createSettingsEntryMethodDescriptor = + "$EXTENSION_CLASS_DESCRIPTOR->createSettingsEntry(" + + "Ljava/lang/String;" + + "Ljava/lang/String;" + + ")Ljava/lang/Object;" + + fun String.toClassName(): String = substring(1, this.length - 1).replace("/", ".") + + // Find the class name of classes which construct a settings entry + val settingsButtonClass = settingsEntryMatch.classDef.type.toClassName() + val settingsButtonInfoClass = settingsEntryInfoMatch.classDef.type.toClassName() + + // Create a settings entry for 'revanced settings' and add it to settings fragment + addSettingsEntryMatch.mutableMethod.apply { + val markIndex = implementation!!.instructions.indexOfFirst { + it.opcode == Opcode.IGET_OBJECT && ((it as Instruction22c).reference as FieldReference).name == "headerUnit" + } + + val getUnitManager = getInstruction(markIndex + 2) + val addEntry = getInstruction(markIndex + 1) + + addInstructions( + markIndex + 2, + listOf( + getUnitManager, + addEntry, + ), + ) + + addInstructions( + markIndex + 2, + """ + const-string v0, "$settingsButtonClass" + const-string v1, "$settingsButtonInfoClass" + invoke-static {v0, v1}, $createSettingsEntryMethodDescriptor + move-result-object v0 + check-cast v0, ${settingsEntryMatch.classDef.type} + """, + ) + } + + // Initialize the settings menu once the replaced setting entry is clicked. + adPersonalizationActivityOnCreateMatch.mutableMethod.apply { + val initializeSettingsIndex = implementation!!.instructions.indexOfFirst { + it.opcode == Opcode.INVOKE_SUPER + } + 1 + + val thisRegister = getInstruction(initializeSettingsIndex - 1).registerC + val usableRegister = implementation!!.registerCount - parameters.size - 2 + + addInstructionsWithLabels( + initializeSettingsIndex, + """ + invoke-static {v$thisRegister}, $initializeSettingsMethodDescriptor + move-result v$usableRegister + if-eqz v$usableRegister, :do_not_open + return-void + """, + ExternalLabel("do_not_open", getInstruction(initializeSettingsIndex)), + ) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt similarity index 58% rename from src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt index 1c40b5d9cf..408958179f 100644 --- a/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatch.kt @@ -1,44 +1,46 @@ package app.revanced.patches.tiktok.misc.spoof.sim -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.getInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patches.tiktok.misc.integrations.IntegrationsPatch -import app.revanced.patches.tiktok.misc.settings.SettingsPatch -import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsStatusLoadFingerprint +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch +import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint +import app.revanced.patches.twitch.misc.settings.settingsPatch import app.revanced.util.findMutableMethodOf import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c import com.android.tools.smali.dexlib2.iface.reference.MethodReference -@Patch( +@Suppress("unused") +val spoofSimPatch = bytecodePatch( name = "SIM spoof", description = "Spoofs the information which is retrieved from the SIM card.", - dependencies = [IntegrationsPatch::class, SettingsPatch::class], - compatiblePackages = [ - CompatiblePackage("com.ss.android.ugc.trill"), - CompatiblePackage("com.zhiliaoapp.musically"), - ], use = false, -) -@Suppress("unused") -object SpoofSimPatch : BytecodePatch(emptySet()) { - private val replacements = hashMapOf( - "getSimCountryIso" to "getCountryIso", - "getNetworkCountryIso" to "getCountryIso", - "getSimOperator" to "getOperator", - "getNetworkOperator" to "getOperator", - "getSimOperatorName" to "getOperatorName", - "getNetworkOperatorName" to "getOperatorName", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + ) + + compatibleWith( + "com.ss.android.ugc.trill", + "com.zhiliaoapp.musically", ) - override fun execute(context: BytecodeContext) { + val settingsStatusLoadMatch by settingsStatusLoadFingerprint() + + execute { context -> + val replacements = hashMapOf( + "getSimCountryIso" to "getCountryIso", + "getNetworkCountryIso" to "getCountryIso", + "getSimOperator" to "getOperator", + "getNetworkOperator" to "getOperator", + "getSimOperatorName" to "getOperatorName", + "getNetworkOperatorName" to "getOperatorName", + ) + // Find all api call to check sim information. buildMap { context.classes.forEach { classDef -> @@ -74,7 +76,17 @@ object SpoofSimPatch : BytecodePatch(emptySet()) { with(findMutableMethodOf(method)) { while (!patches.isEmpty()) { val (index, replacement) = patches.removeLast() - replaceReference(index, replacement) + + val resultReg = getInstruction(index + 1).registerA + + // Patch Android API and return fake sim information. + addInstructions( + index + 2, + """ + invoke-static {v$resultReg}, Lapp/revanced/extension/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$resultReg + """, + ) } } } @@ -82,24 +94,9 @@ object SpoofSimPatch : BytecodePatch(emptySet()) { } // Enable patch in settings. - with(SettingsStatusLoadFingerprint.result!!.mutableMethod) { - addInstruction( - 0, - "invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableSimSpoof()V", - ) - } - } - - // Patch Android API and return fake sim information. - private fun MutableMethod.replaceReference(index: Int, replacement: String) { - val resultReg = getInstruction(index + 1).registerA - - addInstructions( - index + 2, - """ - invoke-static {v$resultReg}, Lapp/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$resultReg - """, + settingsStatusLoadMatch.mutableMethod.addInstruction( + 0, + "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableSimSpoof()V", ) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt new file mode 100644 index 0000000000..3e98d213e5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/shared/Fingerprints.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.tiktok.shared + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val getEnterFromFingerprint = fingerprint { + returns("Ljava/lang/String;") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Z") + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.RETURN_OBJECT, + ) + custom { methodDef, _ -> + methodDef.definingClass.endsWith("/BaseListFragmentPanel;") + } +} + +internal val onRenderFirstFrameFingerprint = fingerprint { + strings("method_enable_viewpager_preload_duration") + custom { _, classDef -> + classDef.endsWith("/BaseListFragmentPanel;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt new file mode 100644 index 0000000000..4a02c62215 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/trakt/Fingerprints.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.trakt + +import app.revanced.patcher.fingerprint + +internal val isVIPEPFingerprint = fingerprint { + custom { method, classDef -> + if (!classDef.endsWith("RemoteUser;")) return@custom false + + method.name == "isVIPEP" + } +} + +internal val isVIPFingerprint = fingerprint { + custom { method, classDef -> + if (!classDef.endsWith("RemoteUser;")) return@custom false + + method.name == "isVIP" + } +} + +internal val remoteUserFingerprint = fingerprint { + custom { _, classDef -> + classDef.endsWith("RemoteUser;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt new file mode 100644 index 0000000000..4b3138d674 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/trakt/UnlockProPatch.kt @@ -0,0 +1,36 @@ +package app.revanced.patches.trakt + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.exception + +@Suppress("unused") +val unlockProPatch = bytecodePatch( + name = "Unlock pro", +) { + compatibleWith("tv.trakt.trakt"("1.1.1")) + + val remoteUserMatch by remoteUserFingerprint() + + execute { context -> + remoteUserMatch.classDef.let { remoteUserClass -> + arrayOf(isVIPFingerprint, isVIPEPFingerprint).onEach { fingerprint -> + // Resolve both fingerprints on the same class. + if (!fingerprint.match(context, remoteUserClass)) { + throw fingerprint.exception + } + }.forEach { fingerprint -> + // Return true for both VIP check methods. + fingerprint.match?.mutableMethod?.addInstructions( + 0, + """ + const/4 v0, 0x1 + invoke-static {v0}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean; + move-result-object v1 + return-object v1 + """, + ) ?: throw fingerprint.exception + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt new file mode 100644 index 0000000000..12ffa15c15 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/Fingerprints.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.tudortmund.lockscreen + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val brightnessFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("V") + parameters() + custom { method, classDef -> + method.name == "run" && + method.definingClass.contains("/ScreenPlugin\$") && + classDef.fields.any { it.type == "Ljava/lang/Float;" } + } +} diff --git a/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt similarity index 70% rename from src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt index 368879cfab..c8121f7053 100644 --- a/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/patch/ShowOnLockscreenPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tudortmund/lockscreen/ShowOnLockscreenPatch.kt @@ -1,36 +1,35 @@ -package app.revanced.patches.tudortmund.lockscreen.patch +package app.revanced.patches.tudortmund.lockscreen -import app.revanced.util.exception -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.instructions import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.tudortmund.lockscreen.fingerprints.BrightnessFingerprint +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tudortmund.misc.extension.sharedExtensionPatch import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference -@Patch( +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/tudortmund/lockscreen/ShowOnLockscreenPatch;" + +@Suppress("unused") +val showOnLockscreenPatch = bytecodePatch( name = "Show on lockscreen", description = "Shows student id and student ticket on lockscreen.", - compatiblePackages = [CompatiblePackage("de.tudortmund.app")], - requiresIntegrations = true -) -@Suppress("unused") -object ShowOnLockscreenPatch : BytecodePatch( - setOf(BrightnessFingerprint) ) { - private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/tudortmund/lockscreen/ShowOnLockscreenPatch;" + dependsOn(sharedExtensionPatch) + + compatibleWith("de.tudortmund.app") - override fun execute(context: BytecodeContext) { - BrightnessFingerprint.result?.mutableMethod?.apply { + val brightnessMatch by brightnessFingerprint() + + execute { + brightnessMatch.mutableMethod.apply { // Find the instruction where the brightness value is loaded into a register - val brightnessInstruction = implementation!!.instructions.firstNotNullOf { instruction -> + val brightnessInstruction = instructions.firstNotNullOf { instruction -> if (instruction.opcode != Opcode.IGET_OBJECT) return@firstNotNullOf null val getInstruction = instruction as Instruction22c @@ -45,29 +44,30 @@ object ShowOnLockscreenPatch : BytecodePatch( // Gets the index of that instruction and the register of the Activity. val (windowIndex, activityRegister) = implementation!!.instructions.withIndex() .firstNotNullOf { (index, instruction) -> - if (instruction.opcode != Opcode.INVOKE_VIRTUAL) + if (instruction.opcode != Opcode.INVOKE_VIRTUAL) { return@firstNotNullOf null + } val invokeInstruction = instruction as Instruction35c val methodRef = invokeInstruction.reference as MethodReference - if (methodRef.name != "getWindow" || methodRef.returnType != "Landroid/view/Window;") + if (methodRef.name != "getWindow" || methodRef.returnType != "Landroid/view/Window;") { return@firstNotNullOf null + } Pair(index, invokeInstruction.registerC) } // The register in which the brightness value is loaded val brightnessRegister = brightnessInstruction.registerA - // Replaces the getWindow call with our custom one to run the lockscreen code replaceInstruction( windowIndex, "invoke-static { v$activityRegister, v$brightnessRegister }, " + - "$INTEGRATIONS_CLASS_DESCRIPTOR->" + - "getWindow" + - "(Landroidx/appcompat/app/AppCompatActivity;F)" + - "Landroid/view/Window;" + "$EXTENSION_CLASS_DESCRIPTOR->" + + "getWindow" + + "(Landroidx/appcompat/app/AppCompatActivity;F)" + + "Landroid/view/Window;", ) // Normally, the brightness is loaded into a register after the getWindow call. @@ -79,11 +79,10 @@ object ShowOnLockscreenPatch : BytecodePatch( """ invoke-virtual { v$brightnessRegister }, Ljava/lang/Float;->floatValue()F move-result v$brightnessRegister - """ + """, ) addInstruction(windowIndex, brightnessInstruction) - - } ?: throw BrightnessFingerprint.exception + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tudortmund/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tudortmund/misc/extension/ExtensionPatch.kt new file mode 100644 index 0000000000..1c056b0c64 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tudortmund/misc/extension/ExtensionPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.tudortmund.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch() diff --git a/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt similarity index 66% rename from src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt rename to patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt index 77d31b9f3f..4a9bbe297e 100644 --- a/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/ads/DisableDashboardAds.kt @@ -1,20 +1,19 @@ package app.revanced.patches.tumblr.ads -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.tumblr.timelinefilter.TimelineFilterPatch +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tumblr.timelinefilter.addTimelineObjectTypeFilter +import app.revanced.patches.tumblr.timelinefilter.filterTimelineObjectsPatch -@Patch( +@Suppress("unused") +val disableDashboardAdsPatch = bytecodePatch( name = "Disable dashboard ads", description = "Disables ads in the dashboard.", - compatiblePackages = [CompatiblePackage("com.tumblr")], - dependencies = [TimelineFilterPatch::class], -) -@Suppress("unused") -object DisableDashboardAds : BytecodePatch(emptySet()) { - override fun execute(context: BytecodeContext) { +) { + dependsOn(filterTimelineObjectsPatch) + + compatibleWith("com.tumblr") + + execute { // The timeline object types are filtered by their name in the TimelineObjectType enum. // This is often different from the "object_type" returned in the api (noted in comments here) arrayOf( @@ -31,7 +30,7 @@ object DisableDashboardAds : BytecodePatch(emptySet()) { "FACEBOOK_BIDDAABLE", // "facebook_biddable_sdk_ad" "GOOGLE_NATIVE", // "google_native_ad" ).forEach { - TimelineFilterPatch.addObjectTypeFilter(it) + addTimelineObjectTypeFilter(it) } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt new file mode 100644 index 0000000000..c166cb83bf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.tumblr.annoyances.adfree + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tumblr.featureflags.addFeatureFlagOverride +import app.revanced.patches.tumblr.featureflags.overrideFeatureFlagsPatch + +@Suppress("unused") +val disableAdFreeBannerPatch = bytecodePatch( + name = "Disable Ad-Free Banner", + description = "Disables the banner with a frog, prompting you to buy Tumblr Ad-Free.", +) { + dependsOn(overrideFeatureFlagsPatch) + + compatibleWith("com.tumblr") + + execute { + // Disable the "AD_FREE_CTA_BANNER" ("Whether or not to show ad free prompt") feature flag. + addFeatureFlagOverride("adFreeCtaBanner", "false") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt new file mode 100644 index 0000000000..3a46c19b26 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.tumblr.annoyances.inappupdate + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tumblr.featureflags.addFeatureFlagOverride +import app.revanced.patches.tumblr.featureflags.overrideFeatureFlagsPatch + +@Suppress("unused") +val disableInAppUpdatePatch = bytecodePatch( + name = "Disable in-app update", + description = "Disables the in-app update check and update prompt.", +) { + dependsOn(overrideFeatureFlagsPatch) + + compatibleWith("com.tumblr") + + execute { + // Before checking for updates using Google Play core AppUpdateManager, the value of this feature flag is checked. + // If this flag is false or the last update check was today and no update check is performed. + addFeatureFlagOverride("inAppUpdate", "false") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt new file mode 100644 index 0000000000..fbd7cf7120 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/DisableBlogNotificationReminderPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.tumblr.annoyances.notifications + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableBlogNotificationReminderPatch = bytecodePatch( + name = "Disable blog notification reminder", + description = "Disables the reminder to enable notifications for blogs you visit.", +) { + compatibleWith("com.tumblr") + + val isBlogNotifyEnabledMatch by isBlogNotifyEnabledFingerprint() + + execute { + isBlogNotifyEnabledMatch.mutableMethod.addInstructions( + 0, + """ + # Return false for BlogNotifyCtaDialog.isEnabled() method. + const/4 v0, 0x0 + return v0 + """, + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/fingerprints/IsBlogNotifyEnabledFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt similarity index 55% rename from src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/fingerprints/IsBlogNotifyEnabledFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt index 55b67f2fa8..67e051a7ba 100644 --- a/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/fingerprints/IsBlogNotifyEnabledFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/notifications/Fingerprints.kt @@ -1,9 +1,11 @@ -package app.revanced.patches.tumblr.annoyances.notifications.fingerprints +package app.revanced.patches.tumblr.annoyances.notifications -import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patcher.fingerprint // The BlogNotifyCtaDialog asks you if you want to enable notifications for a blog. // It shows whenever you visit a certain blog for the second time and disables itself // if it was shown a total of 3 times (stored in app storage). // This targets the BlogNotifyCtaDialog.isEnabled() method to let it always return false. -internal object IsBlogNotifyEnabledFingerprint : MethodFingerprint(strings = listOf("isEnabled --> ", "blog_notify_enabled")) \ No newline at end of file +internal val isBlogNotifyEnabledFingerprint = fingerprint { + strings("isEnabled --> ", "blog_notify_enabled") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt new file mode 100644 index 0000000000..7f1bc208f8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/DisableGiftMessagePopupPatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.tumblr.annoyances.popups + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val disableGiftMessagePopupPatch = bytecodePatch( + name = "Disable gift message popup", + description = "Disables the popup suggesting to buy TumblrMart items for other people.", +) { + compatibleWith("com.tumblr") + + val showGiftMessagePopupMatch by showGiftMessagePopupFingerprint() + + execute { + showGiftMessagePopupMatch.mutableMethod.addInstructions(0, "return-void") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt new file mode 100644 index 0000000000..7d00f2f1bd --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/annoyances/popups/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.tumblr.annoyances.popups + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +// This method is responsible for loading and displaying the visual Layout of the Gift Message Popup. +internal val showGiftMessagePopupFingerprint = fingerprint { + accessFlags(AccessFlags.FINAL, AccessFlags.PUBLIC) + returns("V") + strings("activity", "anchorView", "textMessage") +} diff --git a/src/main/kotlin/app/revanced/patches/tumblr/featureflags/fingerprints/GetFeatureValueFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt similarity index 66% rename from src/main/kotlin/app/revanced/patches/tumblr/featureflags/fingerprints/GetFeatureValueFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt index 0088c3aa6b..e24ca2884a 100644 --- a/src/main/kotlin/app/revanced/patches/tumblr/featureflags/fingerprints/GetFeatureValueFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/Fingerprints.kt @@ -1,9 +1,8 @@ -package app.revanced.patches.tumblr.featureflags.fingerprints +package app.revanced.patches.tumblr.featureflags -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 +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint // This fingerprint targets the method to get the value of a Feature in the class "com.tumblr.configuration.Feature". // Features seem to be Tumblr's A/B testing program. @@ -14,14 +13,14 @@ import com.android.tools.smali.dexlib2.Opcode // Some features seem to be very old and never removed, though, such as Google Login. // The startIndex of the opcode pattern is at the start of the function after the arg null check. // we want to insert our instructions there. -internal object GetFeatureValueFingerprint : MethodFingerprint( - strings = listOf("feature"), - opcodes = listOf( +internal val getFeatureValueFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String;") + parameters("L", "Z") + opcodes( Opcode.IF_EQZ, Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT - ), - returnType = "Ljava/lang/String;", - parameters = listOf("L", "Z"), - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL -) \ No newline at end of file + Opcode.MOVE_RESULT, + ) + strings("feature") +} diff --git a/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt similarity index 61% rename from src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt index 94367009c3..e3d420e93b 100644 --- a/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/featureflags/OverrideFeatureFlagsPatch.kt @@ -1,49 +1,47 @@ package app.revanced.patches.tumblr.featureflags -import app.revanced.util.exception -import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.or -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable -import app.revanced.patches.tumblr.featureflags.fingerprints.GetFeatureValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter -@Patch(description = "Forcibly set the value of A/B testing features of your choice.") -object OverrideFeatureFlagsPatch : BytecodePatch( - setOf(GetFeatureValueFingerprint) +/** + * Override a feature flag with a value. + * + * @param name The name of the feature flag to override. + * @param value The value to override the feature flag with. + */ +@Suppress("KDocUnresolvedReference") +internal lateinit var addFeatureFlagOverride: (name: String, value: String) -> Unit + private set + +@Suppress("unused") +val overrideFeatureFlagsPatch = bytecodePatch( + description = "Forcibly set the value of A/B testing features of your choice.", ) { - /** - * Override a feature flag with a value. - * - * @param name The name of the feature flag to override. - * @param value The value to override the feature flag with. - */ - @Suppress("KDocUnresolvedReference") - internal lateinit var addOverride: (name: String, value: String) -> Unit private set + val getFeatureValueMatch by getFeatureValueFingerprint() - override fun execute(context: BytecodeContext) = GetFeatureValueFingerprint.result?.let { - val configurationClass = it.method.definingClass - val featureClass = it.method.parameterTypes[0].toString() + execute { + val configurationClass = getFeatureValueMatch.method.definingClass + val featureClass = getFeatureValueMatch.method.parameterTypes[0].toString() // The method we want to inject into does not have enough registers, so we inject a helper method // and inject more instructions into it later, see addOverride. - // This is not in an integration since the unused variable would get compiled away and the method would + // This is not in an extension since the unused variable would get compiled away and the method would // get compiled to only have one register, which is not enough for our later injected instructions. val helperMethod = ImmutableMethod( - it.method.definingClass, + getFeatureValueMatch.method.definingClass, "getValueOverride", listOf(ImmutableMethodParameter(featureClass, null, "feature")), "Ljava/lang/String;", - AccessFlags.PUBLIC or AccessFlags.FINAL, + AccessFlags.PUBLIC.value or AccessFlags.FINAL.value, null, null, - MutableMethodImplementation(4) + MutableMethodImplementation(4), ).toMutable().apply { // This is the equivalent of // String featureName = feature.toString() @@ -63,36 +61,36 @@ object OverrideFeatureFlagsPatch : BytecodePatch( # If none of the overrides returned a value, we should return null const/4 v0, 0x0 return-object v0 - """ + """, ) }.also { helperMethod -> - it.mutableClass.methods.add(helperMethod) + getFeatureValueMatch.mutableClass.methods.add(helperMethod) } // Here we actually insert the hook to call our helper method and return its value if it returns not null // This is equivalent to // String forcedValue = getValueOverride(feature) // if (forcedValue != null) return forcedValue - val getFeatureIndex = it.scanResult.patternScanResult!!.startIndex - it.mutableMethod.addInstructionsWithLabels( + val getFeatureIndex = getFeatureValueMatch.patternMatch!!.startIndex + getFeatureValueMatch.mutableMethod.addInstructionsWithLabels( getFeatureIndex, """ - # Call the Helper Method with the Feature - invoke-virtual {p0, p1}, $configurationClass->getValueOverride($featureClass)Ljava/lang/String; - move-result-object v0 - # If it returned null, skip - if-eqz v0, :is_null - # If it didnt return null, return that string - return-object v0 - - # If our override helper returned null, we let the function continue normally - :is_null - nop - """ + # Call the Helper Method with the Feature + invoke-virtual {p0, p1}, $configurationClass->getValueOverride($featureClass)Ljava/lang/String; + move-result-object v0 + # If it returned null, skip + if-eqz v0, :is_null + # If it didnt return null, return that string + return-object v0 + + # If our override helper returned null, we let the function continue normally + :is_null + nop + """, ) val helperInsertIndex = 2 - addOverride = { name, value -> + addFeatureFlagOverride = { name, value -> // For every added override, we add a few instructions in the middle of the helper method // to check if the feature is the one we want and return the override value if it is. // This is equivalent to @@ -112,8 +110,8 @@ object OverrideFeatureFlagsPatch : BytecodePatch( # Else we just continue... :no_override nop - """ + """, ) } - } ?: throw GetFeatureValueFingerprint.exception -} \ No newline at end of file + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt new file mode 100644 index 0000000000..11616fcc92 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/Fingerprints.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.tumblr.fixes + +import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.fingerprint + +// Fingerprint for the addQueryParam method from retrofit2 +// https://github.com/square/retrofit/blob/trunk/retrofit/src/main/java/retrofit2/RequestBuilder.java#L186 +// Injecting here allows modifying dynamically set query parameters +internal val addQueryParamFingerprint = fingerprint { + parameters("Ljava/lang/String;", "Ljava/lang/String;", "Z") + strings("Malformed URL. Base: ", ", Relative: ") +} + +// Fingerprint for the parseHttpMethodAndPath method from retrofit2 +// https://github.com/square/retrofit/blob/ebf87b10997e2136af4d335276fa950221852c64/retrofit/src/main/java/retrofit2/RequestFactory.java#L270-L302 +// Injecting here allows modifying the path/query params of API endpoints defined via annotations +internal val httpPathParserFingerprint = fingerprint { + opcodes( + Opcode.IPUT_OBJECT, + Opcode.IPUT_BOOLEAN, + ) + strings("Only one HTTP method is allowed. Found: %s and %s.") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt new file mode 100644 index 0000000000..d51aead2b5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/fixes/FixOldVersionsPatch.kt @@ -0,0 +1,57 @@ +package app.revanced.patches.tumblr.fixes + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val fixOldVersionsPatch = bytecodePatch( + name = "Fix old versions", + description = "Fixes old versions of the app (v33.2 and earlier) breaking due to Tumblr removing remnants of Tumblr" + + " Live from the API, which causes many requests to fail. This patch has no effect on newer versions of the app.", + use = false, +) { + compatibleWith("com.tumblr") + + val httpPathParserMatch by httpPathParserFingerprint() + val addQueryParamMatch by addQueryParamFingerprint() + + execute { + val liveQueryParameters = listOf( + ",?live_now", + ",?live_streaming_user_id", + ) + + // Remove the live query parameters from the path when it's specified via a @METHOD annotation. + for (liveQueryParameter in liveQueryParameters) { + httpPathParserMatch.mutableMethod.addInstructions( + httpPathParserMatch.patternMatch!!.endIndex + 1, + """ + # urlPath = urlPath.replace(liveQueryParameter, "") + const-string p1, "$liveQueryParameter" + const-string p3, "" + invoke-virtual {p2, p1, p3}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String; + move-result-object p2 + """, + ) + } + + // Remove the live query parameters when passed via a parameter which has the @Query annotation. + // e.g. an API call could be defined like this: + // @GET("api/me/info") + // ApiResponse getCurrentUserInfo(@Query("fields[blog]") String value) + // which would result in the path "api/me/inf0?fields[blog]=${value}" + // Here we make sure that this value doesn't contain the broken query parameters. + for (liveQueryParameter in liveQueryParameters) { + addQueryParamMatch.mutableMethod.addInstructions( + 0, + """ + # queryParameterValue = queryParameterValue.replace(liveQueryParameter, "") + const-string v0, "$liveQueryParameter" + const-string v1, "" + invoke-virtual {p2, v0, v1}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String; + move-result-object p2 + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/live/DisableTumblrLivePatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/live/DisableTumblrLivePatch.kt new file mode 100644 index 0000000000..64fa2ed79f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/live/DisableTumblrLivePatch.kt @@ -0,0 +1,29 @@ +package app.revanced.patches.tumblr.live + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tumblr.featureflags.addFeatureFlagOverride +import app.revanced.patches.tumblr.featureflags.overrideFeatureFlagsPatch +import app.revanced.patches.tumblr.timelinefilter.addTimelineObjectTypeFilter +import app.revanced.patches.tumblr.timelinefilter.filterTimelineObjectsPatch + +@Suppress("unused") +@Deprecated("Tumblr Live was removed and is no longer served in the feed, making this patch useless.") +val disableTumblrLivePatch = bytecodePatch( + description = "Disable the Tumblr Live tab button and dashboard carousel.", +) { + dependsOn( + overrideFeatureFlagsPatch, + filterTimelineObjectsPatch, + ) + + compatibleWith("com.tumblr") + + execute { + // Hide the LIVE_MARQUEE timeline element that appears in the feed + // Called "live_marquee" in api response + addTimelineObjectTypeFilter("LIVE_MARQUEE") + + // Hide the Tab button for Tumblr Live by forcing the feature flag to false + addFeatureFlagOverride("liveStreaming", "false") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/misc/extension/ExtensionPatch.kt new file mode 100644 index 0000000000..3a69b74a51 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/misc/extension/ExtensionPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.tumblr.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch() diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt new file mode 100644 index 0000000000..ff44e1bdb8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/FilterTimelineObjectsPatch.kt @@ -0,0 +1,67 @@ +package app.revanced.patches.tumblr.timelinefilter + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.tumblr.misc.extension.sharedExtensionPatch +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c + +/** + * Add a filter to hide the given timeline object type. + * The list of all Timeline object types is found in the TimelineObjectType class, + * where they are mapped from their api name (returned by tumblr via the HTTP API) to the enum value name. + * + * @param typeName The enum name of the timeline object type to hide. + */ +@Suppress("KDocUnresolvedReference") +lateinit var addTimelineObjectTypeFilter: (typeName: String) -> Unit + +@Suppress("unused") +val filterTimelineObjectsPatch = bytecodePatch( + description = "Filter timeline objects.", +) { + dependsOn(sharedExtensionPatch) + + val timelineConstructorMatch by timelineConstructorFingerprint() + val timelineFilterExtensionMatch by timelineFilterExtensionFingerprint() + val postsResponseConstructorMatch by postsResponseConstructorFingerprint() + + execute { + val filterInsertIndex = timelineFilterExtensionMatch.patternMatch!!.startIndex + + timelineFilterExtensionMatch.mutableMethod.apply { + val addInstruction = getInstruction(filterInsertIndex + 1) + + val filterListRegister = addInstruction.registerC + val stringRegister = addInstruction.registerD + + // Remove "BLOCKED_OBJECT_DUMMY" + removeInstructions(filterInsertIndex, 2) + + addTimelineObjectTypeFilter = { typeName -> + // blockedObjectTypes.add({typeName}) + addInstructionsWithLabels( + filterInsertIndex, + """ + const-string v$stringRegister, "$typeName" + invoke-virtual { v$filterListRegister, v$stringRegister }, Ljava/util/HashSet;->add(Ljava/lang/Object;)Z + """, + ) + } + } + + mapOf( + timelineConstructorMatch to 1, + postsResponseConstructorMatch to 2, + ).forEach { (match, timelineObjectsRegister) -> + match.mutableMethod.addInstructions( + 0, + "invoke-static {p$timelineObjectsRegister}, " + + "Lapp/revanced/extension/tumblr/patches/TimelineFilterPatch;->" + + "filterTimeline(Ljava/util/List;)V", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt new file mode 100644 index 0000000000..b8ed3bfc4e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/tumblr/timelinefilter/Fingerprints.kt @@ -0,0 +1,36 @@ +package app.revanced.patches.tumblr.timelinefilter + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +// This is the constructor of the PostsResponse class. +// The same applies here as with the TimelineConstructorFingerprint. +internal val postsResponseConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.CONSTRUCTOR, AccessFlags.PUBLIC) + custom { method, classDef -> classDef.endsWith("/PostsResponse;") && method.parameters.size == 4 } +} + +// This is the constructor of the Timeline class. +// It receives the List as an argument with a @Json annotation, so this should be the first time +// that the List is exposed in non-library code. +internal val timelineConstructorFingerprint = fingerprint { + strings("timelineObjectsList") + custom { method, classDef -> + classDef.endsWith("/Timeline;") && method.parameters[0].type == "Ljava/util/List;" + } +} + +// This fingerprints the extension TimelineFilterPatch.filterTimeline method. +// The opcode fingerprint is searching for +// if ("BLOCKED_OBJECT_DUMMY".equals(elementType)) iterator.remove(); +internal val timelineFilterExtensionFingerprint = fingerprint { + opcodes( + Opcode.CONST_STRING, // "BLOCKED_OBJECT_DUMMY" + Opcode.INVOKE_VIRTUAL, // HashSet.add(^) + ) + strings("BLOCKED_OBJECT_DUMMY") + custom { _, classDef -> + classDef.endsWith("/TimelineFilterPatch;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt new file mode 100644 index 0000000000..6827a99e36 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/AudioAdsPatch.kt @@ -0,0 +1,48 @@ +package app.revanced.patches.twitch.ad.audio + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch +import app.revanced.patches.twitch.misc.settings.PreferenceScreen +import app.revanced.patches.twitch.misc.settings.settingsPatch + +@Suppress("unused") +val audioAdsPatch = bytecodePatch( + name = "Block audio ads", + description = "Blocks audio ads in streams and VODs.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1")) + + val audioAdsPresenterPlayMatch by audioAdsPresenterPlayFingerprint() + + execute { + addResources("twitch", "ad.audio.audioAdsPatch") + + PreferenceScreen.ADS.CLIENT_SIDE.addPreferences( + SwitchPreference("revanced_block_audio_ads"), + ) + + // Block playAds call + audioAdsPresenterPlayMatch.mutableMethod.addInstructionsWithLabels( + 0, + """ + invoke-static { }, Lapp/revanced/extension/twitch/patches/AudioAdsPatch;->shouldBlockAudioAds()Z + move-result v0 + if-eqz v0, :show_audio_ads + return-void + """, + ExternalLabel("show_audio_ads", audioAdsPresenterPlayMatch.mutableMethod.getInstruction(0)), + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt new file mode 100644 index 0000000000..21e9cb6d2a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/audio/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.twitch.ad.audio + +import app.revanced.patcher.fingerprint + +internal val audioAdsPresenterPlayFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("AudioAdsPlayerPresenter;") && method.name == "playAd" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt new file mode 100644 index 0000000000..4d36bc22d9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/EmbeddedAdsPatch.kt @@ -0,0 +1,44 @@ +package app.revanced.patches.twitch.ad.embedded + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.twitch.ad.video.videoAdsPatch +import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch +import app.revanced.patches.twitch.misc.settings.PreferenceScreen +import app.revanced.patches.twitch.misc.settings.settingsPatch + +@Suppress("unused") +val embeddedAdsPatch = bytecodePatch( + name = "Block embedded ads", + description = "Blocks embedded stream ads using services like Luminous or PurpleAdBlocker.", +) { + dependsOn( + videoAdsPatch, + sharedExtensionPatch, + settingsPatch, + ) + + compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1")) + + val createUsherClientMatch by createsUsherClientFingerprint() + + execute { + addResources("twitch", "ad.embedded.embeddedAdsPatch") + + PreferenceScreen.ADS.SURESTREAM.addPreferences( + ListPreference("revanced_block_embedded_ads", summaryKey = null), + ) + + // Inject OkHttp3 application interceptor + createUsherClientMatch.mutableMethod.addInstructions( + 3, + """ + invoke-static {}, Lapp/revanced/extension/twitch/patches/EmbeddedAdsPatch;->createRequestInterceptor()Lapp/revanced/extension/twitch/api/RequestInterceptor; + move-result-object v2 + invoke-virtual {v0, v2}, Lokhttp3/OkHttpClient${"$"}Builder;->addInterceptor(Lokhttp3/Interceptor;)Lokhttp3/OkHttpClient${"$"}Builder; + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt new file mode 100644 index 0000000000..cbf8174693 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/embedded/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.twitch.ad.embedded + +import app.revanced.patcher.fingerprint + +internal val createsUsherClientFingerprint = fingerprint { + custom { method, _ -> + method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;") && method.name == "buildOkHttpClient" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt new file mode 100644 index 0000000000..48ecefba8e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/shared/util/AdPatch.kt @@ -0,0 +1,67 @@ +package app.revanced.patches.twitch.ad.shared.util + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.BytecodePatchBuilder +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel + +fun adPatch( + conditionCall: String, + skipLabelName: String, + block: BytecodePatchBuilder.( + createConditionInstructions: (register: String) -> String, + blockMethods: BytecodePatchContext.( + clazz: String, + methodNames: Set, + returnMethod: ReturnMethod, + ) -> Boolean, + ) -> Unit, +) = bytecodePatch { + fun createConditionInstructions(register: String) = """ + invoke-static { }, $conditionCall + move-result $register + if-eqz $register, :$skipLabelName + """ + + fun BytecodePatchContext.blockMethods( + classDefType: String, + methodNames: Set, + returnMethod: ReturnMethod, + ) = with(classBy { classDefType == it.type }?.mutableClass) { + this ?: return false + + methods.filter { it.name in methodNames }.forEach { + val retInstruction = when (returnMethod.returnType) { + 'V' -> "return-void" + 'Z' -> + """ + const/4 v0, ${returnMethod.value} + return v0 + """ + + else -> throw NotImplementedError() + } + + it.addInstructionsWithLabels( + 0, + """ + ${createConditionInstructions("v0")} + $retInstruction + """, + ExternalLabel(skipLabelName, it.getInstruction(0)), + ) + } + + true + } + + block(::createConditionInstructions, BytecodePatchContext::blockMethods) +} + +class ReturnMethod(val returnType: Char, val value: String) { + companion object { + val default = ReturnMethod('V', "") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt new file mode 100644 index 0000000000..d03449733f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/Fingerprints.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.twitch.ad.video + +import app.revanced.patcher.fingerprint + +internal val checkAdEligibilityLambdaFingerprint = fingerprint { + returns("Lio/reactivex/Single;") + parameters("L") + custom { method, _ -> + method.definingClass.endsWith("/AdEligibilityFetcher;") && + method.name == "shouldRequestAd" + } +} + +internal val contentConfigShowAdsFingerprint = fingerprint { + returns("Z") + parameters() + custom { method, _ -> + method.definingClass.endsWith("/ContentConfigData;") && method.name == "getShowAds" + } +} + +internal val getReadyToShowAdFingerprint = fingerprint { + returns("Ltv/twitch/android/core/mvp/presenter/StateAndAction;") + parameters("L", "L") + custom { method, _ -> + method.definingClass.endsWith("/StreamDisplayAdsPresenter;") && method.name == "getReadyToShowAdOrAbort" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt new file mode 100644 index 0000000000..5c0eed6ebe --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/ad/video/VideoAdsPatch.kt @@ -0,0 +1,167 @@ +package app.revanced.patches.twitch.ad.video + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.twitch.ad.shared.util.ReturnMethod +import app.revanced.patches.twitch.ad.shared.util.adPatch +import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch +import app.revanced.patches.twitch.misc.settings.PreferenceScreen +import app.revanced.patches.twitch.misc.settings.settingsPatch + +val videoAdsPatch = bytecodePatch( + name = "Block video ads", + description = "Blocks video ads in streams and VODs.", +) { + val conditionCall = "Lapp/revanced/extension/twitch/patches/VideoAdsPatch;->shouldBlockVideoAds()Z" + val skipLabelName = "show_video_ads" + + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + adPatch(conditionCall, skipLabelName) { createConditionInstructions, blockMethods -> + val checkAdEligibilityLambdaMatch by checkAdEligibilityLambdaFingerprint() + val getReadyToShowAdMatch by getReadyToShowAdFingerprint() + val contentConfigShowAdsMatch by contentConfigShowAdsFingerprint() + + execute { context -> + addResources("twitch", "ad.video.videoAdsPatch") + + PreferenceScreen.ADS.CLIENT_SIDE.addPreferences( + SwitchPreference("revanced_block_video_ads"), + ) + + /* Amazon ads SDK */ + context.blockMethods( + "Lcom/amazon/ads/video/player/AdsManagerImpl;", + setOf("playAds"), + ReturnMethod.default, + ) + + /* Twitch ads manager */ + context.blockMethods( + "Ltv/twitch/android/shared/ads/VideoAdManager;", + setOf( + "checkAdEligibilityAndRequestAd", + "requestAd", + "requestAds", + ), + ReturnMethod.default, + ) + + /* Various ad presenters */ + context.blockMethods( + "Ltv/twitch/android/shared/ads/AdsPlayerPresenter;", + setOf( + "requestAd", + "requestFirstAd", + "requestFirstAdIfEligible", + "requestMidroll", + "requestAdFromMultiAdFormatEvent", + ), + ReturnMethod.default, + ) + + context.blockMethods( + "Ltv/twitch/android/shared/ads/AdsVodPlayerPresenter;", + setOf( + "requestAd", + "requestFirstAd", + ), + ReturnMethod.default, + ) + + context.blockMethods( + "Ltv/twitch/android/feature/theatre/ads/AdEdgeAllocationPresenter;", + setOf( + "parseAdAndCheckEligibility", + "requestAdsAfterEligibilityCheck", + "showAd", + "bindMultiAdFormatAllocation", + ), + ReturnMethod.default, + ) + + /* A/B ad testing experiments */ + context.blockMethods( + "Ltv/twitch/android/provider/experiments/helpers/DisplayAdsExperimentHelper;", + setOf("areDisplayAdsEnabled"), + ReturnMethod('Z', "0"), + ) + + context.blockMethods( + "Ltv/twitch/android/shared/ads/tracking/MultiFormatAdsTrackingExperiment;", + setOf( + "shouldUseMultiAdFormatTracker", + "shouldUseVideoAdTracker", + ), + ReturnMethod('Z', "0"), + ) + + context.blockMethods( + "Ltv/twitch/android/shared/ads/MultiformatAdsExperiment;", + setOf( + "shouldDisableClientSideLivePreroll", + "shouldDisableClientSideVodPreroll", + ), + ReturnMethod('Z', "1"), + ) + + // Pretend our player is ineligible for all ads. + checkAdEligibilityLambdaMatch.mutableMethod.addInstructionsWithLabels( + 0, + """ + ${createConditionInstructions("v0")} + const/4 v0, 0 + invoke-static {v0}, Lio/reactivex/Single;->just(Ljava/lang/Object;)Lio/reactivex/Single; + move-result-object p0 + return-object p0 + """, + ExternalLabel( + skipLabelName, + checkAdEligibilityLambdaMatch.mutableMethod.getInstruction(0), + ), + ) + + val adFormatDeclined = + "Ltv/twitch/android/shared/display/ads/theatre/StreamDisplayAdsPresenter\$Action\$AdFormatDeclined;" + getReadyToShowAdMatch.mutableMethod.addInstructionsWithLabels( + 0, + """ + ${createConditionInstructions("v0")} + sget-object p2, $adFormatDeclined->INSTANCE:$adFormatDeclined + invoke-static {p1, p2}, Ltv/twitch/android/core/mvp/presenter/StateMachineKt;->plus(Ltv/twitch/android/core/mvp/presenter/PresenterState;Ltv/twitch/android/core/mvp/presenter/PresenterAction;)Ltv/twitch/android/core/mvp/presenter/StateAndAction; + move-result-object p1 + return-object p1 + """, + ExternalLabel(skipLabelName, getReadyToShowAdMatch.mutableMethod.getInstruction(0)), + ) + + // Spoof showAds JSON field. + contentConfigShowAdsMatch.mutableMethod.addInstructions( + 0, + """ + ${createConditionInstructions("v0")} + const/4 v0, 0 + :$skipLabelName + return v0 + """, + ) + } + }, + ) + + compatibleWith( + "tv.twitch.android.app"( + "15.4.1", + "16.1.0", + "16.9.1", + ), + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt new file mode 100644 index 0000000000..21da99a5bb --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/Fingerprints.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.twitch.chat.antidelete + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val chatUtilCreateDeletedSpanFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("ChatUtil\$Companion;") && method.name == "createDeletedSpanFromChatMessageSpan" + } +} + +internal val deletedMessageClickableSpanCtorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + custom { _, classDef -> + classDef.endsWith("DeletedMessageClickableSpan;") + } +} + +internal val setHasModAccessFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("DeletedMessageClickableSpan;") && method.name == "setHasModAccess" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt new file mode 100644 index 0000000000..93996e77a9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/antidelete/ShowDeletedMessagesPatch.kt @@ -0,0 +1,78 @@ +package app.revanced.patches.twitch.chat.antidelete + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch +import app.revanced.patches.twitch.misc.settings.PreferenceScreen +import app.revanced.patches.twitch.misc.settings.settingsPatch + +@Suppress("unused") +val showDeletedMessagesPatch = bytecodePatch( + name = "Show deleted messages", + description = "Shows deleted chat messages behind a clickable spoiler.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1")) + + fun createSpoilerConditionInstructions(register: String = "v0") = """ + invoke-static {}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->shouldUseSpoiler()Z + move-result $register + if-eqz $register, :no_spoiler + """ + + val setHasModAccessMatch by setHasModAccessFingerprint() + val deletedMessageClickableSpanCtorMatch by deletedMessageClickableSpanCtorFingerprint() + val chatUtilCreateDeletedSpanMatch by chatUtilCreateDeletedSpanFingerprint() + + execute { + addResources("twitch", "chat.antidelete.showDeletedMessagesPatch") + + PreferenceScreen.CHAT.GENERAL.addPreferences( + ListPreference( + key = "revanced_show_deleted_messages", + summaryKey = null, + ), + ) + + // Spoiler mode: Force set hasModAccess member to true in constructor + deletedMessageClickableSpanCtorMatch.mutableMethod.apply { + addInstructionsWithLabels( + implementation!!.instructions.lastIndex, /* place in front of return-void */ + """ + ${createSpoilerConditionInstructions()} + const/4 v0, 1 + iput-boolean v0, p0, $definingClass->hasModAccess:Z + """, + ExternalLabel("no_spoiler", getInstruction(implementation!!.instructions.lastIndex)), + ) + } + + // Spoiler mode: Disable setHasModAccess setter + setHasModAccessMatch.mutableMethod.addInstruction(0, "return-void") + + // Cross-out mode: Reformat span of deleted message + chatUtilCreateDeletedSpanMatch.mutableMethod.apply { + addInstructionsWithLabels( + 0, + """ + invoke-static {p2}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->reformatDeletedMessage(Landroid/text/Spanned;)Landroid/text/Spanned; + move-result-object v0 + if-eqz v0, :no_reformat + return-object v0 + """, + ExternalLabel("no_reformat", getInstruction(0)), + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt similarity index 50% rename from src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt index 740f8cedf8..15a03f7c3c 100644 --- a/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/AutoClaimChannelPointsPatch.kt @@ -1,41 +1,42 @@ package app.revanced.patches.twitch.chat.autoclaim -import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.all.misc.resources.AddResourcesPatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.twitch.chat.autoclaim.fingerprints.CommunityPointsButtonViewDelegateFingerprint -import app.revanced.patches.twitch.misc.settings.SettingsPatch -import app.revanced.util.exception +import app.revanced.patches.twitch.misc.settings.PreferenceScreen +import app.revanced.patches.twitch.misc.settings.settingsPatch -@Patch( +@Suppress("unused") +val autoClaimChannelPointsPatch = bytecodePatch( name = "Auto claim channel points", description = "Automatically claim Channel Points.", - dependencies = [SettingsPatch::class, AddResourcesPatch::class], - compatiblePackages = [CompatiblePackage("tv.twitch.android.app", ["15.4.1", "16.1.0", "16.9.1"])] -) -@Suppress("unused") -object AutoClaimChannelPointsPatch : BytecodePatch( - setOf(CommunityPointsButtonViewDelegateFingerprint) ) { - override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) + dependsOn( + settingsPatch, + addResourcesPatch, + ) + + compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1")) + + val communityPointsButtonViewDelegateMatch by communityPointsButtonViewDelegateFingerprint() + + execute { + addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch") - SettingsPatch.PreferenceScreen.CHAT.GENERAL.addPreferences( - SwitchPreference("revanced_auto_claim_channel_points") + PreferenceScreen.CHAT.GENERAL.addPreferences( + SwitchPreference("revanced_auto_claim_channel_points"), ) - CommunityPointsButtonViewDelegateFingerprint.result?.mutableMethod?.apply { + communityPointsButtonViewDelegateMatch.mutableMethod.apply { val lastIndex = implementation!!.instructions.lastIndex addInstructionsWithLabels( lastIndex, // place in front of return-void """ - invoke-static {}, Lapp/revanced/integrations/twitch/patches/AutoClaimChannelPointsPatch;->shouldAutoClaim()Z + invoke-static {}, Lapp/revanced/extension/twitch/patches/AutoClaimChannelPointsPatch;->shouldAutoClaim()Z move-result v0 if-eqz v0, :auto_claim @@ -44,8 +45,8 @@ object AutoClaimChannelPointsPatch : BytecodePatch( iget-object v0, p0, Ltv/twitch/android/shared/community/points/viewdelegate/CommunityPointsButtonViewDelegate;->buttonLayout:Landroid/view/ViewGroup; invoke-virtual { v0 }, Landroid/view/View;->callOnClick()Z """, - ExternalLabel("auto_claim", getInstruction(lastIndex)) + ExternalLabel("auto_claim", getInstruction(lastIndex)), ) - } ?: throw CommunityPointsButtonViewDelegateFingerprint.exception + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt new file mode 100644 index 0000000000..80abc9ac41 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/chat/autoclaim/Fingerprints.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.twitch.chat.autoclaim + +import app.revanced.patcher.fingerprint + +internal val communityPointsButtonViewDelegateFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("CommunityPointsButtonViewDelegate;") && + method.name == "showClaimAvailable" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt new file mode 100644 index 0000000000..25cd5acc1e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/DebugModePatch.kt @@ -0,0 +1,52 @@ +package app.revanced.patches.twitch.debug + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch +import app.revanced.patches.twitch.misc.settings.PreferenceScreen +import app.revanced.patches.twitch.misc.settings.settingsPatch + +@Suppress("unused") +val debugModePatch = bytecodePatch( + name = "Debug mode", + description = "Enables Twitch's internal debugging mode.", + use = false, +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith("tv.twitch.android.app") + + val isDebugConfigEnabledMatch by isDebugConfigEnabledFingerprint() + val isOmVerificationEnabledMatch by isOmVerificationEnabledFingerprint() + val shouldShowDebugOptionsMatch by shouldShowDebugOptionsFingerprint() + + execute { + addResources("twitch", "debug.debugModePatch") + + PreferenceScreen.MISC.OTHER.addPreferences( + SwitchPreference("revanced_twitch_debug_mode"), + ) + + listOf( + isDebugConfigEnabledMatch, + isOmVerificationEnabledMatch, + shouldShowDebugOptionsMatch, + ).forEach { + it.mutableMethod.addInstructions( + 0, + """ + invoke-static {}, Lapp/revanced/extension/twitch/patches/DebugModePatch;->isDebugModeEnabled()Z + move-result v0 + return v0 + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt new file mode 100644 index 0000000000..665180c19b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/debug/Fingerprints.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.twitch.debug + +import app.revanced.patcher.fingerprint + +internal val isDebugConfigEnabledFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/BuildConfigUtil;") && method.name == "isDebugConfigEnabled" + } +} + +internal val isOmVerificationEnabledFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/BuildConfigUtil;") && method.name == "isOmVerificationEnabled" + } +} + +internal val shouldShowDebugOptionsFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/BuildConfigUtil;") && method.name == "shouldShowDebugOptions" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt new file mode 100644 index 0000000000..9a46867ab5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/Hooks.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.twitch.misc.extension + +import app.revanced.patches.shared.misc.extension.extensionHook + +internal val initHook = extensionHook { + custom { method, classDef -> + method.name == "onCreate" && classDef.endsWith("/TwitchApplication;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/SharedExtensionPatch.kt new file mode 100644 index 0000000000..2299d3bb5f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/extension/SharedExtensionPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.twitch.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch(initHook) diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/Fingerprints.kt new file mode 100644 index 0000000000..43d5bb39bf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/Fingerprints.kt @@ -0,0 +1,34 @@ +package app.revanced.patches.twitch.misc.settings + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val menuGroupsOnClickFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC, AccessFlags.FINAL) + returns("V") + parameters("L", "L", "L") + custom { method, classDef -> + classDef.endsWith("/SettingsMenuViewDelegate;") && + method.name.contains("render") + } +} + +internal val menuGroupsUpdatedFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/SettingsMenuPresenter\$Event\$MenuGroupsUpdated;") && + method.name == "" + } +} + +internal val settingsActivityOnCreateFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/SettingsActivity;") && + method.name == "onCreate" + } +} + +internal val settingsMenuItemEnumFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("/SettingsMenuItem;") && method.name == "" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt new file mode 100644 index 0000000000..4236877924 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt @@ -0,0 +1,208 @@ +package app.revanced.patches.twitch.misc.settings + +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.BasePreference +import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen +import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.settings.settingsPatch +import app.revanced.patches.twitch.misc.extension.sharedExtensionPatch +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.immutable.ImmutableField + +private const val REVANCED_SETTINGS_MENU_ITEM_NAME = "RevancedSettings" +private const val REVANCED_SETTINGS_MENU_ITEM_ID = 0x7 +private const val REVANCED_SETTINGS_MENU_ITEM_TITLE_RES = "revanced_settings" +private const val REVANCED_SETTINGS_MENU_ITEM_ICON_RES = "ic_settings" + +private const val MENU_ITEM_ENUM_CLASS_DESCRIPTOR = "Ltv/twitch/android/feature/settings/menu/SettingsMenuItem;" +private const val MENU_DISMISS_EVENT_CLASS_DESCRIPTOR = + "Ltv/twitch/android/feature/settings/menu/SettingsMenuViewDelegate\$Event\$OnDismissClicked;" + +private const val EXTENSION_PACKAGE = "app/revanced/extension/twitch" +private const val ACTIVITY_HOOKS_CLASS_DESCRIPTOR = "L$EXTENSION_PACKAGE/settings/AppCompatActivityHook;" +private const val UTILS_CLASS_DESCRIPTOR = "L$EXTENSION_PACKAGE/Utils;" + +private val preferences = mutableSetOf() + +fun addSettingPreference(screen: BasePreference) { + preferences += screen +} + +val settingsPatch = bytecodePatch( + name = "Settings", + description = "Adds settings menu to Twitch.", +) { + dependsOn( + sharedExtensionPatch, + addResourcesPatch, + settingsPatch(preferences = preferences), + ) + + compatibleWith( + "tv.twitch.android.app"( + "15.4.1", + "16.1.0", + "16.9.1", + ), + ) + + val settingsActivityOnCreateMatch by settingsActivityOnCreateFingerprint() + val settingsMenuItemEnumMatch by settingsMenuItemEnumFingerprint() + val menuGroupsUpdatedMatch by menuGroupsUpdatedFingerprint() + val menuGroupsOnClickMatch by menuGroupsOnClickFingerprint() + + execute { + addResources("twitch", "misc.settings.settingsPatch") + + PreferenceScreen.MISC.OTHER.addPreferences( + // The debug setting is shared across multiple apps and the key must be the same. + // But the title and summary must be different, otherwise when the strings file is flattened + // for Crowdin push, Crowdin gets confused by the duplicate keys. + // FIXME: Ideally the shared debug strings are extracted into a common app group + // and then both apps import that. But for now unique unique title and summary keys also works. + SwitchPreference( + key = "revanced_debug", + titleKey = "revanced_twitch_debug_title", + summaryOnKey = "revanced_twitch_debug_summary_on", + summaryOffKey = "revanced_twitch_debug_summary_off", + ), + ) + + // Hook onCreate to handle fragment creation. + val insertIndex = settingsActivityOnCreateMatch.mutableMethod.implementation!!.instructions.size - 2 + settingsActivityOnCreateMatch.mutableMethod.addInstructionsWithLabels( + insertIndex, + """ + invoke-static { p0 }, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingsCreation(Landroidx/appcompat/app/AppCompatActivity;)Z + move-result v0 + if-eqz v0, :no_rv_settings_init + return-void + """, + ExternalLabel( + "no_rv_settings_init", + settingsActivityOnCreateMatch.mutableMethod.getInstruction(insertIndex), + ), + ) + + // Create new menu item for settings menu. + fun Match.injectMenuItem( + name: String, + value: Int, + titleResourceName: String, + iconResourceName: String, + ) { + // Add new static enum member field + mutableClass.staticFields.add( + ImmutableField( + mutableMethod.definingClass, + name, + MENU_ITEM_ENUM_CLASS_DESCRIPTOR, + AccessFlags.PUBLIC.value or + AccessFlags.FINAL.value or + AccessFlags.ENUM.value or + AccessFlags.STATIC.value, + null, + null, + null, + ).toMutable(), + ) + + // Add initializer for the new enum member + mutableMethod.addInstructions( + mutableMethod.implementation!!.instructions.size - 4, + """ + new-instance v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR + const-string v1, "$titleResourceName" + invoke-static {v1}, $UTILS_CLASS_DESCRIPTOR->getStringId(Ljava/lang/String;)I + move-result v1 + const-string v3, "$iconResourceName" + invoke-static {v3}, $UTILS_CLASS_DESCRIPTOR->getDrawableId(Ljava/lang/String;)I + move-result v3 + const-string v4, "$name" + const/4 v5, $value + invoke-direct {v0, v4, v5, v1, v3}, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->(Ljava/lang/String;III)V + sput-object v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->$name:$MENU_ITEM_ENUM_CLASS_DESCRIPTOR + """, + ) + } + + settingsMenuItemEnumMatch.injectMenuItem( + REVANCED_SETTINGS_MENU_ITEM_NAME, + REVANCED_SETTINGS_MENU_ITEM_ID, + REVANCED_SETTINGS_MENU_ITEM_TITLE_RES, + REVANCED_SETTINGS_MENU_ITEM_ICON_RES, + ) + + // Intercept settings menu creation and add new menu item. + menuGroupsUpdatedMatch.mutableMethod.addInstructions( + 0, + """ + sget-object v0, $MENU_ITEM_ENUM_CLASS_DESCRIPTOR->$REVANCED_SETTINGS_MENU_ITEM_NAME:$MENU_ITEM_ENUM_CLASS_DESCRIPTOR + invoke-static { p1, v0 }, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingMenuCreation(Ljava/util/List;Ljava/lang/Object;)Ljava/util/List; + move-result-object p1 + """, + ) + + // Intercept onclick events for the settings menu + + menuGroupsOnClickMatch.mutableMethod.addInstructionsWithLabels( + 0, + """ + invoke-static {p1}, $ACTIVITY_HOOKS_CLASS_DESCRIPTOR->handleSettingMenuOnClick(Ljava/lang/Enum;)Z + move-result p2 + if-eqz p2, :no_rv_settings_onclick + sget-object p1, $MENU_DISMISS_EVENT_CLASS_DESCRIPTOR->INSTANCE:$MENU_DISMISS_EVENT_CLASS_DESCRIPTOR + invoke-virtual { p0, p1 }, Ltv/twitch/android/core/mvp/viewdelegate/RxViewDelegate;->pushEvent(Ltv/twitch/android/core/mvp/viewdelegate/ViewDelegateEvent;)V + return-void + """, + ExternalLabel( + "no_rv_settings_onclick", + menuGroupsOnClickMatch.mutableMethod.getInstruction(0), + ), + ) + } + + finalize { + PreferenceScreen.close() + } +} + +/** + * Preference screens patches should add their settings to. + */ +@Suppress("ktlint:standard:property-naming") +internal object PreferenceScreen : BasePreferenceScreen() { + val ADS = CustomScreen("revanced_ads_screen") + val CHAT = CustomScreen("revanced_chat_screen") + val MISC = CustomScreen("revanced_misc_screen") + + internal class CustomScreen(key: String) : Screen(key) { + /* Categories */ + val GENERAL = CustomCategory("revanced_general_category") + val OTHER = CustomCategory("revanced_other_category") + val CLIENT_SIDE = CustomCategory("revanced_client_ads_category") + val SURESTREAM = CustomCategory("revanced_surestream_ads_category") + + internal inner class CustomCategory(key: String) : Category(key) { + /* For Twitch, we need to load our CustomPreferenceCategory class instead of the default one. */ + override fun transform(): PreferenceCategory = PreferenceCategory( + key, + preferences = preferences, + tag = "app.revanced.extension.twitch.settings.preference.CustomPreferenceCategory", + ) + } + } + + override fun commit(screen: app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference) { + preferences += screen + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt new file mode 100644 index 0000000000..dc100acb10 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/Fingerprints.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.twitter.interaction.downloads + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val buildMediaOptionsSheetFingerprint = fingerprint { + opcodes( + Opcode.IF_EQ, + Opcode.SGET_OBJECT, + Opcode.GOTO_16, + Opcode.NEW_INSTANCE, + ) + strings("mediaEntity", "media_options_sheet") +} + +internal val constructMediaOptionsSheetFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + strings("captionsState") +} + +internal val showDownloadVideoUpsellBottomSheetFingerprint = fingerprint { + returns("Z") + strings("mediaEntity", "url") + opcodes(Opcode.IF_EQZ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt new file mode 100644 index 0000000000..0f9157d6b1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt @@ -0,0 +1,70 @@ +package app.revanced.patches.twitter.interaction.downloads + +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +@Suppress("unused") +val unlockDownloadsPatch = bytecodePatch( + name = "Unlock downloads", + description = "Unlocks the ability to download any video. GIFs can be downloaded via the menu on long press.", +) { + compatibleWith("com.twitter.android") + + val constructMediaOptionsSheetMatch by constructMediaOptionsSheetFingerprint() + val showDownloadVideoUpsellBottomSheetMatch by showDownloadVideoUpsellBottomSheetFingerprint() + val buildMediaOptionsSheetMatch by buildMediaOptionsSheetFingerprint() + + fun Match.patch(getRegisterAndIndex: Match.() -> Pair) { + val (index, register) = getRegisterAndIndex() + mutableMethod.addInstruction(index, "const/4 v$register, 0x1") + } + + execute { + // Allow downloads for non-premium users. + showDownloadVideoUpsellBottomSheetMatch.patch { + val checkIndex = patternMatch!!.startIndex + val register = mutableMethod.getInstruction(checkIndex).registerA + + checkIndex to register + } + + // Force show the download menu item. + constructMediaOptionsSheetMatch.patch { + val showDownloadButtonIndex = mutableMethod.instructions.lastIndex - 1 + val register = mutableMethod.getInstruction(showDownloadButtonIndex).registerA + + showDownloadButtonIndex to register + } + + // Make GIFs downloadable. + val patternMatch = buildMediaOptionsSheetMatch.patternMatch!! + buildMediaOptionsSheetMatch.mutableMethod.apply { + val checkMediaTypeIndex = patternMatch.startIndex + val checkMediaTypeInstruction = getInstruction(checkMediaTypeIndex) + + // Treat GIFs as videos. + addInstructionsWithLabels( + checkMediaTypeIndex + 1, + """ + const/4 v${checkMediaTypeInstruction.registerB}, 0x2 # GIF + if-eq v${checkMediaTypeInstruction.registerA}, v${checkMediaTypeInstruction.registerB}, :video + """, + ExternalLabel("video", getInstruction(patternMatch.endIndex)), + ) + + // Remove media.isDownloadable check. + removeInstruction( + instructions.first { it.opcode == Opcode.IGET_BOOLEAN }.location.index + 1, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/Fingerprints.kt new file mode 100644 index 0000000000..625b6f0bb7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/Fingerprints.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.twitter.layout.viewcount + +import app.revanced.patcher.fingerprint + +internal val viewCountsEnabledFingerprint = fingerprint { + returns("Z") + strings("view_counts_public_visibility_enabled") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt new file mode 100644 index 0000000000..83b65581ef --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/layout/viewcount/HideViewCountPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.twitter.layout.viewcount + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val hideViewCountPatch = bytecodePatch( + name = "Hide view count", + description = "Hides the view count of Posts.", + use = false, +) { + compatibleWith("com.twitter.android") + + val viewCountsEnabledMatch by viewCountsEnabledFingerprint() + + execute { + viewCountsEnabledMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x0 + return v0 + """, + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt similarity index 81% rename from src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt index 51d62a9989..b7c3d3e7a8 100644 --- a/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch.kt @@ -1,22 +1,19 @@ package app.revanced.patches.twitter.misc.dynamiccolor -import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch import java.io.FileWriter import java.nio.file.Files -@Patch( +@Suppress("unused") +val dynamicColorPatch = resourcePatch( name = "Dynamic color", description = "Replaces the default X (Formerly Twitter) Blue with the user's Material You palette.", - compatiblePackages = [CompatiblePackage("com.twitter.android")], -) -@Suppress("unused") -object DynamicColorPatch : ResourcePatch() { - override fun execute(context: ResourceContext) { - val resDirectory = context.get("res") +) { + compatibleWith("com.twitter.android") + + execute { context -> + val resDirectory = context["res"] if (!resDirectory.isDirectory) throw PatchException("The res folder can not be found.") val valuesV31Directory = resDirectory.resolve("values-v31") @@ -35,8 +32,7 @@ object DynamicColorPatch : ResourcePatch() { } } - context.xmlEditor["res/values-v31/colors.xml"].use { editor -> - val document = editor.file + context.document["res/values-v31/colors.xml"].use { document -> mapOf( "ps__twitter_blue" to "@color/twitter_blue", @@ -57,9 +53,7 @@ object DynamicColorPatch : ResourcePatch() { } } - context.xmlEditor["res/values-night-v31/colors.xml"].use { editor -> - val document = editor.file - + context.document["res/values-night-v31/colors.xml"].use { document -> mapOf( "twitter_blue" to "@android:color/system_accent1_200", "twitter_blue_fill_pressed" to "@android:color/system_accent1_300", diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/extension/ExtensionPatch.kt new file mode 100644 index 0000000000..f1e6a879bf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/extension/ExtensionPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.twitter.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch() diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HideAdsHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HideAdsHookPatch.kt new file mode 100644 index 0000000000..4f9b38ff6d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HideAdsHookPatch.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.twitter.misc.hook + +@Suppress("unused") +val hideAdsHookPatch = hookPatch( + name = "Hide ads", + hookClassDescriptor = "Lapp/revanced/extension/twitter/patches/hook/patch/ads/HideAdsHook;", +) diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HideRecommendedUsersPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HideRecommendedUsersPatch.kt new file mode 100644 index 0000000000..2533796159 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HideRecommendedUsersPatch.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.twitter.misc.hook + +@Suppress("unused") +val hideRecommendedUsersPatch = hookPatch( + name = "Hide recommended users", + hookClassDescriptor = "Lapp/revanced/extension/twitter/patches/hook/patch/recommendation/RecommendedUsersHook;", +) diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HookPatch.kt new file mode 100644 index 0000000000..ca757dcdd6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/HookPatch.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.twitter.misc.hook + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.twitter.misc.hook.json.JsonHook +import app.revanced.patches.twitter.misc.hook.json.jsonHookPatch +import app.revanced.patches.twitter.misc.hook.json.jsonHooks + +fun hookPatch( + name: String, + hookClassDescriptor: String, +) = bytecodePatch(name) { + dependsOn(jsonHookPatch) + + compatibleWith("com.twitter.android") + + execute { + jsonHooks.addHook(JsonHook(it, hookClassDescriptor)) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/Fingerprints.kt new file mode 100644 index 0000000000..cf4578b0c9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/Fingerprints.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.twitter.misc.hook.json + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val jsonHookPatchFingerprint = fingerprint { + opcodes( + Opcode.INVOKE_INTERFACE, // Add dummy hook to hooks list. + // Add hooks to the hooks list. + Opcode.INVOKE_STATIC, // Call buildList. + ) + custom { method, _ -> method.name == "" } +} + +internal val jsonInputStreamFingerprint = fingerprint { + custom { method, _ -> + if (method.parameterTypes.size == 0) { + false + } else { + method.parameterTypes.first() == "Ljava/io/InputStream;" + } + } +} + +internal val loganSquareFingerprint = fingerprint { + custom { _, classDef -> classDef.endsWith("LoganSquare;") } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt new file mode 100644 index 0000000000..6cde25b50d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/hook/json/JsonHookPatch.kt @@ -0,0 +1,141 @@ +package app.revanced.patches.twitter.misc.hook.json + +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.reddit.misc.extension.sharedExtensionPatch +import java.io.Closeable +import java.io.InvalidClassException + +/** + * The [JsonHookPatchHook] of the [jsonHookPatch]. + * + * @see JsonHookPatchHook + */ +internal lateinit var jsonHooks: JsonHookPatchHook + private set + +private const val JSON_HOOK_CLASS_NAMESPACE = "app/revanced/extension/twitter/patches/hook/json" +private const val JSON_HOOK_PATCH_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/JsonHookPatch;" +private const val BASE_PATCH_CLASS_NAME = "BaseJsonHook" +private const val JSON_HOOK_CLASS_DESCRIPTOR = "L$JSON_HOOK_CLASS_NAMESPACE/$BASE_PATCH_CLASS_NAME;" + +val jsonHookPatch = bytecodePatch( + description = "Hooks the stream which reads JSON responses.", +) { + dependsOn(sharedExtensionPatch) + + val loganSquareMatch by loganSquareFingerprint() + + execute { context -> + jsonHookPatchFingerprint.apply { + // Make sure the extension is present. + val jsonHookPatch = context.classBy { classDef -> classDef.type == JSON_HOOK_PATCH_CLASS_DESCRIPTOR } + ?: throw PatchException("Could not find the extension.") + + if (!match(context, jsonHookPatch.immutableClass)) { + throw PatchException("Unexpected extension.") + } + }.let { jsonHooks = JsonHookPatchHook(it) } + + // Conveniently find the type to hook a method in, via a named field. + val jsonFactory = loganSquareMatch + .classDef + .fields + .firstOrNull { it.name == "JSON_FACTORY" } + ?.type + .let { type -> + context.classBy { it.type == type }?.mutableClass + } ?: throw PatchException("Could not find required class.") + + // Hook the methods first parameter. + jsonInputStreamFingerprint + .apply { match(context, jsonFactory) } + .match + ?.mutableMethod + ?.addInstructions( + 0, + """ + invoke-static { p1 }, $JSON_HOOK_PATCH_CLASS_DESCRIPTOR->parseJsonHook(Ljava/io/InputStream;)Ljava/io/InputStream; + move-result-object p1 + """, + ) ?: throw PatchException("Could not find method to hook.") + } + + finalize { + jsonHooks.close() + } +} + +/** + * Create a hook class. + * The class has to extend on **JsonHook**. + * The class has to be a Kotlin object class, or at least have an INSTANCE field of itself. + * + * @param context The [BytecodePatchContext] of the current patch. + * @param descriptor The class descriptor of the hook. + * @throws ClassNotFoundException If the class could not be found. + */ +class JsonHook(context: BytecodePatchContext, internal val descriptor: String) { + internal var added = false + + init { + context.classBy { it.type == descriptor }?.let { + it.mutableClass.also { classDef -> + if ( + classDef.superclass != JSON_HOOK_CLASS_DESCRIPTOR || + !classDef.fields.any { field -> field.name == "INSTANCE" } + ) { + throw InvalidClassException(classDef.type, "Not a hook class") + } + } + } ?: throw ClassNotFoundException("Failed to find hook class $descriptor") + } +} + +/** + * A hook for the [jsonHookPatch]. + * + * @param jsonHookPatchFingerprint The [jsonHookPatchFingerprint] to hook. + */ +class JsonHookPatchHook(jsonHookPatchFingerprint: Fingerprint) : Closeable { + private val jsonHookPatchMatch = jsonHookPatchFingerprint.match!! + private val jsonHookPatchIndex = jsonHookPatchMatch.patternMatch!!.endIndex + + /** + * Add a hook to the [jsonHookPatch]. + * Will not add the hook if it's already added. + * + * @param jsonHook The [JsonHook] to add. + */ + fun addHook(jsonHook: JsonHook) { + if (jsonHook.added) return + + jsonHookPatchMatch.mutableMethod.apply { + // Insert hooks right before calling buildList. + val insertIndex = jsonHookPatchIndex + + addInstructions( + insertIndex, + """ + sget-object v1, ${jsonHook.descriptor}->INSTANCE:${jsonHook.descriptor} + invoke-interface {v0, v1}, Ljava/util/List;->add(Ljava/lang/Object;)Z + """, + ) + } + + jsonHook.added = true + } + + override fun close() { + // Remove hooks.add(dummyHook). + jsonHookPatchMatch.mutableMethod.apply { + val addDummyHookIndex = jsonHookPatchIndex - 2 + + removeInstructions(addDummyHookIndex, 2) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt new file mode 100644 index 0000000000..cefe3226f0 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt @@ -0,0 +1,96 @@ +package app.revanced.patches.twitter.misc.links + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.patch.stringOption +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c + +internal var tweetShareLinkTemplateId = -1L + private set + +internal val changeLinkSharingDomainResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + execute { + tweetShareLinkTemplateId = resourceMappings["string", "tweet_share_link"] + } +} + +// This method is used to build the link that is shared when the "Share via..." button is pressed. +private const val FORMAT_METHOD_RESOURCE_REFERENCE = + "Lapp/revanced/extension/twitter/patches/links/ChangeLinkSharingDomainPatch;->" + + "formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;" + +// This method is used to build the link that is shared when the "Copy link" button is pressed. +private const val FORMAT_METHOD_REFERENCE = + "Lapp/revanced/extension/twitter/patches/links/ChangeLinkSharingDomainPatch;->" + + "formatLink(JLjava/lang/String;)Ljava/lang/String;" + +@Suppress("unused") +val changeLinkSharingDomainPatch = bytecodePatch( + name = "Change link sharing domain", + description = "Replaces the domain name of Twitter links when sharing them.", +) { + dependsOn(changeLinkSharingDomainResourcePatch) + + compatibleWith("com.twitter.android") + + val domainName by stringOption( + key = "domainName", + default = "fxtwitter.com", + title = "Domain name", + description = "The domain name to use when sharing links.", + required = true, + ) + + val linkSharingDomainMatch by linkSharingDomainFingerprint() + val linkBuilderMatch by linkBuilderFingerprint() + val linkResourceGetterMatch by linkResourceGetterFingerprint() + + execute { + val replacementIndex = + linkSharingDomainMatch.stringMatches!!.first().index + val domainRegister = + linkSharingDomainMatch.mutableMethod.getInstruction(replacementIndex).registerA + + linkSharingDomainMatch.mutableMethod.replaceInstruction( + replacementIndex, + "const-string v$domainRegister, \"https://$domainName\"", + ) + + // Replace the domain name when copying a link with "Copy link" button. + linkBuilderMatch.mutableMethod.apply { + addInstructions( + 0, + """ + invoke-static { p0, p1, p2 }, $FORMAT_METHOD_REFERENCE + move-result-object p0 + return-object p0 + """, + ) + } + + // Used in the Share via... dialog. + linkResourceGetterMatch.mutableMethod.apply { + val templateIdConstIndex = indexOfFirstWideLiteralInstructionValueOrThrow(tweetShareLinkTemplateId) + + // Format the link with the new domain name register (1 instruction below the const). + val formatLinkCallIndex = templateIdConstIndex + 1 + val formatLinkCall = getInstruction(formatLinkCallIndex) + + // Replace the original method call with the new method call. + replaceInstruction( + formatLinkCallIndex, + "invoke-static { v${formatLinkCall.registerE} }, $FORMAT_METHOD_RESOURCE_REFERENCE", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt new file mode 100644 index 0000000000..0d5d0e6f80 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.twitter.misc.links + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags + +internal val openLinkFingerprint = fingerprint { + returns("V") + parameters("Landroid/content/Context;", "Landroid/content/Intent;", "Landroid/os/Bundle;") +} + +internal val sanitizeSharingLinksFingerprint = fingerprint { + returns("Ljava/lang/String;") + strings("", "shareParam", "sessionToken") +} + +// Returns a shareable link string based on a tweet ID and a username. +internal val linkBuilderFingerprint = fingerprint { + strings("/%1\$s/status/%2\$d") +} + +// Gets Resource string for share link view available by pressing "Share via" button. +internal val linkResourceGetterFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Landroid/content/res/Resources;") + literal { tweetShareLinkTemplateId } +} + +internal val linkSharingDomainFingerprint = fingerprint { + strings("https://fxtwitter.com") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch.kt new file mode 100644 index 0000000000..c9cea00c77 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.twitter.misc.links + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val openLinksWithAppChooserPatch = bytecodePatch( + name = "Open links with app chooser", + description = "Instead of opening links directly, open them with an app chooser. " + + "As a result you can select a browser to open the link with.", + use = false, +) { + compatibleWith("com.twitter.android"("10.48.0-release.0")) + + val openLinkMatch by openLinkFingerprint() + + execute { + val methodReference = + "Lapp/revanced/extension/twitter/patches/links/OpenLinksWithAppChooserPatch;->" + + "openWithChooser(Landroid/content/Context;Landroid/content/Intent;)V" + + openLinkMatch.mutableMethod.addInstructions( + 0, + """ + invoke-static { p0, p1 }, $methodReference + return-void + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch.kt b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch.kt new file mode 100644 index 0000000000..62e7a4f005 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/SanitizeSharingLinksPatch.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.twitter.misc.links + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val sanitizeSharingLinksPatch = bytecodePatch( + name = "Sanitize sharing links", + description = "Removes the tracking query parameters from links before they are shared.", +) { + compatibleWith("com.twitter.android") + + val sanitizeSharingLinksMatch by sanitizeSharingLinksFingerprint() + + execute { + sanitizeSharingLinksMatch.mutableMethod.addInstructions( + 0, + """ + # Method takes in a link (string, param 0) and then appends the tracking query params, + # so all we need to do is return back the passed-in string + return-object p0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/Fingerprints.kt new file mode 100644 index 0000000000..0809324c7f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/Fingerprints.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.vsco.misc.pro + +import app.revanced.patcher.fingerprint + +internal val revCatSubscriptionFingerprint = fingerprint { + returns("V") + strings("use_debug_subscription_settings") + custom { _, classDef -> + classDef.endsWith("/RevCatSubscriptionSettingsRepository;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/UnlockProPatch.kt new file mode 100644 index 0000000000..dae6091646 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/vsco/misc/pro/UnlockProPatch.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.vsco.misc.pro + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockProPatch = bytecodePatch( + name = "Unlock pro", + description = "Unlocks pro features.", +) { + compatibleWith("com.vsco.cam"("345")) + + val revCatSubscriptionMatch by revCatSubscriptionFingerprint() + + execute { + // Set isSubscribed to true. + revCatSubscriptionMatch.mutableMethod.addInstruction(0, "const p1, 0x1") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/Fingerprints.kt new file mode 100644 index 0000000000..41627aaaa4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/Fingerprints.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.warnwetter.misc.firebasegetcert + +import app.revanced.patcher.fingerprint + +internal val getMessagingCertFingerprint = fingerprint { + returns("Ljava/lang/String;") + strings( + "ContentValues", + "Could not get fingerprint hash for package: ", + "No such package: ", + ) +} + +internal val getReqistrationCertFingerprint = fingerprint { + returns("Ljava/lang/String;") + strings( + "FirebaseRemoteConfig", + "Could not get fingerprint hash for package: ", + "No such package: ", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt new file mode 100644 index 0000000000..818c04cbdf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/firebasegetcert/FirebaseGetCertPatch.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.warnwetter.misc.firebasegetcert + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val firebaseGetCertPatch = bytecodePatch( + description = "Spoofs the X-Android-Cert header.", +) { + compatibleWith("de.dwd.warnapp") + + val getRegistrationCertMatch by getReqistrationCertFingerprint() + val getMessagingCertMatch by getMessagingCertFingerprint() + + execute { + listOf(getRegistrationCertMatch, getMessagingCertMatch).forEach { match -> + match.mutableMethod.addInstructions( + 0, + """ + const-string v0, "0799DDF0414D3B3475E88743C91C0676793ED450" + return-object v0 + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/Fingerprints.kt new file mode 100644 index 0000000000..d33880de70 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.warnwetter.misc.promocode + +import app.revanced.patcher.fingerprint + +internal val promoCodeUnlockFingerprint = fingerprint { + custom { method, classDef -> + classDef.endsWith("PromoTokenVerification;") && method.name == "isValid" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt new file mode 100644 index 0000000000..67b930ae82 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.warnwetter.misc.promocode + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.warnwetter.misc.firebasegetcert.firebaseGetCertPatch + +@Suppress("unused") +val promoCodeUnlockPatch = bytecodePatch( + name = "Promo code unlock", + description = "Disables the validation of promo code. Any code will work to unlock all features.", +) { + dependsOn(firebaseGetCertPatch) + + compatibleWith("de.dwd.warnapp"("4.2.2")) + + val promoCodeUnlockMatch by promoCodeUnlockFingerprint() + + execute { + promoCodeUnlockMatch.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/Fingerprints.kt new file mode 100644 index 0000000000..e326c26825 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/Fingerprints.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.willhaben.ads + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val adResolverFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters("L", "L") + strings( + "Google Ad is invalid ", + "Google Native Ad is invalid ", + "Criteo Ad is invalid ", + "Amazon Ad is invalid ", + ) +} + +internal val whAdViewInjectorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "L", "L", "Z") + strings("successfulAdView") + custom { _, classDef -> + classDef.type == "Lat/willhaben/advertising/WHAdView;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt new file mode 100644 index 0000000000..6c0d99cca9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/willhaben/ads/HideAdsPatch.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.willhaben.ads + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.returnEarly + +@Suppress("unused") +internal val hideAdsPatch = bytecodePatch( + name = "Hide ads", + description = "Hides all in-app ads.", +) { + compatibleWith("at.willhaben") + + adResolverFingerprint() + whAdViewInjectorFingerprint() + + execute { + adResolverFingerprint.returnEarly() + whAdViewInjectorFingerprint.returnEarly() + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/Fingerprints.kt new file mode 100644 index 0000000000..f199f127c7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/Fingerprints.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.windyapp.misc.unlockpro + +import app.revanced.patcher.fingerprint + +internal val checkProFingerprint = fingerprint { + returns("I") + custom { method, classDef -> + classDef.endsWith("RawUserData;") && method.name == "isPro" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch.kt b/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch.kt new file mode 100644 index 0000000000..3f4cac7577 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/windyapp/misc/unlockpro/UnlockProPatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.windyapp.misc.unlockpro + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +@Suppress("unused") +val unlockProPatch = bytecodePatch( + name = "Unlock pro", + description = "Unlocks all pro features.", +) { + compatibleWith("co.windyapp.android") + + val checkProMatch by checkProFingerprint() + + execute { + checkProMatch.mutableMethod.addInstructions( + 0, + """ + const/16 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt new file mode 100644 index 0000000000..7abaeda887 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt @@ -0,0 +1,118 @@ +package app.revanced.patches.youtube.ad.general + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.fix.verticalscroll.verticalScrollPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.ad.getpremium.hideGetPremiumPatch +import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.findMutableMethodOf +import app.revanced.util.injectHideViewCall +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c + +internal var adAttributionId = -1L + private set + +private val hideAdsResourcePatch = resourcePatch { + dependsOn( + lithoFilterPatch, + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + ) + + execute { + addResources("youtube", "ad.general.hideAdsResourcePatch") + + PreferenceScreen.ADS.addPreferences( + SwitchPreference("revanced_hide_general_ads"), + SwitchPreference("revanced_hide_fullscreen_ads"), + SwitchPreference("revanced_hide_buttoned_ads"), + SwitchPreference("revanced_hide_paid_promotion_label"), + SwitchPreference("revanced_hide_player_store_shelf"), + SwitchPreference("revanced_hide_self_sponsor_ads"), + SwitchPreference("revanced_hide_products_banner"), + SwitchPreference("revanced_hide_shopping_links"), + SwitchPreference("revanced_hide_visit_store_button"), + SwitchPreference("revanced_hide_web_search_results"), + SwitchPreference("revanced_hide_merchandise_banners"), + ) + + addLithoFilter("Lapp/revanced/extension/youtube/patches/components/AdsFilter;") + + adAttributionId = resourceMappings["id", "ad_attribution"] + } +} + +@Suppress("unused") +val hideAdsPatch = bytecodePatch( + name = "Hide ads", + description = "Adds options to remove general ads.", +) { + dependsOn( + hideGetPremiumPatch, + hideAdsResourcePatch, + verticalScrollPatch, + fixBackToExitGesturePatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + execute { context -> + context.classes.forEach { classDef -> + classDef.methods.forEach { method -> + with(method.implementation) { + this?.instructions?.forEachIndexed { index, instruction -> + if (instruction.opcode != Opcode.CONST) { + return@forEachIndexed + } + // Instruction to store the id adAttribution into a register + if ((instruction as Instruction31i).wideLiteral != adAttributionId) { + return@forEachIndexed + } + + val insertIndex = index + 1 + + // Call to get the view with the id adAttribution + with(instructions.elementAt(insertIndex)) { + if (opcode != Opcode.INVOKE_VIRTUAL) { + return@forEachIndexed + } + + // Hide the view + val viewRegister = (this as Instruction35c).registerC + context.proxy(classDef) + .mutableClass + .findMutableMethodOf(method) + .injectHideViewCall( + insertIndex, + viewRegister, + "Lapp/revanced/extension/youtube/patches/components/AdsFilter;", + "hideAdAttributionView", + ) + } + } + } + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/Fingerprints.kt new file mode 100644 index 0000000000..7629d1760d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/Fingerprints.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.ad.getpremium + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val getPremiumViewFingerprint = fingerprint { + accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) + returns("V") + parameters("I", "I") + opcodes( + Opcode.ADD_INT_2ADDR, + Opcode.ADD_INT_2ADDR, + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_VOID, + ) + custom { method, _ -> + method.name == "onMeasure" && + method.definingClass == "Lcom/google/android/apps/youtube/app/red/presenter/CompactYpcOfferModuleView;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt new file mode 100644 index 0000000000..ced8123934 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatch.kt @@ -0,0 +1,70 @@ +package app.revanced.patches.youtube.ad.getpremium + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/HideGetPremiumPatch;" + +@Suppress("unused") +val hideGetPremiumPatch = bytecodePatch( + description = "Hides YouTube Premium signup promotions under the video player.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val getPremiumViewMatch by getPremiumViewFingerprint() + + execute { + addResources("youtube", "ad.getpremium.hideGetPremiumPatch") + + PreferenceScreen.ADS.addPreferences( + SwitchPreference("revanced_hide_get_premium"), + ) + + getPremiumViewMatch.mutableMethod.apply { + val startIndex = getPremiumViewMatch.patternMatch!!.startIndex + val measuredWidthRegister = getInstruction(startIndex).registerA + val measuredHeightInstruction = getInstruction(startIndex + 1) + + val measuredHeightRegister = measuredHeightInstruction.registerA + val tempRegister = measuredHeightInstruction.registerB + + addInstructionsWithLabels( + startIndex + 2, + """ + # Override the internal measurement of the layout with zero values. + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideGetPremiumView()Z + move-result v$tempRegister + if-eqz v$tempRegister, :allow + const/4 v$measuredWidthRegister, 0x0 + const/4 v$measuredHeightRegister, 0x0 + :allow + nop + # Layout width/height is then passed to a protected class method. + """, + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/video/fingerprints/LoadVideoAdsFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/Fingerprints.kt similarity index 56% rename from src/main/kotlin/app/revanced/patches/youtube/ad/video/fingerprints/LoadVideoAdsFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/Fingerprints.kt index 240886f012..91cc0e8dfa 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ad/video/fingerprints/LoadVideoAdsFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/Fingerprints.kt @@ -1,12 +1,11 @@ -package app.revanced.patches.youtube.ad.video.fingerprints +package app.revanced.patches.youtube.ad.video +import app.revanced.patcher.fingerprint -import app.revanced.patcher.fingerprint.MethodFingerprint - -internal object LoadVideoAdsFingerprint : MethodFingerprint( - strings = listOf( +internal val loadVideoAdsFingerprint = fingerprint { + strings( "TriggerBundle doesn't have the required metadata specified by the trigger ", "Tried to enter slot with no assigned slotAdapter", "Trying to enter a slot when a slot of same type and physical position is already active. Its status: ", ) -) \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt new file mode 100644 index 0000000000..5c098d5512 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/video/VideoAdsPatch.kt @@ -0,0 +1,55 @@ +package app.revanced.patches.youtube.ad.video + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +@Suppress("unused") +val videoAdsPatch = bytecodePatch( + name = "Video ads", + description = "Adds an option to remove ads in the video player.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val loadVideoAdsMatch by loadVideoAdsFingerprint() + + execute { + addResources("youtube", "ad.video.videoAdsPatch") + + PreferenceScreen.ADS.addPreferences( + SwitchPreference("revanced_hide_video_ads"), + ) + + loadVideoAdsMatch.mutableMethod.addInstructionsWithLabels( + 0, + """ + invoke-static { }, Lapp/revanced/extension/youtube/patches/VideoAdsPatch;->shouldShowAds()Z + move-result v0 + if-nez v0, :show_video_ads + return-void + """, + ExternalLabel("show_video_ads", loadVideoAdsMatch.mutableMethod.getInstruction(0)), + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatch.kt new file mode 100644 index 0000000000..0c9fe544cf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatch.kt @@ -0,0 +1,76 @@ +package app.revanced.patches.youtube.interaction.copyvideourl + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.playercontrols.* +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.video.information.videoInformationPatch +import app.revanced.util.ResourceGroup +import app.revanced.util.copyResources + +private val copyVideoUrlResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + playerControlsResourcePatch, + addResourcesPatch, + ) + + execute { context -> + addResources("youtube", "interaction.copyvideourl.copyVideoUrlResourcePatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_copy_video_url"), + SwitchPreference("revanced_copy_video_url_timestamp"), + ) + + context.copyResources( + "copyvideourl", + ResourceGroup( + resourceDirectoryName = "drawable", + "revanced_yt_copy.xml", + "revanced_yt_copy_timestamp.xml", + ), + ) + + addBottomControl("copyvideourl") + } +} + +@Suppress("unused") +val copyVideoUrlPatch = bytecodePatch( + name = "Copy video URL", + description = "Adds options to display buttons in the video player to copy video URLs.", +) { + dependsOn( + copyVideoUrlResourcePatch, + playerControlsPatch, + videoInformationPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + execute { + val extensionPlayerPackage = "Lapp/revanced/extension/youtube/videoplayer" + val buttonsDescriptors = listOf( + "$extensionPlayerPackage/CopyVideoUrlButton;", + "$extensionPlayerPackage/CopyVideoUrlTimestampButton;", + ) + + buttonsDescriptors.forEach { descriptor -> + initializeBottomControl(descriptor) + injectVisibilityCheckCall(descriptor) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/fingerprints/CreateDialogFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/Fingerprints.kt similarity index 51% rename from src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/fingerprints/CreateDialogFingerprint.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/Fingerprints.kt index 232e65e6a9..b252875901 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/fingerprints/CreateDialogFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/Fingerprints.kt @@ -1,14 +1,14 @@ -package app.revanced.patches.youtube.interaction.dialog.fingerprints +package app.revanced.patches.youtube.interaction.dialog -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint -internal object CreateDialogFingerprint : MethodFingerprint( - "V", - AccessFlags.PROTECTED.value, - listOf("L", "L", "Ljava/lang/String;"), - listOf( +internal val createDialogFingerprint = fingerprint { + accessFlags(AccessFlags.PROTECTED) + returns("V") + parameters("L", "L", "Ljava/lang/String;") + opcodes( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT_OBJECT, Opcode.INVOKE_VIRTUAL, @@ -17,6 +17,6 @@ internal object CreateDialogFingerprint : MethodFingerprint( Opcode.MOVE_RESULT_OBJECT, Opcode.IPUT_OBJECT, Opcode.IGET_OBJECT, - Opcode.INVOKE_VIRTUAL // dialog.show() + Opcode.INVOKE_VIRTUAL, // dialog.show() ) -) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt new file mode 100644 index 0000000000..caccae3822 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/dialog/RemoveViewerDiscretionDialogPatch.kt @@ -0,0 +1,59 @@ +package app.revanced.patches.youtube.interaction.dialog + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction + +@Suppress("unused") +val removeViewerDiscretionDialogPatch = bytecodePatch( + name = "Remove viewer discretion dialog", + description = "Adds an option to remove the dialog that appears when opening a video that has been age-restricted " + + "by accepting it automatically. This does not bypass the age restriction.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val createDialogMatch by createDialogFingerprint() + + val extensionMethodDescriptor = + "Lapp/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch;->" + + "confirmDialog(Landroid/app/AlertDialog;)V" + + execute { + addResources("youtube", "interaction.dialog.removeViewerDiscretionDialogPatch") + + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + SwitchPreference("revanced_remove_viewer_discretion_dialog"), + ) + + createDialogMatch.mutableMethod.apply { + val showDialogIndex = implementation!!.instructions.lastIndex - 2 + val dialogRegister = getInstruction(showDialogIndex).registerC + + replaceInstructions( + showDialogIndex, + "invoke-static { v$dialogRegister }, $extensionMethodDescriptor", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt new file mode 100644 index 0000000000..34b64a8ad2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt @@ -0,0 +1,108 @@ +package app.revanced.patches.youtube.interaction.downloads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.youtube.misc.playercontrols.* +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.mainActivityFingerprint +import app.revanced.patches.youtube.video.information.videoInformationPatch +import app.revanced.util.ResourceGroup +import app.revanced.util.copyResources + +private val downloadsResourcePatch = resourcePatch { + dependsOn( + playerControlsResourcePatch, + settingsPatch, + addResourcesPatch, + ) + + execute { context -> + addResources("youtube", "interaction.downloads.downloadsResourcePatch") + + PreferenceScreen.PLAYER.addPreferences( + PreferenceScreenPreference( + key = "revanced_external_downloader_screen", + sorting = Sorting.UNSORTED, + preferences = setOf( + SwitchPreference("revanced_external_downloader"), + SwitchPreference("revanced_external_downloader_action_button"), + TextPreference("revanced_external_downloader_name", inputType = InputType.TEXT), + ), + ), + ) + + context.copyResources( + "downloads", + ResourceGroup("drawable", "revanced_yt_download_button.xml"), + ) + + addBottomControl("downloads") + } +} + +internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DownloadsPatch;" + +internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;" + +@Suppress("unused") +val downloadsPatch = bytecodePatch( + name = "Downloads", + description = "Adds support to download videos with an external downloader app " + + "using the in-app download button or a video player action button.", +) { + dependsOn( + downloadsResourcePatch, + playerControlsPatch, + videoInformationPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val offlineVideoEndpointMatch by offlineVideoEndpointFingerprint() + val mainActivityMatch by mainActivityFingerprint() + + execute { + initializeBottomControl(BUTTON_DESCRIPTOR) + injectVisibilityCheckCall(BUTTON_DESCRIPTOR) + + // Main activity is used to launch downloader intent. + mainActivityMatch.mutableMethod.apply { + addInstruction( + implementation!!.instructions.lastIndex, + "invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->activityCreated(Landroid/app/Activity;)V", + ) + } + + offlineVideoEndpointMatch.mutableMethod.apply { + addInstructionsWithLabels( + 0, + """ + invoke-static/range {p3 .. p3}, $EXTENSION_CLASS_DESCRIPTOR->inAppDownloadButtonOnClick(Ljava/lang/String;)Z + move-result v0 + if-eqz v0, :show_native_downloader + return-void + :show_native_downloader + nop + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/Fingerprints.kt new file mode 100644 index 0000000000..f10fc8e834 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/Fingerprints.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.interaction.downloads + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val offlineVideoEndpointFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters( + "Ljava/util/Map;", + "L", + "Ljava/lang/String", // VideoId + "L", + ) + strings("Object is not an offlineable video: ") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt new file mode 100644 index 0000000000..df2f161e41 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatch.kt @@ -0,0 +1,80 @@ +package app.revanced.patches.youtube.interaction.seekbar + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.applyMatch + +@Suppress("unused") +val disablePreciseSeekingGesturePatch = bytecodePatch( + name = "Disable precise seeking gesture", + description = "Adds an option to disable precise seeking when swiping up on the seekbar.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val swipingUpGestureParentMatch by swipingUpGestureParentFingerprint() + + execute { context -> + addResources("youtube", "interaction.seekbar.disablePreciseSeekingGesturePatch") + + PreferenceScreen.SEEKBAR.addPreferences( + SwitchPreference("revanced_disable_precise_seeking_gesture"), + ) + val extensionMethodDescriptor = + "Lapp/revanced/extension/youtube/patches/DisablePreciseSeekingGesturePatch;" + + allowSwipingUpGestureFingerprint.applyMatch( + context, + swipingUpGestureParentMatch, + ).mutableMethod.apply { + addInstructionsWithLabels( + 0, + """ + invoke-static { }, $extensionMethodDescriptor->isGestureDisabled()Z + move-result v0 + if-eqz v0, :disabled + return-void + """, + ExternalLabel("disabled", getInstruction(0)), + ) + } + + showSwipingUpGuideFingerprint.applyMatch( + context, + swipingUpGestureParentMatch, + ).mutableMethod.apply { + addInstructionsWithLabels( + 0, + """ + invoke-static { }, $extensionMethodDescriptor->isGestureDisabled()Z + move-result v0 + if-eqz v0, :disabled + const/4 v0, 0x0 + return v0 + """, + ExternalLabel("disabled", getInstruction(0)), + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt new file mode 100644 index 0000000000..f07aeeca04 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatch.kt @@ -0,0 +1,86 @@ +package app.revanced.patches.youtube.interaction.seekbar + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Suppress("unused") +val enableSeekbarTappingPatch = bytecodePatch( + name = "Seekbar tapping", + description = "Adds an option to enable tap-to-seek on the seekbar of the video player.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + // 18.38.44 patches but crashes on startup. + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val onTouchEventHandlerMatch by onTouchEventHandlerFingerprint() + val seekbarTappingMatch by seekbarTappingFingerprint() + + execute { + addResources("youtube", "interaction.seekbar.enableSeekbarTappingPatch") + + PreferenceScreen.SEEKBAR.addPreferences( + SwitchPreference("revanced_seekbar_tapping"), + ) + + // Find the required methods to tap the seekbar. + val patternMatch = onTouchEventHandlerMatch.patternMatch!! + + fun getReference(index: Int) = onTouchEventHandlerMatch.mutableMethod.getInstruction(index) + .reference as MethodReference + + val seekbarTappingMethods = buildMap { + put("N", getReference(patternMatch.startIndex)) + put("O", getReference(patternMatch.endIndex)) + } + + val insertIndex = seekbarTappingMatch.patternMatch!!.endIndex - 1 + + seekbarTappingMatch.mutableMethod.apply { + val thisInstanceRegister = getInstruction(insertIndex - 1).registerC + + val freeRegister = 0 + val xAxisRegister = 2 + + val oMethod = seekbarTappingMethods["O"]!! + val nMethod = seekbarTappingMethods["N"]!! + + fun MethodReference.toInvokeInstructionString() = + "invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $this" + + addInstructionsWithLabels( + insertIndex, + """ + invoke-static { }, Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;->seekbarTappingEnabled()Z + move-result v$freeRegister + if-eqz v$freeRegister, :disabled + ${oMethod.toInvokeInstructionString()} + ${nMethod.toInvokeInstructionString()} + """, + ExternalLabel("disabled", getInstruction(insertIndex)), + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt new file mode 100644 index 0000000000..2e5eebba96 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatch.kt @@ -0,0 +1,127 @@ +package app.revanced.patches.youtube.interaction.seekbar + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.sun.org.apache.bcel.internal.generic.InstructionConst.getInstruction + +internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SlideToSeekPatch;" + +@Suppress("unused") +val enableSlideToSeekPatch = bytecodePatch( + name = "Enable slide to seek", + description = "Adds an option to enable slide to seek " + + "instead of playing at 2x speed when pressing and holding in the video player. " + + "Including this patch may cause issues with tapping or double tapping the video player overlay.", + use = false, +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + versionCheckPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val slideToSeekMatch by slideToSeekFingerprint() + val disableFastForwardLegacyMatch by disableFastForwardLegacyFingerprint() + val disableFastForwardGestureMatch by disableFastForwardGestureFingerprint() + val disableFastForwardNoticeMatch by disableFastForwardNoticeFingerprint() + + execute { + addResources("youtube", "interaction.seekbar.enableSlideToSeekPatch") + + PreferenceScreen.SEEKBAR.addPreferences( + SwitchPreference("revanced_slide_to_seek"), + ) + + var modifiedMethods = false + + // Restore the behaviour to slide to seek. + val checkIndex = + slideToSeekMatch.patternMatch!!.startIndex + val checkReference = + slideToSeekMatch.mutableMethod.getInstruction(checkIndex).getReference()!! + + // A/B check method was only called on this class. + slideToSeekMatch.mutableClass.methods.forEach { method -> + method.implementation!!.instructions.forEachIndexed { index, instruction -> + if (instruction.opcode == Opcode.INVOKE_VIRTUAL && + instruction.getReference() == checkReference + ) { + method.apply { + val targetRegister = getInstruction(index + 1).registerA + + addInstructions( + index + 2, + """ + invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR + move-result v$targetRegister + """, + ) + } + + modifiedMethods = true + } + } + } + + if (!modifiedMethods) throw PatchException("Could not find methods to modify") + + // Disable the double speed seek gesture. + if (!is_19_17_or_greater) { + disableFastForwardLegacyMatch.mutableMethod.apply { + val insertIndex = disableFastForwardLegacyMatch.patternMatch!!.endIndex + 1 + val targetRegister = getInstruction(insertIndex).registerA + + addInstructions( + insertIndex, + """ + invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR + move-result v$targetRegister + """, + ) + } + } else { + arrayOf( + disableFastForwardGestureMatch, + disableFastForwardNoticeMatch, + ).forEach { + it.mutableMethod.apply { + val targetIndex = it.patternMatch!!.endIndex + val targetRegister = getInstruction(targetIndex).registerA + + addInstructions( + targetIndex + 1, + """ + invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR + move-result v$targetRegister + """, + ) + } + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt new file mode 100644 index 0000000000..3087769f8b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/Fingerprints.kt @@ -0,0 +1,119 @@ +package app.revanced.patches.youtube.interaction.seekbar + +import app.revanced.patcher.fingerprint +import app.revanced.util.containsWideLiteralInstructionValue +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val swipingUpGestureParentFingerprint = fingerprint { + returns("Z") + parameters() + literal { 45379021 } +} + +/** + * Resolves using the class found in [swipingUpGestureParentFingerprint]. + */ +internal val showSwipingUpGuideFingerprint = fingerprint { + accessFlags(AccessFlags.FINAL) + returns("Z") + parameters() + literal { 1 } +} + +/** + * Resolves using the class found in [swipingUpGestureParentFingerprint]. + */ +internal val allowSwipingUpGestureFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L") +} + +internal val disableFastForwardLegacyFingerprint = fingerprint { + returns("Z") + parameters() + opcodes(Opcode.MOVE_RESULT) + literal { 45411330 } +} + +internal val disableFastForwardGestureFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + opcodes( + Opcode.IF_EQZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + ) + custom { methodDef, classDef -> + methodDef.implementation!!.instructions.count() > 30 && + classDef.type.endsWith("/NextGenWatchLayout;") + } +} + +internal val disableFastForwardNoticeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + opcodes( + Opcode.CHECK_CAST, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + ) + strings("Failed to easy seek haptics vibrate") +} + +internal val onTouchEventHandlerFingerprint = fingerprint(fuzzyPatternScanThreshold = 3) { + accessFlags(AccessFlags.PUBLIC, AccessFlags.PUBLIC) + returns("Z") + parameters("L") + opcodes( + Opcode.INVOKE_VIRTUAL, // nMethodReference + Opcode.RETURN, + Opcode.IGET_OBJECT, + Opcode.IGET_BOOLEAN, + Opcode.IF_EQZ, + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN, + Opcode.INT_TO_FLOAT, + Opcode.INT_TO_FLOAT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, // oMethodReference + ) + custom { method, _ -> method.name == "onTouchEvent" } +} + +internal val seekbarTappingFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters("L") + opcodes( + Opcode.IPUT_OBJECT, + Opcode.INVOKE_VIRTUAL, + // Insert seekbar tapping instructions here. + Opcode.RETURN, + Opcode.INVOKE_VIRTUAL, + ) + custom { method, _ -> + method.containsWideLiteralInstructionValue(Integer.MAX_VALUE.toLong()) + } +} + +internal val slideToSeekFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("Z") + parameters("Landroid/view/View;", "F") + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.GOTO_16, + ) + literal { 67108864 } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/Fingerprints.kt new file mode 100644 index 0000000000..0a31f1c302 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/Fingerprints.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.interaction.swipecontrols + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val swipeControlsHostActivityFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters() + custom { method, _ -> + method.definingClass == "Lapp/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity;" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatch.kt new file mode 100644 index 0000000000..3cee8b4539 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatch.kt @@ -0,0 +1,107 @@ +package app.revanced.patches.youtube.interaction.swipecontrols + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.mainActivityFingerprint +import app.revanced.util.ResourceGroup +import app.revanced.util.copyResources +import app.revanced.util.transformMethods +import app.revanced.util.traverseClassHierarchy +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod + +private val swipeControlsResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + addResourcesPatch, + ) + + execute { context -> + addResources("youtube", "interaction.swipecontrols.swipeControlsResourcePatch") + + PreferenceScreen.SWIPE_CONTROLS.addPreferences( + SwitchPreference("revanced_swipe_brightness"), + SwitchPreference("revanced_swipe_volume"), + SwitchPreference("revanced_swipe_press_to_engage"), + SwitchPreference("revanced_swipe_haptic_feedback"), + SwitchPreference("revanced_swipe_save_and_restore_brightness"), + SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"), + TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER), + TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER), + TextPreference("revanced_swipe_overlay_background_alpha", inputType = InputType.NUMBER), + TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER), + ) + + context.copyResources( + "swipecontrols", + ResourceGroup( + "drawable", + "revanced_ic_sc_brightness_auto.xml", + "revanced_ic_sc_brightness_manual.xml", + "revanced_ic_sc_volume_mute.xml", + "revanced_ic_sc_volume_normal.xml", + ), + ) + } +} + +@Suppress("unused") +val swipeControlsPatch = bytecodePatch( + name = "Swipe controls", + description = "Adds options to enable and configure volume and brightness swipe controls.", +) { + dependsOn( + sharedExtensionPatch, + playerTypeHookPatch, + swipeControlsResourcePatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val mainActivityMatch by mainActivityFingerprint() + val swipeControlsHostActivityMatch by swipeControlsHostActivityFingerprint() + + execute { context -> + val wrapperClass = swipeControlsHostActivityMatch.mutableClass + val targetClass = mainActivityMatch.mutableClass + + // Inject the wrapper class from the extension into the class hierarchy of MainActivity. + wrapperClass.setSuperClass(targetClass.superclass) + targetClass.setSuperClass(wrapperClass.type) + + // Ensure all classes and methods in the hierarchy are non-final, so we can override them in the extension. + context.traverseClassHierarchy(targetClass) { + accessFlags = accessFlags and AccessFlags.FINAL.value.inv() + transformMethods { + ImmutableMethod( + definingClass, + name, + parameters, + returnType, + accessFlags and AccessFlags.FINAL.value.inv(), + annotations, + hiddenApiRestrictions, + implementation, + ).toMutable() + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt new file mode 100644 index 0000000000..8a46798d47 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/AutoCaptionsPatch.kt @@ -0,0 +1,73 @@ +package app.revanced.patches.youtube.layout.autocaptions + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.subtitleButtonControllerFingerprint + +@Suppress("unused") +val autoCaptionsPatch = bytecodePatch( + name = "Disable auto captions", + description = "Adds an option to disable captions from being automatically enabled.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val startVideoInformerMatch by startVideoInformerFingerprint() + val subtitleButtonControllerMatch by subtitleButtonControllerFingerprint() + val subtitleTrackMatch by subtitleTrackFingerprint() + + execute { + addResources("youtube", "layout.autocaptions.autoCaptionsPatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_auto_captions"), + ) + + mapOf( + startVideoInformerMatch to 0, + subtitleButtonControllerMatch to 1, + ).forEach { (match, enabled) -> + match.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x$enabled + sput-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z + """, + ) + } + + subtitleTrackMatch.mutableMethod.addInstructions( + 0, + """ + invoke-static {}, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->autoCaptionsEnabled()Z + move-result v0 + if-eqz v0, :auto_captions_enabled + sget-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z + if-nez v0, :auto_captions_enabled + const/4 v0, 0x1 + return v0 + :auto_captions_enabled + nop + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/Fingerprints.kt new file mode 100644 index 0000000000..3e1c156291 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/autocaptions/Fingerprints.kt @@ -0,0 +1,33 @@ +package app.revanced.patches.youtube.layout.autocaptions + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val startVideoInformerFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + opcodes( + Opcode.INVOKE_INTERFACE, + Opcode.RETURN_VOID, + ) + strings("pc") +} + +internal val subtitleTrackFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + opcodes( + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.RETURN, + ) + strings("DISABLE_CAPTIONS_OPTION") + custom { _, classDef -> + classDef.endsWith("SubtitleTrack;") + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt similarity index 68% rename from src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt index ec720ec974..d1670aeda3 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt @@ -1,45 +1,40 @@ package app.revanced.patches.youtube.layout.branding -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.patch.stringOption import app.revanced.util.ResourceGroup import app.revanced.util.Utils.trimIndentMultiline import app.revanced.util.copyResources import java.io.File import java.nio.file.Files -@Patch( +private const val REVANCED_ICON = "ReVanced*Logo" // Can never be a valid path. +private const val APP_NAME = "YouTube ReVanced" + +private val iconResourceFileNames = arrayOf( + "adaptiveproduct_youtube_background_color_108", + "adaptiveproduct_youtube_foreground_color_108", + "ic_launcher", + "ic_launcher_round", +).map { "$it.png" }.toTypedArray() + +private val mipmapDirectories = arrayOf( + "xxxhdpi", + "xxhdpi", + "xhdpi", + "hdpi", + "mdpi", +).map { "mipmap-$it" } + +@Suppress("unused") +val customBrandingPatch = resourcePatch( name = "Custom branding", description = "Applies a custom app name and icon. Defaults to \"YouTube ReVanced\" and the ReVanced logo.", - compatiblePackages = [ - CompatiblePackage("com.google.android.youtube"), - ], use = false, -) -@Suppress("unused") -object CustomBrandingPatch : ResourcePatch() { - private const val REVANCED_ICON = "ReVanced*Logo" // Can never be a valid path. - private const val APP_NAME = "YouTube ReVanced" - - private val iconResourceFileNames = arrayOf( - "adaptiveproduct_youtube_background_color_108", - "adaptiveproduct_youtube_foreground_color_108", - "ic_launcher", - "ic_launcher_round", - ).map { "$it.png" }.toTypedArray() - - private val mipmapDirectories = arrayOf( - "xxxhdpi", - "xxhdpi", - "xhdpi", - "hdpi", - "mdpi", - ).map { "mipmap-$it" } +) { + compatibleWith("com.google.android.youtube") - private var appName by stringPatchOption( + val appName by stringOption( key = "appName", default = APP_NAME, values = mapOf( @@ -52,7 +47,7 @@ object CustomBrandingPatch : ResourcePatch() { description = "The name of the app.", ) - private var icon by stringPatchOption( + val icon by stringOption( key = "iconPath", default = REVANCED_ICON, values = mapOf("ReVanced Logo" to REVANCED_ICON), @@ -70,7 +65,7 @@ object CustomBrandingPatch : ResourcePatch() { """.trimIndentMultiline(), ) - override fun execute(context: ResourceContext) { + execute { context -> icon?.let { icon -> // Change the app icon. mipmapDirectories.map { directory -> @@ -81,7 +76,7 @@ object CustomBrandingPatch : ResourcePatch() { }.let { resourceGroups -> if (icon != REVANCED_ICON) { val path = File(icon) - val resourceDirectory = context.get("res") + val resourceDirectory = context["res"] resourceGroups.forEach { group -> val fromDirectory = path.resolve(group.resourceDirectoryName) @@ -102,7 +97,7 @@ object CustomBrandingPatch : ResourcePatch() { appName?.let { name -> // Change the app name. - val manifest = context.get("AndroidManifest.xml") + val manifest = context["AndroidManifest.xml"] manifest.writeText( manifest.readText() .replace( diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt similarity index 78% rename from src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt index d0614297a7..8b184c57f3 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt @@ -1,47 +1,42 @@ package app.revanced.patches.youtube.layout.branding.header -import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.patch.stringOption import app.revanced.util.ResourceGroup import app.revanced.util.Utils.trimIndentMultiline import app.revanced.util.copyResources import java.io.File -@Patch( +private const val HEADER_FILE_NAME = "yt_wordmark_header" +private const val PREMIUM_HEADER_FILE_NAME = "yt_premium_wordmark_header" + +private const val HEADER_OPTION = "header*" +private const val PREMIUM_HEADER_OPTION = "premium*header" +private const val REVANCED_HEADER_OPTION = "revanced*" +private const val REVANCED_BORDERLESS_HEADER_OPTION = "revanced*borderless" + +private val targetResourceDirectoryNames = mapOf( + "xxxhdpi" to "512px x 192px", + "xxhdpi" to "387px x 144px", + "xhdpi" to "258px x 96px", + "hdpi" to "194px x 72px", + "mdpi" to "129px x 48px", +).map { (dpi, dim) -> + "drawable-$dpi" to dim +}.toMap() + +private val variants = arrayOf("light", "dark") + +@Suppress("unused") +val changeHeaderPatch = resourcePatch( name = "Change header", description = "Applies a custom header in the top left corner within the app. Defaults to the ReVanced header.", - compatiblePackages = [ - CompatiblePackage("com.google.android.youtube"), - ], use = false, -) -@Suppress("unused") -object ChangeHeaderPatch : ResourcePatch() { - private const val HEADER_FILE_NAME = "yt_wordmark_header" - private const val PREMIUM_HEADER_FILE_NAME = "yt_premium_wordmark_header" - - private const val HEADER_OPTION = "header*" - private const val PREMIUM_HEADER_OPTION = "premium*header" - private const val REVANCED_HEADER_OPTION = "revanced*" - private const val REVANCED_BORDERLESS_HEADER_OPTION = "revanced*borderless" - - private val targetResourceDirectoryNames = mapOf( - "xxxhdpi" to "512px x 192px", - "xxhdpi" to "387px x 144px", - "xhdpi" to "258px x 96px", - "hdpi" to "194px x 72px", - "mdpi" to "129px x 48px", - ).map { (dpi, dim) -> - "drawable-$dpi" to dim - }.toMap() - - private val variants = arrayOf("light", "dark") - - private val header by stringPatchOption( +) { + compatibleWith("com.google.android.youtube") + + val header by stringOption( key = "header", default = REVANCED_BORDERLESS_HEADER_OPTION, values = mapOf( @@ -68,10 +63,10 @@ object ChangeHeaderPatch : ResourcePatch() { required = true, ) - override fun execute(context: ResourceContext) { + execute { context -> // The directories to copy the header to. val targetResourceDirectories = targetResourceDirectoryNames.keys.mapNotNull { - context.get("res").resolve(it).takeIf(File::exists) + context["res"].resolve(it).takeIf(File::exists) } // The files to replace in the target directories. val targetResourceFiles = targetResourceDirectoryNames.keys.map { directoryName -> diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt new file mode 100644 index 0000000000..e0ee582732 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt @@ -0,0 +1,55 @@ +package app.revanced.patches.youtube.layout.buttons.action + +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen + +@Suppress("unused") +val hideButtonsPatch = resourcePatch( + name = "Hide video action buttons", + description = "Adds options to hide action buttons (such as the Download button) under videos.", +) { + dependsOn( + resourceMappingPatch, + lithoFilterPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + execute { + addResources("youtube", "layout.buttons.action.hideButtonsPatch") + + PreferenceScreen.PLAYER.addPreferences( + PreferenceScreenPreference( + "revanced_hide_buttons_screen", + preferences = setOf( + SwitchPreference("revanced_hide_like_dislike_button"), + SwitchPreference("revanced_hide_share_button"), + SwitchPreference("revanced_hide_report_button"), + SwitchPreference("revanced_hide_remix_button"), + SwitchPreference("revanced_hide_download_button"), + SwitchPreference("revanced_hide_thanks_button"), + SwitchPreference("revanced_hide_clip_button"), + SwitchPreference("revanced_hide_playlist_button"), + ), + ), + ) + + addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/Fingerprints.kt new file mode 100644 index 0000000000..eebd0befb9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/Fingerprints.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.youtube.layout.buttons.navigation + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal const val ANDROID_AUTOMOTIVE_STRING = "Android Automotive" + +internal val addCreateButtonViewFingerprint = fingerprint { + strings("Android Wear", ANDROID_AUTOMOTIVE_STRING) +} + +internal val createPivotBarFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters( + "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;", + "Landroid/widget/TextView;", + "Ljava/lang/CharSequence;", + ) + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_VOID, + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt new file mode 100644 index 0000000000..43b221ade2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt @@ -0,0 +1,106 @@ +package app.revanced.patches.youtube.layout.buttons.navigation + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated +import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +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.reference.MethodReference + +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;" + +@Suppress("unused") +val navigationButtonsPatch = bytecodePatch( + name = "Navigation buttons", + description = "Adds options to hide and change navigation buttons (such as the Shorts button).", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + navigationBarHookPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val addCreateButtonViewMatch by addCreateButtonViewFingerprint() + val createPivotBarMatch by createPivotBarFingerprint() + + execute { + addResources("youtube", "layout.buttons.navigation.navigationButtonsPatch") + + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + PreferenceScreenPreference( + key = "revanced_navigation_buttons_screen", + sorting = Sorting.UNSORTED, + preferences = setOf( + SwitchPreference("revanced_hide_home_button"), + SwitchPreference("revanced_hide_shorts_button"), + SwitchPreference("revanced_hide_create_button"), + SwitchPreference("revanced_hide_subscriptions_button"), + SwitchPreference("revanced_switch_create_with_notifications_button"), + SwitchPreference("revanced_hide_navigation_button_labels"), + ), + ), + ) + + // Switch create with notifications button. + addCreateButtonViewMatch.mutableMethod.apply { + val stringIndex = addCreateButtonViewMatch.stringMatches!!.find { match -> + match.string == ANDROID_AUTOMOTIVE_STRING + }!!.index + + val conditionalCheckIndex = stringIndex - 1 + val conditionRegister = + getInstruction(conditionalCheckIndex).registerA + + addInstructions( + conditionalCheckIndex, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->switchCreateWithNotificationButton()Z + move-result v$conditionRegister + """, + ) + } + + // Hide navigation button labels. + createPivotBarMatch.mutableMethod.apply { + val setTextIndex = indexOfFirstInstructionOrThrow { + getReference()?.name == "setText" + } + + val targetRegister = getInstruction(setTextIndex).registerC + + addInstruction( + setTextIndex, + "invoke-static { v$targetRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->hideNavigationButtonLabels(Landroid/widget/TextView;)V", + ) + } + + // Hook navigation button created, in order to hide them. + hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/Fingerprints.kt new file mode 100644 index 0000000000..386f18bcd3 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/Fingerprints.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.youtube.layout.buttons.overlay + +import app.revanced.patcher.fingerprint +import app.revanced.util.containsWideLiteralInstructionValue +import com.android.tools.smali.dexlib2.AccessFlags + +internal val playerControlsPreviousNextOverlayTouchFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + strings("1.0x") + custom { methodDef, _ -> + methodDef.containsWideLiteralInstructionValue(playerControlPreviousButtonTouchArea) && + methodDef.containsWideLiteralInstructionValue(playerControlNextButtonTouchArea) + } +} + +internal val mediaRouteButtonFingerprint = fingerprint { + parameters("I") + custom { methodDef, _ -> + methodDef.definingClass.endsWith("/MediaRouteButton;") && methodDef.name == "setVisibility" + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt new file mode 100644 index 0000000000..380b9309f3 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatch.kt @@ -0,0 +1,158 @@ +package app.revanced.patches.youtube.layout.buttons.overlay + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.layoutConstructorFingerprint +import app.revanced.patches.youtube.shared.subtitleButtonControllerFingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow +import app.revanced.util.indexOfIdResourceOrThrow +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.reference.MethodReference + +internal var playerControlPreviousButtonTouchArea = -1L + private set +internal var playerControlNextButtonTouchArea = -1L + private set + +private val hidePlayerOverlayButtonsResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + execute { + playerControlPreviousButtonTouchArea = resourceMappings["id", "player_control_previous_button_touch_area"] + playerControlNextButtonTouchArea = resourceMappings["id", "player_control_next_button_touch_area"] + } +} + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/HidePlayerOverlayButtonsPatch;" + +@Suppress("unused") +val hidePlayerOverlayButtonsPatch = bytecodePatch( + name = "Hide player overlay buttons", + description = "Adds options to hide the player cast, autoplay, caption button and next/ previous buttons.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + hidePlayerOverlayButtonsResourcePatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val playerControlsPreviousNextOverlayTouchMatch by playerControlsPreviousNextOverlayTouchFingerprint() + val mediaRouteButtonMatch by mediaRouteButtonFingerprint() + val subtitleButtonControllerMatch by subtitleButtonControllerFingerprint() + val layoutConstructorMatch by layoutConstructorFingerprint() + + execute { + addResources("youtube", "layout.buttons.overlay.hidePlayerOverlayButtonsPatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_hide_player_previous_next_buttons"), + SwitchPreference("revanced_hide_cast_button"), + SwitchPreference("revanced_hide_captions_button"), + SwitchPreference("revanced_hide_autoplay_button"), + ) + + // region Hide player next/previous button. + + playerControlsPreviousNextOverlayTouchMatch.mutableMethod.apply { + val resourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(playerControlPreviousButtonTouchArea) + + val insertIndex = indexOfFirstInstructionOrThrow(resourceIndex) { + opcode == Opcode.INVOKE_STATIC && + getReference()?.parameterTypes?.firstOrNull() == "Landroid/view/View;" + } + + val viewRegister = getInstruction(insertIndex).registerC + + addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR" + + "->hidePreviousNextButtons(Landroid/view/View;)V", + ) + } + + // endregion + + // region Hide cast button. + + mediaRouteButtonMatch.mutableMethod.addInstructions( + 0, + """ + invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getCastButtonOverrideV2(I)I + move-result p1 + """, + ) + + // endregion + + // region Hide captions button. + + subtitleButtonControllerMatch.mutableMethod.apply { + // Due to previously applied patches, scanResult index cannot be used in this context + val insertIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1 + + addInstruction( + insertIndex, + "invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->hideCaptionsButton(Landroid/widget/ImageView;)V", + ) + } + + // endregion + + // region Hide autoplay button. + + layoutConstructorMatch.mutableMethod.apply { + val constIndex = indexOfIdResourceOrThrow("autonav_toggle") + val constRegister = getInstruction(constIndex).registerA + + // Add a conditional branch around the code that inflates and adds the auto-repeat button. + val gotoIndex = indexOfFirstInstructionOrThrow(constIndex) { + val parameterTypes = getReference()?.parameterTypes + opcode == Opcode.INVOKE_VIRTUAL && + parameterTypes?.size == 2 && + parameterTypes.first() == "Landroid/view/ViewStub;" + } + 1 + + addInstructionsWithLabels( + constIndex, + """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideAutoPlayButton()Z + move-result v$constRegister + if-nez v$constRegister, :hidden + """, + ExternalLabel("hidden", getInstruction(gotoIndex)), + ) + } + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/Fingerprints.kt new file mode 100644 index 0000000000..f165098f06 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/player/hide/Fingerprints.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.youtube.layout.buttons.player.hide + +import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.fingerprint + +internal val playerControlsVisibilityModelFingerprint = fingerprint { + opcodes(Opcode.INVOKE_DIRECT_RANGE) + strings("Missing required properties:", "hasNext", "hasPrevious") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch.kt new file mode 100644 index 0000000000..a7955c59e2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/breakingnews/BreakingNewsPatch.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.youtube.layout.hide.breakingnews + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.layout.hide.general.hideLayoutComponentsPatch + +@Deprecated("This patch has been merged to HideLayoutComponentsPatch.") +@Suppress("unused") +val breakingNewsPatch = bytecodePatch { + dependsOn(hideLayoutComponentsPatch) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/Fingerprints.kt new file mode 100644 index 0000000000..59d859e800 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/Fingerprints.kt @@ -0,0 +1,40 @@ +package app.revanced.patches.youtube.layout.hide.endscreencards + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.Opcode + +internal val layoutCircleFingerprint = fingerprint { + returns("Landroid/view/View;") + opcodes( + Opcode.CONST, + Opcode.CONST_4, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + ) + literal { layoutCircle } +} + +internal val layoutIconFingerprint = fingerprint { + returns("Landroid/view/View;") + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + + ) + literal { layoutIcon } +} + +internal val layoutVideoFingerprint = fingerprint { + returns("Landroid/view/View;") + opcodes( + Opcode.CONST, + Opcode.CONST_4, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + ) + literal { layoutVideo } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch.kt new file mode 100644 index 0000000000..e63f8950d5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatch.kt @@ -0,0 +1,90 @@ +package app.revanced.patches.youtube.layout.hide.endscreencards + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c + +internal var layoutCircle = -1L + private set +internal var layoutIcon = -1L + private set +internal var layoutVideo = -1L + private set + +private val hideEndscreenCardsResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + ) + + execute { + addResources("youtube", "layout.hide.endscreencards.hideEndscreenCardsResourcePatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_hide_endscreen_cards"), + ) + + fun idOf(name: String) = resourceMappings["layout", "endscreen_element_layout_$name"] + + layoutCircle = idOf("circle") + layoutIcon = idOf("icon") + layoutVideo = idOf("video") + } +} + +@Suppress("unused") +val hideEndscreenCardsPatch = bytecodePatch( + name = "Hide endscreen cards", + description = "Adds an option to hide suggested video cards at the end of videos.", +) { + dependsOn( + sharedExtensionPatch, + hideEndscreenCardsResourcePatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val layoutCircleMatch by layoutCircleFingerprint() + val layoutIconMatch by layoutIconFingerprint() + val layoutVideoMatch by layoutVideoFingerprint() + + execute { + listOf( + layoutCircleMatch, + layoutIconMatch, + layoutVideoMatch, + ).forEach { + it.mutableMethod.apply { + val insertIndex = it.patternMatch!!.endIndex + 1 + val viewRegister = getInstruction(insertIndex - 1).registerA + + addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, " + + "Lapp/revanced/extension/youtube/patches/HideEndscreenCardsPatch;->" + + "hideEndscreen(Landroid/view/View;)V", + ) + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt new file mode 100644 index 0000000000..a286d70fd7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatch.kt @@ -0,0 +1,67 @@ +package app.revanced.patches.youtube.layout.hide.fullscreenambientmode + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import java.util.logging.Logger + +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/DisableFullscreenAmbientModePatch;" + +@Suppress("unused") +val disableFullscreenAmbientModePatch = bytecodePatch( + name = "Disable fullscreen ambient mode", + description = "Adds an option to disable the ambient mode when in fullscreen.", +) { + dependsOn( + settingsPatch, + sharedExtensionPatch, + addResourcesPatch, + versionCheckPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val initializeAmbientModeMatch by initializeAmbientModeFingerprint() + + execute { + // TODO: fix this patch when 19.43+ is eventually supported. + if (is_19_43_or_greater) { + // 19.43+ the feature flag was inlined as false and no longer exists. + // This patch can be updated to change a single method, but for now show a more descriptive error. + return@execute Logger.getLogger(this::class.java.name) + .severe("'Disable fullscreen ambient mode' does not yet support 19.43+") + } + + addResources("youtube", "layout.hide.fullscreenambientmode.disableFullscreenAmbientModePatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_disable_fullscreen_ambient_mode"), + ) + + initializeAmbientModeMatch.mutableMethod.apply { + val moveIsEnabledIndex = initializeAmbientModeMatch.patternMatch!!.endIndex + + addInstruction( + moveIsEnabledIndex, + "invoke-static { }, " + + "$EXTENSION_CLASS_DESCRIPTOR->enableFullScreenAmbientMode()Z", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/Fingerprints.kt new file mode 100644 index 0000000000..7f20c1433b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/fullscreenambientmode/Fingerprints.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.layout.hide.fullscreenambientmode + +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val initializeAmbientModeFingerprint = fingerprint { + returns("V") + accessFlags(AccessFlags.CONSTRUCTOR, AccessFlags.PUBLIC) + opcodes(Opcode.MOVE_RESULT) + literal { 45389368 } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt new file mode 100644 index 0000000000..463e4cdc6d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt @@ -0,0 +1,116 @@ +package app.revanced.patches.youtube.layout.hide.general + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val hideShowMoreButtonFingerprint = fingerprint { + opcodes( + Opcode.CONST, + Opcode.CONST_4, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + ) + literal { expandButtonDownId } +} + +internal val parseElementFromBufferFingerprint = fingerprint { + parameters("L", "L", "[B", "L", "L") + opcodes( + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + ) + strings("Failed to parse Element") // String is a partial match. +} + +internal val playerOverlayFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + strings("player_overlay_in_video_programming") +} + +internal val showWatermarkFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "L") +} + +internal val yoodlesImageViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/view/View;") + parameters("L", "L") + literal { youTubeLogo } +} + +internal val crowdfundingBoxFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IPUT_OBJECT, + ) + literal { crowdfundingBoxId } +} + +internal val albumCardsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + opcodes( + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST, + Opcode.CONST_4, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + ) + literal { albumCardId } +} + +internal val filterBarHeightFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + opcodes( + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IPUT, + ) + literal { filterBarHeightId } +} + +internal val relatedChipCloudFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + opcodes( + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + ) + literal { relatedChipCloudMarginId } +} + +internal val searchResultsChipBarFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + opcodes( + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + ) + literal { barContainerHeightId } +} + +internal val showFloatingMicrophoneButtonFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + opcodes( + Opcode.IGET_BOOLEAN, + Opcode.IF_EQZ, + Opcode.RETURN_VOID, + ) + literal { fabButtonId } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt similarity index 53% rename from src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index 229f08d306..0b4e008f98 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -1,98 +1,153 @@ package app.revanced.patches.youtube.layout.hide.general -import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.Match import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction -import app.revanced.patcher.fingerprint.MethodFingerprint -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.util.smali.ExternalLabel -import app.revanced.patches.all.misc.resources.AddResourcesPatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.* -import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting -import app.revanced.patches.youtube.layout.hide.general.fingerprints.AlbumCardsFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.CrowdfundingBoxFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.FilterBarHeightFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.HideShowMoreButtonFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.ParseElementFromBufferFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.PlayerOverlayFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.RelatedChipCloudFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.SearchResultsChipBarFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.ShowFloatingMicrophoneButtonFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.ShowWatermarkFingerprint -import app.revanced.patches.youtube.layout.hide.general.fingerprints.YoodlesImageViewFingerprint -import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch -import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch -import app.revanced.patches.youtube.misc.settings.SettingsPatch -import app.revanced.util.alsoResolve +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.applyMatch import app.revanced.util.findOpcodeIndicesReversed import app.revanced.util.getReference -import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method 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.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.sun.org.apache.bcel.internal.generic.InstructionConst.getInstruction + +var expandButtonDownId = -1L + private set +var albumCardId = -1L + private set +var crowdfundingBoxId = -1L + private set +var youTubeLogo = -1L + private set + +var filterBarHeightId = -1L + private set +var relatedChipCloudMarginId = -1L + private set +var barContainerHeightId = -1L + private set + +var fabButtonId = -1L + private set + +private val hideLayoutComponentsResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + execute { + expandButtonDownId = resourceMappings[ + "layout", + "expand_button_down", + ] + + albumCardId = resourceMappings[ + "layout", + "album_card", + ] + + crowdfundingBoxId = resourceMappings[ + "layout", + "donation_companion", + ] + + youTubeLogo = resourceMappings[ + "id", + "youtube_logo", + ] + + relatedChipCloudMarginId = resourceMappings[ + "layout", + "related_chip_cloud_reduced_margins", + ] + + filterBarHeightId = resourceMappings[ + "dimen", + "filter_bar_height", + ] + + barContainerHeightId = resourceMappings[ + "dimen", + "bar_container_height", + ] + + fabButtonId = resourceMappings[ + "id", + "fab", + ] + } +} + +private const val LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/components/LayoutComponentsFilter;" +private const val DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME = + "Lapp/revanced/extension/youtube/patches/components/DescriptionComponentsFilter;" +private const val COMMENTS_FILTER_CLASS_NAME = + "Lapp/revanced/extension/youtube/patches/components/CommentsFilter;" +private const val CUSTOM_FILTER_CLASS_NAME = + "Lapp/revanced/extension/youtube/patches/components/CustomFilter;" +private const val KEYWORD_FILTER_CLASS_NAME = + "Lapp/revanced/extension/youtube/patches/components/KeywordContentFilter;" -@Patch( +@Suppress("unused") +val hideLayoutComponentsPatch = bytecodePatch( name = "Hide layout components", description = "Adds options to hide general layout components.", - dependencies = [ - LithoFilterPatch::class, - SettingsPatch::class, - AddResourcesPatch::class, - HideLayoutComponentsResourcePatch::class, - NavigationBarHookPatch::class, - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ], - ), - ], -) -@Suppress("unused") -object HideLayoutComponentsPatch : BytecodePatch( - setOf( - ParseElementFromBufferFingerprint, - PlayerOverlayFingerprint, - HideShowMoreButtonFingerprint, - AlbumCardsFingerprint, - CrowdfundingBoxFingerprint, - YoodlesImageViewFingerprint, - RelatedChipCloudFingerprint, - SearchResultsChipBarFingerprint, - ShowFloatingMicrophoneButtonFingerprint, - FilterBarHeightFingerprint - ), + ) { - private const val LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR = - "Lapp/revanced/integrations/youtube/patches/components/LayoutComponentsFilter;" - private const val DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME = - "Lapp/revanced/integrations/youtube/patches/components/DescriptionComponentsFilter;" - private const val COMMENTS_FILTER_CLASS_NAME = - "Lapp/revanced/integrations/youtube/patches/components/CommentsFilter;" - private const val CUSTOM_FILTER_CLASS_NAME = - "Lapp/revanced/integrations/youtube/patches/components/CustomFilter;" - private const val KEYWORD_FILTER_CLASS_NAME = - "Lapp/revanced/integrations/youtube/patches/components/KeywordContentFilter;" - - override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.PLAYER.addPreferences( - PreferenceScreen( + dependsOn( + lithoFilterPatch, + settingsPatch, + addResourcesPatch, + hideLayoutComponentsResourcePatch, + navigationBarHookPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val parseElementFromBufferMatch by parseElementFromBufferFingerprint() + val playerOverlayMatch by playerOverlayFingerprint() + val hideShowMoreButtonMatch by hideShowMoreButtonFingerprint() + val albumCardsMatch by albumCardsFingerprint() + val crowdfundingBoxMatch by crowdfundingBoxFingerprint() + val yoodlesImageViewMatch by yoodlesImageViewFingerprint() + val relatedChipCloudMatch by relatedChipCloudFingerprint() + val searchResultsChipBarMatch by searchResultsChipBarFingerprint() + val showFloatingMicrophoneButtonMatch by showFloatingMicrophoneButtonFingerprint() + val filterBarHeightMatch by filterBarHeightFingerprint() + + execute { context -> + addResources("youtube", "layout.hide.general.hideLayoutComponentsPatch") + + PreferenceScreen.PLAYER.addPreferences( + PreferenceScreenPreference( key = "revanced_hide_description_components_screen", preferences = setOf( SwitchPreference("revanced_hide_attributes_section"), @@ -103,7 +158,7 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_transcript_section"), ), ), - PreferenceScreen( + PreferenceScreenPreference( "revanced_comments_screen", preferences = setOf( SwitchPreference("revanced_hide_comments_by_members_header"), @@ -111,9 +166,9 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_comments_create_a_short_button"), SwitchPreference("revanced_hide_comments_preview_comment"), SwitchPreference("revanced_hide_comments_thanks_button"), - SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons") + SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons"), ), - sorting = Sorting.UNSORTED + sorting = PreferenceScreenPreference.Sorting.UNSORTED, ), SwitchPreference("revanced_hide_channel_bar"), SwitchPreference("revanced_hide_channel_guidelines"), @@ -131,21 +186,23 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_timed_reactions"), ) - SettingsPatch.PreferenceScreen.FEED.addPreferences( - PreferenceScreen( + PreferenceScreen.FEED.addPreferences( + PreferenceScreenPreference( key = "revanced_hide_keyword_content_screen", - sorting = Sorting.UNSORTED, + sorting = PreferenceScreenPreference.Sorting.UNSORTED, preferences = setOf( SwitchPreference("revanced_hide_keyword_content_home"), SwitchPreference("revanced_hide_keyword_content_subscriptions"), SwitchPreference("revanced_hide_keyword_content_search"), TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE), NonInteractivePreference("revanced_hide_keyword_content_about"), - NonInteractivePreference(key = "revanced_hide_keyword_content_about_whole_words", - tag = "app.revanced.integrations.youtube.settings.preference.HtmlPreference") - ) + NonInteractivePreference( + key = "revanced_hide_keyword_content_about_whole_words", + tag = "app.revanced.extension.youtube.settings.preference.HtmlPreference", + ), + ), ), - PreferenceScreen( + PreferenceScreenPreference( key = "revanced_hide_filter_bar_screen", preferences = setOf( SwitchPreference("revanced_hide_filter_bar_feed_in_feed"), @@ -176,11 +233,11 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_doodles"), ) - SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences( + PreferenceScreen.GENERAL_LAYOUT.addPreferences( SwitchPreference("revanced_hide_gray_separator"), - PreferenceScreen( + PreferenceScreenPreference( key = "revanced_custom_filter_screen", - sorting = Sorting.UNSORTED, + sorting = PreferenceScreenPreference.Sorting.UNSORTED, preferences = setOf( SwitchPreference("revanced_custom_filter"), // TODO: This should be a dynamic ListPreference, which does not exist yet @@ -189,43 +246,41 @@ object HideLayoutComponentsPatch : BytecodePatch( ), ) - LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR) - LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME) - LithoFilterPatch.addFilter(COMMENTS_FILTER_CLASS_NAME) - LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME) - LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_NAME) + addLithoFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR) + addLithoFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME) + addLithoFilter(COMMENTS_FILTER_CLASS_NAME) + addLithoFilter(KEYWORD_FILTER_CLASS_NAME) + addLithoFilter(CUSTOM_FILTER_CLASS_NAME) // region Mix playlists - ParseElementFromBufferFingerprint.resultOrThrow().let { result -> - val startIndex = result.scanResult.patternScanResult!!.startIndex + val startIndex = parseElementFromBufferMatch.patternMatch!!.startIndex - result.mutableMethod.apply { - val freeRegister = "v0" - val byteArrayParameter = "p3" - val conversionContextRegister = getInstruction(startIndex).registerA - val returnEmptyComponentInstruction = getInstructions().last { it.opcode == Opcode.INVOKE_STATIC } + parseElementFromBufferMatch.mutableMethod.apply { + val freeRegister = "v0" + val byteArrayParameter = "p3" + val conversionContextRegister = getInstruction(startIndex).registerA + val returnEmptyComponentInstruction = instructions.last { it.opcode == Opcode.INVOKE_STATIC } - addInstructionsWithLabels( - startIndex + 1, - """ + addInstructionsWithLabels( + startIndex + 1, + """ invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z move-result $freeRegister if-nez $freeRegister, :return_empty_component const/4 $freeRegister, 0x0 # Restore register, required for 19.16 """, - ExternalLabel("return_empty_component", returnEmptyComponentInstruction), - ) - } + ExternalLabel("return_empty_component", returnEmptyComponentInstruction), + ) } // endregion // region Watermark (legacy code for old versions of YouTube) - ShowWatermarkFingerprint.alsoResolve( + showWatermarkFingerprint.applyMatch( context, - PlayerOverlayFingerprint + playerOverlayMatch, ).mutableMethod.apply { val index = implementation!!.instructions.size - 5 @@ -243,33 +298,31 @@ object HideLayoutComponentsPatch : BytecodePatch( // region Show more button - HideShowMoreButtonFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - val moveRegisterIndex = it.scanResult.patternScanResult!!.endIndex - val viewRegister = - getInstruction(moveRegisterIndex).registerA + hideShowMoreButtonMatch.mutableMethod.apply { + val moveRegisterIndex = hideShowMoreButtonMatch.patternMatch!!.endIndex + val viewRegister = getInstruction(moveRegisterIndex).registerA - val insertIndex = moveRegisterIndex + 1 - addInstruction( - insertIndex, - "invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + - "->hideShowMoreButton(Landroid/view/View;)V", - ) - } + val insertIndex = moveRegisterIndex + 1 + addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + + "->hideShowMoreButton(Landroid/view/View;)V", + ) } // endregion - // region crowd funding box - CrowdfundingBoxFingerprint.resultOrThrow().let { + // region crowdfunding box + crowdfundingBoxMatch.let { it.mutableMethod.apply { - val insertIndex = it.scanResult.patternScanResult!!.endIndex + val insertIndex = it.patternMatch!!.endIndex val objectRegister = getInstruction(insertIndex).registerA addInstruction( insertIndex, "invoke-static {v$objectRegister}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + - "->hideCrowdfundingBox(Landroid/view/View;)V") + "->hideCrowdfundingBox(Landroid/view/View;)V", + ) } } @@ -277,16 +330,16 @@ object HideLayoutComponentsPatch : BytecodePatch( // region hide album cards - AlbumCardsFingerprint.resultOrThrow().let { + albumCardsMatch.let { it.mutableMethod.apply { - val checkCastAnchorIndex = it.scanResult.patternScanResult!!.endIndex + val checkCastAnchorIndex = it.patternMatch!!.endIndex val insertIndex = checkCastAnchorIndex + 1 val register = getInstruction(checkCastAnchorIndex).registerA addInstruction( insertIndex, "invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" + - "->hideAlbumCard(Landroid/view/View;)V" + "->hideAlbumCard(Landroid/view/View;)V", ) } } @@ -295,17 +348,17 @@ object HideLayoutComponentsPatch : BytecodePatch( // region hide floating microphone - ShowFloatingMicrophoneButtonFingerprint.resultOrThrow().let { result -> - with(result.mutableMethod) { - val startIndex = result.scanResult.patternScanResult!!.startIndex + showFloatingMicrophoneButtonMatch.let { + it.mutableMethod.apply { + val startIndex = it.patternMatch!!.startIndex val register = getInstruction(startIndex).registerA addInstructions( startIndex + 1, """ - invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideFloatingMicrophoneButton(Z)Z - move-result v$register - """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideFloatingMicrophoneButton(Z)Z + move-result v$register + """, ) } } @@ -314,10 +367,9 @@ object HideLayoutComponentsPatch : BytecodePatch( // region 'Yoodles' - YoodlesImageViewFingerprint.resultOrThrow().mutableMethod.apply { - findOpcodeIndicesReversed{ - opcode == Opcode.INVOKE_VIRTUAL - && getReference()?.name == "setImageDrawable" + yoodlesImageViewMatch.mutableMethod.apply { + findOpcodeIndicesReversed { + getReference()?.name == "setImageDrawable" }.forEach { insertIndex -> val register = getInstruction(insertIndex).registerD @@ -337,47 +389,45 @@ object HideLayoutComponentsPatch : BytecodePatch( // region hide filter bar - FilterBarHeightFingerprint.patch { register -> + /** + * Patch a [Method] with a given [instructions]. + * + * @param RegisterInstruction The type of instruction to get the register from. + * @param insertIndexOffset The offset to add to the end index of the [Match.patternMatch]. + * @param hookRegisterOffset The offset to add to the register of the hook. + * @param instructions The instructions to add with the register as a parameter. + */ + fun Match.patch( + insertIndexOffset: Int = 0, + hookRegisterOffset: Int = 0, + instructions: (Int) -> String, + ) = mutableMethod.apply { + val endIndex = patternMatch!!.endIndex + + val insertIndex = endIndex + insertIndexOffset + val register = + getInstruction(endIndex + hookRegisterOffset).registerA + + addInstructions(insertIndex, instructions(register)) + } + + filterBarHeightMatch.patch { register -> """ invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInFeed(I)I move-result v$register """ } - SearchResultsChipBarFingerprint.patch(-1, -2) { register -> + searchResultsChipBarMatch.patch(-1, -2) { register -> """ invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInSearch(I)I move-result v$register """ } - RelatedChipCloudFingerprint.patch(1) { register -> - "invoke-static { v$register }, " + - "$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V" - } - } - - /** - * Patch a [MethodFingerprint] with a given [instructions]. - * - * @param RegisterInstruction The type of instruction to get the register from. - * @param insertIndexOffset The offset to add to the end index of the [MethodFingerprint]. - * @param hookRegisterOffset The offset to add to the register of the hook. - * @param instructions The instructions to add with the register as a parameter. - */ - private fun MethodFingerprint.patch( - insertIndexOffset: Int = 0, - hookRegisterOffset: Int = 0, - instructions: (Int) -> String - ) = resultOrThrow().let { - it.mutableMethod.apply { - val endIndex = it.scanResult.patternScanResult!!.endIndex - - val insertIndex = endIndex + insertIndexOffset - val register = - getInstruction(endIndex + hookRegisterOffset).registerA - - addInstructions(insertIndex, instructions(register)) + relatedChipCloudMatch.patch(1) { register -> + "invoke-static { v$register }, " + + "$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V" } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/Fingerprints.kt new file mode 100644 index 0000000000..5088472a1b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/Fingerprints.kt @@ -0,0 +1,29 @@ +package app.revanced.patches.youtube.layout.hide.infocards + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val infocardsIncognitoFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/Boolean;") + parameters("L", "J") + strings("vibrator") +} + +internal val infocardsIncognitoParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String;") + strings("player_overlay_info_card_teaser") +} + +internal val infocardsMethodCallFingerprint = fingerprint { + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + ) + strings("Missing ControlsOverlayPresenter for InfoCards to work.") + literal { drawerResourceId } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt new file mode 100644 index 0000000000..5e5b4c9a65 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt @@ -0,0 +1,109 @@ +package app.revanced.patches.youtube.layout.hide.infocards + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.applyMatch +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.sun.org.apache.bcel.internal.generic.InstructionConst.getInstruction + +internal var drawerResourceId = -1L + private set + +private val hideInfocardsResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + ) + execute { + addResources("youtube", "layout.hide.infocards.hideInfocardsResourcePatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_hide_info_cards"), + ) + + drawerResourceId = resourceMappings[ + "id", + "info_cards_drawer_header", + ] + } +} + +@Suppress("unused") +val hideInfoCardsPatch = bytecodePatch( + name = "Hide info cards", + description = "Adds an option to hide info cards that creators add in the video player.", +) { + dependsOn( + sharedExtensionPatch, + lithoFilterPatch, + hideInfocardsResourcePatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val infocardsIncognitoParentMatch by infocardsIncognitoParentFingerprint() + val infocardsMethodCallMatch by infocardsMethodCallFingerprint() + + execute { context -> + infocardsIncognitoFingerprint.applyMatch(context, infocardsIncognitoParentMatch).mutableMethod.apply { + val invokeInstructionIndex = implementation!!.instructions.indexOfFirst { + it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal && + ((it as ReferenceInstruction).reference.toString() == "Landroid/view/View;->setVisibility(I)V") + } + + addInstruction( + invokeInstructionIndex, + "invoke-static {v${getInstruction(invokeInstructionIndex).registerC}}," + + " Lapp/revanced/extension/youtube/patches/HideInfoCardsPatch;->hideInfoCardsIncognito(Landroid/view/View;)V", + ) + } + + val hideInfoCardsCallMethod = infocardsMethodCallMatch.mutableMethod + + val invokeInterfaceIndex = infocardsMethodCallMatch.patternMatch!!.endIndex + val toggleRegister = infocardsMethodCallMatch.mutableMethod.implementation!!.registerCount - 1 + + hideInfoCardsCallMethod.addInstructionsWithLabels( + invokeInterfaceIndex, + """ + invoke-static {}, Lapp/revanced/extension/youtube/patches/HideInfoCardsPatch;->hideInfoCardsMethodCall()Z + move-result v$toggleRegister + if-nez v$toggleRegister, :hide_info_cards + """, + ExternalLabel( + "hide_info_cards", + hideInfoCardsCallMethod.getInstruction(invokeInterfaceIndex + 1), + ), + ) + + // Info cards can also appear as Litho components. + val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/components/HideInfoCardsFilterPatch;" + addLithoFilter(filterClassDescriptor) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt new file mode 100644 index 0000000000..9062a5331b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt @@ -0,0 +1,62 @@ +package app.revanced.patches.youtube.layout.hide.player.flyoutmenupanel + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +@Suppress("unused") +val hidePlayerFlyoutMenuPatch = bytecodePatch( + name = "Hide player flyout menu items", + description = "Adds options to hide menu items that appear when pressing the gear icon in the video player.", +) { + dependsOn( + lithoFilterPatch, + playerTypeHookPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + execute { + val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/components/PlayerFlyoutMenuItemsFilter;" + + addResources("youtube", "layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch") + + PreferenceScreen.PLAYER.addPreferences( + PreferenceScreenPreference( + key = "revanced_hide_player_flyout", + preferences = setOf( + SwitchPreference("revanced_hide_player_flyout_captions"), + SwitchPreference("revanced_hide_player_flyout_additional_settings"), + SwitchPreference("revanced_hide_player_flyout_loop_video"), + SwitchPreference("revanced_hide_player_flyout_ambient_mode"), + SwitchPreference("revanced_hide_player_flyout_help"), + SwitchPreference("revanced_hide_player_flyout_speed"), + SwitchPreference("revanced_hide_player_flyout_lock_screen"), + SwitchPreference("revanced_hide_player_flyout_more_info"), + SwitchPreference("revanced_hide_player_flyout_audio_track"), + SwitchPreference("revanced_hide_player_flyout_watch_in_vr"), + SwitchPreference("revanced_hide_video_quality_menu_footer"), + ), + ), + ) + + addLithoFilter(filterClassDescriptor) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt new file mode 100644 index 0000000000..2c8ac9d053 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatch.kt @@ -0,0 +1,74 @@ +package app.revanced.patches.youtube.layout.hide.rollingnumber + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateFingerprint +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/DisableRollingNumberAnimationsPatch;" + +@Suppress("unused") +val disableRollingNumberAnimationPatch = bytecodePatch( + name = "Disable rolling number animations", + description = "Adds an option to disable rolling number animations of video view count, user likes, and upload time.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + // 18.43 is the earliest target this patch works. + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val rollingNumberTextViewAnimationUpdateMatch by rollingNumberTextViewAnimationUpdateFingerprint() + + execute { + addResources("youtube", "layout.hide.rollingnumber.disableRollingNumberAnimationPatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_disable_rolling_number_animations"), + ) + + // Animations are disabled by preventing an Image from being applied to the text span, + // which prevents the animations from appearing. + val patternMatch = rollingNumberTextViewAnimationUpdateMatch.patternMatch!! + val blockStartIndex = patternMatch.startIndex + val blockEndIndex = patternMatch.endIndex + 1 + rollingNumberTextViewAnimationUpdateMatch.mutableMethod.apply { + val freeRegister = getInstruction(blockStartIndex).registerA + + // ReturnYouTubeDislike also makes changes to this same method, + // and must add control flow label to a noop instruction to + // ensure RYD patch adds its changes after the control flow label. + addInstructions(blockEndIndex, "nop") + + addInstructionsWithLabels( + blockStartIndex, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableRollingNumberAnimations()Z + move-result v$freeRegister + if-nez v$freeRegister, :disable_animations + """, + ExternalLabel("disable_animations", getInstruction(blockEndIndex)), + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch.kt new file mode 100644 index 0000000000..0759935cd4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/seekbar/HideSeekbarPatch.kt @@ -0,0 +1,61 @@ +package app.revanced.patches.youtube.layout.hide.seekbar + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.seekbarFingerprint +import app.revanced.patches.youtube.shared.seekbarOnDrawFingerprint +import app.revanced.util.applyMatch + +@Suppress("unused") +val hideSeekbarPatch = bytecodePatch( + name = "Hide seekbar", + description = "Adds an option to hide the seekbar.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + seekbarColorPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val seekbarMatch by seekbarFingerprint() + + execute { context -> + addResources("youtube", "layout.hide.seekbar.hideSeekbarPatch") + + PreferenceScreen.SEEKBAR.addPreferences( + SwitchPreference("revanced_hide_seekbar"), + SwitchPreference("revanced_hide_seekbar_thumbnail"), + ) + + seekbarOnDrawFingerprint.applyMatch(context, seekbarMatch).mutableMethod.addInstructionsWithLabels( + 0, + """ + const/4 v0, 0x0 + invoke-static { }, Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;->hideSeekbar()Z + move-result v0 + if-eqz v0, :hide_seekbar + return-void + :hide_seekbar + nop + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/Fingerprints.kt new file mode 100644 index 0000000000..e38ea167b2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/Fingerprints.kt @@ -0,0 +1,103 @@ +package app.revanced.patches.youtube.layout.hide.shorts + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val bottomNavigationBarFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Landroid/view/View;", "Landroid/os/Bundle;") + opcodes( + Opcode.CONST, // R.id.app_engagement_panel_wrapper + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + ) + strings("ReelWatchPaneFragmentViewModelKey") +} + +internal val legacyRenderBottomNavigationBarParentFingerprint = fingerprint { + parameters( + "I", + "I", + "L", + "L", + "J", + "L", + ) + strings("aa") +} + +internal val shortsBottomBarContainerFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Landroid/view/View;", "Landroid/os/Bundle;") + strings("r_pfvc") + literal { bottomBarContainer } +} + +internal val createShortsButtonsFingerprint = fingerprint { + returns("V") + literal { reelPlayerRightCellButtonHeight } +} + +internal val reelConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + opcodes(Opcode.INVOKE_VIRTUAL) + literal { reelMultipleItemShelfId } +} + +internal val renderBottomNavigationBarFingerprint = fingerprint { + returns("V") + parameters("Ljava/lang/String;") + opcodes( + Opcode.IGET_OBJECT, + Opcode.MONITOR_ENTER, + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.INVOKE_INTERFACE, + Opcode.MONITOR_EXIT, + Opcode.RETURN_VOID, + Opcode.MOVE_EXCEPTION, + Opcode.MONITOR_EXIT, + Opcode.THROW, + ) +} + +/** + * Identical to [legacyRenderBottomNavigationBarParentFingerprint] + * except this has an extra parameter. + */ +internal val renderBottomNavigationBarParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters( + "I", + "I", + "L", // ReelWatchEndpointOuterClass + "L", + "J", + "Ljava/lang/String;", + "L", + ) + strings("aa") +} + +internal val setPivotBarVisibilityFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + parameters("Z") + opcodes( + Opcode.CHECK_CAST, + Opcode.IF_EQZ, + ) +} + +internal val setPivotBarVisibilityParentFingerprint = fingerprint { + parameters("Z") + strings("FEnotifications_inbox") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt new file mode 100644 index 0000000000..6d84b1526a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt @@ -0,0 +1,329 @@ +package app.revanced.patches.youtube.layout.hide.shorts + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.booleanOption +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch +import app.revanced.patches.youtube.misc.playservice.is_19_03_or_greater +import app.revanced.patches.youtube.misc.playservice.is_19_41_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.* +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.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal var reelMultipleItemShelfId = -1L + private set +internal var reelPlayerRightCellButtonHeight = -1L + private set +internal var bottomBarContainer = -1L + private set +internal var reelPlayerRightPivotV2Size = -1L + private set + +internal val hideShortsAppShortcutOption = booleanOption( + key = "hideShortsAppShortcut", + default = false, + title = "Hide Shorts app shortcut", + description = "Permanently hides the shortcut to open Shorts when long pressing the app icon in your launcher.", +) + +internal val hideShortsWidgetOption = booleanOption( + key = "hideShortsWidget", + default = false, + title = "Hide Shorts widget", + description = "Permanently hides the launcher widget Shorts button.", +) + +private val hideShortsComponentsResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + versionCheckPatch, + ) + + execute { context -> + val hideShortsAppShortcut by hideShortsAppShortcutOption + val hideShortsWidget by hideShortsWidgetOption + + addResources("youtube", "layout.hide.shorts.hideShortsComponentsResourcePatch") + + PreferenceScreen.SHORTS.addPreferences( + SwitchPreference("revanced_hide_shorts_home"), + SwitchPreference("revanced_hide_shorts_subscriptions"), + SwitchPreference("revanced_hide_shorts_search"), + + PreferenceScreenPreference( + key = "revanced_shorts_player_screen", + sorting = PreferenceScreenPreference.Sorting.UNSORTED, + preferences = setOf( + // Shorts player components. + // Ideally each group should be ordered similar to how they appear in the UI + + // Vertical row of buttons on right side of the screen. + SwitchPreference("revanced_hide_shorts_like_fountain"), + SwitchPreference("revanced_hide_shorts_like_button"), + SwitchPreference("revanced_hide_shorts_dislike_button"), + SwitchPreference("revanced_hide_shorts_comments_button"), + SwitchPreference("revanced_hide_shorts_share_button"), + SwitchPreference("revanced_hide_shorts_remix_button"), + SwitchPreference("revanced_hide_shorts_sound_button"), + + // Upper and middle area of the player. + SwitchPreference("revanced_hide_shorts_join_button"), + SwitchPreference("revanced_hide_shorts_subscribe_button"), + SwitchPreference("revanced_hide_shorts_paused_overlay_buttons"), + + // Suggested actions. + SwitchPreference("revanced_hide_shorts_save_sound_button"), + SwitchPreference("revanced_hide_shorts_use_template_button"), + SwitchPreference("revanced_hide_shorts_upcoming_button"), + SwitchPreference("revanced_hide_shorts_green_screen_button"), + SwitchPreference("revanced_hide_shorts_hashtag_button"), + SwitchPreference("revanced_hide_shorts_shop_button"), + SwitchPreference("revanced_hide_shorts_tagged_products"), + SwitchPreference("revanced_hide_shorts_search_suggestions"), + SwitchPreference("revanced_hide_shorts_super_thanks_button"), + SwitchPreference("revanced_hide_shorts_stickers"), + + // Bottom of the screen. + SwitchPreference("revanced_hide_shorts_location_label"), + SwitchPreference("revanced_hide_shorts_channel_bar"), + SwitchPreference("revanced_hide_shorts_info_panel"), + SwitchPreference("revanced_hide_shorts_full_video_link_label"), + SwitchPreference("revanced_hide_shorts_video_title"), + SwitchPreference("revanced_hide_shorts_sound_metadata_label"), + SwitchPreference("revanced_hide_shorts_navigation_bar"), + ), + ), + ) + + // Verify the file has the expected node, even if the patch option is off. + context.document["res/xml/main_shortcuts.xml"].use { document -> + val shortsItem = document.childNodes.findElementByAttributeValueOrThrow( + "android:shortcutId", + "shorts-shortcut", + ) + + if (hideShortsAppShortcut == true) { + shortsItem.parentNode.removeChild(shortsItem) + } + } + + context.document["res/layout/appwidget_two_rows.xml"].use { document -> + val shortsItem = document.childNodes.findElementByAttributeValueOrThrow( + "android:id", + "@id/button_shorts_container", + ) + + if (hideShortsWidget == true) { + shortsItem.parentNode.removeChild(shortsItem) + } + } + + reelPlayerRightCellButtonHeight = resourceMappings[ + "dimen", + "reel_player_right_cell_button_height", + ] + + bottomBarContainer = resourceMappings[ + "id", + "bottom_bar_container", + ] + + reelPlayerRightPivotV2Size = resourceMappings[ + "dimen", + "reel_player_right_pivot_v2_size", + ] + + if (!is_19_03_or_greater) { + reelMultipleItemShelfId = resourceMappings[ + "dimen", + "reel_player_right_cell_button_height", + ] + } + } +} + +private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/ShortsFilter;" + +@Suppress("unused") +val hideShortsComponentsPatch = bytecodePatch( + name = "Hide Shorts components", + description = "Adds options to hide components related to YouTube Shorts.", +) { + dependsOn( + sharedExtensionPatch, + lithoFilterPatch, + hideShortsComponentsResourcePatch, + resourceMappingPatch, + navigationBarHookPatch, + versionCheckPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + hideShortsAppShortcutOption() + hideShortsWidgetOption() + + val createShortsButtonsMatch by createShortsButtonsFingerprint() + val shortsBottomBarContainerMatch by shortsBottomBarContainerFingerprint() + val legacyRenderBottomNavigationBarParentMatch by legacyRenderBottomNavigationBarParentFingerprint() + val renderBottomNavigationBarParentMatch by renderBottomNavigationBarParentFingerprint() + val setPivotBarVisibilityParentMatch by setPivotBarVisibilityParentFingerprint() + reelConstructorFingerprint() + + execute { context -> + // region Hide the Shorts shelf. + + // This patch point is not present in 19.03.x and greater. + if (!is_19_03_or_greater) { + reelConstructorFingerprint.match?.let { + it.mutableMethod.apply { + val insertIndex = it.patternMatch!!.startIndex + 2 + val viewRegister = getInstruction(insertIndex).registerA + + injectHideViewCall( + insertIndex, + viewRegister, + FILTER_CLASS_DESCRIPTOR, + "hideShortsShelf", + ) + } + } + } + + // endregion + + // region Hide the Shorts buttons in older versions of YouTube. + + // Some Shorts buttons are views, hide them by setting their visibility to GONE. + ShortsButtons.entries.forEach { button -> button.injectHideCall(createShortsButtonsMatch.mutableMethod) } + + // endregion + + // region Hide the Shorts buttons in newer versions of YouTube. + + addLithoFilter(FILTER_CLASS_DESCRIPTOR) + + context.forEachLiteralValueInstruction( + reelPlayerRightPivotV2Size, + ) { literalInstructionIndex -> + val targetIndex = indexOfFirstInstructionOrThrow(literalInstructionIndex) { + getReference()?.name == "getDimensionPixelSize" + } + 1 + + val sizeRegister = getInstruction(targetIndex).registerA + + addInstructions( + targetIndex + 1, + """ + invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I + move-result v$sizeRegister + """, + ) + } + + // endregion + + // region Hide the navigation bar. + + // Hook to get the pivotBar view. + setPivotBarVisibilityFingerprint.applyMatch( + context, + setPivotBarVisibilityParentMatch, + ).let { result -> + result.mutableMethod.apply { + val insertIndex = result.patternMatch!!.endIndex + val viewRegister = getInstruction(insertIndex - 1).registerA + addInstruction( + insertIndex, + "invoke-static {v$viewRegister}," + + " $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V", + ) + } + } + + // Hook to hide the shared navigation bar when the Shorts player is opened. + renderBottomNavigationBarFingerprint.applyMatch( + context, + if (is_19_41_or_greater) { + renderBottomNavigationBarParentMatch + } else { + legacyRenderBottomNavigationBarParentMatch + }, + ).mutableMethod.addInstruction( + 0, + "invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V", + ) + + // Hide the bottom bar container of the Shorts player. + shortsBottomBarContainerMatch.mutableMethod.apply { + val resourceIndex = indexOfFirstWideLiteralInstructionValue(bottomBarContainer) + + val targetIndex = indexOfFirstInstructionOrThrow(resourceIndex) { + getReference()?.name == "getHeight" + } + 1 + + val heightRegister = getInstruction(targetIndex).registerA + + addInstructions( + targetIndex + 1, + """ + invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I + move-result v$heightRegister + """, + ) + } + + // endregion + } +} + +private enum class ShortsButtons(private val resourceName: String, private val methodName: String) { + LIKE("reel_dyn_like", "hideLikeButton"), + DISLIKE("reel_dyn_dislike", "hideDislikeButton"), + COMMENTS("reel_dyn_comment", "hideShortsCommentsButton"), + REMIX("reel_dyn_remix", "hideShortsRemixButton"), + SHARE("reel_dyn_share", "hideShortsShareButton"), + ; + + fun injectHideCall(method: MutableMethod) { + val referencedIndex = method.indexOfIdResourceOrThrow(resourceName) + + val setIdIndex = method.indexOfFirstInstructionOrThrow(referencedIndex) { + opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setId" + } + + val viewRegister = method.getInstruction(setIdIndex).registerC + + method.injectHideViewCall(setIdIndex + 1, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch.kt new file mode 100644 index 0000000000..436f3db2f5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/DisableSuggestedVideoEndScreenPatch.kt @@ -0,0 +1,79 @@ +package app.revanced.patches.youtube.layout.hide.suggestedvideoendscreen + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction + +internal var sizeAdjustableLiteAutoNavOverlay = -1L + private set + +internal val disableSuggestedVideoEndScreenResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + ) + + execute { + addResources("youtube", "layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_disable_suggested_video_end_screen"), + ) + + sizeAdjustableLiteAutoNavOverlay = resourceMappings[ + "layout", + "size_adjustable_lite_autonav_overlay", + ] + } +} + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/DisableSuggestedVideoEndScreenPatch;" + +@Suppress("unused") +val disableSuggestedVideoEndScreenPatch = bytecodePatch( + name = "Disable suggested video end screen", + description = "Adds an option to disable the suggested video end screen at the end of videos.", +) { + dependsOn( + sharedExtensionPatch, + disableSuggestedVideoEndScreenResourcePatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val createEndScreenViewMatch by createEndScreenViewFingerprint() + + execute { + createEndScreenViewMatch.mutableMethod.apply { + val addOnClickEventListenerIndex = createEndScreenViewMatch.patternMatch!!.endIndex - 1 + val viewRegister = getInstruction(addOnClickEventListenerIndex).registerC + + addInstruction( + addOnClickEventListenerIndex + 1, + "invoke-static {v$viewRegister}, " + + "$EXTENSION_CLASS_DESCRIPTOR->closeEndScreen(Landroid/widget/ImageView;)V", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/Fingerprints.kt new file mode 100644 index 0000000000..78efc0680c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/suggestedvideoendscreen/Fingerprints.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.youtube.layout.hide.suggestedvideoendscreen + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val createEndScreenViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/view/View;") + parameters("Landroid/content/Context;") + opcodes( + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST, + ) + literal { sizeAdjustableLiteAutoNavOverlay } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/Fingerprints.kt new file mode 100644 index 0000000000..d109c6455f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/Fingerprints.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.youtube.layout.hide.time + +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import app.revanced.patcher.fingerprint + +internal val timeCounterFingerprint = fingerprint( + fuzzyPatternScanThreshold = 1, +) { + returns("V") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() + opcodes( + Opcode.SUB_LONG_2ADDR, + Opcode.IGET_WIDE, + Opcode.SUB_LONG_2ADDR, + Opcode.IGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_WIDE, + Opcode.IGET_WIDE, + Opcode.SUB_LONG_2ADDR + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt new file mode 100644 index 0000000000..ae9604807f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/time/HideTimestampPatch.kt @@ -0,0 +1,54 @@ +package app.revanced.patches.youtube.layout.hide.time + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +@Suppress("unused") +val hideTimestampPatch = bytecodePatch( + name = "Hide timestamp", + description = "Adds an option to hide the timestamp in the bottom left of the video player.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val timeCounterMatch by timeCounterFingerprint() + + execute { + addResources("youtube", "layout.hide.time.hideTimestampPatch") + + PreferenceScreen.SEEKBAR.addPreferences( + SwitchPreference("revanced_hide_timestamp"), + ) + + timeCounterMatch.mutableMethod.addInstructionsWithLabels( + 0, + """ + invoke-static { }, Lapp/revanced/extension/youtube/patches/HideTimestampPatch;->hideTimestamp()Z + move-result v0 + if-eqz v0, :hide_time + return-void + :hide_time + nop + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt new file mode 100644 index 0000000000..28abd6cba9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt @@ -0,0 +1,152 @@ +package app.revanced.patches.youtube.layout.miniplayer + +import app.revanced.patcher.fingerprint +import app.revanced.util.containsWideLiteralInstructionValue +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val miniplayerDimensionsCalculatorParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L") + literal { floatyBarButtonTopMargin } +} + +/** + * Matches using the class found in [miniplayerModernViewParentFingerprint]. + */ +internal val miniplayerModernAddViewListenerFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Landroid/view/View;") +} + +/** + * Matches using the class found in [miniplayerModernViewParentFingerprint]. + */ + +internal val miniplayerModernCloseButtonFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/widget/ImageView;") + parameters() + literal { modernMiniplayerClose } +} + +const val MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL = 45622882L + +// In later targets this feature flag does nothing and is dead code. +const val MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY = 45630429L +const val DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL = 45628823L +const val DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL = 45628752L +const val INITIAL_SIZE_FEATURE_KEY_LITERAL = 45640023L +const val ANIMATION_INTERPOLATION_FEATURE_KEY = 45647018L +const val DROP_SHADOW_FEATURE_KEY = 45652223L +const val ROUNDED_CORNERS_FEATURE_KEY = 45652224L + +internal val miniplayerModernConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters("L") + literal { 45623000L } +} + +/** + * Matches using the class found in [miniplayerModernViewParentFingerprint]. + */ +internal val miniplayerModernExpandButtonFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/widget/ImageView;") + parameters() + literal { modernMiniplayerExpand } +} + +/** + * Matches using the class found in [miniplayerModernViewParentFingerprint]. + */ +internal val miniplayerModernExpandCloseDrawablesFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L") + literal { ytOutlinePictureInPictureWhite24 } +} + +/** + * Matches using the class found in [miniplayerModernViewParentFingerprint]. + */ +internal val miniplayerModernForwardButtonFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/widget/ImageView;") + parameters() + literal { modernMiniplayerForwardButton } +} + +/** + * Matches using the class found in [miniplayerModernViewParentFingerprint]. + */ +internal val miniplayerModernOverlayViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + literal { scrimOverlay } +} + +/** + * Matches using the class found in [miniplayerModernViewParentFingerprint]. + */ +internal val miniplayerModernRewindButtonFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/widget/ImageView;") + parameters() + literal { modernMiniplayerRewindButton } +} + +internal val miniplayerModernViewParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String;") + parameters() + strings("player_overlay_modern_mini_player_controls") +} + +internal val miniplayerMinimumSizeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + custom { method, _ -> + method.containsWideLiteralInstructionValue(192) && + method.containsWideLiteralInstructionValue(128) && + method.containsWideLiteralInstructionValue(miniplayerMaxSize) + } +} + +internal val miniplayerOverrideFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + strings("appName") +} + +internal val miniplayerOverrideNoContextFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("Z") + opcodes(Opcode.IGET_BOOLEAN) // Anchor to insert the instruction. +} + +internal val miniplayerResponseModelSizeCheckFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters("Ljava/lang/Object;", "Ljava/lang/Object;") + opcodes( + Opcode.RETURN_OBJECT, + Opcode.CHECK_CAST, + Opcode.CHECK_CAST, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + ) +} + +internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME = + "Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;" + +internal val playerOverlaysLayoutFingerprint = fingerprint { + custom { method, _ -> + method.definingClass == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt new file mode 100644 index 0000000000..111f93426e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt @@ -0,0 +1,560 @@ +package app.revanced.patches.youtube.layout.miniplayer + +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.* +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.* +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.* +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +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.TypeReference +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter + +var floatyBarButtonTopMargin = -1L + private set + +// Only available in 19.15 and upwards. +var ytOutlineXWhite24 = -1L + private set +var ytOutlinePictureInPictureWhite24 = -1L + private set +var scrimOverlay = -1L + private set +var modernMiniplayerClose = -1L + private set +var modernMiniplayerExpand = -1L + private set +var modernMiniplayerRewindButton = -1L + private set +var modernMiniplayerForwardButton = -1L + private set +var playerOverlays = -1L + private set +var miniplayerMaxSize = -1L + private set + +private val miniplayerResourcePatch = resourcePatch { + dependsOn( + resourceMappingPatch, + versionCheckPatch, + ) + + execute { + floatyBarButtonTopMargin = resourceMappings[ + "dimen", + "floaty_bar_button_top_margin", + ] + + scrimOverlay = resourceMappings[ + "id", + "scrim_overlay", + ] + + playerOverlays = resourceMappings[ + "layout", + "player_overlays", + ] + + if (is_19_16_or_greater) { + modernMiniplayerClose = resourceMappings[ + "id", + "modern_miniplayer_close", + ] + + modernMiniplayerExpand = resourceMappings[ + "id", + "modern_miniplayer_expand", + ] + + modernMiniplayerRewindButton = resourceMappings[ + "id", + "modern_miniplayer_rewind_button", + ] + + modernMiniplayerForwardButton = resourceMappings[ + "id", + "modern_miniplayer_forward_button", + ] + + // Resource id is not used during patching, but is used by extension. + // Verify the resource is present while patching. + resourceMappings[ + "id", + "modern_miniplayer_subtitle_text", + ] + + // Only required for exactly 19.16 + if (!is_19_17_or_greater) { + ytOutlinePictureInPictureWhite24 = resourceMappings[ + "drawable", + "yt_outline_picture_in_picture_white_24", + ] + + ytOutlineXWhite24 = resourceMappings[ + "drawable", + "yt_outline_x_white_24", + ] + } + + if (is_19_26_or_greater) { + miniplayerMaxSize = resourceMappings[ + "dimen", + "miniplayer_max_size", + ] + } + } + } +} + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/MiniplayerPatch;" + +@Suppress("unused") +val miniplayerPatch = bytecodePatch( + name = "Miniplayer", + description = "Adds options to change the in app minimized player. " + + "Patching target 19.16+ adds modern miniplayers.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + miniplayerResourcePatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + // 19.14.43 // Incomplete code for modern miniplayers. + // 19.15.36 // Different code for handling subtitle texts and not worth supporting. + "19.16.39", // First with modern miniplayers. + // 19.17.41 // Works without issues, but no reason to recommend over 19.16. + // 19.18.41 // Works without issues, but no reason to recommend over 19.16. + // 19.19.39 // Last bug free version with smaller Modern 1 miniplayer, but no reason to recommend over 19.16. + // 19.20.35 // Cannot swipe to expand. + // 19.21.40 // Cannot swipe to expand. + // 19.22.43 // Cannot swipe to expand. + // 19.23.40 // First with Modern 1 drag and drop, Cannot swipe to expand. + // 19.24.45 // First with larger Modern 1, Cannot swipe to expand. + "19.25.37", // First with double tap, last with skip forward/back buttons, last with swipe to expand/close, and last before double tap to expand seems to be required. + // 19.26.42 // Modern 1 Pause/play button are always hidden. Unusable. + // 19.28.42 // First with custom miniplayer size, screen flickers when swiping to maximize Modern 1. Swipe to close miniplayer is broken. + // 19.29.42 // All modern players are broken and ignore tapping the miniplayer video. + // 19.30.39 // Modern 3 is less broken when double tap expand is enabled, but cannot swipe to expand when double tap is off. + // 19.31.36 // All Modern 1 buttons are missing. Unusable. + // 19.32.36 // 19.32+ and beyond all work without issues. + // 19.33.35 + "19.34.42", + ), + ) + + val miniplayerDimensionsCalculatorParentMatch by miniplayerDimensionsCalculatorParentFingerprint() + val miniplayerResponseModelSizeCheckMatch by miniplayerResponseModelSizeCheckFingerprint() + val miniplayerOverrideMatch by miniplayerOverrideFingerprint() + val miniplayerModernConstructorMatch by miniplayerModernConstructorFingerprint() + val miniplayerModernViewParentMatch by miniplayerModernViewParentFingerprint() + val miniplayerMinimumSizeMatch by miniplayerMinimumSizeFingerprint() + val playerOverlaysLayoutMatch by playerOverlaysLayoutFingerprint() + + execute { context -> + addResources("youtube", "layout.miniplayer.miniplayerPatch") + + val preferences = mutableSetOf() + + if (!is_19_16_or_greater) { + preferences += ListPreference( + "revanced_miniplayer_type", + summaryKey = null, + entriesKey = "revanced_miniplayer_type_legacy_entries", + entryValuesKey = "revanced_miniplayer_type_legacy_entry_values", + ) + } else { + preferences += ListPreference( + "revanced_miniplayer_type", + summaryKey = null, + ) + + if (is_19_25_or_greater) { + if (!is_19_29_or_greater) { + preferences += SwitchPreference("revanced_miniplayer_double_tap_action") + } + preferences += SwitchPreference("revanced_miniplayer_drag_and_drop") + } + + if (is_19_36_or_greater) { + preferences += SwitchPreference("revanced_miniplayer_rounded_corners") + } + + preferences += SwitchPreference("revanced_miniplayer_hide_subtext") + + preferences += if (is_19_26_or_greater) { + SwitchPreference("revanced_miniplayer_hide_expand_close") + } else { + SwitchPreference( + key = "revanced_miniplayer_hide_expand_close", + titleKey = "revanced_miniplayer_hide_expand_close_legacy_title", + summaryOnKey = "revanced_miniplayer_hide_expand_close_legacy_summary_on", + summaryOffKey = "revanced_miniplayer_hide_expand_close_legacy_summary_off", + ) + } + + if (!is_19_26_or_greater) { + preferences += SwitchPreference("revanced_miniplayer_hide_rewind_forward") + } + + if (is_19_26_or_greater) { + preferences += TextPreference("revanced_miniplayer_width_dip", inputType = InputType.NUMBER) + } + + preferences += TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER) + } + + PreferenceScreen.PLAYER.addPreferences( + PreferenceScreenPreference( + key = "revanced_miniplayer_screen", + sorting = PreferenceScreenPreference.Sorting.UNSORTED, + preferences = preferences, + ), + ) + + fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) { + val register = getInstruction(index).registerA + addInstructions( + index, + """ + invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z + move-result v$register + """, + ) + } + + fun Method.findReturnIndicesReversed() = findOpcodeIndicesReversed(Opcode.RETURN) + + /** + * Adds an override to force legacy tablet miniplayer to be used or not used. + */ + fun MutableMethod.insertLegacyTabletMiniplayerOverride(index: Int) { + insertBooleanOverride(index, "getLegacyTabletMiniplayerOverride") + } + + /** + * Adds an override to force modern miniplayer to be used or not used. + */ + fun MutableMethod.insertModernMiniplayerOverride(index: Int) { + insertBooleanOverride(index, "getModernMiniplayerOverride") + } + + fun Match.insertLiteralValueBooleanOverride( + literal: Long, + extensionMethod: String, + ) { + mutableMethod.apply { + val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(literal) + val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) + + insertBooleanOverride(targetIndex + 1, extensionMethod) + } + } + + fun Match.insertLiteralValueFloatOverride( + literal: Long, + extensionMethod: String, + ) { + mutableMethod.apply { + val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(literal) + val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT) + val register = getInstruction(targetIndex).registerA + + addInstructions( + targetIndex + 1, + """ + invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F + move-result v$register + """, + ) + } + } + + /** + * Adds an override to specify which modern miniplayer is used. + */ + fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) { + val targetInstruction = getInstruction(iPutIndex) + + addInstructionsAtControlFlowLabel( + iPutIndex, + """ + invoke-static { v${targetInstruction.registerA} }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I + move-result v${targetInstruction.registerA} + """, + ) + } + + fun MutableMethod.hookInflatedView( + literalValue: Long, + hookedClassType: String, + extensionMethodName: String, + ) { + val imageViewIndex = indexOfFirstInstructionOrThrow( + indexOfFirstWideLiteralInstructionValueOrThrow(literalValue), + ) { + opcode == Opcode.CHECK_CAST && getReference()?.type == hookedClassType + } + + val register = getInstruction(imageViewIndex).registerA + addInstruction( + imageViewIndex + 1, + "invoke-static { v$register }, $extensionMethodName", + ) + } + + // region Enable tablet miniplayer. + + miniplayerOverrideNoContextFingerprint.applyMatch( + context, + miniplayerDimensionsCalculatorParentMatch, + ).mutableMethod.apply { + findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) } + } + + // endregion + + // region Legacy tablet miniplayer hooks. + + val appNameStringIndex = miniplayerOverrideMatch.stringMatches!!.first().index + 2 + context.navigate(miniplayerOverrideMatch.mutableMethod).at(appNameStringIndex).mutable().apply { + findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) } + } + + miniplayerResponseModelSizeCheckMatch.let { + it.mutableMethod.insertLegacyTabletMiniplayerOverride(it.patternMatch!!.endIndex) + } + + if (!is_19_16_or_greater) { + // Return here, as patch below is only for the current versions of the app. + return@execute + } + + // endregion + + // region Enable modern miniplayer. + + miniplayerModernConstructorMatch.mutableClass.methods.forEach { + it.apply { + if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { + val iPutIndex = indexOfFirstInstructionOrThrow { + this.opcode == Opcode.IPUT && this.getReference()?.type == "I" + } + + insertModernMiniplayerTypeOverride(iPutIndex) + } else { + findReturnIndicesReversed().forEach { index -> insertModernMiniplayerOverride(index) } + } + } + } + + if (is_19_23_or_greater) { + miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride( + DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL, + "enableMiniplayerDragAndDrop", + ) + } + + if (is_19_25_or_greater) { + miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride( + MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY, + "getModernMiniplayerOverride", + ) + + miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride( + MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL, + "getModernFeatureFlagsActiveOverride", + ) + + miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride( + DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL, + "enableMiniplayerDoubleTapAction", + ) + } + + if (is_19_26_or_greater) { + miniplayerModernConstructorMatch.mutableMethod.apply { + val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow( + INITIAL_SIZE_FEATURE_KEY_LITERAL, + ) + val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT) + + val register = getInstruction(targetIndex).registerA + + addInstructions( + targetIndex + 1, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setMiniplayerDefaultSize(I)I + move-result v$register + """, + ) + } + + // Override a mininimum miniplayer size constant. + miniplayerMinimumSizeMatch.mutableMethod.apply { + val index = indexOfFirstInstructionOrThrow { + opcode == Opcode.CONST_16 && (this as NarrowLiteralInstruction).narrowLiteral == 192 + } + val register = getInstruction(index).registerA + + // Smaller sizes can be used, but the miniplayer will always start in size 170 if set any smaller. + // The 170 initial limit probably could be patched to allow even smaller initial sizes, + // but 170 is already half the horizontal space and smaller does not seem useful. + replaceInstruction(index, "const/16 v$register, 170") + } + } + + if (is_19_32_or_greater) { + // Feature is not exposed in the settings, and currently only for debugging. + miniplayerModernConstructorMatch.insertLiteralValueFloatOverride( + ANIMATION_INTERPOLATION_FEATURE_KEY, + "setMovementBoundFactor", + ) + } + + if (is_19_36_or_greater) { + miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride( + DROP_SHADOW_FEATURE_KEY, + "setDropShadow", + ) + + miniplayerModernConstructorMatch.insertLiteralValueBooleanOverride( + ROUNDED_CORNERS_FEATURE_KEY, + "setRoundedCorners", + ) + } + + // endregion + + // region Fix 19.16 using mixed up drawables for tablet modern. + // YT fixed this mistake in 19.17. + // Fix this, by swapping the drawable resource values with each other. + if (ytOutlinePictureInPictureWhite24 >= 0) { + miniplayerModernExpandCloseDrawablesFingerprint.applyMatch( + context, + miniplayerModernViewParentMatch, + ).mutableMethod.apply { + listOf( + ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24, + ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24, + ).forEach { (originalResource, replacementResource) -> + val imageResourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(originalResource) + val register = getInstruction(imageResourceIndex).registerA + + replaceInstruction(imageResourceIndex, "const v$register, $replacementResource") + } + } + } + + // endregion + + // region Add hooks to hide modern miniplayer buttons. + + listOf( + Triple( + miniplayerModernExpandButtonFingerprint, + modernMiniplayerExpand, + "hideMiniplayerExpandClose", + ), + Triple( + miniplayerModernCloseButtonFingerprint, + modernMiniplayerClose, + "hideMiniplayerExpandClose", + ), + Triple( + miniplayerModernRewindButtonFingerprint, + modernMiniplayerRewindButton, + "hideMiniplayerRewindForward", + ), + Triple( + miniplayerModernForwardButtonFingerprint, + modernMiniplayerForwardButton, + "hideMiniplayerRewindForward", + ), + Triple( + miniplayerModernOverlayViewFingerprint, + scrimOverlay, + "adjustMiniplayerOpacity", + ), + ).forEach { (fingerprint, literalValue, methodName) -> + fingerprint.applyMatch( + context, + miniplayerModernViewParentMatch, + ).mutableMethod.hookInflatedView( + literalValue, + "Landroid/widget/ImageView;", + "$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V", + ) + } + + miniplayerModernAddViewListenerFingerprint.applyMatch( + context, + miniplayerModernViewParentMatch, + ).mutableMethod.addInstruction( + 0, + "invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->" + + "hideMiniplayerSubTexts(Landroid/view/View;)V", + ) + + // Modern 2 has a broken overlay subtitle view that is always present. + // Modern 2 uses the same overlay controls as the regular video player, + // and the overlay views are added at runtime. + // Add a hook to the overlay class, and pass the added views to extension. + // + // NOTE: Modern 2 uses the same video UI as the regular player except resized to smaller. + // This patch code could be used to hide other player overlays that do not use Litho. + playerOverlaysLayoutMatch.mutableClass.methods.add( + ImmutableMethod( + YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME, + "addView", + listOf( + ImmutableMethodParameter("Landroid/view/View;", null, null), + ImmutableMethodParameter("I", null, null), + ImmutableMethodParameter("Landroid/view/ViewGroup\$LayoutParams;", null, null), + ), + "V", + AccessFlags.PUBLIC.value, + null, + null, + MutableMethodImplementation(4), + ).toMutable().apply { + addInstructions( + """ + invoke-super { p0, p1, p2, p3 }, Landroid/view/ViewGroup;->addView(Landroid/view/View;ILandroid/view/ViewGroup${'$'}LayoutParams;)V + invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->playerOverlayGroupCreated(Landroid/view/View;)V + return-void + """, + ) + }, + ) + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/Fingerprints.kt new file mode 100644 index 0000000000..0c31cc83bd --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/Fingerprints.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.layout.panels.popup + +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val engagementPanelControllerFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("L") + strings( + "EngagementPanelController: cannot show EngagementPanel before EngagementPanelController.init() has been called.", + "[EngagementPanel] Cannot show EngagementPanel before EngagementPanelController.init() has been called.", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt new file mode 100644 index 0000000000..3914d53e2a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt @@ -0,0 +1,56 @@ +package app.revanced.patches.youtube.layout.panels.popup + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +@Suppress("unused") +val playerPopupPanelsPatch = bytecodePatch( + name = "Disable player popup panels", + description = "Adds an option to disable panels (such as live chat) from opening automatically.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val engagementPanelControllerMatch by engagementPanelControllerFingerprint() + + execute { + addResources("youtube", "layout.panels.popup.playerPopupPanelsPatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_hide_player_popup_panels"), + ) + + engagementPanelControllerMatch.mutableMethod.addInstructionsWithLabels( + 0, + """ + invoke-static { }, Lapp/revanced/extension/youtube/patches/DisablePlayerPopupPanelsPatch;->disablePlayerPopupPanels()Z + move-result v0 + if-eqz v0, :player_popup_panels + if-eqz p4, :player_popup_panels + const/4 v0, 0x0 + return-object v0 + :player_popup_panels + nop + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch.kt new file mode 100644 index 0000000000..433cce068b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/background/PlayerControlsBackgroundPatch.kt @@ -0,0 +1,35 @@ +package app.revanced.patches.youtube.layout.player.background + +import app.revanced.patcher.patch.resourcePatch +import app.revanced.util.doRecursively +import org.w3c.dom.Element + +@Suppress("unused") +val playerControlsBackgroundPatch = resourcePatch( + name = "Remove player controls background", + description = "Removes the dark background surrounding the video player controls.", + use = false, +) { + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + execute { context -> + context.document["res/drawable/player_button_circle_background.xml"].use { document -> + + document.doRecursively node@{ node -> + if (node !is Element) return@node + + node.getAttributeNode("android:color")?.let { attribute -> + attribute.textContent = "@android:color/transparent" + } + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt new file mode 100644 index 0000000000..998bd61292 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt @@ -0,0 +1,71 @@ +package app.revanced.patches.youtube.layout.player.overlay + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +internal var scrimOverlayId = -1L + private set + +private val customPlayerOverlayOpacityResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + ) + + execute { + addResources("youtube", "layout.player.overlay.customPlayerOverlayOpacityResourcePatch") + + PreferenceScreen.PLAYER.addPreferences( + TextPreference("revanced_player_overlay_opacity", inputType = InputType.NUMBER), + ) + + scrimOverlayId = resourceMappings[ + "id", + "scrim_overlay", + ] + } +} + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/CustomPlayerOverlayOpacityPatch;" + +@Suppress("unused") +val customPlayerOverlayOpacityPatch = bytecodePatch( + name = "Custom player overlay opacity", + description = "Adds an option to change the opacity of the video player background when player controls are visible.", +) { + dependsOn(customPlayerOverlayOpacityResourcePatch) + + compatibleWith("com.google.android.youtube") + + val createPlayerOverviewMatch by createPlayerOverviewFingerprint() + + execute { + createPlayerOverviewMatch.mutableMethod.apply { + val viewRegisterIndex = + indexOfFirstWideLiteralInstructionValueOrThrow(scrimOverlayId) + 3 + val viewRegister = + getInstruction(viewRegisterIndex).registerA + + val insertIndex = viewRegisterIndex + 1 + addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->changeOpacity(Landroid/widget/ImageView;)V", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/Fingerprints.kt new file mode 100644 index 0000000000..abe87d8cf8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/Fingerprints.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.youtube.layout.player.overlay + +import app.revanced.patcher.fingerprint +import app.revanced.util.containsWideLiteralInstructionValue +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val createPlayerOverviewFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + opcodes( + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + ) + custom { method, _ -> + method.containsWideLiteralInstructionValue(scrimOverlayId) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/Fingerprints.kt new file mode 100644 index 0000000000..a56b1d9dda --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/Fingerprints.kt @@ -0,0 +1,155 @@ +package app.revanced.patches.youtube.layout.returnyoutubedislike + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val conversionContextFingerprint = fingerprint { + returns("Ljava/lang/String;") + parameters() + strings( + ", widthConstraint=", + ", heightConstraint=", + ", templateLoggerFactory=", + ", rootDisposableContainer=", + "ConversionContext{containerInternal=", + ) +} + +internal val dislikeFingerprint = fingerprint { + returns("V") + strings("like/dislike") +} + +internal val dislikesOldLayoutTextViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L") + opcodes( + Opcode.CONST, // resource identifier register + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, // textview register + Opcode.GOTO, + ) + literal { oldUIDislikeId } +} + +internal val likeFingerprint = fingerprint { + returns("V") + strings("like/like") +} + +internal val removeLikeFingerprint = fingerprint { + returns("V") + strings("like/removelike") +} + +internal val rollingNumberMeasureAnimatedTextFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Lj\$/util/Optional;") + parameters("L", "Ljava/lang/String;", "L") + opcodes( + Opcode.IGET, // First instruction of method + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.CONST_HIGH16, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.AGET, + Opcode.CONST_4, + Opcode.CONST_4, // Measured text width + ) +} + +/** + * Matches to class found in [rollingNumberMeasureStaticLabelParentFingerprint]. + */ +internal val rollingNumberMeasureStaticLabelFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("F") + parameters("Ljava/lang/String;") + opcodes( + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.RETURN, + ) +} + +internal val rollingNumberMeasureStaticLabelParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String;") + parameters() + strings("RollingNumberFontProperties{paint=") +} + +internal val rollingNumberSetterFingerprint = fingerprint { + opcodes( + Opcode.INVOKE_DIRECT, + Opcode.IGET_OBJECT, + ) + // Partial string match. + strings("RollingNumberType required properties missing! Need") +} + +internal val rollingNumberTextViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "F", "F") + opcodes( + Opcode.IPUT, + null, // invoke-direct or invoke-virtual + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_VOID, + ) + custom { _, classDef -> + classDef.superclass == + "Landroid/support/v7/widget/AppCompatTextView;" || + classDef.superclass == + "Lcom/google/android/libraries/youtube/rendering/ui/spec/typography/YouTubeAppCompatTextView;" + } +} + +internal val shortsTextViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "L") + opcodes( + Opcode.INVOKE_SUPER, // first instruction of method + Opcode.IF_NEZ, + null, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + ) +} + +internal val textComponentConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.CONSTRUCTOR, AccessFlags.PRIVATE) + strings("TextComponent") +} + +internal val textComponentDataFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters("L", "L") + strings("text") + custom { _, classDef -> + classDef.fields.find { it.type == "Ljava/util/BitSet;" } != null + } +} + +/** + * Matches against the same class found in [textComponentConstructorFingerprint]. + */ +internal val textComponentLookupFingerprint = fingerprint { + accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) + returns("L") + parameters("L") + strings("…") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt new file mode 100644 index 0000000000..20f47e9583 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt @@ -0,0 +1,389 @@ +package app.revanced.patches.youtube.layout.returnyoutubedislike + +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.IntentPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.settings.addSettingPreference +import app.revanced.patches.youtube.misc.settings.newIntent +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateFingerprint +import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId +import app.revanced.patches.youtube.video.videoid.hookVideoId +import app.revanced.patches.youtube.video.videoid.videoIdPatch +import app.revanced.util.* +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.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.iface.reference.TypeReference +import com.sun.org.apache.bcel.internal.generic.InstructionConst.getInstruction + +internal var oldUIDislikeId = -1L + private set + +private val returnYouTubeDislikeResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + addResourcesPatch, + ) + + execute { + addResources("youtube", "layout.returnyoutubedislike.returnYouTubeDislikeResourcePatch") + + addSettingPreference( + IntentPreference( + key = "revanced_settings_screen_09", + titleKey = "revanced_ryd_settings_title", + summaryKey = null, + intent = newIntent("revanced_ryd_settings_intent"), + ), + ) + + oldUIDislikeId = resourceMappings[ + "id", + "dislike_button", + ] + } +} + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch;" + +private const val FILTER_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilterPatch;" + +@Suppress("unused") +val returnYouTubeDislikePatch = bytecodePatch( + name = "Return YouTube Dislike", + description = "Adds an option to show the dislike count of videos with Return YouTube Dislike.", +) { + dependsOn( + sharedExtensionPatch, + lithoFilterPatch, + videoIdPatch, + returnYouTubeDislikeResourcePatch, + playerTypeHookPatch, + versionCheckPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val conversionContextMatch by conversionContextFingerprint() + val textComponentConstructorMatch by textComponentConstructorFingerprint() + val textComponentDataMatch by textComponentDataFingerprint() + val shortsTextViewMatch by shortsTextViewFingerprint() + val dislikesOldLayoutTextViewMatch by dislikesOldLayoutTextViewFingerprint() + val likeMatch by likeFingerprint() + val dislikeMatch by dislikeFingerprint() + val removeLikeMatch by removeLikeFingerprint() + val rollingNumberSetterMatch by rollingNumberSetterFingerprint() + val rollingNumberMeasureStaticLabelParentMatch by rollingNumberMeasureStaticLabelParentFingerprint() + val rollingNumberMeasureAnimatedTextMatch by rollingNumberMeasureAnimatedTextFingerprint() + val rollingNumberTextViewMatch by rollingNumberTextViewFingerprint() + val rollingNumberTextViewAnimationUpdateMatch by rollingNumberTextViewAnimationUpdateFingerprint() + + execute { context -> + // region Inject newVideoLoaded event handler to update dislikes when a new video is loaded. + + hookVideoId("$EXTENSION_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V") + + // Hook the player response video id, to start loading RYD sooner in the background. + hookPlayerResponseVideoId("$EXTENSION_CLASS_DESCRIPTOR->preloadVideoId(Ljava/lang/String;Z)V") + + // endregion + + // region Hook like/dislike/remove like button clicks to send votes to the API. + + data class VotePatch(val fingerprint: Match, val voteKind: Vote) + + listOf( + VotePatch(likeMatch, Vote.LIKE), + VotePatch(dislikeMatch, Vote.DISLIKE), + VotePatch(removeLikeMatch, Vote.REMOVE_LIKE), + ).forEach { (match, vote) -> + match.mutableMethod.addInstructions( + 0, + """ + const/4 v0, ${vote.value} + invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->sendVote(I)V + """, + ) + } + + // endregion + + // region Hook code for creation and cached lookup of text Spans. + + // Alternatively the hook can be made at tht it fails to update the Span when the user dislikes, + // // since the underlying (likes only) tee creation of Spans in TextComponentSpec, + // And it works in all situations excepxt did not change. + // This hook handles all situations, as it's where the created Spans are stored and later reused. + // Find the field name of the conversion context. + val conversionContextField = textComponentConstructorMatch.classDef.fields.find { + it.type == conversionContextMatch.classDef.type + } ?: throw PatchException("Could not find conversion context field") + + textComponentLookupFingerprint.applyMatch(context, textComponentConstructorMatch) + textComponentLookupFingerprint.matchOrThrow.mutableMethod.apply { + // Find the instruction for creating the text data object. + val textDataClassType = textComponentDataMatch.classDef.type + + val insertIndex: Int + val tempRegister: Int + val charSequenceRegister: Int + + if (is_19_33_or_greater) { + insertIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_STATIC_RANGE && + getReference()?.returnType == textDataClassType + } + + tempRegister = getInstruction(insertIndex + 1).registerA + + // Find the instruction that sets the span to an instance field. + // The instruction is only a few lines after the creation of the instance. + charSequenceRegister = getInstruction( + indexOfFirstInstructionOrThrow(insertIndex) { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.parameterTypes?.firstOrNull() == "Ljava/lang/CharSequence;" + }, + ).registerD + } else { + insertIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.NEW_INSTANCE && + getReference()?.type == textDataClassType + } + + tempRegister = getInstruction(insertIndex).registerA + + charSequenceRegister = getInstruction( + indexOfFirstInstructionOrThrow(insertIndex) { + opcode == Opcode.IPUT_OBJECT && + getReference()?.type == "Ljava/lang/CharSequence;" + }, + ).registerA + } + + addInstructionsAtControlFlowLabel( + insertIndex, + """ + # Copy conversion context + move-object/from16 v$tempRegister, p0 + iget-object v$tempRegister, v$tempRegister, $conversionContextField + invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence; + move-result-object v$charSequenceRegister + """, + ) + } + + // endregion + + // region Hook for non-litho Short videos. + + shortsTextViewMatch.mutableMethod.apply { + val insertIndex = shortsTextViewMatch.patternMatch!!.endIndex + 1 + + // If the field is true, the TextView is for a dislike button. + val isDisLikesBooleanInstruction = instructions.first { instruction -> + instruction.opcode == Opcode.IGET_BOOLEAN + } as ReferenceInstruction + + val isDisLikesBooleanReference = isDisLikesBooleanInstruction.reference + + // Like/Dislike button TextView field. + val textViewFieldInstruction = instructions.first { instruction -> + instruction.opcode == Opcode.IGET_OBJECT + } as ReferenceInstruction + + val textViewFieldReference = textViewFieldInstruction.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. + 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}, $EXTENSION_CLASS_DESCRIPTOR->setShortsDislikes(Landroid/view/View;)Z + move-result v0 + if-eqz v0, :ryd_disabled + return-void + + :is_like + :ryd_disabled + nop + """, + ) + } + + // endregion + + // region Hook for litho Shorts + + // Filter that parses the video id from the UI + addLithoFilter(FILTER_CLASS_DESCRIPTOR) + + // Player response video id is needed to search for the video ids in Shorts litho components. + 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. + + dislikesOldLayoutTextViewMatch.mutableMethod.apply { + val startIndex = dislikesOldLayoutTextViewMatch.patternMatch!!.startIndex + + val resourceIdentifierRegister = getInstruction(startIndex).registerA + val textViewRegister = getInstruction(startIndex + 4).registerA + + addInstruction( + startIndex + 4, + "invoke-static {v$resourceIdentifierRegister, v$textViewRegister}, " + + "$EXTENSION_CLASS_DESCRIPTOR->setOldUILayoutDislikes(ILandroid/widget/TextView;)V", + ) + } + + // endregion + + // 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 match and throw an exception, + // but everything will still work correctly anyway. + + val dislikesIndex = rollingNumberSetterMatch.patternMatch!!.endIndex + + rollingNumberSetterMatch.mutableMethod.apply { + val insertIndex = 1 + + val charSequenceInstanceRegister = + getInstruction(0).registerA + val charSequenceFieldReference = + getInstruction(dislikesIndex).reference + + val registerCount = implementation!!.registerCount + + // This register is being overwritten, so it is free to use. + val freeRegister = registerCount - 1 + val conversionContextRegister = registerCount - parameters.size + 1 + + addInstructions( + insertIndex, + """ + iget-object v$freeRegister, v$charSequenceInstanceRegister, $charSequenceFieldReference + invoke-static {v$conversionContextRegister, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->onRollingNumberLoaded(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String; + move-result-object v$freeRegister + iput-object v$freeRegister, v$charSequenceInstanceRegister, $charSequenceFieldReference + """, + ) + } + + // Rolling Number text views use the measured width of the raw string for layout. + // Modify the measure text calculation to include the left drawable separator if needed. + val patternMatch = rollingNumberMeasureAnimatedTextMatch.patternMatch!! + // Additional check to verify the opcodes are at the start of the method + if (patternMatch.startIndex != 0) throw PatchException("Unexpected opcode location") + val endIndex = patternMatch.endIndex + rollingNumberMeasureAnimatedTextMatch.mutableMethod.apply { + val measuredTextWidthRegister = getInstruction(endIndex).registerA + + addInstructions( + endIndex + 1, + """ + invoke-static {p1, v$measuredTextWidthRegister}, $EXTENSION_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F + move-result v$measuredTextWidthRegister + """, + ) + } + + // Additional text measurement method. Used if YouTube decides not to animate the likes count + // and sometimes used for initial video load. + rollingNumberMeasureStaticLabelFingerprint.applyMatch( + context, + rollingNumberMeasureStaticLabelParentMatch, + ).let { + val measureTextIndex = it.patternMatch!!.startIndex + 1 + it.mutableMethod.apply { + val freeRegister = getInstruction(0).registerA + + addInstructions( + measureTextIndex + 1, + """ + move-result v$freeRegister + invoke-static {p1, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F + """, + ) + } + } + // The rolling number Span is missing styling since it's initially set as a String. + // Modify the UI text view and use the styled like/dislike Span. + // Initial TextView is set in this method. + val initiallyCreatedTextViewMethod = rollingNumberTextViewMatch.mutableMethod + + // Videos less than 24 hours after uploaded, like counts will be updated in real time. + // Whenever like counts are updated, TextView is set in this method. + arrayOf( + initiallyCreatedTextViewMethod, + rollingNumberTextViewAnimationUpdateMatch.mutableMethod, + ).forEach { insertMethod -> + insertMethod.apply { + val setTextIndex = indexOfFirstInstructionOrThrow { + getReference()?.name == "setText" + } + + val textViewRegister = + getInstruction(setTextIndex).registerC + val textSpanRegister = + getInstruction(setTextIndex).registerD + + addInstructions( + setTextIndex, + """ + invoke-static {v$textViewRegister, v$textSpanRegister}, $EXTENSION_CLASS_DESCRIPTOR->updateRollingNumber(Landroid/widget/TextView;Ljava/lang/CharSequence;)Ljava/lang/CharSequence; + move-result-object v$textSpanRegister + """, + ) + } + } + + // endregion + } +} + +enum class Vote(val value: Int) { + LIKE(1), + DISLIKE(-1), + REMOVE_LIKE(0), +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt new file mode 100644 index 0000000000..69fc26daeb --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.youtube.layout.searchbar + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val createSearchSuggestionsFingerprint = fingerprint { + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + ) + strings("ss_rds") +} + +internal val setWordmarkHeaderFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Landroid/widget/ImageView;") + opcodes( + Opcode.IGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.IGET_BOOLEAN, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.CONST, + null, // invoke-static or invoke-virtual. + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt new file mode 100644 index 0000000000..7f39f9bb52 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt @@ -0,0 +1,84 @@ +package app.revanced.patches.youtube.layout.searchbar + +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/WideSearchbarPatch;" + +@Suppress("unused") +val wideSearchbarPatch = bytecodePatch( + name = "Wide searchbar", + description = "Adds an option to replace the search icon with a wide search bar. This will hide the YouTube logo when active.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val setWordmarkHeaderMatch by setWordmarkHeaderFingerprint() + val createSearchSuggestionsMatch by createSearchSuggestionsFingerprint() + + execute { context -> + addResources("youtube", "layout.searchbar.wideSearchbarPatch") + + PreferenceScreen.FEED.addPreferences( + SwitchPreference("revanced_wide_searchbar"), + ) + + /** + * Navigate a fingerprints method at a given index mutably. + * + * @param index The index to navigate to. + * @param fromMatch The fingerprint match to navigate the method on. + * @return The [MutableMethod] which was navigated on. + */ + fun BytecodePatchContext.walkMutable(index: Int, fromMatch: Match) = + navigate(fromMatch.method).at(index).mutable() + + /** + * Injects instructions required for certain methods. + */ + fun MutableMethod.injectSearchBarHook() { + val insertIndex = implementation!!.instructions.size - 1 + val insertRegister = getInstruction(insertIndex).registerA + + addInstructions( + insertIndex, + """ + invoke-static {v$insertRegister}, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z + move-result v$insertRegister + """, + ) + } + + mapOf( + setWordmarkHeaderMatch to 1, + createSearchSuggestionsMatch to createSearchSuggestionsMatch.patternMatch!!.startIndex, + ).forEach { (fingerprint, callIndex) -> + context.walkMutable(callIndex, fingerprint).injectSearchBarHook() + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt new file mode 100644 index 0000000000..35fafcfe10 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt @@ -0,0 +1,50 @@ +package app.revanced.patches.youtube.layout.seekbar + +import app.revanced.patcher.fingerprint +import app.revanced.util.containsWideLiteralInstructionValue +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val fullscreenSeekbarThumbnailsFingerprint = fingerprint { + returns("Z") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() + literal { 45398577 } +} + +internal val playerSeekbarColorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + custom { method, _ -> + method.containsWideLiteralInstructionValue(inlineTimeBarColorizedBarPlayedColorDarkId) && + method.containsWideLiteralInstructionValue(inlineTimeBarPlayedNotHighlightedColorId) + } +} + +internal val setSeekbarClickedColorFingerprint = fingerprint { + opcodes(Opcode.CONST_HIGH16) + strings("YOUTUBE", "PREROLL", "POSTROLL") + custom { _, classDef -> + classDef.endsWith("ControlsOverlayStyle;") + } +} + +internal val shortsSeekbarColorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + literal { reelTimeBarPlayedColorId } +} + +const val PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG = 45617850L + +internal val playerSeekbarGradientConfigFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters() + literal { PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG } +} + +internal val lithoLinearGradientFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC) + returns("Landroid/graphics/LinearGradient;") + parameters("F", "F", "F", "F", "[I", "[F") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt new file mode 100644 index 0000000000..a21927289a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/RestoreOldSeekbarThumbnailsPatch.kt @@ -0,0 +1,61 @@ +package app.revanced.patches.youtube.layout.seekbar + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/RestoreOldSeekbarThumbnailsPatch;" + +@Suppress("unused") +val restoreOldSeekbarThumbnailsPatch = bytecodePatch( + name = "Restore old seekbar thumbnails", + description = "Adds an option to restore the old seekbar thumbnails that appear above the seekbar while seeking instead of fullscreen thumbnails.", +) { + dependsOn( + sharedExtensionPatch, + addResourcesPatch, + versionCheckPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + // 19.17+ is not supported. + ), + ) + + val fullscreenSeekbarThumbnailsMatch by fullscreenSeekbarThumbnailsFingerprint() + + execute { + if (is_19_17_or_greater) { + // Give a more informative error, if the user has turned off version checks. + throw PatchException("'Restore old seekbar thumbnails' cannot be patched to any version after 19.16.39") + } + + addResources("youtube", "layout.seekbar.restoreOldSeekbarThumbnailsPatch") + + PreferenceScreen.SEEKBAR.addPreferences( + SwitchPreference("revanced_restore_old_seekbar_thumbnails"), + ) + + fullscreenSeekbarThumbnailsMatch.mutableMethod.apply { + val moveResultIndex = instructions.lastIndex - 1 + + addInstruction( + moveResultIndex, + "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->useFullscreenSeekbarThumbnails()Z", + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt new file mode 100644 index 0000000000..8b114bf3c2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -0,0 +1,151 @@ +package app.revanced.patches.youtube.layout.seekbar + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.youtube.layout.theme.lithoColorHookPatch +import app.revanced.patches.youtube.layout.theme.lithoColorOverrideHook +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_19_23_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.sun.org.apache.bcel.internal.generic.InstructionConst.getInstruction +import org.w3c.dom.Element + +internal var reelTimeBarPlayedColorId = -1L + private set +internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L + private set +internal var inlineTimeBarPlayedNotHighlightedColorId = -1L + private set + +private val seekbarColorResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + versionCheckPatch, + ) + + execute { context -> + reelTimeBarPlayedColorId = resourceMappings[ + "color", + "reel_time_bar_played_color", + ] + inlineTimeBarColorizedBarPlayedColorDarkId = resourceMappings[ + "color", + "inline_time_bar_colorized_bar_played_color_dark", + ] + inlineTimeBarPlayedNotHighlightedColorId = resourceMappings[ + "color", + "inline_time_bar_played_not_highlighted_color", + ] + + // Edit the resume playback drawable and replace the progress bar with a custom drawable + context.document["res/drawable/resume_playback_progressbar_drawable.xml"].use { document -> + + val layerList = document.getElementsByTagName("layer-list").item(0) as Element + val progressNode = layerList.getElementsByTagName("item").item(1) as Element + if (!progressNode.getAttributeNode("android:id").value.endsWith("progress")) { + throw PatchException("Could not find progress bar") + } + val scaleNode = progressNode.getElementsByTagName("scale").item(0) as Element + val shapeNode = scaleNode.getElementsByTagName("shape").item(0) as Element + val replacementNode = document.createElement( + "app.revanced.extension.youtube.patches.theme.ProgressBarDrawable", + ) + scaleNode.replaceChild(replacementNode, shapeNode) + } + } +} + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/theme/SeekbarColorPatch;" + +val seekbarColorPatch = bytecodePatch( + description = "Hide or set a custom seekbar color", +) { + dependsOn( + sharedExtensionPatch, + lithoColorHookPatch, + seekbarColorResourcePatch, + ) + + val playerSeekbarColorMatch by playerSeekbarColorFingerprint() + val shortsSeekbarColorMatch by shortsSeekbarColorFingerprint() + val setSeekbarClickedColorMatch by setSeekbarClickedColorFingerprint() + val playerSeekbarGradientConfigMatch by playerSeekbarGradientConfigFingerprint() + val lithoLinearGradientMatch by lithoLinearGradientFingerprint() + + execute { context -> + fun MutableMethod.addColorChangeInstructions(resourceId: Long) { + val registerIndex = indexOfFirstWideLiteralInstructionValueOrThrow(resourceId) + 2 + val colorRegister = getInstruction(registerIndex).registerA + addInstructions( + registerIndex + 1, + """ + invoke-static { v$colorRegister }, $EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I + move-result v$colorRegister + """, + ) + } + + playerSeekbarColorMatch.mutableMethod.apply { + addColorChangeInstructions(inlineTimeBarColorizedBarPlayedColorDarkId) + addColorChangeInstructions(inlineTimeBarPlayedNotHighlightedColorId) + } + + shortsSeekbarColorMatch.mutableMethod.apply { + addColorChangeInstructions(reelTimeBarPlayedColorId) + } + + setSeekbarClickedColorMatch.mutableMethod.let { + val setColorMethodIndex = setSeekbarClickedColorMatch.patternMatch!!.startIndex + 1 + val method = context.navigate(it).at(setColorMethodIndex).mutable() + + method.apply { + val colorRegister = getInstruction(0).registerA + addInstructions( + 0, + """ + invoke-static { v$colorRegister }, $EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarClickedColor(I)I + move-result v$colorRegister + """, + ) + } + } + + if (is_19_23_or_greater) { + playerSeekbarGradientConfigMatch.mutableMethod.apply { + val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG) + val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) + val register = getInstruction(resultIndex).registerA + + addInstructions( + resultIndex + 1, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z + move-result v$register + """, + ) + } + + lithoLinearGradientMatch.mutableMethod.addInstruction( + 0, + "invoke-static/range { p4 .. p5 }, $EXTENSION_CLASS_DESCRIPTOR->setLinearGradient([I[F)V", + ) + } + + lithoColorOverrideHook(EXTENSION_CLASS_DESCRIPTOR, "getLithoColor") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt new file mode 100644 index 0000000000..cd48868f57 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/Fingerprints.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.youtube.layout.shortsautoplay + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val reelEnumConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + opcodes(Opcode.RETURN_VOID) + strings( + "REEL_LOOP_BEHAVIOR_UNKNOWN", + "REEL_LOOP_BEHAVIOR_SINGLE_PLAY", + "REEL_LOOP_BEHAVIOR_REPEAT", + "REEL_LOOP_BEHAVIOR_END_SCREEN", + ) +} + +internal val reelPlaybackRepeatFingerprint = fingerprint { + returns("V") + parameters("L") + strings("YoutubePlayerState is in throwing an Error.") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt new file mode 100644 index 0000000000..d9953de478 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/shortsautoplay/ShortsAutoplayPatch.kt @@ -0,0 +1,103 @@ +package app.revanced.patches.youtube.layout.shortsautoplay + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint +import app.revanced.util.findOpcodeIndicesReversed +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ShortsAutoplayPatch;" + +@Suppress("unused") +val shortsAutoplayPatch = bytecodePatch( + name = "Shorts autoplay", + description = "Adds options to automatically play the next Short.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + resourceMappingPatch, + versionCheckPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint() + val reelEnumConstructorMatch by reelEnumConstructorFingerprint() + val reelPlaybackRepeatMatch by reelPlaybackRepeatFingerprint() + + execute { + addResources("youtube", "layout.shortsautoplay.shortsAutoplayPatch") + + PreferenceScreen.SHORTS.addPreferences( + SwitchPreference("revanced_shorts_autoplay"), + ) + + if (is_19_34_or_greater) { + PreferenceScreen.SHORTS.addPreferences( + SwitchPreference("revanced_shorts_autoplay_background"), + ) + } + + // Main activity is used to check if app is in pip mode. + mainActivityOnCreateMatch.mutableMethod.addInstructions( + 0, + "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" + + "setMainActivity(Landroid/app/Activity;)V", + ) + + val reelEnumClass = reelEnumConstructorMatch.classDef.type + + reelEnumConstructorMatch.mutableMethod.apply { + val insertIndex = reelEnumConstructorMatch.patternMatch!!.startIndex + + addInstructions( + insertIndex, + """ + # Pass the first enum value to extension. + # Any enum value of this type will work. + sget-object v0, $reelEnumClass->a:$reelEnumClass + invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setYTShortsRepeatEnum(Ljava/lang/Enum;)V + """, + ) + } + + reelPlaybackRepeatMatch.mutableMethod.apply { + // The behavior enums are looked up from an ordinal value to an enum type. + findOpcodeIndicesReversed { + val reference = getReference() + reference?.definingClass == reelEnumClass && + reference.parameterTypes.firstOrNull() == "I" && + reference.returnType == reelEnumClass + }.forEach { index -> + val register = getInstruction(index + 1).registerA + + addInstructions( + index + 2, + """ + invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->changeShortsRepeatBehavior(Ljava/lang/Enum;)Ljava/lang/Enum; + move-result-object v$register + """, + ) + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/Fingerprints.kt new file mode 100644 index 0000000000..612ceb6c8f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/Fingerprints.kt @@ -0,0 +1,64 @@ +package app.revanced.patches.youtube.layout.sponsorblock + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal val appendTimeFingerprint = fingerprint { + returns("V") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Ljava/lang/CharSequence;", "Ljava/lang/CharSequence;", "Ljava/lang/CharSequence;") + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.CHECK_CAST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.CHECK_CAST, + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_VOID, + ) +} + +internal val controlsOverlayFingerprint = fingerprint { + returns("V") + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + parameters() + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, // R.id.inset_overlay_view_layout + Opcode.IPUT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.NEW_INSTANCE, + ) +} + +internal val rectangleFieldInvalidatorFingerprint = fingerprint { + returns("V") + custom { method, _ -> + val instructions = method.implementation?.instructions!! + val instructionCount = instructions.count() + + // the method has definitely more than 5 instructions + if (instructionCount < 5) return@custom false + + val referenceInstruction = instructions.elementAt(instructionCount - 2) // the second to last instruction + val reference = ((referenceInstruction as? ReferenceInstruction)?.reference as? MethodReference) + + reference?.parameterTypes?.size == 1 && reference.name == "invalidate" // the reference is the invalidate(..) method + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatch.kt new file mode 100644 index 0000000000..644866da8d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatch.kt @@ -0,0 +1,258 @@ +package app.revanced.patches.youtube.layout.sponsorblock + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.settings.preference.IntentPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playercontrols.* +import app.revanced.patches.youtube.misc.playercontrols.addTopControl +import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.patches.youtube.misc.settings.addSettingPreference +import app.revanced.patches.youtube.misc.settings.newIntent +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.* +import app.revanced.patches.youtube.video.information.onCreateHook +import app.revanced.patches.youtube.video.information.videoInformationPatch +import app.revanced.patches.youtube.video.information.videoTimeHook +import app.revanced.patches.youtube.video.videoid.hookBackgroundPlayVideoId +import app.revanced.patches.youtube.video.videoid.videoIdPatch +import app.revanced.util.* +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.* +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.StringReference +import com.sun.org.apache.bcel.internal.generic.InstructionConst.getInstruction + +private val sponsorBlockResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + playerControlsPatch, + ) + + execute { context -> + addResources("youtube", "layout.sponsorblock.sponsorBlockResourcePatch") + + addSettingPreference( + IntentPreference( + key = "revanced_settings_screen_10", + titleKey = "revanced_sb_settings_title", + summaryKey = null, + intent = newIntent("revanced_sb_settings_intent"), + ), + ) + + arrayOf( + ResourceGroup( + "layout", + "revanced_sb_inline_sponsor_overlay.xml", + "revanced_sb_new_segment.xml", + "revanced_sb_skip_sponsor_button.xml", + ), + ResourceGroup( + // required resource for back button, because when the base APK is used, this resource will not exist + "drawable", + "revanced_sb_adjust.xml", + "revanced_sb_backward.xml", + "revanced_sb_compare.xml", + "revanced_sb_edit.xml", + "revanced_sb_forward.xml", + "revanced_sb_logo.xml", + "revanced_sb_publish.xml", + "revanced_sb_voting.xml", + ), + ResourceGroup( + // required resource for back button, because when the base APK is used, this resource will not exist + "drawable-xxxhdpi", + "quantum_ic_skip_next_white_24.png", + ), + ).forEach { resourceGroup -> + context.copyResources("sponsorblock", resourceGroup) + } + + addTopControl("sponsorblock") + } +} + +private const val EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/sponsorblock/SegmentPlaybackController;" +private const val EXTENSION_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButtonController;" +private const val EXTENSION_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/sponsorblock/ui/VotingButtonController;" +private const val EXTENSION_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController;" + +@Suppress("unused") +val sponsorBlockPatch = bytecodePatch( + name = "SponsorBlock", + description = "Adds options to enable and configure SponsorBlock, which can skip undesired video segments such as sponsored content.", +) { + dependsOn( + sharedExtensionPatch, + videoIdPatch, + // Required to skip segments on time. + videoInformationPatch, + // Used to prevent SponsorBlock from running on Shorts because SponsorBlock does not yet support Shorts. + playerTypeHookPatch, + playerControlsPatch, + sponsorBlockResourcePatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val seekbarMatch by seekbarFingerprint() + val appendTimeMatch by appendTimeFingerprint() + val layoutConstructorMatch by layoutConstructorFingerprint() + val autoRepeatParentMatch by autoRepeatParentFingerprint() + + execute { context -> + // Hook the video time methods. + videoTimeHook( + EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR, + "setVideoTime", + ) + + hookBackgroundPlayVideoId( + EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR + + "->setCurrentVideoId(Ljava/lang/String;)V", + ) + + // Seekbar drawing + seekbarOnDrawFingerprint.applyMatch(context, seekbarMatch).mutableMethod.apply { + // Get left and right of seekbar rectangle. + val moveRectangleToRegisterIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_OBJECT_FROM16) + + addInstruction( + moveRectangleToRegisterIndex + 1, + "invoke-static/range { p0 .. p0 }, " + + "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarRect(Ljava/lang/Object;)V", + ) + + // Set the thickness of the segment. + val thicknessIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_STATIC && getReference()?.name == "round" + } + val thicknessRegister = getInstruction(thicknessIndex).registerC + addInstruction( + thicknessIndex + 2, + "invoke-static { v$thicknessRegister }, " + + "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarThickness(I)V", + ) + + // Find the drawCircle call and draw the segment before it. + val drawCircleIndex = indexOfFirstInstructionReversedOrThrow { + getReference()?.name == "drawCircle" + } + val drawCircleInstruction = getInstruction(drawCircleIndex) + val canvasInstanceRegister = drawCircleInstruction.registerC + val centerYRegister = drawCircleInstruction.registerE + + addInstruction( + drawCircleIndex, + "invoke-static { v$canvasInstanceRegister, v$centerYRegister }, " + + "$EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->" + + "drawSponsorTimeBars(Landroid/graphics/Canvas;F)V", + ) + } + + // Change visibility of the buttons. + initializeTopControl(EXTENSION_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR) + injectVisibilityCheckCall(EXTENSION_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR) + + initializeTopControl(EXTENSION_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR) + injectVisibilityCheckCall(EXTENSION_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR) + + // Append the new time to the player layout. + val appendTimePatternScanStartIndex = appendTimeMatch.patternMatch!!.startIndex + appendTimeMatch.mutableMethod.apply { + val register = getInstruction(appendTimePatternScanStartIndex + 1).registerA + + addInstructions( + appendTimePatternScanStartIndex + 2, + """ + invoke-static { v$register }, $EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->appendTimeWithoutSegments(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$register + """, + ) + } + + // Initialize the player controller. + onCreateHook(EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR, "initialize") + + // Initialize the SponsorBlock view. + controlsOverlayFingerprint.applyMatch(context, layoutConstructorMatch).let { + val startIndex = it.patternMatch!!.startIndex + it.mutableMethod.apply { + val frameLayoutRegister = (getInstruction(startIndex + 2) as OneRegisterInstruction).registerA + addInstruction( + startIndex + 3, + "invoke-static {v$frameLayoutRegister}, $EXTENSION_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/ViewGroup;)V", + ) + } + } + + // Set seekbar draw rectangle. + rectangleFieldInvalidatorFingerprint.applyMatch(context, seekbarOnDrawFingerprint.match!!).mutableMethod.apply { + val fieldIndex = instructions.count() - 3 + val fieldReference = getInstruction(fieldIndex).reference as FieldReference + + // replace the "replaceMeWith*" strings + context + .proxy(context.classes.first { it.type.endsWith("SegmentPlaybackController;") }) + .mutableClass + .methods + .find { it.name == "setSponsorBarRect" } + ?.let { method -> + fun MutableMethod.replaceStringInstruction(index: Int, instruction: Instruction, with: String) { + val register = (instruction as OneRegisterInstruction).registerA + this.replaceInstruction( + index, + "const-string v$register, \"$with\"", + ) + } + for ((index, it) in method.instructions.withIndex()) { + if (it.opcode.ordinal != Opcode.CONST_STRING.ordinal) continue + + when (((it as ReferenceInstruction).reference as StringReference).string) { + "replaceMeWithsetSponsorBarRect" -> method.replaceStringInstruction( + index, + it, + fieldReference.name, + ) + } + } + } ?: throw PatchException("Could not find the method which contains the replaceMeWith* strings") + } + + // The vote and create segment buttons automatically change their visibility when appropriate, + // but if buttons are showing when the end of the video is reached then they will not automatically hide. + // Add a hook to forcefully hide when the end of the video is reached. + autoRepeatFingerprint.applyMatch(context, autoRepeatParentMatch).mutableMethod.addInstruction( + 0, + "invoke-static {}, $EXTENSION_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR->endOfVideoReached()V", + ) + + // TODO: Channel whitelisting. + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt new file mode 100644 index 0000000000..969d90e024 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.youtube.layout.spoofappversion + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val spoofAppVersionFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("L") + parameters("L") + opcodes( + Opcode.IGET_OBJECT, + Opcode.GOTO, + Opcode.CONST_STRING, + ) + // Instead of applying a bytecode patch, it might be possible to only rely on code from the extension and + // manually set the desired version string as this keyed value in the SharedPreferences. + // But, this bytecode patch is simple and it works. + strings("pref_override_build_version_name") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt new file mode 100644 index 0000000000..3ec97ea958 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt @@ -0,0 +1,65 @@ +package app.revanced.patches.youtube.layout.spoofappversion + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch;" + +@Suppress("unused") +val spoofAppVersionPatch = bytecodePatch( + name = "Spoof app version", + description = "Adds an option to trick YouTube into thinking you are running an older version of the app. " + + "This can be used to restore old UI elements and features.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val spoofAppVersionMatch by spoofAppVersionFingerprint() + + execute { + addResources("youtube", "layout.spoofappversion.spoofAppVersionPatch") + + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + SwitchPreference("revanced_spoof_app_version"), + ListPreference( + key = "revanced_spoof_app_version_target", + summaryKey = null, + ), + ) + + val insertIndex = spoofAppVersionMatch.patternMatch!!.startIndex + 1 + val buildOverrideNameRegister = + spoofAppVersionMatch.mutableMethod.getInstruction(insertIndex - 1).registerA + + spoofAppVersionMatch.mutableMethod.addInstructions( + insertIndex, + """ + invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$buildOverrideNameRegister + """, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt new file mode 100644 index 0000000000..5c62ff3b2f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/ChangeStartPagePatch.kt @@ -0,0 +1,77 @@ +package app.revanced.patches.youtube.layout.startpage + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.StringReference + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeStartPagePatch;" + +@Suppress("unused") +val changeStartPagePatch = bytecodePatch( + name = "Change start page", + description = "Adds an option to set which page the app opens in instead of the homepage.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val browseIdMatch by browseIdFingerprint() + val intentActionMatch by intentActionFingerprint() + + execute { _ -> + addResources("youtube", "layout.startpage.changeStartPagePatch") + + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + ListPreference( + key = "revanced_change_start_page", + summaryKey = null, + ), + ) + + // Hook browseId. + browseIdMatch.mutableMethod.apply { + val browseIdIndex = indexOfFirstInstructionOrThrow { + getReference()?.string == "FEwhat_to_watch" + } + val browseIdRegister = getInstruction(browseIdIndex).registerA + + addInstructions( + browseIdIndex + 1, + """ + invoke-static { v$browseIdRegister }, $EXTENSION_CLASS_DESCRIPTOR->overrideBrowseId(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$browseIdRegister + """, + ) + } + + // There is no browserId assigned to Shorts and Search. + // Just hook the Intent action. + intentActionMatch.mutableMethod.addInstruction( + 0, + "invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->overrideIntentAction(Landroid/content/Intent;)V", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/Fingerprints.kt new file mode 100644 index 0000000000..0220840203 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startpage/Fingerprints.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.youtube.layout.startpage + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal val intentActionFingerprint = fingerprint { + parameters("Landroid/content/Intent;") + strings("has_handled_intent") +} + +internal val browseIdFingerprint = fingerprint { + returns("Lcom/google/android/apps/youtube/app/common/ui/navigation/PaneDescriptor;") + parameters() + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.RETURN_OBJECT, + ) + strings("FEwhat_to_watch") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt new file mode 100644 index 0000000000..9f88c08b07 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch.kt @@ -0,0 +1,99 @@ +package app.revanced.patches.youtube.layout.startupshortsreset + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/DisableResumingStartupShortsPlayerPatch;" + +@Suppress("unused") +val disableResumingShortsOnStartupPatch = bytecodePatch( + name = "Disable resuming Shorts on startup", + description = "Adds an option to disable the Shorts player from resuming on app startup when Shorts were last being watched.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val userWasInShortsConfigMatch by userWasInShortsConfigFingerprint() + val userWasInShortsMatch by userWasInShortsFingerprint() + + execute { context -> + addResources("youtube", "layout.startupshortsreset.disableResumingShortsOnStartupPatch") + + PreferenceScreen.SHORTS.addPreferences( + SwitchPreference("revanced_disable_resuming_shorts_player"), + ) + + userWasInShortsConfigMatch.mutableMethod.apply { + val startIndex = indexOfOptionalInstruction(this) + val walkerIndex = indexOfFirstInstructionOrThrow(startIndex) { + val reference = getReference() + opcode == Opcode.INVOKE_VIRTUAL && + reference?.returnType == "Z" && + reference.definingClass != "Lj${'$'}/util/Optional;" && + reference.parameterTypes.size == 0 + } + + // Presumably a method that processes the ProtoDataStore value (boolean) for the 'user_was_in_shorts' key. + context.navigate(this).at(walkerIndex).mutable().addInstructionsWithLabels( + 0, + """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z + move-result v0 + if-eqz v0, :show + const/4 v0, 0x0 + return v0 + :show + nop + """, + ) + } + + userWasInShortsMatch.mutableMethod.apply { + val listenableInstructionIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_INTERFACE && + getReference()?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" && + getReference()?.name == "isDone" + } + val freeRegister = getInstruction(listenableInstructionIndex + 1).registerA + + addInstructionsAtControlFlowLabel( + listenableInstructionIndex, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z + move-result v$freeRegister + if-eqz v$freeRegister, :show_startup_shorts_player + return-void + :show_startup_shorts_player + nop + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/Fingerprints.kt new file mode 100644 index 0000000000..326ebbe14d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/startupshortsreset/Fingerprints.kt @@ -0,0 +1,41 @@ +package app.revanced.patches.youtube.layout.startupshortsreset + +import app.revanced.patcher.fingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil + +internal val userWasInShortsFingerprint = fingerprint { + returns("V") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Ljava/lang/Object;") + strings("Failed to read user_was_in_shorts proto after successful warmup") +} + +/** + * 18.15.40+ + */ +internal val userWasInShortsConfigFingerprint = fingerprint { + returns("V") + strings("Failed to get offline response: ") + custom { method, _ -> + indexOfOptionalInstruction(method) >= 0 + } +} + +private val optionalOfMethodReference = ImmutableMethodReference( + "Lj${'$'}/util/Optional;", + "of", + listOf("Ljava/lang/Object;"), + "Lj${'$'}/util/Optional;", +) + +fun indexOfOptionalInstruction(method: Method) = method.indexOfFirstInstruction { + val reference = getReference() ?: return@indexOfFirstInstruction false + + MethodUtil.methodSignaturesMatch(reference, optionalOfMethodReference) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt new file mode 100644 index 0000000000..7b07f5ecfb --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt @@ -0,0 +1,65 @@ +package app.revanced.patches.youtube.layout.tablet + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/TabletLayoutPatch;" + +@Suppress("unused") +val enableTabletLayoutPatch = bytecodePatch( + name = "Enable tablet layout", + description = "Adds an option to enable tablet layout.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val getFormFactorMatch by getFormFactorFingerprint() + + execute { + addResources("youtube", "layout.tablet.enableTabletLayoutPatch") + + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + SwitchPreference("revanced_tablet_layout"), + ) + + getFormFactorMatch.mutableMethod.apply { + val returnIsLargeFormFactorIndex = instructions.lastIndex - 4 + val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex) + + addInstructionsWithLabels( + 0, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getTabletLayoutEnabled()Z + move-result v0 + if-nez v0, :is_large_form_factor + """, + ExternalLabel( + "is_large_form_factor", + returnIsLargeFormFactorLabel, + ), + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/Fingerprints.kt new file mode 100644 index 0000000000..30667a85bf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/Fingerprints.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.youtube.layout.tablet + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val getFormFactorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("L") + parameters("Landroid/content/Context;", "Ljava/util/List;") + opcodes( + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.SGET_OBJECT, + Opcode.RETURN_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.RETURN_OBJECT, + ) + strings("") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt new file mode 100644 index 0000000000..9ed098a1bf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt @@ -0,0 +1,56 @@ +package app.revanced.patches.youtube.layout.theme + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val lithoThemeFingerprint = fingerprint { + accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL) + returns("V") + parameters("Landroid/graphics/Rect;") + opcodes( + Opcode.APUT, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.IGET_OBJECT, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IPUT_OBJECT, + Opcode.IGET, + Opcode.IF_EQZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_VOID, + ) + custom { method, _ -> + method.name == "onBoundsChange" + } +} + +internal val themeHelperDarkColorFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) + returns("Ljava/lang/String;") + parameters() + custom { method, _ -> + method.name == "darkThemeResourceName" && + method.definingClass == "Lapp/revanced/extension/youtube/ThemeHelper;" + } +} + +internal val themeHelperLightColorFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) + returns("Ljava/lang/String;") + parameters() + custom { method, _ -> + method.name == "lightThemeResourceName" && + method.definingClass == "Lapp/revanced/extension/youtube/ThemeHelper;" + } +} + +internal val useGradientLoadingScreenFingerprint = fingerprint { + literal { GRADIENT_LOADING_SCREEN_AB_CONSTANT } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt new file mode 100644 index 0000000000..5fa87d900c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.youtube.layout.theme + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch + +lateinit var lithoColorOverrideHook: (targetMethodClass: String, targetMethodName: String) -> Unit + private set + +val lithoColorHookPatch = bytecodePatch( + description = "Adds a hook to set color of Litho components.", +) { + val lithoThemeMatch by lithoThemeFingerprint() + + execute { + var insertionIndex = lithoThemeMatch.patternMatch!!.endIndex - 1 + + lithoColorOverrideHook = { targetMethodClass, targetMethodName -> + lithoThemeMatch.mutableMethod.addInstructions( + insertionIndex, + """ + invoke-static { p1 }, $targetMethodClass->$targetMethodName(I)I + move-result p1 + """, + ) + insertionIndex += 2 + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt new file mode 100644 index 0000000000..a99daf7202 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt @@ -0,0 +1,245 @@ +package app.revanced.patches.youtube.layout.theme + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.patch.stringOption +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.forEachChildElement +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import org.w3c.dom.Element + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/theme/ThemePatch;" + +internal const val GRADIENT_LOADING_SCREEN_AB_CONSTANT = 45412406L + +@Suppress("unused") +val themePatch = bytecodePatch( + name = "Theme", + description = "Adds options for theming and applies a custom background theme (dark background theme defaults to amoled black).", +) { + val amoledBlackColor = "@android:color/black" + val whiteColor = "@android:color/white" + + val darkThemeBackgroundColor by stringOption( + key = "darkThemeBackgroundColor", + default = amoledBlackColor, + values = mapOf( + "Amoled black" to amoledBlackColor, + "Material You" to "@android:color/system_neutral1_900", + "Classic (old YouTube)" to "#FF212121", + "Catppuccin (Mocha)" to "#FF181825", + "Dark pink" to "#FF290025", + "Dark blue" to "#FF001029", + "Dark green" to "#FF002905", + "Dark yellow" to "#FF282900", + "Dark orange" to "#FF291800", + "Dark red" to "#FF290000", + ), + title = "Dark theme background color", + description = "Can be a hex color (#AARRGGBB) or a color resource reference.", + ) + + val lightThemeBackgroundColor by stringOption( + key = "lightThemeBackgroundColor", + default = whiteColor, + values = mapOf( + "White" to whiteColor, + "Material You" to "@android:color/system_neutral1_50", + "Catppuccin (Latte)" to "#FFE6E9EF", + "Light pink" to "#FFFCCFF3", + "Light blue" to "#FFD1E0FF", + "Light green" to "#FFCCFFCC", + "Light yellow" to "#FFFDFFCC", + "Light orange" to "#FFFFE6CC", + "Light red" to "#FFFFD6D6", + ), + title = "Light theme background color", + description = "Can be a hex color (#AARRGGBB) or a color resource reference.", + ) + + dependsOn( + lithoColorHookPatch, + seekbarColorPatch, + resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + ) + + execute { context -> + addResources("youtube", "layout.theme.themeResourcePatch") + + PreferenceScreen.SEEKBAR.addPreferences( + SwitchPreference("revanced_seekbar_custom_color"), + TextPreference("revanced_seekbar_custom_color_value", inputType = InputType.TEXT_CAP_CHARACTERS), + ) + + // Edit theme colors via resources. + context.document["res/values/colors.xml"].use { document -> + + val resourcesNode = document.getElementsByTagName("resources").item(0) as Element + + val children = resourcesNode.childNodes + for (i in 0 until children.length) { + val node = children.item(i) as? Element ?: continue + + node.textContent = + when (node.getAttribute("name")) { + "yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98", "yt_black2", "yt_black3", + "yt_black4", "yt_status_bar_background_dark", "material_grey_850", + -> darkThemeBackgroundColor ?: continue + + "yt_white1", "yt_white1_opacity95", "yt_white1_opacity98", + "yt_white2", "yt_white3", "yt_white4", + -> lightThemeBackgroundColor ?: continue + + else -> continue + } + } + } + + fun addColorResource( + resourceFile: String, + colorName: String, + colorValue: String, + ) { + context.document[resourceFile].use { document -> + + val resourcesNode = document.getElementsByTagName("resources").item(0) as Element + + resourcesNode.appendChild( + document.createElement("color").apply { + setAttribute("name", colorName) + setAttribute("category", "color") + textContent = colorValue + }, + ) + } + } + + val splashBackgroundColor = "revanced_splash_background_color" + + // Add a dynamic background color to the colors.xml file. + lightThemeBackgroundColor?.let { + addColorResource("res/values/colors.xml", splashBackgroundColor, it) + } + + darkThemeBackgroundColor?.let { + addColorResource("res/values-night/colors.xml", splashBackgroundColor, it) + } + + // Edit splash screen files and change the background color, + // if the background colors are set. + if (darkThemeBackgroundColor != null && lightThemeBackgroundColor != null) { + val splashScreenResourceFiles = + listOf( + "res/drawable/quantum_launchscreen_youtube.xml", + "res/drawable-sw600dp/quantum_launchscreen_youtube.xml", + ) + + splashScreenResourceFiles.forEach editSplashScreen@{ resourceFile -> + context.document[resourceFile].use { document -> + document.getElementsByTagName("layer-list").item(0).forEachChildElement { node -> + if (node.hasAttribute("android:drawable")) { + node.setAttribute("android:drawable", "@color/$splashBackgroundColor") + return@editSplashScreen + } + } + + throw PatchException("Failed to modify launch screen") + } + } + + // Fix the splash screen dark mode background color. + // In earlier versions of the app this is white and makes no sense for dark mode. + // This is only required for 19.32 and greater, but is applied to all targets. + // Only dark mode needs this fix as light mode correctly uses the custom color. + context.document["res/values-night/styles.xml"].use { document -> + // Create a night mode specific override for the splash screen background. + val style = document.createElement("style") + style.setAttribute("name", "Theme.YouTube.Home") + style.setAttribute("parent", "@style/Base.V23.Theme.YouTube.Home") + + val windowItem = document.createElement("item") + windowItem.setAttribute("name", "android:windowBackground") + windowItem.textContent = "@color/$splashBackgroundColor" + style.appendChild(windowItem) + + val resourcesNode = document.getElementsByTagName("resources").item(0) as Element + resourcesNode.appendChild(style) + } + } + } + }, + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val useGradientLoadingScreenMatch by useGradientLoadingScreenFingerprint() + val themeHelperLightColorMatch by themeHelperLightColorFingerprint() + val themeHelperDarkColorMatch by themeHelperDarkColorFingerprint() + + execute { + addResources("youtube", "layout.theme.themePatch") + + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + SwitchPreference("revanced_gradient_loading_screen"), + ) + + useGradientLoadingScreenMatch.mutableMethod.apply { + + val isEnabledIndex = indexOfFirstWideLiteralInstructionValueOrThrow(GRADIENT_LOADING_SCREEN_AB_CONSTANT) + 3 + val isEnabledRegister = getInstruction(isEnabledIndex - 1).registerA + + addInstructions( + isEnabledIndex, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled()Z + move-result v$isEnabledRegister + """, + ) + } + mapOf( + themeHelperLightColorMatch to lightThemeBackgroundColor, + themeHelperDarkColorMatch to darkThemeBackgroundColor, + ).forEach { (match, color) -> + match.mutableMethod.apply { + addInstructions( + 0, + """ + const-string v0, "$color" + return-object v0 + """, + ) + } + } + + lithoColorOverrideHook(EXTENSION_CLASS_DESCRIPTOR, "getValue") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt new file mode 100644 index 0000000000..614ae90527 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt @@ -0,0 +1,98 @@ +package app.revanced.patches.youtube.layout.thumbnails + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.imageurlhook.addImageUrlErrorCallbackHook +import app.revanced.patches.youtube.misc.imageurlhook.addImageUrlHook +import app.revanced.patches.youtube.misc.imageurlhook.addImageUrlSuccessCallbackHook +import app.revanced.patches.youtube.misc.imageurlhook.cronetImageUrlHookPatch +import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/AlternativeThumbnailsPatch;" + +@Suppress("unused") +val alternativeThumbnailsPatch = bytecodePatch( + name = "Alternative thumbnails", + description = "Adds options to replace video thumbnails using the DeArrow API or image captures from the video.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + navigationBarHookPatch, + cronetImageUrlHookPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + execute { + addResources("youtube", "layout.thumbnails.alternativeThumbnailsPatch") + + val entries = "revanced_alt_thumbnail_options_entries" + val values = "revanced_alt_thumbnail_options_entry_values" + PreferenceScreen.ALTERNATIVE_THUMBNAILS.addPreferences( + ListPreference( + "revanced_alt_thumbnail_home", + summaryKey = null, + entriesKey = entries, + entryValuesKey = values, + ), + ListPreference( + "revanced_alt_thumbnail_subscription", + summaryKey = null, + entriesKey = entries, + entryValuesKey = values, + ), + ListPreference( + "revanced_alt_thumbnail_library", + summaryKey = null, + entriesKey = entries, + entryValuesKey = values, + ), + ListPreference( + "revanced_alt_thumbnail_player", + summaryKey = null, + entriesKey = entries, + entryValuesKey = values, + ), + ListPreference( + "revanced_alt_thumbnail_search", + summaryKey = null, + entriesKey = entries, + entryValuesKey = values, + ), + NonInteractivePreference( + "revanced_alt_thumbnail_dearrow_about", + // Custom about preference with link to the DeArrow website. + tag = "app.revanced.extension.youtube.settings.preference.AlternativeThumbnailsAboutDeArrowPreference", + selectable = true, + ), + SwitchPreference("revanced_alt_thumbnail_dearrow_connection_toast"), + TextPreference("revanced_alt_thumbnail_dearrow_api_url"), + NonInteractivePreference("revanced_alt_thumbnail_stills_about"), + SwitchPreference("revanced_alt_thumbnail_stills_fast"), + ListPreference("revanced_alt_thumbnail_stills_time", summaryKey = null), + ) + + addImageUrlHook(EXTENSION_CLASS_DESCRIPTOR) + addImageUrlSuccessCallbackHook(EXTENSION_CLASS_DESCRIPTOR) + addImageUrlErrorCallbackHook(EXTENSION_CLASS_DESCRIPTOR) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatch.kt new file mode 100644 index 0000000000..cdb7dc4ed8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/BypassImageRegionRestrictionsPatch.kt @@ -0,0 +1,50 @@ +package app.revanced.patches.youtube.layout.thumbnails + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.imageurlhook.addImageUrlHook +import app.revanced.patches.youtube.misc.imageurlhook.cronetImageUrlHookPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/BypassImageRegionRestrictionsPatch;" + +@Suppress("unused") +val bypassImageRegionRestrictionsPatch = bytecodePatch( + name = "Bypass image region restrictions", + description = "Adds an option to use a different host for user avatar and channel images " + + "and can fix missing images that are blocked in some countries.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + cronetImageUrlHookPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + execute { + addResources("youtube", "layout.thumbnails.bypassImageRegionRestrictionsPatch") + + PreferenceScreen.MISC.addPreferences( + SwitchPreference("revanced_bypass_image_region_restrictions"), + ) + + // A priority hook is not needed, as the image urls of interest are not modified + // by AlternativeThumbnails or any other patch in this repo. + addImageUrlHook(EXTENSION_CLASS_DESCRIPTOR) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt new file mode 100644 index 0000000000..00684b2f7f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/announcements/AnnouncementsPatch.kt @@ -0,0 +1,43 @@ +package app.revanced.patches.youtube.misc.announcements + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/announcements/AnnouncementsPatch;" + +@Suppress("unused") +val announcementsPatch = bytecodePatch( + name = "Announcements", + description = "Adds an option to show announcements from ReVanced on app startup.", +) { + dependsOn( + settingsPatch, + addResourcesPatch, + ) + + compatibleWith("com.google.android.youtube") + + val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint() + + execute { + addResources("youtube", "misc.announcements.announcementsPatch") + + PreferenceScreen.MISC.addPreferences( + SwitchPreference("revanced_announcements"), + ) + + mainActivityOnCreateMatch.mutableMethod.addInstructions( + // Insert index must be greater than the insert index used by GmsCoreSupport, + // as both patch the same method and GmsCore check should be first. + 1, + "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->showAnnouncement(Landroid/app/Activity;)V", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch.kt new file mode 100644 index 0000000000..9558b965c1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch.kt @@ -0,0 +1,68 @@ +package app.revanced.patches.youtube.misc.autorepeat + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.shared.autoRepeatFingerprint +import app.revanced.patches.youtube.shared.autoRepeatParentFingerprint +import app.revanced.util.matchOrThrow + +// TODO: Rename this patch to AlwaysRepeatPatch (as well as strings and references in the extension). +@Suppress("unused") +val autoRepeatPatch = bytecodePatch( + name = "Always repeat", + description = "Adds an option to always repeat videos when they end.", +) { + dependsOn( + sharedExtensionPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val autoRepeatParentMatch by autoRepeatParentFingerprint() + + execute { context -> + addResources("youtube", "misc.autorepeat.autoRepeatPatch") + + PreferenceScreen.MISC.addPreferences( + SwitchPreference("revanced_auto_repeat"), + ) + + autoRepeatFingerprint.apply { + match(context, autoRepeatParentMatch.classDef) + }.matchOrThrow.mutableMethod.apply { + val playMethod = autoRepeatParentMatch.mutableMethod + val index = instructions.lastIndex + + // Remove return-void. + removeInstruction(index) + // Add own instructions there. + addInstructionsWithLabels( + index, + """ + invoke-static {}, Lapp/revanced/extension/youtube/patches/AutoRepeatPatch;->shouldAutoRepeat()Z + move-result v0 + if-eqz v0, :noautorepeat + invoke-virtual { p0 }, $playMethod + :noautorepeat + return-void + """, + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt new file mode 100644 index 0000000000..b954b27e12 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt @@ -0,0 +1,103 @@ +package app.revanced.patches.youtube.misc.backgroundplayback + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.video.information.videoInformationPatch +import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.findOpcodeIndicesReversed +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal var prefBackgroundAndOfflineCategoryId = -1L + private set + +private val backgroundPlaybackResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + execute { + prefBackgroundAndOfflineCategoryId = resourceMappings["string", "pref_background_and_offline_category"] + } +} + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/BackgroundPlaybackPatch;" + +@Suppress("unused") +val backgroundPlaybackPatch = bytecodePatch( + name = "Remove background playback restrictions", + description = "Removes restrictions on background playback, including playing kids videos in the background.", +) { + dependsOn( + backgroundPlaybackResourcePatch, + sharedExtensionPatch, + playerTypeHookPatch, + videoInformationPatch, + settingsPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val backgroundPlaybackManagerMatch by backgroundPlaybackManagerFingerprint() + val backgroundPlaybackSettingsMatch by backgroundPlaybackSettingsFingerprint() + val kidsBackgroundPlaybackPolicyControllerMatch by kidsBackgroundPlaybackPolicyControllerFingerprint() + + execute { context -> + backgroundPlaybackManagerMatch.mutableMethod.apply { + findOpcodeIndicesReversed(Opcode.RETURN).forEach { index -> + val register = getInstruction(index).registerA + + addInstructionsAtControlFlowLabel( + index, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->allowBackgroundPlayback(Z)Z + move-result v$register + """, + ) + } + } + + // Enable background playback option in YouTube settings + backgroundPlaybackSettingsMatch.mutableMethod.apply { + val booleanCalls = instructions.withIndex() + .filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" } + + val settingsBooleanIndex = booleanCalls.elementAt(1).index + val settingsBooleanMethod = context.navigate(this).at(settingsBooleanIndex).mutable() + + settingsBooleanMethod.addInstructions( + 0, + """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->overrideBackgroundPlaybackAvailable()Z + move-result v0 + return v0 + """, + ) + } + + // Force allowing background play for videos labeled for kids. + kidsBackgroundPlaybackPolicyControllerMatch.mutableMethod.addInstruction( + 0, + "return-void", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt new file mode 100644 index 0000000000..aa4f6f9220 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/Fingerprints.kt @@ -0,0 +1,72 @@ +package app.revanced.patches.youtube.misc.backgroundplayback + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val backgroundPlaybackManagerFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters("L") + opcodes( + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.IGET, + Opcode.AND_INT_LIT16, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.IGET, + Opcode.CONST, + Opcode.IF_NE, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.IGET, + Opcode.IF_NE, + Opcode.IGET_OBJECT, + Opcode.CHECK_CAST, + Opcode.GOTO, + Opcode.SGET_OBJECT, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.IGET_BOOLEAN, + Opcode.IF_EQZ, + ) +} + +internal val backgroundPlaybackSettingsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String;") + parameters() + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.IF_NEZ, + Opcode.GOTO, + ) + literal { prefBackgroundAndOfflineCategoryId } +} + +internal val kidsBackgroundPlaybackPolicyControllerFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("I", "L", "L") + opcodes( + Opcode.CONST_4, + Opcode.IF_NE, + Opcode.SGET_OBJECT, + Opcode.IF_NE, + Opcode.IGET, + Opcode.CONST_4, + Opcode.IF_NE, + Opcode.IGET_OBJECT, + ) + literal { 5 } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt new file mode 100644 index 0000000000..f0cc13531c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/check/CheckEnvironmentPatch.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.misc.check + +import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint + +@Suppress("unused") +val checkEnvironmentPatch = checkEnvironmentPatch( + mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + extensionPatch = sharedExtensionPatch, + "com.google.android.youtube", +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatch.kt new file mode 100644 index 0000000000..b354c75e1b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatch.kt @@ -0,0 +1,41 @@ +package app.revanced.patches.youtube.misc.debugging + +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +val enableDebuggingPatch = resourcePatch( + name = "Enable debugging", + description = "Adds options for debugging.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith("com.google.android.youtube") + + execute { + addResources("youtube", "misc.debugging.enableDebuggingPatch") + + PreferenceScreen.MISC.addPreferences( + PreferenceScreenPreference( + key = "revanced_debug_screen", + sorting = Sorting.UNSORTED, + preferences = setOf( + SwitchPreference("revanced_debug"), + SwitchPreference("revanced_debug_protobuffer"), + SwitchPreference("revanced_debug_stacktrace"), + SwitchPreference("revanced_debug_toast_on_error"), + ), + ), + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/Fingerprints.kt new file mode 100644 index 0000000000..4f99a4cf54 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/Fingerprints.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.youtube.misc.dimensions.spoof + +import app.revanced.patcher.fingerprint + +internal val deviceDimensionsModelToStringFingerprint = fingerprint { + returns("L") + strings("minh.", ";maxh.") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt new file mode 100644 index 0000000000..c82d363e14 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDeviceDimensionsPatch.kt @@ -0,0 +1,63 @@ +package app.revanced.patches.youtube.misc.dimensions.spoof + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/spoof/SpoofDeviceDimensionsPatch;" + +@Suppress("unused") +val spoofDeviceDimensionsPatch = bytecodePatch( + name = "Spoof device dimensions", + description = "Adds an option to spoof the device dimensions which can unlock higher video qualities.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val deviceDimensionsModelToStringMatch by deviceDimensionsModelToStringFingerprint() + + execute { + addResources("youtube", "misc.dimensions.spoof.spoofDeviceDimensionsPatch") + + PreferenceScreen.MISC.addPreferences( + SwitchPreference("revanced_spoof_device_dimensions"), + ) + + deviceDimensionsModelToStringMatch + .mutableClass.methods.first { method -> method.name == "" } + // Override the parameters containing the dimensions. + .addInstructions( + 1, // Add after super call. + mapOf( + 1 to "MinHeightOrWidth", // p1 = min height + 2 to "MaxHeightOrWidth", // p2 = max height + 3 to "MinHeightOrWidth", // p3 = min width + 4 to "MaxHeightOrWidth", // p4 = max width + ).map { (parameter, method) -> + """ + invoke-static { p$parameter }, $EXTENSION_CLASS_DESCRIPTOR->get$method(I)I + move-result p$parameter + """ + }.joinToString("\n") { it }, + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt new file mode 100644 index 0000000000..f4433fe29e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt @@ -0,0 +1,41 @@ +package app.revanced.patches.youtube.misc.dns + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/CheckWatchHistoryDomainNameResolutionPatch;" + +@Suppress("unused") +val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch( + name = "Check watch history domain name resolution", + description = "Checks if the device DNS server is preventing user watch history from being saved.", +) { + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val mainActivityOnCreateMatch by mainActivityOnCreateFingerprint() + + execute { + addResources("youtube", "misc.dns.checkWatchHistoryDomainNameResolutionPatch") + + mainActivityOnCreateMatch.mutableMethod.addInstructions( + // FIXME: Insert index must be greater than the insert index used by GmsCoreSupport, + // as both patch the same method and GmsCoreSupport check should be first, + // but the patch does not depend on GmsCoreSupport, so it should not be possible to enforce this + // unless a third patch is added that this patch and GmsCoreSupport depend on to manage + // the order of the patches. + 1, + "invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->checkDnsResolver(Landroid/app/Activity;)V", + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/SharedExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/SharedExtensionPatch.kt new file mode 100644 index 0000000000..369e3ea748 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/SharedExtensionPatch.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.youtube.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.extension.hooks.* + +// TODO: Move this to a "Hook.kt" file. Same for other extension hook patches. +val sharedExtensionPatch = sharedExtensionPatch( + applicationInitHook, +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt new file mode 100644 index 0000000000..6a0e7d1f48 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.youtube.misc.extension.hooks + +import app.revanced.patches.shared.misc.extension.extensionHook + +/** + * Hooks the context when the app is launched as a regular application (and is not an embedded video playback). + */ +// Extension context is the Activity itself. +internal val applicationInitHook = extensionHook { + strings("Application creation", "Application.onCreate") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt new file mode 100644 index 0000000000..fed0199aa4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt @@ -0,0 +1,73 @@ +package app.revanced.patches.youtube.misc.fix.backtoexitgesture + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val onBackPressedFingerprint = fingerprint { + returns("V") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + opcodes(Opcode.RETURN_VOID) + custom { method, classDef -> + method.name == "onBackPressed" && + // Old versions of YouTube called this class "WatchWhileActivity" instead. + (classDef.endsWith("MainActivity;") || classDef.endsWith("WatchWhileActivity;")) + } +} + +internal val recyclerViewScrollingFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + parameters() + opcodes( + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.CHECK_CAST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_LEZ, + Opcode.IGET_OBJECT, + Opcode.CONST_4, + ) +} + +internal val recyclerViewTopScrollingFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + opcodes( + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.CONST_4, + Opcode.INVOKE_VIRTUAL, + Opcode.GOTO, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + ) +} + +internal val recyclerViewTopScrollingParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters("L", "L", "Landroid/view/ViewGroup;", "Landroid/view/ViewGroup;") + opcodes( + Opcode.INVOKE_DIRECT, + Opcode.IPUT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.NEW_INSTANCE, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt new file mode 100644 index 0000000000..ae5aee5e79 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt @@ -0,0 +1,61 @@ +package app.revanced.patches.youtube.misc.fix.backtoexitgesture + +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.matchOrThrow + +@Suppress("unused") +internal val fixBackToExitGesturePatch = bytecodePatch( + description = "Fixes the swipe back to exit gesture.", +) { + val recyclerViewTopScrollingParentMatch by recyclerViewTopScrollingParentFingerprint() + val recyclerViewScrollingMatch by recyclerViewScrollingFingerprint() + val onBackPressedMatch by onBackPressedFingerprint() + + execute { context -> + recyclerViewTopScrollingFingerprint.apply { + match(context, recyclerViewTopScrollingParentMatch.classDef) + } + + /** + * Inject a call to a method from the extension. + * + * @param targetMethod The target method to call. + */ + fun Match.injectCall(targetMethod: ExtensionMethod) = mutableMethod.addInstruction( + patternMatch!!.endIndex, + targetMethod.toString(), + ) + + mapOf( + recyclerViewTopScrollingFingerprint.matchOrThrow to ExtensionMethod( + methodName = "onTopView", + ), + recyclerViewScrollingMatch to ExtensionMethod( + methodName = "onScrollingViews", + ), + onBackPressedMatch to ExtensionMethod( + "p0", + "onBackPressed", + "Landroid/app/Activity;", + ), + ).forEach { (match, target) -> match.injectCall(target) } + } +} + +/** + * A reference to a method from the extension for [fixBackToExitGesturePatch]. + * + * @param register The method registers. + * @param methodName The method name. + * @param parameterTypes The parameters of the method. + */ +private class ExtensionMethod( + val register: String = "", + val methodName: String, + val parameterTypes: String = "", +) { + override fun toString() = + "invoke-static {$register}, Lapp/revanced/extension/youtube/patches/FixBackToExitGesturePatch;->$methodName($parameterTypes)V" +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt similarity index 59% rename from src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt index b936d7bf91..902d65bf12 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt @@ -1,31 +1,27 @@ package app.revanced.patches.youtube.misc.fix.cairo -import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackPatch -import app.revanced.patches.youtube.misc.fix.cairo.fingerprints.CarioFragmentConfigFingerprint -import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.misc.backgroundplayback.backgroundPlaybackPatch +import app.revanced.patches.youtube.misc.playservice.is_19_04_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow -import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.sun.org.apache.bcel.internal.generic.InstructionConst.getInstruction -@Patch( +internal val disableCairoSettingsPatch = bytecodePatch( description = "Disables Cairo Fragment from being used.", - dependencies = [ - VersionCheckPatch::class - ] -) -internal object DisableCairoSettingsPatch : BytecodePatch( - setOf(CarioFragmentConfigFingerprint) ) { - override fun execute(context: BytecodeContext) { - if (!VersionCheckPatch.is_19_04_or_greater) { - return + dependsOn(versionCheckPatch) + + val cairoFragmentConfigMatch by cairoFragmentConfigFingerprint() + + execute { + if (!is_19_04_or_greater) { + return@execute } /** @@ -33,24 +29,25 @@ internal object DisableCairoSettingsPatch : BytecodePatch( * Cairo Fragment was added since YouTube v19.04.38. * * Disable this for the following reasons: - * 1. [BackgroundPlaybackPatch] does not activate the Minimized playback setting of Cairo Fragment. + * 1. [backgroundPlaybackPatch] does not activate the Minimized playback setting of Cairo Fragment. * 2. Some patches do not yet support Cairo Fragments (ie: custom Seekbar color). * 3. Settings preferences added by ReVanced are missing. * * Screenshots of the Cairo Fragment: * uYouPlus#1468. */ - CarioFragmentConfigFingerprint.resultOrThrow().mutableMethod.apply { + cairoFragmentConfigMatch.mutableMethod.apply { val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow( - CarioFragmentConfigFingerprint.CAIRO_CONFIG_LITERAL_VALUE + CAIRO_CONFIG_LITERAL_VALUE, ) + val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) val register = getInstruction(resultIndex).registerA addInstruction( resultIndex + 1, - "const/16 v$register, 0x0" + "const/16 v$register, 0x0", ) } } -} \ No newline at end of file +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/Fingerprints.kt new file mode 100644 index 0000000000..35ac5f7ae1 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/Fingerprints.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.youtube.misc.fix.cairo + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Added in YouTube v19.04.38. + * + * When this value is true, Cairo Fragment is used. + * In this case, some of the patches may be broken, so set this value to FALSE. + */ +const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L + +internal val cairoFragmentConfigFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + literal { CAIRO_CONFIG_LITERAL_VALUE } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/Fingerprints.kt new file mode 100644 index 0000000000..71bd1c62ca --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/Fingerprints.kt @@ -0,0 +1,261 @@ +package app.revanced.patches.youtube.misc.fix.playback + +import app.revanced.patcher.fingerprint +import app.revanced.util.containsWideLiteralInstructionValue +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal val buildInitPlaybackRequestFingerprint = fingerprint { + returns("Lorg/chromium/net/UrlRequest\$Builder;") + opcodes( + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with. + ) + strings( + "Content-Type", + "Range", + ) +} + +internal val buildPlayerRequestURIFingerprint = fingerprint { + returns("Ljava/lang/String;") + opcodes( + Opcode.INVOKE_VIRTUAL, // Register holds player request URI. + Opcode.MOVE_RESULT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.MONITOR_EXIT, + Opcode.RETURN_OBJECT, + ) + strings( + "youtubei/v1", + "key", + "asig", + ) +} + +internal val createPlaybackSpeedMenuItemFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + opcodes( + Opcode.IGET_OBJECT, // First instruction of the method + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.INVOKE_INTERFACE, + null, // MOVE_RESULT or MOVE_RESULT_OBJECT, Return value controls the creation of the playback speed menu item. + ) + // 19.01 and earlier is missing the second parameter. + // Since this fingerprint is somewhat weak, work around by checking for both method parameter signatures. + custom { method, _ -> + // 19.01 and earlier parameters are: "[L" + // 19.02+ parameters are "[L", "F" + val parameterTypes = method.parameterTypes + val firstParameter = parameterTypes.firstOrNull() + + if (firstParameter == null || !firstParameter.startsWith("[L")) { + return@custom false + } + + parameterTypes.size == 1 || (parameterTypes.size == 2 && parameterTypes[1] == "F") + } +} + +internal val createPlayerRequestBodyFingerprint = fingerprint { + returns("V") + parameters("L") + opcodes( + Opcode.CHECK_CAST, + Opcode.IGET, + Opcode.AND_INT_LIT16, + ) + strings("ms") +} + +internal fun indexOfBuildModelInstruction(method: Method) = + method.indexOfFirstInstruction { + val reference = getReference() + reference?.definingClass == "Landroid/os/Build;" && + reference.name == "MODEL" && + reference.type == "Ljava/lang/String;" + } + +internal val createPlayerRequestBodyWithModelFingerprint = fingerprint { + returns("L") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() + custom { method, _ -> + method.containsWideLiteralInstructionValue(1073741824) && indexOfBuildModelInstruction(method) >= 0 + } +} + +internal val playerGestureConfigSyntheticFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Ljava/lang/Object;") + opcodes( + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed. + Opcode.MOVE_RESULT, + Opcode.CHECK_CAST, + Opcode.IPUT_BOOLEAN, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed. + Opcode.MOVE_RESULT, + Opcode.IPUT_BOOLEAN, + Opcode.RETURN_VOID, + ) + custom { method, classDef -> + fun indexOfDownAndOutAllowedInstruction() = + method.indexOfFirstInstruction { + val reference = getReference() + reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" && + reference.parameterTypes.isEmpty() && + reference.returnType == "Z" + } + + // This method is always called "a" because this kind of class always has a single method. + method.name == "a" && + classDef.methods.count() == 2 && + indexOfDownAndOutAllowedInstruction() >= 0 + } +} + +internal val setPlayerRequestClientTypeFingerprint = fingerprint { + opcodes( + Opcode.IGET, + Opcode.IPUT, // Sets ClientInfo.clientId. + ) + strings("10.29") + literal { 134217728 } +} + +internal fun indexOfBuildVersionReleaseInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + val reference = getReference() + reference?.definingClass == "Landroid/os/Build\$VERSION;" && + reference.name == "RELEASE" && + reference.type == "Ljava/lang/String;" + } + +internal val createPlayerRequestBodyWithVersionReleaseFingerprint = fingerprint { + returns("L") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() + custom { method, _ -> + method.containsWideLiteralInstructionValue(1073741824) && indexOfBuildVersionReleaseInstruction(method) >= 0 + } +} + +internal val buildRequestFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Lorg/chromium/net/UrlRequest;") + custom { methodDef, _ -> + // Different targets have slightly different parameters + + // Earlier targets have parameters: + // L + // Ljava/util/Map; + // [B + // L + // L + // L + // Lorg/chromium/net/UrlRequest$Callback; + + // Later targets have parameters: + // L + // Ljava/util/Map; + // [B + // L + // L + // L + // Lorg/chromium/net/UrlRequest\$Callback; + // L + + val parameterTypes = methodDef.parameterTypes + (parameterTypes.size == 7 || parameterTypes.size == 8) && + parameterTypes[1] == "Ljava/util/Map;" // URL headers. + } +} + +internal val playerResponseModelBackgroundAudioPlaybackFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;") + opcodes( + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.GOTO, + Opcode.RETURN, + null, // Opcode.CONST_4 or Opcode.MOVE + Opcode.RETURN, + ) +} + +internal val protobufClassParseByteBufferFingerprint = fingerprint { + accessFlags(AccessFlags.PROTECTED, AccessFlags.STATIC) + returns("L") + parameters("L", "Ljava/nio/ByteBuffer;") + opcodes( + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.RETURN_OBJECT, + ) + custom { method, _ -> method.name == "parseFrom" } +} + +internal val createStreamingDataFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters("L") + opcodes( + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.IPUT_OBJECT, + ) + custom { method, classDef -> + classDef.fields.any { field -> + field.name == "a" && field.type.endsWith("/StreamingDataOuterClass\$StreamingData;") + } + } +} + +internal val buildMediaDataSourceFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters( + "Landroid/net/Uri;", + "J", + "I", + "[B", + "Ljava/util/Map;", + "J", + "J", + "Ljava/lang/String;", + "I", + "Ljava/lang/Object;", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch.kt new file mode 100644 index 0000000000..c560d7e72a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofVideoStreamsPatch.kt @@ -0,0 +1,248 @@ +package app.revanced.patches.youtube.misc.fix.playback + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +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.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.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch;" + +@Suppress("unused") +val spoofVideoStreamsPatch = bytecodePatch( + name = "Spoof video streams", + description = "Spoofs the client video streams to allow video playback.", +) { + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + dependsOn( + settingsPatch, + addResourcesPatch, + userAgentClientSpoofPatch, + ) + + val buildInitPlaybackRequestMatch by buildInitPlaybackRequestFingerprint() + val buildPlayerRequestURIMatch by buildPlayerRequestURIFingerprint() + val createStreamingDataMatch by createStreamingDataFingerprint() + val buildMediaDataSourceMatch by buildMediaDataSourceFingerprint() + val buildRequestMatch by buildRequestFingerprint() + val protobufClassParseByteBufferMatch by protobufClassParseByteBufferFingerprint() + + execute { + addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch") + + PreferenceScreen.MISC.addPreferences( + PreferenceScreenPreference( + key = "revanced_spoof_video_streams_screen", + sorting = PreferenceScreenPreference.Sorting.UNSORTED, + preferences = setOf( + SwitchPreference("revanced_spoof_video_streams"), + ListPreference( + "revanced_spoof_video_streams_client", + summaryKey = null, + ), + SwitchPreference( + "revanced_spoof_video_streams_ios_force_avc", + tag = "app.revanced.extension.youtube.settings.preference.ForceAVCSpoofingPreference", + ), + NonInteractivePreference("revanced_spoof_video_streams_about_android_vr"), + NonInteractivePreference("revanced_spoof_video_streams_about_ios"), + ), + ), + ) + + // region Block /initplayback requests to fall back to /get_watch requests. + + val moveUriStringIndex = buildInitPlaybackRequestMatch.patternMatch!!.startIndex + + buildInitPlaybackRequestMatch.mutableMethod.apply { + val targetRegister = getInstruction(moveUriStringIndex).registerA + + addInstructions( + moveUriStringIndex + 1, + """ + invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$targetRegister + """, + ) + } + + // endregion + + // region Block /get_watch requests to fall back to /player requests. + + val invokeToStringIndex = buildPlayerRequestURIMatch.patternMatch!!.startIndex + + buildPlayerRequestURIMatch.mutableMethod.apply { + val uriRegister = getInstruction(invokeToStringIndex).registerC + + addInstructions( + invokeToStringIndex, + """ + invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; + move-result-object v$uriRegister + """, + ) + } + + // endregion + + // region Get replacement streams at player requests. + + buildRequestMatch.mutableMethod.apply { + val newRequestBuilderIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_VIRTUAL && + getReference()?.name == "newUrlRequestBuilder" + } + val urlRegister = getInstruction(newRequestBuilderIndex).registerD + val freeRegister = getInstruction(newRequestBuilderIndex + 1).registerA + + addInstructions( + newRequestBuilderIndex, + """ + move-object v$freeRegister, p1 + invoke-static { v$urlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V + """, + ) + } + + // endregion + + // region Replace the streaming data with the replacement streams. + + createStreamingDataMatch.mutableMethod.apply { + val setStreamDataMethodName = "patch_setStreamingData" + val resultMethodType = createStreamingDataMatch.mutableClass.type + val videoDetailsIndex = createStreamingDataMatch.patternMatch!!.endIndex + val videoDetailsRegister = getInstruction(videoDetailsIndex).registerA + val videoDetailsClass = getInstruction(videoDetailsIndex).getReference()!!.type + + addInstruction( + videoDetailsIndex + 1, + "invoke-direct { p0, v$videoDetailsRegister }, " + + "$resultMethodType->$setStreamDataMethodName($videoDetailsClass)V", + ) + + val protobufClass = protobufClassParseByteBufferMatch.mutableMethod.definingClass + val setStreamingDataIndex = createStreamingDataMatch.patternMatch!!.startIndex + + val playerProtoClass = getInstruction(setStreamingDataIndex + 1) + .getReference()!!.definingClass + + val setStreamingDataField = getInstruction(setStreamingDataIndex).getReference() + + val getStreamingDataField = getInstruction( + indexOfFirstInstructionOrThrow { + opcode == Opcode.IGET_OBJECT && getReference()?.definingClass == playerProtoClass + }, + ).getReference() + + // Use a helper method to avoid the need of picking out multiple free registers from the hooked code. + createStreamingDataMatch.mutableClass.methods.add( + ImmutableMethod( + resultMethodType, + setStreamDataMethodName, + listOf(ImmutableMethodParameter(videoDetailsClass, null, "videoDetails")), + "V", + AccessFlags.PRIVATE.value or AccessFlags.FINAL.value, + null, + null, + MutableMethodImplementation(9), + ).toMutable().apply { + addInstructionsWithLabels( + 0, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isSpoofingEnabled()Z + move-result v0 + if-eqz v0, :disabled + + # Get video id. + iget-object v2, p1, $videoDetailsClass->c:Ljava/lang/String; + if-eqz v2, :disabled + + # Get streaming data. + invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer; + move-result-object v3 + if-eqz v3, :disabled + + # Parse streaming data. + sget-object v4, $playerProtoClass->a:$playerProtoClass + invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass + move-result-object v5 + check-cast v5, $playerProtoClass + + # Set streaming data. + iget-object v6, v5, $getStreamingDataField + if-eqz v6, :disabled + iput-object v6, p0, $setStreamingDataField + + :disabled + return-void + """, + ) + }, + ) + } + + // endregion + + // region Remove /videoplayback request body to fix playback. + // It is assumed, YouTube makes a request with a body tuned for Android. + // Requesting streams intended for other platforms with a body tuned for Android could be the cause of 400 errors. + // A proper fix may include modifying the request body to match the platforms expected body. + + buildMediaDataSourceMatch.mutableMethod.apply { + val targetIndex = instructions.lastIndex + + // Instructions are added just before the method returns, + // so there's no concern of clobbering in-use registers. + addInstructions( + targetIndex, + """ + # Field a: Stream uri. + # Field c: Http method. + # Field d: Post data. + move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register. + iget-object v1, v0, $definingClass->a:Landroid/net/Uri; + iget v2, v0, $definingClass->c:I + iget-object v3, v0, $definingClass->d:[B + invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B + move-result-object v1 + iput-object v1, v0, $definingClass->d:[B + """, + ) + } + // endregion + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt similarity index 54% rename from src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt index 2437dfda61..8bbd0ec778 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt @@ -2,39 +2,30 @@ package app.revanced.patches.youtube.misc.fix.playback import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch import app.revanced.patches.all.misc.transformation.IMethodCall -import app.revanced.patches.all.misc.transformation.Instruction35cInfo import app.revanced.patches.all.misc.transformation.filterMapInstruction35c +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.ClassDef -import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.StringReference -object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch() { - private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube" - private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE = - "Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;" +private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube" +private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE = + "Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;" - override fun filterMap( - classDef: ClassDef, - method: Method, - instruction: Instruction, - instructionIndex: Int, - ) = filterMapInstruction35c( - "Lapp/revanced/integrations", - classDef, - instruction, - instructionIndex, - ) - - override fun transform(mutableMethod: MutableMethod, entry: Instruction35cInfo) { +val userAgentClientSpoofPatch = transformInstructionsPatch( + filterMap = { classDef, _, instruction, instructionIndex -> + filterMapInstruction35c( + "Lapp/revanced/extension", + classDef, + instruction, + instructionIndex, + ) + }, + transform = transform@{ mutableMethod, entry -> val (_, _, instructionIndex) = entry // Replace the result of context.getPackageName(), if it is used in a user agent string. @@ -42,16 +33,16 @@ object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch()?.toString() // Only replace string builder usage. if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) { - return + return@transform } // Do not change the package name in methods that use resources, or for methods that use GmsCore. @@ -60,10 +51,10 @@ object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch() opcode == Opcode.CONST_STRING && - (reference?.string == "android.resource://" || reference?.string == "gcore_") + (reference?.string == "android.resource://" || reference?.string == "gcore_") } if (resourceOrGmsStringInstructionIndex >= 0) { - return + return@transform } // Overwrite the result of context.getPackageName() with the original package name. @@ -72,20 +63,20 @@ object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch, - override val returnType: String, - ) : IMethodCall { - GetPackageName( - "Landroid/content/Context;", - "getPackageName", - emptyArray(), - "Ljava/lang/String;", - ), - } +@Suppress("unused") +private enum class MethodCall( + override val definedClassName: String, + override val methodName: String, + override val methodParams: Array, + override val returnType: String, +) : IMethodCall { + GetPackageName( + "Landroid/content/Context;", + "getPackageName", + emptyArray(), + "Ljava/lang/String;", + ), } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/gms/Constants.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/Constants.kt similarity index 100% rename from src/main/kotlin/app/revanced/patches/youtube/misc/gms/Constants.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/Constants.kt diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt new file mode 100644 index 0000000000..1fea1f9bd6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt @@ -0,0 +1,70 @@ +package app.revanced.patches.youtube.misc.gms + +import app.revanced.patcher.patch.Option +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.castContextFetchFingerprint +import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch +import app.revanced.patches.shared.misc.settings.preference.IntentPreference +import app.revanced.patches.shared.primeMethodFingerprint +import app.revanced.patches.youtube.layout.buttons.overlay.hidePlayerOverlayButtonsPatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.fix.playback.spoofVideoStreamsPatch +import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME +import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint + +@Suppress("unused") +val gmsCoreSupportPatch = gmsCoreSupportPatch( + fromPackageName = YOUTUBE_PACKAGE_NAME, + toPackageName = REVANCED_YOUTUBE_PACKAGE_NAME, + primeMethodFingerprint = primeMethodFingerprint, + earlyReturnFingerprints = setOf( + castContextFetchFingerprint, + ), + mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + extensionPatch = sharedExtensionPatch, + gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch, +) { + dependsOn( + hidePlayerOverlayButtonsPatch, // Hide non-functional cast button. + spoofVideoStreamsPatch, + ) + + compatibleWith( + YOUTUBE_PACKAGE_NAME( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) +} + +private fun gmsCoreSupportResourcePatch( + gmsCoreVendorGroupIdOption: Option, +) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch( + fromPackageName = YOUTUBE_PACKAGE_NAME, + toPackageName = REVANCED_YOUTUBE_PACKAGE_NAME, + gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption, + spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600", + executeBlock = { + addResources("youtube", "misc.gms.gmsCoreSupportResourcePatch") + + val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption + + PreferenceScreen.MISC.addPreferences( + IntentPreference( + "microg_settings", + intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") { + "$gmsCoreVendorGroupId.android.gms" + }, + ), + ) + }, +) { + dependsOn(settingsPatch, addResourcesPatch) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt new file mode 100644 index 0000000000..8e21147f6e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt @@ -0,0 +1,114 @@ +package app.revanced.patches.youtube.misc.imageurlhook + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.util.applyMatch +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod + +private lateinit var loadImageUrlMethod: MutableMethod +private var loadImageUrlIndex = 0 + +private lateinit var loadImageSuccessCallbackMethod: MutableMethod +private var loadImageSuccessCallbackIndex = 0 + +private lateinit var loadImageErrorCallbackMethod: MutableMethod +private var loadImageErrorCallbackIndex = 0 + +val cronetImageUrlHookPatch = bytecodePatch( + description = "Hooks Cronet image urls.", +) { + dependsOn(sharedExtensionPatch) + + val messageDigestImageUrlParentMatch by messageDigestImageUrlParentFingerprint() + val onResponseStartedMatch by onResponseStartedFingerprint() + val requestMatch by requestFingerprint() + + execute { context -> + loadImageUrlMethod = messageDigestImageUrlFingerprint + .applyMatch(context, messageDigestImageUrlParentMatch).mutableMethod + + loadImageSuccessCallbackMethod = onSucceededFingerprint + .applyMatch(context, onResponseStartedMatch).mutableMethod + + loadImageErrorCallbackMethod = onFailureFingerprint + .applyMatch(context, onResponseStartedMatch).mutableMethod + + // The URL is required for the failure callback hook, but the URL field is obfuscated. + // Add a helper get method that returns the URL field. + val urlFieldInstruction = requestMatch.mutableMethod.instructions.first { + val reference = it.getReference() + it.opcode == Opcode.IPUT_OBJECT && reference?.type == "Ljava/lang/String;" + } as ReferenceInstruction + + val urlFieldName = (urlFieldInstruction.reference as FieldReference).name + val definingClass = CRONET_URL_REQUEST_CLASS_DESCRIPTOR + val addedMethodName = "getHookedUrl" + requestMatch.mutableClass.methods.add( + ImmutableMethod( + definingClass, + addedMethodName, + emptyList(), + "Ljava/lang/String;", + AccessFlags.PUBLIC.value, + null, + null, + MutableMethodImplementation(2), + ).toMutable().apply { + addInstructions( + """ + iget-object v0, p0, $definingClass->$urlFieldName:Ljava/lang/String; + return-object v0 + """, + ) + }, + ) + } +} + +/** + * @param highPriority If the hook should be called before all other hooks. + */ +fun addImageUrlHook(targetMethodClass: String, highPriority: Boolean = false) { + loadImageUrlMethod.addInstructions( + if (highPriority) 0 else loadImageUrlIndex, +""" + invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String; + move-result-object p1 + """, + ) + loadImageUrlIndex += 2 +} + +/** + * If a connection completed, which includes normal 200 responses but also includes + * status 404 and other error like http responses. + */ +fun addImageUrlSuccessCallbackHook(targetMethodClass: String) { + loadImageSuccessCallbackMethod.addInstruction( + loadImageSuccessCallbackIndex++, + "invoke-static { p1, p2 }, $targetMethodClass->handleCronetSuccess(" + + "Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;)V", + ) +} + +/** + * If a connection outright failed to complete any connection. + */ +fun addImageUrlErrorCallbackHook(targetMethodClass: String) { + loadImageErrorCallbackMethod.addInstruction( + loadImageErrorCallbackIndex++, + "invoke-static { p1, p2, p3 }, $targetMethodClass->handleCronetFailure(" + + "Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/Fingerprints.kt new file mode 100644 index 0000000000..fcd5298acf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/Fingerprints.kt @@ -0,0 +1,64 @@ +package app.revanced.patches.youtube.misc.imageurlhook + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val onFailureFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters( + "Lorg/chromium/net/UrlRequest;", + "Lorg/chromium/net/UrlResponseInfo;", + "Lorg/chromium/net/CronetException;" + ) + custom { method, _ -> + method.name == "onFailed" + } +} + +// Acts as a parent fingerprint. +internal val onResponseStartedFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;") + strings( + "Content-Length", + "Content-Type", + "identity", + "application/x-protobuf", + ) + custom { method, _ -> + method.name == "onResponseStarted" + } +} + +internal val onSucceededFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;") + custom { method, _ -> + method.name == "onSucceeded" + } +} + +internal const val CRONET_URL_REQUEST_CLASS_DESCRIPTOR = "Lorg/chromium/net/impl/CronetUrlRequest;" + +internal val requestFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + custom { _, classDef -> + classDef.type == CRONET_URL_REQUEST_CLASS_DESCRIPTOR + } +} + +internal val messageDigestImageUrlFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters("Ljava/lang/String;", "L") +} + +internal val messageDigestImageUrlParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/String;") + parameters() + strings("@#&=*+-_.,:!?()/~'%;\$") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt new file mode 100644 index 0000000000..002c6e8639 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt @@ -0,0 +1,86 @@ +package app.revanced.patches.youtube.misc.links + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Suppress("unused") +val bypassURLRedirectsPatch = bytecodePatch( + name = "Bypass URL redirects", + description = "Adds an option to bypass URL redirects and open the original URL directly.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + versionCheckPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val abUriParserMatch by abUriParserFingerprint() + val abUriParserLegacyMatch by abUriParserLegacyFingerprint() + val httpUriParserMatch by httpUriParserFingerprint() + val httpUriParserLegacyMatch by httpUriParserLegacyFingerprint() + + execute { + addResources("youtube", "misc.links.bypassURLRedirectsPatch") + + PreferenceScreen.MISC.addPreferences( + SwitchPreference("revanced_bypass_url_redirects"), + ) + + val matches = if (is_19_33_or_greater) { + arrayOf( + abUriParserMatch, + httpUriParserMatch, + ) + } else { + arrayOf( + abUriParserLegacyMatch, + httpUriParserLegacyMatch, + ) + } + + matches.forEach { + it.mutableMethod.apply { + val insertIndex = findUriParseIndex() + val uriStringRegister = getInstruction(insertIndex).registerC + + replaceInstruction( + insertIndex, + "invoke-static {v$uriStringRegister}," + + "Lapp/revanced/extension/youtube/patches/BypassURLRedirectsPatch;" + + "->" + + "parseRedirectUri(Ljava/lang/String;)Landroid/net/Uri;", + ) + } + } + } +} + +internal fun Method.findUriParseIndex() = indexOfFirstInstruction { + val reference = getReference() + reference?.returnType == "Landroid/net/Uri;" && reference.name == "parse" +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt new file mode 100644 index 0000000000..73f6fae75b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt @@ -0,0 +1,72 @@ +package app.revanced.patches.youtube.misc.links + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +/** + * Target 19.33+ + */ +internal val abUriParserFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/Object") + parameters("Ljava/lang/Object") + strings( + "Found entityKey=`", + "` that does not contain a PlaylistVideoEntityId message as it's identifier.", + ) + custom { method, _ -> + method.findUriParseIndex() >= 0 + } +} + +internal val abUriParserLegacyFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/Object") + parameters("Ljava/lang/Object") + opcodes( + Opcode.RETURN_OBJECT, + Opcode.CHECK_CAST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.RETURN_OBJECT, + Opcode.CHECK_CAST, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.RETURN_OBJECT, + Opcode.CHECK_CAST, + ) + custom { methodDef, classDef -> + // This method is always called "a" because this kind of class always has a single (non-synthetic) method. + + if (methodDef.name != "a") return@custom false + + val count = classDef.methods.count() + count == 2 || count == 3 + } +} + +/** + * Target 19.33+ + */ +internal val httpUriParserFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Landroid/net/Uri") + parameters("Ljava/lang/String") + strings("https", "https:", "://") + custom { methodDef, _ -> + methodDef.findUriParseIndex() >= 0 + } +} + +internal val httpUriParserLegacyFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Landroid/net/Uri") + parameters("Ljava/lang/String") + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + ) + strings("://") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt new file mode 100644 index 0000000000..15e7b3c6e4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/OpenLinksExternallyPatch.kt @@ -0,0 +1,60 @@ +package app.revanced.patches.youtube.misc.links + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +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.reference.StringReference + +@Suppress("unused") +val openLinksExternallyPatch = bytecodePatch( + name = "Open links externally", + description = "Adds an option to always open links in your browser instead of in the in-app-browser.", +) { + dependsOn( + transformInstructionsPatch( + filterMap = filterMap@{ _, _, instruction, instructionIndex -> + if (instruction !is ReferenceInstruction) return@filterMap null + val reference = instruction.reference as? StringReference ?: return@filterMap null + + if (reference.string != "android.support.customtabs.action.CustomTabsService") return@filterMap null + + return@filterMap instructionIndex to (instruction as OneRegisterInstruction).registerA + }, + transform = { mutableMethod, entry -> + val (intentStringIndex, register) = entry + + // Hook the intent string. + mutableMethod.addInstructions( + intentStringIndex + 1, + """ + invoke-static {v$register}, Lapp/revanced/extension/youtube/patches/OpenLinksExternallyPatch;->getIntent(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$register + """, + ) + }, + ), + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + execute { + addResources("youtube", "misc.links.openLinksExternallyPatch") + + PreferenceScreen.MISC.addPreferences( + SwitchPreference("revanced_external_browser"), + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt new file mode 100644 index 0000000000..5c0b0c816d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt @@ -0,0 +1,57 @@ +package app.revanced.patches.youtube.misc.litho.filter + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +/** + * In 19.17 and earlier, this resolves to the same method as [readComponentIdentifierFingerprint]. + * In 19.18+ this resolves to a different method. + */ +internal val componentContextParserFingerprint = fingerprint { + strings("Component was not found %s because it was removed due to duplicate converter bindings.") +} + +internal val emptyComponentBuilderFingerprint = fingerprint { + opcodes( + Opcode.INVOKE_INTERFACE, + Opcode.INVOKE_STATIC_RANGE, + ) +} + +internal val lithoFilterFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + returns("V") + custom { _, classDef -> + classDef.endsWith("LithoFilterPatch;") + } +} + +internal val protobufBufferReferenceFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("I", "Ljava/nio/ByteBuffer;") + opcodes( + Opcode.IPUT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.SUB_INT_2ADDR, + ) +} + +/** +* In 19.17 and earlier, this resolves to the same method as [componentContextParserFingerprint]. +* In 19.18+ this resolves to a different method. +*/ +internal val readComponentIdentifierFingerprint = fingerprint { + strings("Number of bits must be positive") +} + +internal val emptyComponentFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR) + parameters() + strings("EmptyComponent") + custom { _, classDef -> + classDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt new file mode 100644 index 0000000000..a3936d54d2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt @@ -0,0 +1,243 @@ +@file:Suppress("SpellCheckingInspection") + +package app.revanced.patches.youtube.misc.litho.filter + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_19_18_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.* +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +lateinit var addLithoFilter: (String) -> Unit + private set + +internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/LithoFilterPatch;" + +val lithoFilterPatch = bytecodePatch( + description = "Hooks the method which parses the bytes into a ComponentContext to filter components.", +) { + dependsOn( + sharedExtensionPatch, + versionCheckPatch, + ) + + val componentContextParserMatch by componentContextParserFingerprint() + val lithoFilterMatch by lithoFilterFingerprint() + val protobufBufferReferenceMatch by protobufBufferReferenceFingerprint() + val readComponentIdentifierMatch by readComponentIdentifierFingerprint() + val emptyComponentMatch by emptyComponentFingerprint() + + var filterCount = 0 + + /** + * The following patch inserts a hook into the method that parses the bytes into a ComponentContext. + * This method contains a StringBuilder object that represents the pathBuilder of the component. + * The pathBuilder is used to filter components by their path. + * + * Additionally, the method contains a reference to the component's identifier. + * The identifier is used to filter components by their identifier. + * + * The protobuf buffer is passed along from a different injection point before the filtering occurs. + * The buffer is a large byte array that represents the component tree. + * This byte array is searched for strings that indicate the current component. + * + * The following pseudocode shows how the patch works: + * + * class SomeOtherClass { + * // Called before ComponentContextParser.parseBytesToComponentContext method. + * public void someOtherMethod(ByteBuffer byteBuffer) { + * ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch. + * ... + * } + * } + * + * When patching 19.17 and earlier: + * + * class ComponentContextParser { + * public ComponentContext ReadComponentIdentifierFingerprint(...) { + * ... + * if (extensionClass.filter(identifier, pathBuilder)); // Inserted by this patch. + * return emptyComponent; + * ... + * } + * } + * + * When patching 19.18 and later: + * + * class ComponentContextParser { + * public ComponentContext parseBytesToComponentContext(...) { + * ... + * if (ReadComponentIdentifierFingerprint() == null); // Inserted by this patch. + * return emptyComponent; + * ... + * } + * + * public ComponentIdentifierObj readComponentIdentifier(...) { + * ... + * if (extensionClass.filter(identifier, pathBuilder)); // Inserted by this patch. + * return null; + * ... + * } + * } + */ + execute { context -> + // Remove dummy filter from extenion static field + // and add the filters included during patching. + lithoFilterMatch.mutableMethod.apply { + removeInstructions(2, 4) // Remove dummy filter. + + addLithoFilter = { classDescriptor -> + addInstructions( + 2, + """ + new-instance v1, $classDescriptor + invoke-direct {v1}, $classDescriptor->()V + const/16 v2, ${filterCount++} + aput-object v1, v0, v2 + """, + ) + } + } + + // region Pass the buffer into extension. + + protobufBufferReferenceMatch.mutableMethod.addInstruction( + 0, + " invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", + ) + + // endregion + + // region Hook the method that parses bytes into a ComponentContext. + + val readComponentMethod = readComponentIdentifierMatch.method + // Get the only static method in the class. + val builderMethodDescriptor = emptyComponentMatch.classDef.methods.first { method -> + AccessFlags.STATIC.isSet(method.accessFlags) + } + // Only one field. + val emptyComponentField = context.classBy { classDef -> + builderMethodDescriptor.returnType == classDef.type + }!!.immutableClass.fields.single() + + componentContextParserMatch.mutableMethod.apply { + // 19.18 and later require patching 2 methods instead of one. + // Otherwise, the patched code is the same. + if (is_19_18_or_greater) { + // Get the method name of the ReadComponentIdentifierFingerprint call. + val readComponentMethodCallIndex = indexOfFirstInstructionOrThrow { + val reference = getReference() + reference?.definingClass == readComponentMethod.definingClass && + reference.name == readComponentMethod.name + } + + // Result of read component, and also a free register. + val register = getInstruction(readComponentMethodCallIndex + 1).registerA + + // Insert after 'move-result-object' + val insertHookIndex = readComponentMethodCallIndex + 2 + + // Return an EmptyComponent instead of the original component if the filterState method returns true. + addInstructionsWithLabels( + insertHookIndex, + """ + if-nez v$register, :unfiltered + + # Component was filtered in ReadComponentIdentifierFingerprint hook + move-object/from16 v$register, p1 + invoke-static { v$register }, $builderMethodDescriptor + move-result-object v$register + iget-object v$register, v$register, $emptyComponentField + return-object v$register + """, + ExternalLabel("unfiltered", getInstruction(insertHookIndex)), + ) + } + } + + // endregion + + // region Read component then store the result. + + readComponentIdentifierMatch.mutableMethod.apply { + val insertHookIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.IPUT_OBJECT && + getReference()?.type == "Ljava/lang/StringBuilder;" + } + val stringBuilderRegister = getInstruction(insertHookIndex).registerA + + // Identifier is saved to a field just before the string builder. + val identifierRegister = getInstruction( + indexOfFirstInstructionReversedOrThrow(insertHookIndex) { + opcode == Opcode.IPUT_OBJECT && + getReference()?.type == "Ljava/lang/String;" + }, + ).registerA + + // Find a free temporary register. + val register = getInstruction( + // Immediately before is a StringBuilder append constant character. + indexOfFirstInstructionReversedOrThrow(insertHookIndex, Opcode.CONST_16), + ).registerA + + // Verify the temp register will not clobber the method result register. + if (stringBuilderRegister == register) { + throw PatchException("Free register will clobber StringBuilder register") + } + + val invokeFilterInstructions = """ + invoke-static { v$identifierRegister, v$stringBuilderRegister }, $EXTENSION_CLASS_DESCRIPTOR->filter(Ljava/lang/String;Ljava/lang/StringBuilder;)Z + move-result v$register + if-eqz v$register, :unfiltered + """ + + addInstructionsWithLabels( + insertHookIndex, + if (is_19_18_or_greater) { + """ + $invokeFilterInstructions + + # Return null, and the ComponentContextParserFingerprint hook + # handles returning an empty component. + const/4 v$register, 0x0 + return-object v$register + """ + } else { + """ + $invokeFilterInstructions + + # Exact same code as ComponentContextParserFingerprint hook, + # but with the free register of this method. + move-object/from16 v$register, p1 + invoke-static { v$register }, $builderMethodDescriptor + move-result-object v$register + iget-object v$register, v$register, $emptyComponentField + return-object v$register + """ + }, + ExternalLabel("unfiltered", getInstruction(insertHookIndex)), + ) + } + + // endregion + } + + finalize { + lithoFilterMatch.mutableMethod.replaceInstruction(0, "const/16 v0, $filterCount") + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/Fingerprints.kt new file mode 100644 index 0000000000..0f5ade30a6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/Fingerprints.kt @@ -0,0 +1,107 @@ +package app.revanced.patches.youtube.misc.navigation + +import app.revanced.patcher.fingerprint +import app.revanced.patches.youtube.layout.buttons.navigation.navigationButtonsPatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal val actionBarSearchResultsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/view/View;") + literal { actionBarSearchResultsViewMicId } +} + +/** + * Matches to the class found in [pivotBarConstructorFingerprint]. + */ +internal val initializeButtonsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + literal { imageOnlyTabResourceId } +} + +internal val mainActivityOnBackPressedFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + custom { method, classDef -> + val matchesClass = classDef.endsWith("MainActivity;") || + // Old versions of YouTube called this class "WatchWhileActivity" instead. + classDef.endsWith("WatchWhileActivity;") + + matchesClass && method.name == "onBackPressed" + } +} + +/** + * Extension method, used for callback into to other patches. + * Specifically, [navigationButtonsPatch]. + */ +internal val navigationBarHookCallbackFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) + returns("V") + parameters(EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR, "Landroid/view/View;") + custom { method, _ -> + method.name == "navigationTabCreatedCallback" && + method.definingClass == EXTENSION_CLASS_DESCRIPTOR + } +} + +/** + * Matches to the Enum class that looks up ordinal -> instance. + */ +internal val navigationEnumFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + strings( + "PIVOT_HOME", + "TAB_SHORTS", + "CREATION_TAB_LARGE", + "PIVOT_SUBSCRIPTIONS", + "TAB_ACTIVITY", + "VIDEO_LIBRARY_WHITE", + "INCOGNITO_CIRCLE", + ) +} + +internal val pivotBarButtonsCreateDrawableViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/view/View;") + custom { method, _ -> + method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" && + // Only one method has a Drawable parameter. + method.parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;" + } +} + +internal val pivotBarButtonsCreateResourceViewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/view/View;") + parameters("L", "Z", "I", "L") + custom { method, _ -> + method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" + } +} + +internal fun indexOfSetViewSelectedInstruction(method: Method) = method.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setSelected" +} + +internal val pivotBarButtonsViewSetSelectedFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("I", "Z") + custom { method, _ -> + indexOfSetViewSelectedInstruction(method) >= 0 && + method.definingClass == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" + } +} + +internal val pivotBarConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + strings("com.google.android.apps.youtube.app.endpoint.flags") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt new file mode 100644 index 0000000000..fe9002a326 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt @@ -0,0 +1,163 @@ +package app.revanced.patches.youtube.misc.navigation + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.util.* +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil + +internal var imageOnlyTabResourceId = -1L + private set +internal var actionBarSearchResultsViewMicId = -1L + private set + +private val navigationBarHookResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + execute { + imageOnlyTabResourceId = resourceMappings["layout", "image_only_tab"] + actionBarSearchResultsViewMicId = resourceMappings["layout", "action_bar_search_results_view_mic"] + } +} + +internal const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/shared/NavigationBar;" +internal const val EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR = + "Lapp/revanced/extension/youtube/shared/NavigationBar\$NavigationButton;" + +lateinit var hookNavigationButtonCreated: (String) -> Unit + +@Suppress("unused") +val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navigation or search bar.") { + dependsOn( + sharedExtensionPatch, + navigationBarHookResourcePatch, + playerTypeHookPatch, // Required to detect the search bar in all situations. + ) + + val pivotBarConstructorMatch by pivotBarConstructorFingerprint() + val navigationEnumMatch by navigationEnumFingerprint() + val pivotBarButtonsCreateDrawableViewMatch by pivotBarButtonsCreateDrawableViewFingerprint() + val pivotBarButtonsCreateResourceViewMatch by pivotBarButtonsCreateResourceViewFingerprint() + val pivotBarButtonsViewSetSelectedMatch by pivotBarButtonsViewSetSelectedFingerprint() + val navigationBarHookCallbackMatch by navigationBarHookCallbackFingerprint() + val mainActivityOnBackPressedMatch by mainActivityOnBackPressedFingerprint() + val actionBarSearchResultsMatch by actionBarSearchResultsFingerprint() + + execute { context -> + fun MutableMethod.addHook(hook: Hook, insertPredicate: Instruction.() -> Boolean) { + val filtered = instructions.filter(insertPredicate) + if (filtered.isEmpty()) throw PatchException("Could not find insert indexes") + filtered.forEach { + val insertIndex = it.location.index + 2 + val register = getInstruction(insertIndex - 1).registerA + + addInstruction( + insertIndex, + "invoke-static { v$register }, " + + "$EXTENSION_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V", + ) + } + } + + initializeButtonsFingerprint.applyMatch(context, pivotBarConstructorMatch).mutableMethod.apply { + // Hook the current navigation bar enum value. Note, the 'You' tab does not have an enum value. + val navigationEnumClassName = navigationEnumMatch.mutableClass.type + addHook(Hook.SET_LAST_APP_NAVIGATION_ENUM) { + opcode == Opcode.INVOKE_STATIC && + getReference()?.definingClass == navigationEnumClassName + } + + // Hook the creation of navigation tab views. + val drawableTabMethod = pivotBarButtonsCreateDrawableViewMatch.mutableMethod + addHook(Hook.NAVIGATION_TAB_LOADED) predicate@{ + MethodUtil.methodSignaturesMatch( + getReference() ?: return@predicate false, + drawableTabMethod, + ) + } + + val imageResourceTabMethod = pivotBarButtonsCreateResourceViewMatch.method + addHook(Hook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{ + MethodUtil.methodSignaturesMatch( + getReference() ?: return@predicate false, + imageResourceTabMethod, + ) + } + } + + pivotBarButtonsViewSetSelectedMatch.mutableMethod.apply { + val index = indexOfSetViewSelectedInstruction(this) + val instruction = getInstruction(index) + val viewRegister = instruction.registerC + val isSelectedRegister = instruction.registerD + + addInstruction( + index + 1, + "invoke-static { v$viewRegister, v$isSelectedRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->navigationTabSelected(Landroid/view/View;Z)V", + ) + } + + // Hook onto back button pressed. Needed to fix race problem with + // Litho filtering based on navigation tab before the tab is updated. + mainActivityOnBackPressedMatch.mutableMethod.addInstruction( + 0, + "invoke-static { p0 }, " + + "$EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V", + ) + + // Hook the search bar. + + // Two different layouts are used at the hooked code. + // Insert before the first ViewGroup method call after inflating, + // so this works regardless which layout is used. + actionBarSearchResultsMatch.mutableMethod.apply { + val searchBarResourceId = indexOfFirstWideLiteralInstructionValueOrThrow( + actionBarSearchResultsViewMicId, + ) + + val instructionIndex = indexOfFirstInstructionOrThrow(searchBarResourceId) { + opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setLayoutDirection" + } + + val viewRegister = getInstruction(instructionIndex).registerC + + addInstruction( + instructionIndex, + "invoke-static { v$viewRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V", + ) + } + + hookNavigationButtonCreated = { extensionClassDescriptor -> + navigationBarHookCallbackMatch.mutableMethod.addInstruction( + 0, + "invoke-static { p0, p1 }, " + + "$extensionClassDescriptor->navigationTabCreated" + + "(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V", + ) + } + } +} + +private enum class Hook(val methodName: String, val parameters: String) { + SET_LAST_APP_NAVIGATION_ENUM("setLastAppNavigationEnum", "Ljava/lang/Enum;"), + NAVIGATION_TAB_LOADED("navigationTabLoaded", "Landroid/view/View;"), + NAVIGATION_IMAGE_RESOURCE_TAB_LOADED("navigationImageResourceTabLoaded", "Landroid/view/View;"), + SEARCH_BAR_RESULTS_VIEW_LOADED("searchBarResultsViewLoaded", "Landroid/view/View;"), +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt new file mode 100644 index 0000000000..5bf4561f19 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt @@ -0,0 +1,48 @@ +package app.revanced.patches.youtube.misc.playercontrols + +import app.revanced.patcher.fingerprint +import app.revanced.util.containsWideLiteralInstructionValue +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags + +internal val playerTopControlsInflateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + literal { controlsLayoutStub } +} + +internal val playerControlsExtensionHookFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("V") + parameters("Z") + custom { methodDef, classDef -> + methodDef.name == "fullscreenButtonVisibilityChanged" && + classDef.type == "Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;" + } +} + +internal val playerBottomControlsInflateFingerprint = fingerprint { + returns("Ljava/lang/Object;") + parameters() + literal { bottomUiContainerResourceId } +} + +internal val overlayViewInflateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Landroid/view/View;") + custom { methodDef, _ -> + methodDef.containsWideLiteralInstructionValue(fullscreenButton) && + methodDef.containsWideLiteralInstructionValue(heatseekerViewstub) + } +} + +/** + * Resolves to the class found in [playerTopControlsInflateFingerprint]. + */ +internal val controlsOverlayVisibilityFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + parameters("Z", "Z") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt new file mode 100644 index 0000000000..f682f6439a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt @@ -0,0 +1,273 @@ +package app.revanced.patches.youtube.misc.playercontrols + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.Document +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.util.* +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.iface.reference.TypeReference +import org.w3c.dom.Node + +/** + * Add a new top to the bottom of the YouTube player. + * + * @param resourceDirectoryName The name of the directory containing the hosting resource. + */ +@Suppress("KDocUnresolvedReference") +// Internal until this is modified to work with any patch (and not just SponsorBlock). +internal lateinit var addTopControl: (String) -> Unit + private set + +/** + * Add a new bottom to the bottom of the YouTube player. + * + * @param resourceDirectoryName The name of the directory containing the hosting resource. + */ +@Suppress("KDocUnresolvedReference") +lateinit var addBottomControl: (String) -> Unit + private set + +internal var bottomUiContainerResourceId = -1L + private set +internal var controlsLayoutStub = -1L + private set +internal var heatseekerViewstub = -1L + private set +internal var fullscreenButton = -1L + private set + +val playerControlsResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + /** + * The element to the left of the element being added. + */ + /** + * The element to the left of the element being added. + */ + var bottomLastLeftOf = "@id/fullscreen_button" + + lateinit var bottomTargetDocument: Document + + execute { context -> + val targetResourceName = "youtube_controls_bottom_ui_container.xml" + + bottomUiContainerResourceId = resourceMappings["id", "bottom_ui_container_stub"] + controlsLayoutStub = resourceMappings["id", "controls_layout_stub"] + heatseekerViewstub = resourceMappings["id", "heatseeker_viewstub"] + fullscreenButton = resourceMappings["id", "fullscreen_button"] + + bottomTargetDocument = context.document["res/layout/$targetResourceName"] + + val bottomTargetElement: Node = bottomTargetDocument.getElementsByTagName( + "android.support.constraint.ConstraintLayout", + ).item(0) + + var bottomInsertBeforeNode: Node = bottomTargetDocument.childNodes.findElementByAttributeValue( + "android:inflatedId", + bottomLastLeftOf, + ) ?: bottomTargetDocument.childNodes.findElementByAttributeValueOrThrow( + "android:id", // Older targets use non-inflated id. + bottomLastLeftOf, + ) + + addTopControl = { resourceDirectoryName -> + val resourceFileName = "host/layout/youtube_controls_layout.xml" + val hostingResourceStream = inputStreamFromBundledResource( + resourceDirectoryName, + resourceFileName + ) ?: throw PatchException("Could not find $resourceFileName") + + val document = context.document["res/layout/youtube_controls_layout.xml"] + + "RelativeLayout".copyXmlNode( + context.document[hostingResourceStream], + document, + ).use { + val element = document.childNodes.findElementByAttributeValueOrThrow( + "android:id", + "@id/player_video_heading", + ) + + // FIXME: This uses hard coded values that only works with SponsorBlock. + // If other top buttons are added by other patches, this code must be changed. + // voting button id from the voting button view from the youtube_controls_layout.xml host file + val votingButtonId = "@+id/revanced_sb_voting_button" + element.attributes.getNamedItem("android:layout_toStartOf").nodeValue = votingButtonId + } + } + + addBottomControl = { resourceDirectoryName -> + val resourceFileName = "host/layout/youtube_controls_bottom_ui_container.xml" + val sourceDocument = context.document[ + inputStreamFromBundledResource(resourceDirectoryName, resourceFileName) + ?: throw PatchException("Could not find $resourceFileName") + ] + + val sourceElements = sourceDocument.getElementsByTagName( + "android.support.constraint.ConstraintLayout", + ).item(0).childNodes + + // Copy the patch layout xml into the target layout file. + for (index in 1 until sourceElements.length) { + val element = sourceElements.item(index).cloneNode(true) + + // If the element has no attributes there's no point adding it to the destination. + if (!element.hasAttributes()) continue + + element.attributes.getNamedItem("yt:layout_constraintRight_toLeftOf").nodeValue = bottomLastLeftOf + bottomLastLeftOf = element.attributes.getNamedItem("android:id").nodeValue + + bottomTargetDocument.adoptNode(element) + // Elements do not need to be added in the layout order since a layout constraint is used, + // but in order is easier to make sense of while debugging. + bottomTargetElement.insertBefore(element, bottomInsertBeforeNode) + bottomInsertBeforeNode = element + } + + sourceDocument.close() + } + } + + finalize { + arrayOf( + "@id/bottom_end_container", + "@id/multiview_button", + ).forEach { + bottomTargetDocument.childNodes.findElementByAttributeValue( + "android:id", + it, + )?.setAttribute("yt:layout_constraintRight_toLeftOf", bottomLastLeftOf) + } + + bottomTargetDocument.close() + } +} + +/** + * Injects the code to initialize the controls. + * @param descriptor The descriptor of the method which should be called. + */ +internal fun initializeTopControl(descriptor: String) { + inflateTopControlMethod.addInstruction( + inflateTopControlInsertIndex++, + "invoke-static { v$inflateTopControlRegister }, $descriptor->initialize(Landroid/view/View;)V", + ) +} + +/** + * Injects the code to initialize the controls. + * @param descriptor The descriptor of the method which should be called. + */ +fun initializeBottomControl(descriptor: String) { + inflateBottomControlMethod.addInstruction( + inflateBottomControlInsertIndex++, + "invoke-static { v$inflateBottomControlRegister }, $descriptor->initializeButton(Landroid/view/View;)V", + ) +} + +/** + * Injects the code to change the visibility of controls. + * @param descriptor The descriptor of the method which should be called. + */ +fun injectVisibilityCheckCall(descriptor: String) { + visibilityMethod.addInstruction( + visibilityInsertIndex++, + "invoke-static { p1 , p2 }, $descriptor->changeVisibility(ZZ)V", + ) + + visibilityImmediateMethod.addInstruction( + visibilityImmediateInsertIndex++, + "invoke-static { p0 }, $descriptor->changeVisibilityImmediate(Z)V", + ) +} + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;" + +private lateinit var inflateTopControlMethod: MutableMethod +private var inflateTopControlInsertIndex: Int = -1 +private var inflateTopControlRegister: Int = -1 + +private lateinit var inflateBottomControlMethod: MutableMethod +private var inflateBottomControlInsertIndex: Int = -1 +private var inflateBottomControlRegister: Int = -1 + +private lateinit var visibilityMethod: MutableMethod +private var visibilityInsertIndex: Int = 0 + +private lateinit var visibilityImmediateMethod: MutableMethod +private var visibilityImmediateInsertIndex: Int = 0 + +val playerControlsPatch = bytecodePatch( + description = "Manages the code for the player controls of the YouTube player.", +) { + dependsOn(playerControlsResourcePatch) + + val playerBottomControlsInflateMatch by playerBottomControlsInflateFingerprint() + val playerTopControlsInflateMatch by playerTopControlsInflateFingerprint() + val overlayViewInflateMatch by overlayViewInflateFingerprint() + val playerControlsExtensionHookMatch by playerControlsExtensionHookFingerprint() + + execute { context -> + fun MutableMethod.indexOfFirstViewInflateOrThrow() = + indexOfFirstInstructionOrThrow { + val reference = getReference() + reference?.definingClass == "Landroid/view/ViewStub;" && + reference.name == "inflate" + } + + playerBottomControlsInflateMatch.mutableMethod.apply { + inflateBottomControlMethod = this + + val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1 + inflateBottomControlRegister = getInstruction(inflateReturnObjectIndex).registerA + inflateBottomControlInsertIndex = inflateReturnObjectIndex + 1 + } + + playerTopControlsInflateMatch.mutableMethod.apply { + inflateTopControlMethod = this + + val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1 + inflateTopControlRegister = getInstruction(inflateReturnObjectIndex).registerA + inflateTopControlInsertIndex = inflateReturnObjectIndex + 1 + } + + controlsOverlayVisibilityFingerprint.applyMatch( + context, + playerTopControlsInflateMatch, + ).mutableMethod.apply { + visibilityMethod = this + } + + // Hook the fullscreen close button. Used to fix visibility + // when seeking and other situations. + overlayViewInflateMatch.mutableMethod.apply { + val resourceIndex = indexOfFirstWideLiteralInstructionValueReversedOrThrow(fullscreenButton) + + val index = indexOfFirstInstructionOrThrow(resourceIndex) { + opcode == Opcode.CHECK_CAST && + getReference()?.type == + "Landroid/widget/ImageView;" + } + val register = getInstruction(index).registerA + + addInstruction( + index + 1, + "invoke-static { v$register }, " + + "$EXTENSION_CLASS_DESCRIPTOR->setFullscreenCloseButton(Landroid/widget/ImageView;)V", + ) + } + + visibilityImmediateMethod = playerControlsExtensionHookMatch.mutableMethod + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/Fingerprints.kt new file mode 100644 index 0000000000..2faae3acc4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/Fingerprints.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.youtube.misc.playertype + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val playerTypeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L") + opcodes( + Opcode.IF_NE, + Opcode.RETURN_VOID, + ) + custom { _, classDef -> classDef.endsWith("/YouTubePlayerOverlaysLayout;") } +} + +internal val videoStateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Lcom/google/android/libraries/youtube/player/features/overlay/controls/ControlsState;") + opcodes( + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, // obfuscated parameter field name + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt new file mode 100644 index 0000000000..5ae0f61226 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playertype/PlayerTypeHookPatch.kt @@ -0,0 +1,40 @@ +package app.revanced.patches.youtube.misc.playertype + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction + +internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PlayerTypeHookPatch;" + +@Suppress("unused") +val playerTypeHookPatch = bytecodePatch( + description = "Hook to get the current player type and video playback state.", +) { + dependsOn(sharedExtensionPatch) + + val playerTypeMatch by playerTypeFingerprint() + val videoStateMatch by videoStateFingerprint() + + execute { + playerTypeMatch.mutableMethod.addInstruction( + 0, + "invoke-static {p1}, $EXTENSION_CLASS_DESCRIPTOR->setPlayerType(Ljava/lang/Enum;)V", + ) + + videoStateMatch.mutableMethod.apply { + val endIndex = videoStateMatch.patternMatch!!.endIndex + val videoStateFieldName = getInstruction(endIndex).reference + + addInstructions( + 0, + """ + iget-object v0, p1, $videoStateFieldName # copy VideoState parameter field + invoke-static {v0}, $EXTENSION_CLASS_DESCRIPTOR->setVideoState(Ljava/lang/Enum;)V + """, + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt similarity index 55% rename from src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt index 4a704ca541..882e4537fa 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt @@ -1,37 +1,52 @@ +@file:Suppress("ktlint:standard:property-naming") + package app.revanced.patches.youtube.misc.playservice -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.resourcePatch import app.revanced.util.findElementByAttributeValueOrThrow -import kotlin.properties.Delegates - -@Patch(description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.") -internal object VersionCheckPatch : ResourcePatch() { - - var is_19_03_or_greater by Delegates.notNull() - var is_19_04_or_greater by Delegates.notNull() - var is_19_16_or_greater by Delegates.notNull() - var is_19_17_or_greater by Delegates.notNull() - var is_19_18_or_greater by Delegates.notNull() - var is_19_23_or_greater by Delegates.notNull() - var is_19_25_or_greater by Delegates.notNull() - var is_19_26_or_greater by Delegates.notNull() - var is_19_29_or_greater by Delegates.notNull() - var is_19_32_or_greater by Delegates.notNull() - var is_19_33_or_greater by Delegates.notNull() - var is_19_34_or_greater by Delegates.notNull() - var is_19_36_or_greater by Delegates.notNull() - var is_19_41_or_greater by Delegates.notNull() - override fun execute(context: ResourceContext) { +var is_19_03_or_greater = false + private set +var is_19_04_or_greater = false + private set +var is_19_16_or_greater = false + private set +var is_19_17_or_greater = false + private set +var is_19_18_or_greater = false + private set +var is_19_23_or_greater = false + private set +var is_19_25_or_greater = false + private set +var is_19_26_or_greater = false + private set +var is_19_29_or_greater = false + private set +var is_19_32_or_greater = false + private set +var is_19_33_or_greater = false + private set +var is_19_34_or_greater = false + private set +var is_19_36_or_greater = false + private set +var is_19_41_or_greater = false + private set +var is_19_43_or_greater = false + private set +@Suppress("unused") +val versionCheckPatch = resourcePatch( + description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.", +) { + execute { context -> // The app version is missing from the decompiled manifest, // so instead use the Google Play services version and compare against specific releases. val playStoreServicesVersion = context.document["res/values/integers.xml"].use { document -> document.documentElement.childNodes.findElementByAttributeValueOrThrow( "name", - "google_play_services_version" + "google_play_services_version", ).textContent.toInt() } @@ -50,5 +65,6 @@ internal object VersionCheckPatch : ResourcePatch() { is_19_34_or_greater = 243499000 <= playStoreServicesVersion is_19_36_or_greater = 243705000 <= playStoreServicesVersion is_19_41_or_greater = 244305000 <= playStoreServicesVersion + is_19_43_or_greater = 244405000 <= playStoreServicesVersion } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/Fingerprints.kt new file mode 100644 index 0000000000..72734bba70 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/Fingerprints.kt @@ -0,0 +1,44 @@ +package app.revanced.patches.youtube.misc.privacy + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val copyTextFingerprint = fingerprint { + returns("V") + parameters("L", "Ljava/util/Map;") + opcodes( + Opcode.IGET_OBJECT, // Contains the text to copy to be sanitized. + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, // ClipData.newPlainText + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.RETURN_VOID, + ) + strings("text/plain") +} + +internal val systemShareSheetFingerprint = fingerprint { + returns("V") + parameters("L", "Ljava/util/Map;") + opcodes( + Opcode.CHECK_CAST, + Opcode.GOTO, + ) + strings("YTShare_Logging_Share_Intent_Endpoint_Byte_Array") +} + +internal val youtubeShareSheetFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "Ljava/util/Map;") + opcodes( + Opcode.CHECK_CAST, + Opcode.GOTO, + Opcode.MOVE_OBJECT, + Opcode.INVOKE_VIRTUAL, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt new file mode 100644 index 0000000000..04da4a967c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt @@ -0,0 +1,82 @@ +package app.revanced.patches.youtube.misc.privacy + +import app.revanced.patcher.Match +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/RemoveTrackingQueryParameterPatch;" + +@Suppress("unused") +val removeTrackingQueryParameterPatch = bytecodePatch( + name = "Remove tracking query parameter", + description = "Adds an option to remove the tracking info from links you share.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val youTubeShareSheetMatch by youtubeShareSheetFingerprint() + val systemShareSheetMatch by systemShareSheetFingerprint() + val copyTextMatch by copyTextFingerprint() + + execute { + addResources("youtube", "misc.privacy.removeTrackingQueryParameterPatch") + + PreferenceScreen.MISC.addPreferences( + SwitchPreference("revanced_remove_tracking_query_parameter"), + ) + + fun Match.hook( + getInsertIndex: Match.PatternMatch.() -> Int, + getUrlRegister: MutableMethod.(insertIndex: Int) -> Int, + ) { + val insertIndex = patternMatch!!.getInsertIndex() + val urlRegister = mutableMethod.getUrlRegister(insertIndex) + + mutableMethod.addInstructions( + insertIndex, + """ + invoke-static {v$urlRegister}, $EXTENSION_CLASS_DESCRIPTOR->sanitize(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$urlRegister + """, + ) + } + + // YouTube share sheet. + youTubeShareSheetMatch.hook(getInsertIndex = { startIndex + 1 }) { insertIndex -> + getInstruction(insertIndex - 1).registerA + } + + // Native system share sheet. + systemShareSheetMatch.hook(getInsertIndex = { endIndex }) { insertIndex -> + getInstruction(insertIndex - 1).registerA + } + + copyTextMatch.hook(getInsertIndex = { startIndex + 2 }) { insertIndex -> + getInstruction(insertIndex - 2).registerA + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/Fingerprints.kt new file mode 100644 index 0000000000..09aa7bf4cd --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/Fingerprints.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.youtube.misc.recyclerviewtree.hook + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val recyclerViewTreeObserverFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + opcodes( + Opcode.CHECK_CAST, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL, + Opcode.NEW_INSTANCE, + ) + strings("LithoRVSLCBinder") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt new file mode 100644 index 0000000000..16863b200c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/recyclerviewtree/hook/RecyclerViewTreeHookPatch.kt @@ -0,0 +1,29 @@ +package app.revanced.patches.youtube.misc.recyclerviewtree.hook + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch + +lateinit var addRecyclerViewTreeHook: (String) -> Unit + private set + +val recyclerViewTreeHookPatch = bytecodePatch { + dependsOn(sharedExtensionPatch) + + val recyclerViewTreeObserverMatch by recyclerViewTreeObserverFingerprint() + + execute { + recyclerViewTreeObserverMatch.mutableMethod.apply { + val insertIndex = recyclerViewTreeObserverMatch.patternMatch!!.startIndex + 1 + val recyclerViewParameter = 2 + + addRecyclerViewTreeHook = { classDescriptor -> + addInstruction( + insertIndex, + "invoke-static/range { p$recyclerViewParameter .. p$recyclerViewParameter }, " + + "$classDescriptor->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V", + ) + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt new file mode 100644 index 0000000000..42298d82f2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.youtube.misc.settings + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val licenseActivityOnCreateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L") + custom { method, classDef -> + classDef.endsWith("LicenseActivity;") && method.name == "onCreate" + } +} + +internal val setThemeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters() + opcodes(Opcode.RETURN_OBJECT) + literal { appearanceStringId } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt new file mode 100644 index 0000000000..5a731c9050 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt @@ -0,0 +1,272 @@ +package app.revanced.patches.youtube.misc.settings + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.* +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting +import app.revanced.patches.shared.misc.settings.settingsPatch +import app.revanced.patches.youtube.misc.check.checkEnvironmentPatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.fix.cairo.disableCairoSettingsPatch +import app.revanced.util.* +import app.revanced.util.inputStreamFromBundledResource +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.util.MethodUtil + +// Used by a fingerprint() from SettingsPatch. +internal var appearanceStringId = -1L + private set + +private val preferences = mutableSetOf() + +fun addSettingPreference(screen: BasePreference) { + preferences += screen +} + +private val settingsResourcePatch = resourcePatch { + dependsOn( + resourceMappingPatch, + settingsPatch( + rootPreference = IntentPreference( + titleKey = "revanced_settings_title", + summaryKey = null, + intent = newIntent("revanced_settings_intent"), + ) to "settings_fragment", + preferences, + ), + ) + + execute { context -> + // Used for a fingerprint from SettingsPatch. + appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"] + + arrayOf( + ResourceGroup("layout", "revanced_settings_with_toolbar.xml"), + ).forEach { resourceGroup -> + context.copyResources("settings", resourceGroup) + } + + // Copy style properties used to fix over-sized copy menu that appear in EditTextPreference. + // For a full explanation of how this fixes the issue, see the comments in this style file + // and the comments in the extension code. + val targetResource = "values/styles.xml" + inputStreamFromBundledResource( + "settings/host", + targetResource, + )!!.let { inputStream -> + "resources".copyXmlNode( + context.document[inputStream], + context.document["res/$targetResource"], + ).close() + } + + // Remove horizontal divider from the settings Preferences + // To better match the appearance of the stock YouTube settings. + context.document["res/values/styles.xml"].use { document -> + + arrayOf( + "Theme.YouTube.Settings", + "Theme.YouTube.Settings.Dark", + ).forEach { value -> + val listDividerNode = document.createElement("item") + listDividerNode.setAttribute("name", "android:listDivider") + listDividerNode.appendChild(document.createTextNode("@null")) + + document.childNodes.findElementByAttributeValueOrThrow( + "name", + value, + ).appendChild(listDividerNode) + } + } + + // Modify the manifest and add a data intent filter to the LicenseActivity. + // Some devices freak out if undeclared data is passed to an intent, + // and this change appears to fix the issue. + context.document["AndroidManifest.xml"].use { document -> + + val licenseElement = document.childNodes.findElementByAttributeValueOrThrow( + "android:name", + "com.google.android.libraries.social.licenses.LicenseActivity", + ) + + val mimeType = document.createElement("data") + mimeType.setAttribute("android:mimeType", "text/plain") + + val intentFilter = document.createElement("intent-filter") + intentFilter.appendChild(mimeType) + + licenseElement.appendChild(intentFilter) + } + } +} + +val settingsPatch = bytecodePatch( + description = "Adds settings for ReVanced to YouTube.", +) { + dependsOn( + sharedExtensionPatch, + settingsResourcePatch, + addResourcesPatch, + disableCairoSettingsPatch, + // Currently there is no easy way to make a mandatory patch, + // so for now this is a dependent of this patch. + checkEnvironmentPatch, + ) + + val setThemeMatch by setThemeFingerprint() + val licenseActivityOnCreateMatch by licenseActivityOnCreateFingerprint() + + val extensionPackage = "app/revanced/extension/youtube" + val activityHookClassDescriptor = "L$extensionPackage/settings/LicenseActivityHook;" + + val themeHelperDescriptor = "L$extensionPackage/ThemeHelper;" + val setThemeMethodName = "setTheme" + + execute { + addResources("youtube", "misc.settings.settingsPatch") + + // Add an "about" preference to the top. + preferences += NonInteractivePreference( + key = "revanced_settings_screen_00_about", + summaryKey = null, + tag = "app.revanced.extension.youtube.settings.preference.ReVancedYouTubeAboutPreference", + selectable = true, + ) + + PreferenceScreen.MISC.addPreferences( + TextPreference( + key = null, + titleKey = "revanced_pref_import_export_title", + summaryKey = "revanced_pref_import_export_summary", + inputType = InputType.TEXT_MULTI_LINE, + tag = "app.revanced.extension.shared.settings.preference.ImportExportPreference", + ), + ) + + setThemeMatch.mutableMethod.let { setThemeMethod -> + setThemeMethod.implementation!!.instructions.mapIndexedNotNull { i, instruction -> + if (instruction.opcode == Opcode.RETURN_OBJECT) i else null + }.asReversed().forEach { returnIndex -> + // The following strategy is to replace the return instruction with the setTheme instruction, + // then add a return instruction after the setTheme instruction. + // This is done because the return instruction is a target of another instruction. + + setThemeMethod.apply { + // This register is returned by the setTheme method. + val register = getInstruction(returnIndex).registerA + replaceInstruction( + returnIndex, + "invoke-static { v$register }, " + + "$themeHelperDescriptor->$setThemeMethodName(Ljava/lang/Enum;)V", + ) + addInstruction(returnIndex + 1, "return-object v$register") + } + } + } + + // Modify the license activity and remove all existing layout code. + // Must modify an existing activity and cannot add a new activity to the manifest, + // as that fails for root installations. + licenseActivityOnCreateMatch.mutableMethod.addInstructions( + 1, + """ + invoke-static { p0 }, $activityHookClassDescriptor->initialize(Landroid/app/Activity;)V + return-void + """, + ) + + // Remove other methods as they will break as the onCreate method is modified above. + licenseActivityOnCreateMatch.mutableClass.apply { + methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) } + } + } + + finalize { + PreferenceScreen.close() + } +} + +/** + * Creates an intent to open ReVanced settings. + */ +fun newIntent(settingsName: String) = IntentPreference.Intent( + data = settingsName, + targetClass = "com.google.android.libraries.social.licenses.LicenseActivity", +) { + // The package name change has to be reflected in the intent. + setOrGetFallbackPackageName("com.google.android.youtube") +} + +object PreferenceScreen : BasePreferenceScreen() { + // Sort screens in the root menu by key, to not scatter related items apart + // (sorting key is set in revanced_prefs.xml). + // If no preferences are added to a screen, the screen will not be added to the settings. + val ADS = Screen( + key = "revanced_settings_screen_01_ads", + summaryKey = null, + ) + val ALTERNATIVE_THUMBNAILS = Screen( + key = "revanced_settings_screen_02_alt_thumbnails", + summaryKey = null, + sorting = Sorting.UNSORTED, + ) + val FEED = Screen( + key = "revanced_settings_screen_03_feed", + summaryKey = null, + ) + val PLAYER = Screen( + key = "revanced_settings_screen_04_player", + summaryKey = null, + ) + val GENERAL_LAYOUT = Screen( + key = "revanced_settings_screen_05_general", + summaryKey = null, + ) + + // Don't sort, as related preferences are scattered apart. + // Can use title sorting after PreferenceCategory support is added. + val SHORTS = Screen( + key = "revanced_settings_screen_06_shorts", + summaryKey = null, + sorting = Sorting.UNSORTED, + ) + + // Don't sort, because title sorting scatters the custom color preferences. + val SEEKBAR = Screen( + key = "revanced_settings_screen_07_seekbar", + summaryKey = null, + sorting = Sorting.UNSORTED, + ) + val SWIPE_CONTROLS = Screen( + key = "revanced_settings_screen_08_swipe_controls", + summaryKey = null, + sorting = Sorting.UNSORTED, + ) + + // RYD and SB are items 9 and 10. + // Menus are added in their own patch because they use an Intent and not a Screen. + + val MISC = Screen( + key = "revanced_settings_screen_11_misc", + summaryKey = null, + ) + val VIDEO = Screen( + key = "revanced_settings_screen_12_video", + summaryKey = null, + ) + + override fun commit(screen: PreferenceScreenPreference) { + preferences += screen + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/Fingerprints.kt new file mode 100644 index 0000000000..65cb707693 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/Fingerprints.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.youtube.misc.zoomhaptics + +import app.revanced.patcher.fingerprint + +internal val zoomHapticsFingerprint = fingerprint { + strings("Failed to haptics vibrate for video zoom") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch.kt new file mode 100644 index 0000000000..31cdccd8b4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatch.kt @@ -0,0 +1,47 @@ +package app.revanced.patches.youtube.misc.zoomhaptics + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch + +@Suppress("unused") +val zoomHapticsPatch = bytecodePatch( + name = "Disable zoom haptics", + description = "Adds an option to disable haptics when zooming.", +) { + dependsOn( + settingsPatch, + addResourcesPatch, + ) + + compatibleWith("com.google.android.youtube") + + val zoomHapticsMatch by zoomHapticsFingerprint() + + execute { + addResources("youtube", "misc.zoomhaptics.zoomHapticsPatch") + + PreferenceScreen.MISC.addPreferences( + SwitchPreference("revanced_disable_zoom_haptics"), + ) + + zoomHapticsMatch.mutableMethod.apply { + addInstructionsWithLabels( + 0, + """ + invoke-static { }, Lapp/revanced/extension/youtube/patches/ZoomHapticsPatch;->shouldVibrate()Z + move-result v0 + if-nez v0, :vibrate + return-void + """, + ExternalLabel("vibrate", getInstruction(0)), + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt new file mode 100644 index 0000000000..a7c72504ef --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt @@ -0,0 +1,130 @@ +package app.revanced.patches.youtube.shared + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val autoRepeatFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + custom { method, _ -> + method.implementation!!.instructions.count() == 3 && method.annotations.isEmpty() + } +} + +internal val autoRepeatParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + strings( + "play() called when the player wasn't loaded.", + "play() blocked because Background Playability failed", + ) +} + +internal val layoutConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + strings("1.0x") +} + +internal val mainActivityFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters() + custom { _, classDef -> + // Old versions of YouTube called this class "WatchWhileActivity" instead. + classDef.endsWith("MainActivity;") || classDef.endsWith("WatchWhileActivity;") + } +} + +internal val mainActivityOnCreateFingerprint = fingerprint { + returns("V") + parameters("Landroid/os/Bundle;") + custom { method, classDef -> + method.name == "onCreate" && + ( + classDef.endsWith("MainActivity;") || + // Old versions of YouTube called this class "WatchWhileActivity" instead. + classDef.endsWith("WatchWhileActivity;") + ) + } +} + +val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Landroid/graphics/Bitmap;") + opcodes( + Opcode.NEW_INSTANCE, // bitmap ImageSpan + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_4, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.INT_TO_FLOAT, + Opcode.INVOKE_VIRTUAL, // set textview padding using bitmap width + ) + custom { _, classDef -> + classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" || + classDef.superclass == + "Lcom/google/android/libraries/youtube/rendering/ui/spec/typography/YouTubeAppCompatTextView;" + } +} + +internal val seekbarFingerprint = fingerprint { + returns("V") + strings("timed_markers_width") +} + +internal val seekbarOnDrawFingerprint = fingerprint { + custom { method, _ -> method.name == "onDraw" } +} + +internal val subtitleButtonControllerFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Lcom/google/android/libraries/youtube/player/subtitles/model/SubtitleTrack;") + opcodes( + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.RETURN_VOID, + Opcode.IGET_BOOLEAN, + Opcode.CONST_4, + Opcode.IF_NEZ, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.IGET_OBJECT, + ) +} + +internal val newVideoQualityChangedFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters("L") + opcodes( + Opcode.IGET, // Video resolution (human readable). + Opcode.IGET_OBJECT, + Opcode.IGET_BOOLEAN, + Opcode.IGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_DIRECT, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.IF_NE, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/Fingerprints.kt new file mode 100644 index 0000000000..d0c961c9d4 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/Fingerprints.kt @@ -0,0 +1,132 @@ +package app.revanced.patches.youtube.video.information + +import app.revanced.patcher.fingerprint +import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.reference.FieldReference + +internal val createVideoPlayerSeekbarFingerprint = fingerprint { + returns("V") + strings("timed_markers_width") +} + +internal val onPlaybackSpeedItemClickFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "L", "I", "J") + custom { method, _ -> + method.name == "onItemClick" && + method.implementation?.instructions?.find { + it.opcode == Opcode.IGET_OBJECT && + it.getReference()!!.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" + } != null + } +} + +internal val playerControllerSetTimeReferenceFingerprint = fingerprint { + opcodes(Opcode.INVOKE_DIRECT_RANGE, Opcode.IGET_OBJECT) + strings("Media progress reported outside media playback: ") +} + +internal val playerInitFingerprint = fingerprint { + strings("playVideo called on player response with no videoStreamingData.") +} + +/** + * Matched using class found in [playerInitFingerprint]. + */ +internal val seekFingerprint = fingerprint { + strings("Attempting to seek during an ad") +} + +internal val videoLengthFingerprint = fingerprint { + opcodes( + Opcode.MOVE_RESULT_WIDE, + Opcode.CMP_LONG, + Opcode.IF_LEZ, + Opcode.IGET_OBJECT, + Opcode.CHECK_CAST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_WIDE, + Opcode.GOTO, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_WIDE, + Opcode.CONST_4, + Opcode.INVOKE_VIRTUAL, + ) +} + +/** + * Matches using class found in [mdxPlayerDirectorSetVideoStageFingerprint]. + */ +internal val mdxSeekFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + parameters("J", "L") + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.RETURN, + ) + custom { methodDef, _ -> + // The instruction count is necessary here to avoid matching the relative version + // of the seek method we're after, which has the same function signature as the + // regular one, is in the same class, and even has the exact same 3 opcodes pattern. + methodDef.implementation!!.instructions.count() == 3 + } +} + +internal val mdxPlayerDirectorSetVideoStageFingerprint = fingerprint { + strings("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state ") +} + +/** + * Matches using class found in [mdxPlayerDirectorSetVideoStageFingerprint]. + */ +internal val mdxSeekRelativeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + // Return type is boolean up to 19.39, and void with 19.39+. + parameters("J", "L") + opcodes( + + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + ) +} + +/** + * Matches using class found in [playerInitFingerprint]. + */ +internal val seekRelativeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + // Return type is boolean up to 19.39, and void with 19.39+. + parameters("J", "L") + opcodes( + Opcode.ADD_LONG_2ADDR, + Opcode.INVOKE_VIRTUAL, + ) +} + +/** + * Resolves with the class found in [newVideoQualityChangedFingerprint]. + */ +internal val playbackSpeedMenuSpeedChangedFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters("L") + opcodes( + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET, + Opcode.INVOKE_VIRTUAL, + Opcode.SGET_OBJECT, + Opcode.RETURN_OBJECT, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt new file mode 100644 index 0000000000..a52a8e0d4b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt @@ -0,0 +1,311 @@ +package app.revanced.patches.youtube.video.information + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableClass +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint +import app.revanced.patches.youtube.video.playerresponse.Hook +import app.revanced.patches.youtube.video.playerresponse.addPlayerResponseMethodHook +import app.revanced.patches.youtube.video.playerresponse.playerResponseMethodHookPatch +import app.revanced.patches.youtube.video.videoid.hookBackgroundPlayVideoId +import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId +import app.revanced.patches.youtube.video.videoid.hookVideoId +import app.revanced.patches.youtube.video.videoid.videoIdPatch +import app.revanced.util.applyMatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.BuilderInstruction +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.iface.Method +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.reference.MethodReference +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter +import com.android.tools.smali.dexlib2.util.MethodUtil +import com.sun.org.apache.bcel.internal.generic.InstructionConst.getInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/VideoInformation;" +private const val EXTENSION_PLAYER_INTERFACE = "Lapp/revanced/extension/youtube/patches/VideoInformation${'$'}PlaybackController;" + +private lateinit var playerInitMethod: MutableMethod +private var playerInitInsertIndex = -1 +private var playerInitInsertRegister = -1 + +private lateinit var mdxInitMethod: MutableMethod +private var mdxInitInsertIndex = -1 +private var mdxInitInsertRegister = -1 + +private lateinit var timeMethod: MutableMethod +private var timeInitInsertIndex = 2 + +// Old speed menu, where speeds are entries in a list. Method is also used by the player speed button. +private lateinit var legacySpeedSelectionInsertMethod: MutableMethod +private var legacySpeedSelectionInsertIndex = -1 +private var legacySpeedSelectionValueRegister = -1 + +// New speed menu, with preset buttons and 0.05x fine adjustments buttons. +private lateinit var speedSelectionInsertMethod: MutableMethod +private var speedSelectionInsertIndex = -1 +private var speedSelectionValueRegister = -1 + +// Used by other patches. +lateinit var setPlaybackSpeedContainerClassFieldReference: String + private set +lateinit var setPlaybackSpeedClassFieldReference: String + private set +lateinit var setPlaybackSpeedMethodReference: String + private set + +val videoInformationPatch = bytecodePatch( + description = "Hooks YouTube to get information about the current playing video.", +) { + dependsOn( + sharedExtensionPatch, + videoIdPatch, + playerResponseMethodHookPatch, + ) + + val playerInitMatch by playerInitFingerprint() + val mdxPlayerDirectorSetVideoStageMatch by mdxPlayerDirectorSetVideoStageFingerprint() + val createVideoPlayerSeekbarMatch by createVideoPlayerSeekbarFingerprint() + val playerControllerSetTimeReferenceMatch by playerControllerSetTimeReferenceFingerprint() + val onPlaybackSpeedItemClickMatch by onPlaybackSpeedItemClickFingerprint() + val newVideoQualityChangedMatch by newVideoQualityChangedFingerprint() + + execute { context -> + playerInitMethod = playerInitMatch.mutableClass.methods.first { MethodUtil.isConstructor(it) } + + // Find the location of the first invoke-direct call and extract the register storing the 'this' object reference. + val initThisIndex = playerInitMethod.indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_DIRECT && getReference()?.name == "" + } + playerInitInsertRegister = playerInitMethod.getInstruction(initThisIndex).registerC + playerInitInsertIndex = initThisIndex + 1 + + // Hook the player controller for use through the extension. + onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "initialize") + + val seekFingerprintResultMethod = seekFingerprint.applyMatch(context, playerInitMatch).method + val seekRelativeFingerprintResultMethod = seekRelativeFingerprint.applyMatch(context, playerInitMatch).method + + // Create extension interface methods. + addSeekInterfaceMethods(playerInitMatch.mutableClass, seekFingerprintResultMethod, seekRelativeFingerprintResultMethod) + + with(mdxPlayerDirectorSetVideoStageMatch) { + mdxInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) } + + val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_DIRECT && getReference()?.name == "" + } + mdxInitInsertRegister = mdxInitMethod.getInstruction(initThisIndex).registerC + mdxInitInsertIndex = initThisIndex + 1 + + // Hook the MDX director for use through the extension. + onCreateHookMdx(EXTENSION_CLASS_DESCRIPTOR, "initializeMdx") + + val mdxSeekFingerprintResultMethod = + mdxSeekFingerprint.applyMatch(context, mdxPlayerDirectorSetVideoStageMatch).method + val mdxSeekRelativeFingerprintResultMethod = + mdxSeekRelativeFingerprint.applyMatch(context, mdxPlayerDirectorSetVideoStageMatch).method + + addSeekInterfaceMethods(mutableClass, mdxSeekFingerprintResultMethod, mdxSeekRelativeFingerprintResultMethod) + } + + with(createVideoPlayerSeekbarMatch) { + val videoLengthMethodMatch = videoLengthFingerprint.apply { match(context, classDef) }.match!! + + with(videoLengthMethodMatch.mutableMethod) { + val videoLengthRegisterIndex = videoLengthMethodMatch.patternMatch!!.endIndex - 2 + val videoLengthRegister = getInstruction(videoLengthRegisterIndex).registerA + val dummyRegisterForLong = videoLengthRegister + 1 // required for long values since they are wide + + addInstruction( + videoLengthMethodMatch.patternMatch!!.endIndex, + "invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, " + + "$EXTENSION_CLASS_DESCRIPTOR->setVideoLength(J)V", + ) + } + } + + /* + * Inject call for video ids + */ + val videoIdMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V" + hookVideoId(videoIdMethodDescriptor) + hookBackgroundPlayVideoId(videoIdMethodDescriptor) + hookPlayerResponseVideoId( + "$EXTENSION_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;Z)V", + ) + // Call before any other video id hooks, + // so they can use VideoInformation and check if the video id is for a Short. + addPlayerResponseMethodHook( + Hook.ProtoBufferParameterBeforeVideoId( + "$EXTENSION_CLASS_DESCRIPTOR->" + + "newPlayerResponseSignature(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;", + ), + ) + + /* + * Set the video time method + */ + timeMethod = context.navigate(playerControllerSetTimeReferenceMatch.method) + .at(playerControllerSetTimeReferenceMatch.patternMatch!!.startIndex) + .mutable() + + /* + * Hook the methods which set the time + */ + videoTimeHook(EXTENSION_CLASS_DESCRIPTOR, "setVideoTime") + + /* + * Hook the user playback speed selection + */ + onPlaybackSpeedItemClickMatch.mutableMethod.apply { + speedSelectionInsertMethod = this + val speedSelectionMethodInstructions = this.implementation!!.instructions + val speedSelectionValueInstructionIndex = speedSelectionMethodInstructions.indexOfFirst { + it.opcode == Opcode.IGET + } + legacySpeedSelectionValueRegister = + getInstruction(speedSelectionValueInstructionIndex).registerA + setPlaybackSpeedClassFieldReference = + getInstruction(speedSelectionValueInstructionIndex + 1).reference.toString() + setPlaybackSpeedMethodReference = + getInstruction(speedSelectionValueInstructionIndex + 2).reference.toString() + setPlaybackSpeedContainerClassFieldReference = + getReference(speedSelectionMethodInstructions, -1, Opcode.IF_EQZ) + legacySpeedSelectionInsertIndex = speedSelectionValueInstructionIndex + 1 + } + + // Handle new playback speed menu. + playbackSpeedMenuSpeedChangedFingerprint.applyMatch( + context, + newVideoQualityChangedMatch, + ).mutableMethod.apply { + val index = indexOfFirstInstructionOrThrow(Opcode.IGET) + speedSelectionInsertMethod = this + speedSelectionInsertIndex = index + 1 + speedSelectionValueRegister = getInstruction(index).registerA + } + + userSelectedPlaybackSpeedHook(EXTENSION_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed") + } +} +private fun addSeekInterfaceMethods(targetClass: MutableClass, seekToMethod: Method, seekToRelativeMethod: Method) { + // Add the interface and methods that extension calls. + targetClass.interfaces.add(EXTENSION_PLAYER_INTERFACE) + + arrayOf( + Triple(seekToMethod, "seekTo", true), + Triple(seekToRelativeMethod, "seekToRelative", false), + ).forEach { (method, name, returnsBoolean) -> + // Add interface method. + // Get enum type for the seek helper method. + val seekSourceEnumType = method.parameterTypes[1].toString() + + val interfaceImplementation = ImmutableMethod( + targetClass.type, + name, + listOf(ImmutableMethodParameter("J", null, "time")), + if (returnsBoolean) "Z" else "V", + AccessFlags.PUBLIC.value or AccessFlags.FINAL.value, + null, + null, + MutableMethodImplementation(4), + ).toMutable() + + var instructions = """ + # First enum (field a) is SEEK_SOURCE_UNKNOWN. + sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType + invoke-virtual { p0, p1, p2, v0 }, $method + """ + + instructions += if (returnsBoolean) { + """ + move-result p1 + return p1 + """ + } else { + "return-void" + } + + // Insert helper method instructions. + interfaceImplementation.addInstructions( + 0, + instructions, + ) + + targetClass.methods.add(interfaceImplementation) + } +} + +private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) = + addInstruction(insertIndex, "invoke-static { $register }, $descriptor") + +private fun MutableMethod.insertTimeHook(insertIndex: Int, descriptor: String) = + insert(insertIndex, "p1, p2", descriptor) + +/** + * Hook the player controller. Called when a video is opened or the current video is changed. + * + * Note: This hook is called very early and is called before the video id, video time, video length, + * and many other data fields are set. + * + * @param targetMethodClass The descriptor for the class to invoke when the player controller is created. + * @param targetMethodName The name of the static method to invoke when the player controller is created. + */ +internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) = + playerInitMethod.insert( + playerInitInsertIndex++, + "v$playerInitInsertRegister", + "$targetMethodClass->$targetMethodName($EXTENSION_PLAYER_INTERFACE)V", + ) + +/** + * Hook the MDX player director. Called when playing videos while casting to a big screen device. + * + * @param targetMethodClass The descriptor for the class to invoke when the player controller is created. + * @param targetMethodName The name of the static method to invoke when the player controller is created. + */ +internal fun onCreateHookMdx(targetMethodClass: String, targetMethodName: String) = + mdxInitMethod.insert( + mdxInitInsertIndex++, + "v$mdxInitInsertRegister", + "$targetMethodClass->$targetMethodName($EXTENSION_PLAYER_INTERFACE)V", + ) + +/** + * Hook the video time. + * The hook is usually called once per second. + * + * @param targetMethodClass The descriptor for the static method to invoke when the player controller is created. + * @param targetMethodName The name of the static method to invoke when the player controller is created. + */ +fun videoTimeHook(targetMethodClass: String, targetMethodName: String) = + timeMethod.insertTimeHook( + timeInitInsertIndex++, + "$targetMethodClass->$targetMethodName(J)V", + ) + +private fun getReference(instructions: List, offset: Int, opcode: Opcode) = + (instructions[instructions.indexOfFirst { it.opcode == opcode } + offset] as ReferenceInstruction) + .reference.toString() + +/** + * Hook the video speed selected by the user. + */ +fun userSelectedPlaybackSpeedHook(targetMethodClass: String, targetMethodName: String) = + speedSelectionInsertMethod.addInstruction( + speedSelectionInsertIndex++, + "invoke-static {v$speedSelectionValueRegister}, $targetMethodClass->$targetMethodName(F)V", + ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt new file mode 100644 index 0000000000..603329de7d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt @@ -0,0 +1,52 @@ +package app.revanced.patches.youtube.video.playerresponse + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * For targets 19.25 and later. + */ +internal val playerParameterBuilderFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters( + "Ljava/lang/String;", // VideoId. + "[B", + "Ljava/lang/String;", // Player parameters proto buffer. + "Ljava/lang/String;", + "I", + "I", + "L", // 19.25+ parameter + "Ljava/util/Set;", + "Ljava/lang/String;", + "Ljava/lang/String;", + "L", + "Z", // Appears to indicate if the video id is being opened or is currently playing. + "Z", + "Z", + ) + strings("psps") +} + +/** + * For targets 19.24 and earlier. + */ +internal val playerParameterBuilderLegacyFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters( + "Ljava/lang/String;", // VideoId. + "[B", + "Ljava/lang/String;", // Player parameters proto buffer. + "Ljava/lang/String;", + "I", + "I", + "Ljava/util/Set;", + "Ljava/lang/String;", + "Ljava/lang/String;", + "L", + "Z", // Appears to indicate if the video id is being opened or is currently playing. + "Z", + "Z", + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt new file mode 100644 index 0000000000..3febd4a731 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt @@ -0,0 +1,122 @@ +package app.revanced.patches.youtube.video.playerresponse + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playservice.is_19_23_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch + +private val hooks = mutableSetOf() + +fun addPlayerResponseMethodHook(hook: Hook) { + hooks += hook +} + +// Parameter numbers of the patched method. +private const val PARAMETER_VIDEO_ID = 1 +private const val PARAMETER_PROTO_BUFFER = 3 +private var parameterIsShortAndOpeningOrPlaying = -1 + +// Registers used to pass the parameters to the extension. +private var playerResponseMethodCopyRegisters = false +private lateinit var registerVideoId: String +private lateinit var registerProtoBuffer: String +private lateinit var registerIsShortAndOpeningOrPlaying: String + +private lateinit var playerResponseMethod: MutableMethod +private var numberOfInstructionsAdded = 0 + +val playerResponseMethodHookPatch = bytecodePatch { + dependsOn( + sharedExtensionPatch, + versionCheckPatch, + ) + + val playerParameterBuilderMatch by playerParameterBuilderFingerprint() + val playerParameterBuilderLegacyMatch by playerParameterBuilderLegacyFingerprint() + + execute { + if (is_19_23_or_greater) { + playerResponseMethod = playerParameterBuilderMatch.mutableMethod + parameterIsShortAndOpeningOrPlaying = 12 + } else { + playerResponseMethod = playerParameterBuilderLegacyMatch.mutableMethod + parameterIsShortAndOpeningOrPlaying = 11 + } + + // On some app targets the method has too many registers pushing the parameters past v15. + // If needed, move the parameters to 4-bit registers, so they can be passed to the extension. + playerResponseMethodCopyRegisters = playerResponseMethod.implementation!!.registerCount - + playerResponseMethod.parameterTypes.size + parameterIsShortAndOpeningOrPlaying > 15 + + if (playerResponseMethodCopyRegisters) { + registerVideoId = "v0" + registerProtoBuffer = "v1" + registerIsShortAndOpeningOrPlaying = "v2" + } else { + registerVideoId = "p$PARAMETER_VIDEO_ID" + registerProtoBuffer = "p$PARAMETER_PROTO_BUFFER" + registerIsShortAndOpeningOrPlaying = "p$parameterIsShortAndOpeningOrPlaying" + } + } + + finalize { + fun hookVideoId(hook: Hook) { + playerResponseMethod.addInstruction( + 0, + "invoke-static {$registerVideoId, $registerIsShortAndOpeningOrPlaying}, $hook", + ) + numberOfInstructionsAdded++ + } + + fun hookProtoBufferParameter(hook: Hook) { + playerResponseMethod.addInstructions( + 0, + """ + invoke-static {$registerProtoBuffer, $registerVideoId, $registerIsShortAndOpeningOrPlaying}, $hook + move-result-object $registerProtoBuffer + """, + ) + numberOfInstructionsAdded += 2 + } + + // Reverse the order in order to preserve insertion order of the hooks. + val beforeVideoIdHooks = hooks.filterIsInstance().asReversed() + val videoIdHooks = hooks.filterIsInstance().asReversed() + val afterVideoIdHooks = hooks.filterIsInstance().asReversed() + + // Add the hooks in this specific order as they insert instructions at the beginning of the method. + afterVideoIdHooks.forEach(::hookProtoBufferParameter) + videoIdHooks.forEach(::hookVideoId) + beforeVideoIdHooks.forEach(::hookProtoBufferParameter) + + if (playerResponseMethodCopyRegisters) { + playerResponseMethod.addInstructions( + 0, + """ + move-object/from16 $registerVideoId, p$PARAMETER_VIDEO_ID + move-object/from16 $registerProtoBuffer, p$PARAMETER_PROTO_BUFFER + move/from16 $registerIsShortAndOpeningOrPlaying, p$parameterIsShortAndOpeningOrPlaying + """, + ) + numberOfInstructionsAdded += 3 + + // Move the modified register back. + playerResponseMethod.addInstruction( + numberOfInstructionsAdded, + "move-object/from16 p$PARAMETER_PROTO_BUFFER, $registerProtoBuffer", + ) + } + } +} + +sealed class Hook private constructor(private val methodDescriptor: String) { + class VideoId(methodDescriptor: String) : Hook(methodDescriptor) + + class ProtoBufferParameter(methodDescriptor: String) : Hook(methodDescriptor) + class ProtoBufferParameterBeforeVideoId(methodDescriptor: String) : Hook(methodDescriptor) + + override fun toString() = methodDescriptor +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt new file mode 100644 index 0000000000..e33674a389 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt @@ -0,0 +1,37 @@ +package app.revanced.patches.youtube.video.quality + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +/** + * Matches with the class found in [videoQualitySetterFingerprint]. + */ +internal val setQualityByIndexMethodClassFieldReferenceFingerprint = fingerprint { + returns("V") + parameters("L") + opcodes( + Opcode.IGET_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + ) +} + +internal val videoQualityItemOnClickParentFingerprint = fingerprint { + returns("V") + strings("VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT") +} + +internal val videoQualitySetterFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("[L", "I", "Z") + opcodes( + Opcode.IF_EQZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IPUT_BOOLEAN, + ) + strings("menu_item_video_quality") +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt similarity index 51% rename from src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt rename to patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt index 3e3c7000e6..79ca86a4d4 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt @@ -1,77 +1,71 @@ package app.revanced.patches.youtube.video.quality -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.getInstruction -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.Patch -import app.revanced.patches.all.misc.resources.AddResourcesPatch +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch -import app.revanced.patches.youtube.misc.settings.SettingsPatch -import app.revanced.patches.youtube.shared.fingerprints.NewVideoQualityChangedFingerprint -import app.revanced.patches.youtube.video.information.VideoInformationPatch -import app.revanced.patches.youtube.video.quality.fingerprints.SetQualityByIndexMethodClassFieldReferenceFingerprint -import app.revanced.patches.youtube.video.quality.fingerprints.VideoQualityItemOnClickParentFingerprint -import app.revanced.patches.youtube.video.quality.fingerprints.VideoQualitySetterFingerprint -import app.revanced.util.exception +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint +import app.revanced.patches.youtube.video.information.onCreateHook +import app.revanced.patches.youtube.video.information.videoInformationPatch +import app.revanced.util.applyMatch 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.reference.FieldReference -@Patch( +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch;" + +@Suppress("unused") +val rememberVideoQualityPatch = bytecodePatch( name = "Remember video quality", description = "Adds an option to remember the last video quality selected.", - dependencies = [ - IntegrationsPatch::class, - VideoInformationPatch::class, - SettingsPatch::class, - AddResourcesPatch::class - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", [ - "18.38.44", - "18.49.37", - "19.16.39", - "19.25.37", - "19.34.42", - ] - ) - ] -) -@Suppress("unused") -object RememberVideoQualityPatch : BytecodePatch( - setOf( - VideoQualitySetterFingerprint, - VideoQualityItemOnClickParentFingerprint, - NewVideoQualityChangedFingerprint - ) ) { - private const val INTEGRATIONS_CLASS_DESCRIPTOR = - "Lapp/revanced/integrations/youtube/patches/playback/quality/RememberVideoQualityPatch;" + dependsOn( + sharedExtensionPatch, + videoInformationPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val videoQualitySetterMatch by videoQualitySetterFingerprint() + val videoQualityItemOnClickParentMatch by videoQualityItemOnClickParentFingerprint() + val newVideoQualityChangedMatch by newVideoQualityChangedFingerprint() - override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) + execute { context -> + addResources("youtube", "video.quality.rememberVideoQualityPatch") - SettingsPatch.PreferenceScreen.VIDEO.addPreferences( + PreferenceScreen.VIDEO.addPreferences( SwitchPreference("revanced_remember_video_quality_last_selected"), ListPreference( key = "revanced_video_quality_default_wifi", summaryKey = null, entriesKey = "revanced_video_quality_default_entries", - entryValuesKey = "revanced_video_quality_default_entry_values" + entryValuesKey = "revanced_video_quality_default_entry_values", ), ListPreference( key = "revanced_video_quality_default_mobile", summaryKey = null, entriesKey = "revanced_video_quality_default_entries", - entryValuesKey = "revanced_video_quality_default_entry_values" - ) + entryValuesKey = "revanced_video_quality_default_entry_values", + ), ) /* @@ -81,17 +75,15 @@ object RememberVideoQualityPatch : BytecodePatch( * It also hooks the method which is called when the video quality to set is determined. * Conveniently, at this point the video quality is overridden to the remembered playback speed. */ - VideoInformationPatch.onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "newVideoStarted") - + onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted") // Inject a call to set the remembered quality once a video loads. - VideoQualitySetterFingerprint.result?.also { - if (!SetQualityByIndexMethodClassFieldReferenceFingerprint.resolve(context, it.classDef)) - throw PatchException("Could not resolve fingerprint to find setQualityByIndex method") - }?.let { + setQualityByIndexMethodClassFieldReferenceFingerprint.applyMatch( + context, + videoQualitySetterMatch, + ).let { match -> // This instruction refers to the field with the type that contains the setQualityByIndex method. - val instructions = SetQualityByIndexMethodClassFieldReferenceFingerprint.result!! - .method.implementation!!.instructions + val instructions = match.method.implementation!!.instructions val getOnItemClickListenerClassReference = (instructions.elementAt(0) as ReferenceInstruction).reference @@ -109,7 +101,7 @@ object RememberVideoQualityPatch : BytecodePatch( .find { method -> method.parameterTypes.first() == "I" } ?: throw PatchException("Could not find setQualityByIndex method") - it.mutableMethod.addInstructions( + videoQualitySetterMatch.mutableMethod.addInstructions( 0, """ # Get the object instance to invoke the setQualityByIndex method on. @@ -124,39 +116,33 @@ object RememberVideoQualityPatch : BytecodePatch( # The second parameter is the index of the selected quality. # The register v0 stores the object instance to invoke the setQualityByIndex method on. # The register v1 stores the name of the setQualityByIndex method. - invoke-static {p1, p2, v0, v1}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I + invoke-static { p1, p2, v0, v1 }, $EXTENSION_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I move-result p2 """, ) - } ?: throw VideoQualitySetterFingerprint.exception - + } // Inject a call to remember the selected quality. - VideoQualityItemOnClickParentFingerprint.result?.let { - val onItemClickMethod = it.mutableClass.methods.find { method -> method.name == "onItemClick" } - - onItemClickMethod?.apply { - val listItemIndexParameter = 3 - - addInstruction( - 0, - "invoke-static {p$listItemIndexParameter}, $INTEGRATIONS_CLASS_DESCRIPTOR->userChangedQuality(I)V" - ) - } ?: throw PatchException("Failed to find onItemClick method") - } ?: throw VideoQualityItemOnClickParentFingerprint.exception + videoQualityItemOnClickParentMatch.mutableClass.methods.find { it.name == "onItemClick" }?.apply { + val listItemIndexParameter = 3 + addInstruction( + 0, + "invoke-static { p$listItemIndexParameter }, " + + "$EXTENSION_CLASS_DESCRIPTOR->userChangedQuality(I)V", + ) + } ?: throw PatchException("Failed to find onItemClick method") // Remember video quality if not using old layout menu. - NewVideoQualityChangedFingerprint.result?.apply { - mutableMethod.apply { - val index = scanResult.patternScanResult!!.startIndex - val qualityRegister = getInstruction(index).registerA - - addInstruction( - index + 1, - "invoke-static {v$qualityRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->userChangedQualityInNewFlyout(I)V" - ) - } - } ?: throw NewVideoQualityChangedFingerprint.exception + newVideoQualityChangedMatch.mutableMethod.apply { + val index = newVideoQualityChangedMatch.patternMatch!!.startIndex + val qualityRegister = getInstruction(index).registerA + + addInstruction( + index + 1, + "invoke-static { v$qualityRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->userChangedQualityInNewFlyout(I)V", + ) + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt new file mode 100644 index 0000000000..592335bfc8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch.kt @@ -0,0 +1,29 @@ +package app.revanced.patches.youtube.video.speed + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.video.speed.button.playbackSpeedButtonPatch +import app.revanced.patches.youtube.video.speed.custom.customPlaybackSpeedPatch +import app.revanced.patches.youtube.video.speed.remember.rememberPlaybackSpeedPatch + +@Suppress("unused") +val playbackSpeedPatch = bytecodePatch( + name = "Playback speed", + description = "Adds options to customize available playback speeds, remember the last playback speed selected " + + "and show a speed dialog button to the video player.", +) { + dependsOn( + playbackSpeedButtonPatch, + customPlaybackSpeedPatch, + rememberPlaybackSpeedPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt new file mode 100644 index 0000000000..0537d04130 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch.kt @@ -0,0 +1,56 @@ +package app.revanced.patches.youtube.video.speed.button + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.playercontrols.* +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.video.speed.custom.customPlaybackSpeedPatch +import app.revanced.util.ResourceGroup +import app.revanced.util.copyResources + +private val playbackSpeedButtonResourcePatch = resourcePatch { + dependsOn(playerControlsResourcePatch) + + execute { context -> + context.copyResources( + "speedbutton", + ResourceGroup( + "drawable", + "revanced_playback_speed_dialog_button.xml", + ), + ) + + addBottomControl("speedbutton") + } +} + +private const val SPEED_BUTTON_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton;" + +@Suppress("unused") +val playbackSpeedButtonPatch = bytecodePatch( + description = "Adds the option to display playback speed dialog button in the video player.", +) { + dependsOn( + playbackSpeedButtonResourcePatch, + customPlaybackSpeedPatch, + playerControlsPatch, + settingsPatch, + addResourcesPatch, + ) + + execute { + addResources("youtube", "video.speed.button.playbackSpeedButtonPatch") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_playback_speed_dialog_button"), + ) + + initializeBottomControl(SPEED_BUTTON_CLASS_DESCRIPTOR) + injectVisibilityCheckCall(SPEED_BUTTON_CLASS_DESCRIPTOR) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt new file mode 100644 index 0000000000..f6d2ac9de7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt @@ -0,0 +1,173 @@ +package app.revanced.patches.youtube.video.speed.custom + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook +import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.* +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.immutable.ImmutableField + +var speedUnavailableId = -1L + internal set + +private val customPlaybackSpeedResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + execute { + speedUnavailableId = resourceMappings[ + "string", + "varispeed_unavailable_message", + ] + } +} + +private const val FILTER_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch;" + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch;" + +internal val customPlaybackSpeedPatch = bytecodePatch( + description = "Adds custom playback speed options.", +) { + dependsOn( + sharedExtensionPatch, + lithoFilterPatch, + settingsPatch, + recyclerViewTreeHookPatch, + customPlaybackSpeedResourcePatch, + addResourcesPatch, + ) + + val speedArrayGeneratorMatch by speedArrayGeneratorFingerprint() + val speedLimiterMatch by speedLimiterFingerprint() + val getOldPlaybackSpeedsMatch by getOldPlaybackSpeedsFingerprint() + val showOldPlaybackSpeedMenuExtensionMatch by showOldPlaybackSpeedMenuExtensionFingerprint() + + execute { context -> + addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch") + + PreferenceScreen.VIDEO.addPreferences( + TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE), + ) + + // Replace the speeds float array with custom speeds. + speedArrayGeneratorMatch.mutableMethod.apply { + val sizeCallIndex = indexOfFirstInstructionOrThrow { getReference()?.name == "size" } + val sizeCallResultRegister = getInstruction(sizeCallIndex + 1).registerA + + replaceInstruction(sizeCallIndex + 1, "const/4 v$sizeCallResultRegister, 0x0") + + val arrayLengthConstIndex = indexOfFirstWideLiteralInstructionValueOrThrow(7) + val arrayLengthConstDestination = getInstruction(arrayLengthConstIndex).registerA + val playbackSpeedsArrayType = "$EXTENSION_CLASS_DESCRIPTOR->customPlaybackSpeeds:[F" + + addInstructions( + arrayLengthConstIndex + 1, + """ + sget-object v$arrayLengthConstDestination, $playbackSpeedsArrayType + array-length v$arrayLengthConstDestination, v$arrayLengthConstDestination + """, + ) + + val originalArrayFetchIndex = indexOfFirstInstructionOrThrow { + val reference = getReference() + reference?.type == "[F" && reference.definingClass.endsWith("/PlayerConfigModel;") + } + val originalArrayFetchDestination = + getInstruction(originalArrayFetchIndex).registerA + + replaceInstruction( + originalArrayFetchIndex, + "sget-object v$originalArrayFetchDestination, $playbackSpeedsArrayType", + ) + } + + // Override the min/max speeds that can be used. + speedLimiterMatch.mutableMethod.apply { + val limiterMinConstIndex = indexOfFirstWideLiteralInstructionValueOrThrow(0.25f.toRawBits().toLong()) + var limiterMaxConstIndex = indexOfFirstWideLiteralInstructionValue(2.0f.toRawBits().toLong()) + // Newer targets have 4x max speed. + if (limiterMaxConstIndex < 0) { + limiterMaxConstIndex = indexOfFirstWideLiteralInstructionValueOrThrow(4.0f.toRawBits().toLong()) + } + + val limiterMinConstDestination = getInstruction(limiterMinConstIndex).registerA + val limiterMaxConstDestination = getInstruction(limiterMaxConstIndex).registerA + + replaceInstruction(limiterMinConstIndex, "const/high16 v$limiterMinConstDestination, 0.0f") + replaceInstruction(limiterMaxConstIndex, "const/high16 v$limiterMaxConstDestination, 10.0f") + } + + // Add a static INSTANCE field to the class. + // This is later used to call "showOldPlaybackSpeedMenu" on the instance. + val instanceField = ImmutableField( + getOldPlaybackSpeedsMatch.classDef.type, + "INSTANCE", + getOldPlaybackSpeedsMatch.classDef.type, + AccessFlags.PUBLIC.value or AccessFlags.STATIC.value, + null, + null, + null, + ).toMutable() + + getOldPlaybackSpeedsMatch.mutableClass.staticFields.add(instanceField) + // Set the INSTANCE field to the instance of the class. + // In order to prevent a conflict with another patch, add the instruction at index 1. + getOldPlaybackSpeedsMatch.mutableMethod.addInstruction(1, "sput-object p0, $instanceField") + + // Get the "showOldPlaybackSpeedMenu" method. + // This is later called on the field INSTANCE. + val showOldPlaybackSpeedMenuMethod = showOldPlaybackSpeedMenuFingerprint.applyMatch( + context, + getOldPlaybackSpeedsMatch, + ).method.toString() + + // Insert the call to the "showOldPlaybackSpeedMenu" method on the field INSTANCE. + showOldPlaybackSpeedMenuExtensionMatch.mutableMethod.apply { + addInstructionsWithLabels( + instructions.lastIndex, + """ + sget-object v0, $instanceField + if-nez v0, :not_null + return-void + :not_null + invoke-virtual { v0 }, $showOldPlaybackSpeedMenuMethod + """, + ) + } + + // region Force old video quality menu. + // This is necessary, because there is no known way of adding custom playback speeds to the new menu. + + addRecyclerViewTreeHook(EXTENSION_CLASS_DESCRIPTOR) + + // Required to check if the playback speed menu is currently shown. + addLithoFilter(FILTER_CLASS_DESCRIPTOR) + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/Fingerprints.kt new file mode 100644 index 0000000000..d3aa805d8c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/Fingerprints.kt @@ -0,0 +1,50 @@ +package app.revanced.patches.youtube.video.speed.custom + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val getOldPlaybackSpeedsFingerprint = fingerprint { + parameters("[L", "I") + strings("menu_item_playback_speed") +} + +internal val showOldPlaybackSpeedMenuFingerprint = fingerprint { + literal { speedUnavailableId } +} + +internal val showOldPlaybackSpeedMenuExtensionFingerprint = fingerprint { + custom { method, _ -> method.name == "showOldPlaybackSpeedMenu" } +} + +internal val speedArrayGeneratorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("[L") + parameters("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;") + opcodes( + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.GOTO_16, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_OBJECT, + ) + strings("0.0#") +} + +internal val speedLimiterFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("F") + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.CONST_HIGH16, + Opcode.GOTO, + Opcode.CONST_HIGH16, + Opcode.CONST_HIGH16, + Opcode.INVOKE_STATIC, + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/Fingerprints.kt new file mode 100644 index 0000000000..3924588b42 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/Fingerprints.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.youtube.video.speed.remember + +import app.revanced.patcher.fingerprint + +internal val initializePlaybackSpeedValuesFingerprint = fingerprint { + parameters("[L", "I") + strings("menu_item_playback_speed") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt new file mode 100644 index 0000000000..accc2498ef --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/remember/RememberPlaybackSpeedPatch.kt @@ -0,0 +1,87 @@ +package app.revanced.patches.youtube.video.speed.remember + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.patches.youtube.video.information.* +import app.revanced.patches.youtube.video.speed.custom.customPlaybackSpeedPatch +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/playback/speed/RememberPlaybackSpeedPatch;" + +internal val rememberPlaybackSpeedPatch = bytecodePatch { + dependsOn( + sharedExtensionPatch, + settingsPatch, + videoInformationPatch, + customPlaybackSpeedPatch, + addResourcesPatch, + ) + + val initializePlaybackSpeedValuesMatch by initializePlaybackSpeedValuesFingerprint() + + execute { + addResources("youtube", "video.speed.remember.rememberPlaybackSpeedPatch") + + PreferenceScreen.VIDEO.addPreferences( + SwitchPreference("revanced_remember_playback_speed_last_selected"), + ListPreference( + key = "revanced_playback_speed_default", + summaryKey = null, + // Entries and values are set by the extension code based on the actual speeds available. + entriesKey = null, + entryValuesKey = null, + ), + ) + + onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted") + userSelectedPlaybackSpeedHook( + EXTENSION_CLASS_DESCRIPTOR, + "userSelectedPlaybackSpeed", + ) + + /* + * Hook the code that is called when the playback speeds are initialized, and sets the playback speed + */ + initializePlaybackSpeedValuesMatch.mutableMethod.apply { + // Infer everything necessary for calling the method setPlaybackSpeed(). + val onItemClickListenerClassFieldReference = getInstruction(0).reference + + // Registers are not used at index 0, so they can be freely used. + addInstructionsWithLabels( + 0, + """ + invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getPlaybackSpeedOverride()F + move-result v0 + + # Check if the playback speed is not 1.0x. + const/high16 v1, 1.0f + cmpg-float v1, v0, v1 + if-eqz v1, :do_not_override + + # Get the instance of the class which has the container class field below. + iget-object v1, p0, $onItemClickListenerClassFieldReference + + # Get the container class field. + iget-object v1, v1, $setPlaybackSpeedContainerClassFieldReference + + # Get the field from its class. + iget-object v2, v1, $setPlaybackSpeedClassFieldReference + + # Invoke setPlaybackSpeed on that class. + invoke-virtual {v2, v0}, $setPlaybackSpeedMethodReference + """, + ExternalLabel("do_not_override", getInstruction(0)), + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/Fingerprints.kt new file mode 100644 index 0000000000..c2b73fbee5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/Fingerprints.kt @@ -0,0 +1,43 @@ +package app.revanced.patches.youtube.video.videoid + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val videoIdFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L") + opcodes( + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + ) + custom { method, _ -> + method.indexOfPlayerResponseModelString() >= 0 + } +} + +internal val videoIdBackgroundPlayFingerprint = fingerprint { + accessFlags(AccessFlags.DECLARED_SYNCHRONIZED, AccessFlags.FINAL, AccessFlags.PUBLIC) + returns("V") + parameters("L") + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + ) +} + +internal val videoIdParentFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("[L") + parameters("L") + literal { 524288L } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt new file mode 100644 index 0000000000..35d95d8da8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt @@ -0,0 +1,123 @@ +package app.revanced.patches.youtube.video.videoid + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.patches.youtube.video.playerresponse.Hook +import app.revanced.patches.youtube.video.playerresponse.addPlayerResponseMethodHook +import app.revanced.patches.youtube.video.playerresponse.playerResponseMethodHookPatch +import app.revanced.util.applyMatch +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +/** + * Hooks the new video id when the video changes. + * + * Supports all videos (regular videos and Shorts). + * + * _Does not function if playing in the background with no video visible_. + * + * Be aware, this can be called multiple times for the same video id. + * + * @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;` + */ +fun hookVideoId( + methodDescriptor: String, +) = videoIdMethod.addInstruction( + videoIdInsertIndex++, + "invoke-static {v$videoIdRegister}, $methodDescriptor", +) + +/** + * Alternate hook that supports only regular videos, but hook supports changing to new video + * during background play when no video is visible. + * + * _Does not support Shorts_. + * + * Be aware, the hook can be called multiple times for the same video id. + * + * @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;` + */ +fun hookBackgroundPlayVideoId( + methodDescriptor: String, +) = backgroundPlaybackMethod.addInstruction( + backgroundPlaybackInsertIndex++, // move-result-object offset + "invoke-static {v$backgroundPlaybackVideoIdRegister}, $methodDescriptor", +) + +/** + * Hooks the video id of every video when loaded. + * Supports all videos and functions in all situations. + * + * First parameter is the video id. + * Second parameter is if the video is a Short AND it is being opened or is currently playing. + * + * Hook is always called off the main thread. + * + * This hook is called as soon as the player response is parsed, + * and called before many other hooks are updated such as [playerTypeHookPatch]. + * + * Note: The video id returned here may not be the current video that's being played. + * It's common for multiple Shorts to load at once in preparation + * for the user swiping to the next Short. + * + * For most use cases, you probably want to use + * [hookVideoId] or [hookBackgroundPlayVideoId] instead. + * + * Be aware, this can be called multiple times for the same video id. + * + * @param methodDescriptor which method to call. Params must be `Ljava/lang/String;Z` + */ +fun hookPlayerResponseVideoId(methodDescriptor: String) = addPlayerResponseMethodHook( + Hook.VideoId( + methodDescriptor, + ), +) + +private var videoIdRegister = 0 +private var videoIdInsertIndex = 0 +private lateinit var videoIdMethod: MutableMethod + +private var backgroundPlaybackVideoIdRegister = 0 +private var backgroundPlaybackInsertIndex = 0 +private lateinit var backgroundPlaybackMethod: MutableMethod + +val videoIdPatch = bytecodePatch( + description = "Hooks to detect when the video id changes.", +) { + dependsOn( + sharedExtensionPatch, + playerResponseMethodHookPatch, + ) + + val videoIdParentMatch by videoIdParentFingerprint() + val videoIdBackgroundPlayMatch by videoIdBackgroundPlayFingerprint() + + execute { context -> + videoIdFingerprint.applyMatch(context, videoIdParentMatch).mutableMethod.apply { + videoIdMethod = this + val index = indexOfPlayerResponseModelString() + videoIdRegister = getInstruction(index + 1).registerA + videoIdInsertIndex = index + 2 + } + + videoIdBackgroundPlayMatch.mutableMethod.apply { + backgroundPlaybackMethod = this + val index = indexOfPlayerResponseModelString() + backgroundPlaybackVideoIdRegister = getInstruction(index + 1).registerA + backgroundPlaybackInsertIndex = index + 2 + } + } +} + +internal fun Method.indexOfPlayerResponseModelString() = indexOfFirstInstruction { + val reference = getReference() + reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" && + reference.returnType == "Ljava/lang/String;" +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/Fingerprints.kt new file mode 100644 index 0000000000..7f5469da33 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/Fingerprints.kt @@ -0,0 +1,43 @@ +package app.revanced.patches.youtube.video.videoqualitymenu + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val videoQualityMenuOptionsFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC) + returns("[L") + parameters("Landroid/content/Context", "L", "L") + opcodes( + Opcode.CONST_4, // First instruction of method. + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.IGET_BOOLEAN, // Use the quality menu, that contains the advanced menu. + Opcode.IF_NEZ, + ) + literal { videoQualityQuickMenuAdvancedMenuDescription } +} + +internal val videoQualityMenuViewInflateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("L") + parameters("L", "L", "L") + opcodes( + Opcode.INVOKE_SUPER, + Opcode.CONST, + Opcode.CONST_4, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + ) + literal { videoQualityBottomSheetListFragmentTitle } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt new file mode 100644 index 0000000000..182e88996e --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt @@ -0,0 +1,136 @@ +package app.revanced.patches.youtube.video.videoqualitymenu + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.resourcePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook +import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +internal var videoQualityBottomSheetListFragmentTitle = -1L + private set +internal var videoQualityQuickMenuAdvancedMenuDescription = -1L + private set + +private val restoreOldVideoQualityMenuResourcePatch = resourcePatch { + dependsOn( + settingsPatch, + resourceMappingPatch, + addResourcesPatch, + ) + + execute { + addResources("youtube", "video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch") + + PreferenceScreen.VIDEO.addPreferences( + SwitchPreference("revanced_restore_old_video_quality_menu"), + ) + + // Used for the old type of the video quality menu. + videoQualityBottomSheetListFragmentTitle = resourceMappings[ + "layout", + "video_quality_bottom_sheet_list_fragment_title", + ] + + videoQualityQuickMenuAdvancedMenuDescription = resourceMappings[ + "string", + "video_quality_quick_menu_advanced_menu_description", + ] + } +} + +private const val FILTER_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/components/VideoQualityMenuFilterPatch;" + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/playback/quality/RestoreOldVideoQualityMenuPatch;" + +@Suppress("unused") +val restoreOldVideoQualityMenuPatch = bytecodePatch( + name = "Restore old video quality menu", + description = "Adds an option to restore the old video quality menu with specific video resolution options.", + +) { + dependsOn( + sharedExtensionPatch, + restoreOldVideoQualityMenuResourcePatch, + lithoFilterPatch, + recyclerViewTreeHookPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) + + val videoQualityMenuViewInflateMatch by videoQualityMenuViewInflateFingerprint() + val videoQualityMenuOptionsMatch by videoQualityMenuOptionsFingerprint() + + execute { + // region Patch for the old type of the video quality menu. + // Used for regular videos when spoofing to old app version, + // and for the Shorts quality flyout on newer app versions. + + videoQualityMenuViewInflateMatch.mutableMethod.apply { + val checkCastIndex = videoQualityMenuViewInflateMatch.patternMatch!!.endIndex + val listViewRegister = getInstruction(checkCastIndex).registerA + + addInstruction( + checkCastIndex + 1, + "invoke-static { v$listViewRegister }, " + + "$EXTENSION_CLASS_DESCRIPTOR->" + + "showOldVideoQualityMenu(Landroid/widget/ListView;)V", + ) + } + + // Force YT to add the 'advanced' quality menu for Shorts. + val patternMatch = videoQualityMenuOptionsMatch.patternMatch!! + val startIndex = patternMatch.startIndex + if (startIndex != 0) throw PatchException("Unexpected opcode start index: $startIndex") + val insertIndex = patternMatch.endIndex + + videoQualityMenuOptionsMatch.mutableMethod.apply { + val register = getInstruction(insertIndex).registerA + + // A condition controls whether to show the three or four items quality menu. + // Force the four items quality menu to make the "Advanced" item visible, necessary for the patch. + addInstructions( + insertIndex, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation(Z)Z + move-result v$register + """, + ) + } + + // endregion + + // region Patch for the new type of the video quality menu. + + addRecyclerViewTreeHook(EXTENSION_CLASS_DESCRIPTOR) + + // Required to check if the video quality menu is currently shown in order to click on the "Advanced" item. + addLithoFilter(FILTER_CLASS_DESCRIPTOR) + + // endregion + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/Fingerprints.kt new file mode 100644 index 0000000000..af38f28f4d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/Fingerprints.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.yuka.misc.unlockpremium + +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.patcher.fingerprint + +internal val isPremiumFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Z") + opcodes( + Opcode.IGET_BOOLEAN, + Opcode.RETURN, + ) +} + +internal val yukaUserConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + strings("premiumProvider") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch.kt new file mode 100644 index 0000000000..6ca1d5e57b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPatch.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.yuka.misc.unlockpremium + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.util.matchOrThrow + +@Suppress("unused") +val unlockPremiumPatch = bytecodePatch( + name = "Unlock premium", +) { + compatibleWith("io.yuka.android"("4.29")) + + val yukaUserConstructorMatch by yukaUserConstructorFingerprint() + + execute { context -> + isPremiumFingerprint.apply { + match(context, yukaUserConstructorMatch.classDef) + }.matchOrThrow.mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt similarity index 85% rename from src/main/kotlin/app/revanced/util/BytecodeUtils.kt rename to patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt index c9b146645a..97ee4f2aae 100644 --- a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -1,16 +1,21 @@ package app.revanced.util -import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.FingerprintBuilder +import app.revanced.patcher.Match import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction -import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.PatchException import app.revanced.patcher.util.proxy.mutableTypes.MutableClass import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.Instruction @@ -19,15 +24,16 @@ import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction import com.android.tools.smali.dexlib2.iface.reference.Reference import com.android.tools.smali.dexlib2.util.MethodUtil -fun MethodFingerprint.resultOrThrow() = result ?: throw exception +val Fingerprint.matchOrThrow + get() = match ?: throw exception /** - * The [PatchException] of failing to resolve a [MethodFingerprint]. + * The [PatchException] of failing to match a [Fingerprint]. * * @return The [PatchException]. */ -val MethodFingerprint.exception - get() = PatchException("Failed to resolve ${this.javaClass.simpleName}") +val Fingerprint.exception + get() = PatchException("Failed to match the fingerprint: $this") /** * Find the [MutableMethod] from a given [Method] in a [MutableClass]. @@ -102,7 +108,7 @@ internal fun MutableMethod.addInstructionsAtControlFlowLabel( /** * Get the index of the first instruction with the id of the given resource name. * - * Requires [ResourceMappingPatch] as a dependency. + * Requires [resourceMappingPatch] as a dependency. * * @param resourceName the name of the resource to find the id for. * @return the index of the first instruction with the id of the given resource name, or -1 if not found. @@ -110,14 +116,14 @@ internal fun MutableMethod.addInstructionsAtControlFlowLabel( * @see [indexOfIdResourceOrThrow], [indexOfFirstWideLiteralInstructionValueReversed] */ fun Method.indexOfIdResource(resourceName: String): Int { - val resourceId = ResourceMappingPatch["id", resourceName] + val resourceId = resourceMappings["id", resourceName] return indexOfFirstWideLiteralInstructionValue(resourceId) } /** * Get the index of the first instruction with the id of the given resource name or throw a [PatchException]. * - * Requires [ResourceMappingPatch] as a dependency. + * Requires [resourceMappingPatch] as a dependency. * * @throws [PatchException] if the resource is not found, or the method does not contain the resource id literal value. * @see [indexOfIdResource], [indexOfFirstWideLiteralInstructionValueReversedOrThrow] @@ -196,9 +202,12 @@ fun Method.containsWideLiteralInstructionValue(literal: Long) = * @param targetClass the class to start traversing the class hierarchy from. * @param callback function that is called for every class in the hierarchy. */ -fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) { +fun BytecodePatchContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) { callback(targetClass) - this.findClass(targetClass.superclass ?: return)?.mutableClass?.let { + + targetClass.superclass ?: return + + classBy { targetClass.superclass == it.type }?.mutableClass?.let { traverseClassHierarchy(it, callback) } } @@ -368,11 +377,11 @@ fun Method.findOpcodeIndicesReversed(opcode: Opcode): List = * @return The list of indices of the opcode in reverse order. */ fun Method.findOpcodeIndicesReversed(filter: Instruction.() -> Boolean): List { - val indexes = implementation!!.instructions + val indexes = instructions .withIndex() .filter { (_, instruction) -> filter(instruction) } .map { (index, _) -> index } - .reversed() + .reversed() // TODO: Use asReversed here to avoid creating a new list. if (indexes.isEmpty()) throw PatchException("No matching instructions found in: $this") @@ -382,7 +391,7 @@ fun Method.findOpcodeIndicesReversed(filter: Instruction.() -> Boolean): List Unit, ) { @@ -401,47 +410,59 @@ fun BytecodeContext.forEachLiteralValueInstruction( } /** - * Return the resolved method early. + * Return the matched method early. */ -fun MethodFingerprint.returnEarly(bool: Boolean = false) { +fun Fingerprint.returnEarly(bool: Boolean = false) { val const = if (bool) "0x1" else "0x0" - result?.let { result -> - val stringInstructions = when (result.method.returnType.first()) { + match?.let { match -> + val stringInstructions = when (match.method.returnType.first()) { 'L' -> """ - const/4 v0, $const - return-object v0 - """ + const/4 v0, $const + return-object v0 + """ 'V' -> "return-void" 'I', 'Z' -> """ - const/4 v0, $const - return v0 - """ + const/4 v0, $const + return v0 + """ else -> throw Exception("This case should never happen.") } - result.mutableMethod.addInstructions(0, stringInstructions) + match.mutableMethod.addInstructions(0, stringInstructions) } ?: throw exception } /** - * Return the resolved methods early. + * Return the matched methods early. */ -fun Iterable.returnEarly(bool: Boolean = false) = forEach { fingerprint -> +fun Iterable.returnEarly(bool: Boolean = false) = forEach { fingerprint -> fingerprint.returnEarly(bool) } /** - * Return the resolved methods early. + * Return the matched methods early. */ @Deprecated("Use the Iterable version") -fun List.returnEarly(bool: Boolean = false) = forEach { fingerprint -> +fun List.returnEarly(bool: Boolean = false) = forEach { fingerprint -> fingerprint.returnEarly(bool) } /** - * Resolves this fingerprint using the classDef of a parent fingerprint. + * Matches this fingerprint using the classDef of a parent fingerprint match. + */ +fun Fingerprint.applyMatch(context: BytecodePatchContext, parentMatch: Match) = + apply { match(context, parentMatch.classDef) }.matchOrThrow + +/** + * Set the custom condition for this fingerprint to check for a literal value. + * + * @param literalSupplier The supplier for the literal value to check for. */ -fun MethodFingerprint.alsoResolve(context: BytecodeContext, parentFingerprint: MethodFingerprint) = - also { resolve(context, parentFingerprint.resultOrThrow().classDef) }.resultOrThrow() +// TODO: add a way for subclasses to also use their own custom fingerprint. +fun FingerprintBuilder.literal(literalSupplier: () -> Long) { + custom { method, _ -> + method.containsWideLiteralInstructionValue(literalSupplier()) + } +} diff --git a/src/main/kotlin/app/revanced/util/ResourceUtils.kt b/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt similarity index 69% rename from src/main/kotlin/app/revanced/util/ResourceUtils.kt rename to patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt index 2f0d93f840..02671bbd49 100644 --- a/src/main/kotlin/app/revanced/util/ResourceUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/ResourceUtils.kt @@ -1,8 +1,8 @@ package app.revanced.util -import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.ResourcePatchContext import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.util.DomFileEditor +import app.revanced.patcher.util.Document import app.revanced.util.resource.BaseResource import org.w3c.dom.Attr import org.w3c.dom.Element @@ -22,12 +22,13 @@ fun NodeList.asSequence() = (0 until this.length).asSequence().map { this.item(i /** * Returns a sequence for all child nodes. */ -fun Node.childElementsSequence() = this.childNodes.asSequence().filter { it.nodeType == Node.ELEMENT_NODE } +@Suppress("UNCHECKED_CAST") +fun Node.childElementsSequence() = this.childNodes.asSequence().filter { it.nodeType == Node.ELEMENT_NODE } as Sequence /** * Performs the given [action] on each child element. */ -fun Node.forEachChildElement(action: (Node) -> Unit) = +inline fun Node.forEachChildElement(action: (Element) -> Unit) = childElementsSequence().forEach { action(it) } @@ -56,7 +57,7 @@ fun Node.insertFirst(node: Node) { * @param sourceResourceDirectory The source resource directory name. * @param resources The resources to copy. */ -fun ResourceContext.copyResources( +fun ResourcePatchContext.copyResources( sourceResourceDirectory: String, vararg resources: ResourceGroup, ) { @@ -92,27 +93,32 @@ class ResourceGroup(val resourceDirectoryName: String, vararg val resources: Str * @param targetTag The target xml node. * @param callback The callback to call when iterating over the nodes. */ -fun ResourceContext.iterateXmlNodeChildren( +fun ResourcePatchContext.iterateXmlNodeChildren( resource: String, targetTag: String, callback: (node: Node) -> Unit, -) = xmlEditor[classLoader.getResourceAsStream(resource)!!].use { editor -> - val document = editor.file - +) = document[classLoader.getResourceAsStream(resource)!!].use { document -> val stringsNode = document.getElementsByTagName(targetTag).item(0).childNodes for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i)) } -// TODO: After the migration to the new patcher, remove the following code and replace it with the commented code below. -fun String.copyXmlNode(source: DomFileEditor, target: DomFileEditor): AutoCloseable { - val hostNodes = source.file.getElementsByTagName(this).item(0).childNodes +/** + * Copies the specified node of the source [Document] to the target [Document]. + * @param source the source [Document]. + * @param target the target [Document]- + * @return AutoCloseable that closes the [Document]s. + */ +fun String.copyXmlNode( + source: Document, + target: Document, +): AutoCloseable { + val hostNodes = source.getElementsByTagName(this).item(0).childNodes - val destinationResourceFile = target.file - val destinationNode = destinationResourceFile.getElementsByTagName(this).item(0) + val destinationNode = target.getElementsByTagName(this).item(0) for (index in 0 until hostNodes.length) { val node = hostNodes.item(index).cloneNode(true) - destinationResourceFile.adoptNode(node) + target.adoptNode(node) destinationNode.appendChild(node) } @@ -122,45 +128,6 @@ fun String.copyXmlNode(source: DomFileEditor, target: DomFileEditor): AutoClosea } } -// /** -// * Copies the specified node of the source [Document] to the target [Document]. -// * @param source the source [Document]. -// * @param target the target [Document]- -// * @return AutoCloseable that closes the [Document]s. -// */ -// fun String.copyXmlNode( -// source: Document, -// target: Document, -// ): AutoCloseable { -// val hostNodes = source.getElementsByTagName(this).item(0).childNodes -// -// val destinationNode = target.getElementsByTagName(this).item(0) -// -// for (index in 0 until hostNodes.length) { -// val node = hostNodes.item(index).cloneNode(true) -// target.adoptNode(node) -// destinationNode.appendChild(node) -// } -// -// return AutoCloseable { -// source.close() -// target.close() -// } -// } - -// @Deprecated( -// "Use copyXmlNode(Document, Document) instead.", -// ReplaceWith( -// "this.copyXmlNode(source.file as Document, target.file as Document)", -// "app.revanced.patcher.util.Document", -// "app.revanced.patcher.util.Document", -// ), -// ) -// fun String.copyXmlNode( -// source: DomFileEditor, -// target: DomFileEditor, -// ) = this.copyXmlNode(source.file as Document, target.file as Document) - /** * Add a resource node child. * diff --git a/src/main/kotlin/app/revanced/util/Utils.kt b/patches/src/main/kotlin/app/revanced/util/Utils.kt similarity index 100% rename from src/main/kotlin/app/revanced/util/Utils.kt rename to patches/src/main/kotlin/app/revanced/util/Utils.kt diff --git a/src/main/kotlin/app/revanced/util/microg/MicroGBytecodeHelper.kt b/patches/src/main/kotlin/app/revanced/util/microg/MicroGBytecodeHelper.kt similarity index 100% rename from src/main/kotlin/app/revanced/util/microg/MicroGBytecodeHelper.kt rename to patches/src/main/kotlin/app/revanced/util/microg/MicroGBytecodeHelper.kt diff --git a/src/main/kotlin/app/revanced/util/microg/MicroGResourceHelper.kt b/patches/src/main/kotlin/app/revanced/util/microg/MicroGResourceHelper.kt similarity index 100% rename from src/main/kotlin/app/revanced/util/microg/MicroGResourceHelper.kt rename to patches/src/main/kotlin/app/revanced/util/microg/MicroGResourceHelper.kt diff --git a/src/main/kotlin/app/revanced/util/resource/ArrayResource.kt b/patches/src/main/kotlin/app/revanced/util/resource/ArrayResource.kt similarity index 100% rename from src/main/kotlin/app/revanced/util/resource/ArrayResource.kt rename to patches/src/main/kotlin/app/revanced/util/resource/ArrayResource.kt diff --git a/src/main/kotlin/app/revanced/util/resource/BaseResource.kt b/patches/src/main/kotlin/app/revanced/util/resource/BaseResource.kt similarity index 100% rename from src/main/kotlin/app/revanced/util/resource/BaseResource.kt rename to patches/src/main/kotlin/app/revanced/util/resource/BaseResource.kt diff --git a/src/main/kotlin/app/revanced/util/resource/StringResource.kt b/patches/src/main/kotlin/app/revanced/util/resource/StringResource.kt similarity index 100% rename from src/main/kotlin/app/revanced/util/resource/StringResource.kt rename to patches/src/main/kotlin/app/revanced/util/resource/StringResource.kt diff --git a/src/main/resources/addresources/values-af-rZA/strings.xml b/patches/src/main/resources/addresources/values-af-rZA/strings.xml similarity index 100% rename from src/main/resources/addresources/values-af-rZA/strings.xml rename to patches/src/main/resources/addresources/values-af-rZA/strings.xml diff --git a/src/main/resources/addresources/values-am-rET/strings.xml b/patches/src/main/resources/addresources/values-am-rET/strings.xml similarity index 100% rename from src/main/resources/addresources/values-am-rET/strings.xml rename to patches/src/main/resources/addresources/values-am-rET/strings.xml diff --git a/src/main/resources/addresources/values-ar-rSA/strings.xml b/patches/src/main/resources/addresources/values-ar-rSA/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ar-rSA/strings.xml rename to patches/src/main/resources/addresources/values-ar-rSA/strings.xml diff --git a/src/main/resources/addresources/values-as-rIN/strings.xml b/patches/src/main/resources/addresources/values-as-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-as-rIN/strings.xml rename to patches/src/main/resources/addresources/values-as-rIN/strings.xml diff --git a/src/main/resources/addresources/values-az-rAZ/strings.xml b/patches/src/main/resources/addresources/values-az-rAZ/strings.xml similarity index 100% rename from src/main/resources/addresources/values-az-rAZ/strings.xml rename to patches/src/main/resources/addresources/values-az-rAZ/strings.xml diff --git a/src/main/resources/addresources/values-be-rBY/strings.xml b/patches/src/main/resources/addresources/values-be-rBY/strings.xml similarity index 100% rename from src/main/resources/addresources/values-be-rBY/strings.xml rename to patches/src/main/resources/addresources/values-be-rBY/strings.xml diff --git a/src/main/resources/addresources/values-bg-rBG/strings.xml b/patches/src/main/resources/addresources/values-bg-rBG/strings.xml similarity index 100% rename from src/main/resources/addresources/values-bg-rBG/strings.xml rename to patches/src/main/resources/addresources/values-bg-rBG/strings.xml diff --git a/src/main/resources/addresources/values-bn-rBD/strings.xml b/patches/src/main/resources/addresources/values-bn-rBD/strings.xml similarity index 100% rename from src/main/resources/addresources/values-bn-rBD/strings.xml rename to patches/src/main/resources/addresources/values-bn-rBD/strings.xml diff --git a/src/main/resources/addresources/values-bs-rBA/strings.xml b/patches/src/main/resources/addresources/values-bs-rBA/strings.xml similarity index 100% rename from src/main/resources/addresources/values-bs-rBA/strings.xml rename to patches/src/main/resources/addresources/values-bs-rBA/strings.xml diff --git a/src/main/resources/addresources/values-ca-rES/strings.xml b/patches/src/main/resources/addresources/values-ca-rES/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ca-rES/strings.xml rename to patches/src/main/resources/addresources/values-ca-rES/strings.xml diff --git a/src/main/resources/addresources/values-cs-rCZ/strings.xml b/patches/src/main/resources/addresources/values-cs-rCZ/strings.xml similarity index 100% rename from src/main/resources/addresources/values-cs-rCZ/strings.xml rename to patches/src/main/resources/addresources/values-cs-rCZ/strings.xml diff --git a/src/main/resources/addresources/values-da-rDK/strings.xml b/patches/src/main/resources/addresources/values-da-rDK/strings.xml similarity index 100% rename from src/main/resources/addresources/values-da-rDK/strings.xml rename to patches/src/main/resources/addresources/values-da-rDK/strings.xml diff --git a/src/main/resources/addresources/values-de-rDE/strings.xml b/patches/src/main/resources/addresources/values-de-rDE/strings.xml similarity index 100% rename from src/main/resources/addresources/values-de-rDE/strings.xml rename to patches/src/main/resources/addresources/values-de-rDE/strings.xml diff --git a/src/main/resources/addresources/values-el-rGR/strings.xml b/patches/src/main/resources/addresources/values-el-rGR/strings.xml similarity index 100% rename from src/main/resources/addresources/values-el-rGR/strings.xml rename to patches/src/main/resources/addresources/values-el-rGR/strings.xml diff --git a/src/main/resources/addresources/values-es-rES/strings.xml b/patches/src/main/resources/addresources/values-es-rES/strings.xml similarity index 100% rename from src/main/resources/addresources/values-es-rES/strings.xml rename to patches/src/main/resources/addresources/values-es-rES/strings.xml diff --git a/src/main/resources/addresources/values-et-rEE/strings.xml b/patches/src/main/resources/addresources/values-et-rEE/strings.xml similarity index 100% rename from src/main/resources/addresources/values-et-rEE/strings.xml rename to patches/src/main/resources/addresources/values-et-rEE/strings.xml diff --git a/src/main/resources/addresources/values-eu-rES/strings.xml b/patches/src/main/resources/addresources/values-eu-rES/strings.xml similarity index 100% rename from src/main/resources/addresources/values-eu-rES/strings.xml rename to patches/src/main/resources/addresources/values-eu-rES/strings.xml diff --git a/src/main/resources/addresources/values-fa-rIR/strings.xml b/patches/src/main/resources/addresources/values-fa-rIR/strings.xml similarity index 100% rename from src/main/resources/addresources/values-fa-rIR/strings.xml rename to patches/src/main/resources/addresources/values-fa-rIR/strings.xml diff --git a/src/main/resources/addresources/values-fi-rFI/strings.xml b/patches/src/main/resources/addresources/values-fi-rFI/strings.xml similarity index 100% rename from src/main/resources/addresources/values-fi-rFI/strings.xml rename to patches/src/main/resources/addresources/values-fi-rFI/strings.xml diff --git a/src/main/resources/addresources/values-fil-rPH/strings.xml b/patches/src/main/resources/addresources/values-fil-rPH/strings.xml similarity index 100% rename from src/main/resources/addresources/values-fil-rPH/strings.xml rename to patches/src/main/resources/addresources/values-fil-rPH/strings.xml diff --git a/src/main/resources/addresources/values-fr-rFR/strings.xml b/patches/src/main/resources/addresources/values-fr-rFR/strings.xml similarity index 100% rename from src/main/resources/addresources/values-fr-rFR/strings.xml rename to patches/src/main/resources/addresources/values-fr-rFR/strings.xml diff --git a/src/main/resources/addresources/values-gl-rES/strings.xml b/patches/src/main/resources/addresources/values-gl-rES/strings.xml similarity index 100% rename from src/main/resources/addresources/values-gl-rES/strings.xml rename to patches/src/main/resources/addresources/values-gl-rES/strings.xml diff --git a/src/main/resources/addresources/values-gu-rIN/strings.xml b/patches/src/main/resources/addresources/values-gu-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-gu-rIN/strings.xml rename to patches/src/main/resources/addresources/values-gu-rIN/strings.xml diff --git a/src/main/resources/addresources/values-hi-rIN/strings.xml b/patches/src/main/resources/addresources/values-hi-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-hi-rIN/strings.xml rename to patches/src/main/resources/addresources/values-hi-rIN/strings.xml diff --git a/src/main/resources/addresources/values-hr-rHR/strings.xml b/patches/src/main/resources/addresources/values-hr-rHR/strings.xml similarity index 100% rename from src/main/resources/addresources/values-hr-rHR/strings.xml rename to patches/src/main/resources/addresources/values-hr-rHR/strings.xml diff --git a/src/main/resources/addresources/values-hu-rHU/strings.xml b/patches/src/main/resources/addresources/values-hu-rHU/strings.xml similarity index 100% rename from src/main/resources/addresources/values-hu-rHU/strings.xml rename to patches/src/main/resources/addresources/values-hu-rHU/strings.xml diff --git a/src/main/resources/addresources/values-hy-rAM/strings.xml b/patches/src/main/resources/addresources/values-hy-rAM/strings.xml similarity index 100% rename from src/main/resources/addresources/values-hy-rAM/strings.xml rename to patches/src/main/resources/addresources/values-hy-rAM/strings.xml diff --git a/src/main/resources/addresources/values-in-rID/strings.xml b/patches/src/main/resources/addresources/values-in-rID/strings.xml similarity index 100% rename from src/main/resources/addresources/values-in-rID/strings.xml rename to patches/src/main/resources/addresources/values-in-rID/strings.xml diff --git a/src/main/resources/addresources/values-is-rIS/strings.xml b/patches/src/main/resources/addresources/values-is-rIS/strings.xml similarity index 100% rename from src/main/resources/addresources/values-is-rIS/strings.xml rename to patches/src/main/resources/addresources/values-is-rIS/strings.xml diff --git a/src/main/resources/addresources/values-it-rIT/strings.xml b/patches/src/main/resources/addresources/values-it-rIT/strings.xml similarity index 100% rename from src/main/resources/addresources/values-it-rIT/strings.xml rename to patches/src/main/resources/addresources/values-it-rIT/strings.xml diff --git a/src/main/resources/addresources/values-iw-rIL/strings.xml b/patches/src/main/resources/addresources/values-iw-rIL/strings.xml similarity index 100% rename from src/main/resources/addresources/values-iw-rIL/strings.xml rename to patches/src/main/resources/addresources/values-iw-rIL/strings.xml diff --git a/src/main/resources/addresources/values-ja-rJP/strings.xml b/patches/src/main/resources/addresources/values-ja-rJP/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ja-rJP/strings.xml rename to patches/src/main/resources/addresources/values-ja-rJP/strings.xml diff --git a/src/main/resources/addresources/values-ka-rGE/strings.xml b/patches/src/main/resources/addresources/values-ka-rGE/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ka-rGE/strings.xml rename to patches/src/main/resources/addresources/values-ka-rGE/strings.xml diff --git a/src/main/resources/addresources/values-kk-rKZ/strings.xml b/patches/src/main/resources/addresources/values-kk-rKZ/strings.xml similarity index 100% rename from src/main/resources/addresources/values-kk-rKZ/strings.xml rename to patches/src/main/resources/addresources/values-kk-rKZ/strings.xml diff --git a/src/main/resources/addresources/values-km-rKH/strings.xml b/patches/src/main/resources/addresources/values-km-rKH/strings.xml similarity index 100% rename from src/main/resources/addresources/values-km-rKH/strings.xml rename to patches/src/main/resources/addresources/values-km-rKH/strings.xml diff --git a/src/main/resources/addresources/values-kn-rIN/strings.xml b/patches/src/main/resources/addresources/values-kn-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-kn-rIN/strings.xml rename to patches/src/main/resources/addresources/values-kn-rIN/strings.xml diff --git a/src/main/resources/addresources/values-ko-rKR/strings.xml b/patches/src/main/resources/addresources/values-ko-rKR/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ko-rKR/strings.xml rename to patches/src/main/resources/addresources/values-ko-rKR/strings.xml diff --git a/src/main/resources/addresources/values-ky-rKG/strings.xml b/patches/src/main/resources/addresources/values-ky-rKG/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ky-rKG/strings.xml rename to patches/src/main/resources/addresources/values-ky-rKG/strings.xml diff --git a/src/main/resources/addresources/values-lo-rLA/strings.xml b/patches/src/main/resources/addresources/values-lo-rLA/strings.xml similarity index 100% rename from src/main/resources/addresources/values-lo-rLA/strings.xml rename to patches/src/main/resources/addresources/values-lo-rLA/strings.xml diff --git a/src/main/resources/addresources/values-lt-rLT/strings.xml b/patches/src/main/resources/addresources/values-lt-rLT/strings.xml similarity index 100% rename from src/main/resources/addresources/values-lt-rLT/strings.xml rename to patches/src/main/resources/addresources/values-lt-rLT/strings.xml diff --git a/src/main/resources/addresources/values-lv-rLV/strings.xml b/patches/src/main/resources/addresources/values-lv-rLV/strings.xml similarity index 100% rename from src/main/resources/addresources/values-lv-rLV/strings.xml rename to patches/src/main/resources/addresources/values-lv-rLV/strings.xml diff --git a/src/main/resources/addresources/values-mk-rMK/strings.xml b/patches/src/main/resources/addresources/values-mk-rMK/strings.xml similarity index 100% rename from src/main/resources/addresources/values-mk-rMK/strings.xml rename to patches/src/main/resources/addresources/values-mk-rMK/strings.xml diff --git a/src/main/resources/addresources/values-ml-rIN/strings.xml b/patches/src/main/resources/addresources/values-ml-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ml-rIN/strings.xml rename to patches/src/main/resources/addresources/values-ml-rIN/strings.xml diff --git a/src/main/resources/addresources/values-mn-rMN/strings.xml b/patches/src/main/resources/addresources/values-mn-rMN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-mn-rMN/strings.xml rename to patches/src/main/resources/addresources/values-mn-rMN/strings.xml diff --git a/src/main/resources/addresources/values-mr-rIN/strings.xml b/patches/src/main/resources/addresources/values-mr-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-mr-rIN/strings.xml rename to patches/src/main/resources/addresources/values-mr-rIN/strings.xml diff --git a/src/main/resources/addresources/values-ms-rMY/strings.xml b/patches/src/main/resources/addresources/values-ms-rMY/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ms-rMY/strings.xml rename to patches/src/main/resources/addresources/values-ms-rMY/strings.xml diff --git a/src/main/resources/addresources/values-my-rMM/strings.xml b/patches/src/main/resources/addresources/values-my-rMM/strings.xml similarity index 100% rename from src/main/resources/addresources/values-my-rMM/strings.xml rename to patches/src/main/resources/addresources/values-my-rMM/strings.xml diff --git a/src/main/resources/addresources/values-nb-rNO/strings.xml b/patches/src/main/resources/addresources/values-nb-rNO/strings.xml similarity index 100% rename from src/main/resources/addresources/values-nb-rNO/strings.xml rename to patches/src/main/resources/addresources/values-nb-rNO/strings.xml diff --git a/src/main/resources/addresources/values-ne-rIN/strings.xml b/patches/src/main/resources/addresources/values-ne-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ne-rIN/strings.xml rename to patches/src/main/resources/addresources/values-ne-rIN/strings.xml diff --git a/src/main/resources/addresources/values-nl-rNL/strings.xml b/patches/src/main/resources/addresources/values-nl-rNL/strings.xml similarity index 100% rename from src/main/resources/addresources/values-nl-rNL/strings.xml rename to patches/src/main/resources/addresources/values-nl-rNL/strings.xml diff --git a/src/main/resources/addresources/values-or-rIN/strings.xml b/patches/src/main/resources/addresources/values-or-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-or-rIN/strings.xml rename to patches/src/main/resources/addresources/values-or-rIN/strings.xml diff --git a/src/main/resources/addresources/values-pa-rIN/strings.xml b/patches/src/main/resources/addresources/values-pa-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-pa-rIN/strings.xml rename to patches/src/main/resources/addresources/values-pa-rIN/strings.xml diff --git a/src/main/resources/addresources/values-pl-rPL/strings.xml b/patches/src/main/resources/addresources/values-pl-rPL/strings.xml similarity index 100% rename from src/main/resources/addresources/values-pl-rPL/strings.xml rename to patches/src/main/resources/addresources/values-pl-rPL/strings.xml diff --git a/src/main/resources/addresources/values-pt-rBR/strings.xml b/patches/src/main/resources/addresources/values-pt-rBR/strings.xml similarity index 100% rename from src/main/resources/addresources/values-pt-rBR/strings.xml rename to patches/src/main/resources/addresources/values-pt-rBR/strings.xml diff --git a/src/main/resources/addresources/values-pt-rPT/strings.xml b/patches/src/main/resources/addresources/values-pt-rPT/strings.xml similarity index 100% rename from src/main/resources/addresources/values-pt-rPT/strings.xml rename to patches/src/main/resources/addresources/values-pt-rPT/strings.xml diff --git a/src/main/resources/addresources/values-ro-rRO/strings.xml b/patches/src/main/resources/addresources/values-ro-rRO/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ro-rRO/strings.xml rename to patches/src/main/resources/addresources/values-ro-rRO/strings.xml diff --git a/src/main/resources/addresources/values-ru-rRU/strings.xml b/patches/src/main/resources/addresources/values-ru-rRU/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ru-rRU/strings.xml rename to patches/src/main/resources/addresources/values-ru-rRU/strings.xml diff --git a/src/main/resources/addresources/values-si-rLK/strings.xml b/patches/src/main/resources/addresources/values-si-rLK/strings.xml similarity index 100% rename from src/main/resources/addresources/values-si-rLK/strings.xml rename to patches/src/main/resources/addresources/values-si-rLK/strings.xml diff --git a/src/main/resources/addresources/values-sk-rSK/strings.xml b/patches/src/main/resources/addresources/values-sk-rSK/strings.xml similarity index 100% rename from src/main/resources/addresources/values-sk-rSK/strings.xml rename to patches/src/main/resources/addresources/values-sk-rSK/strings.xml diff --git a/src/main/resources/addresources/values-sl-rSI/strings.xml b/patches/src/main/resources/addresources/values-sl-rSI/strings.xml similarity index 100% rename from src/main/resources/addresources/values-sl-rSI/strings.xml rename to patches/src/main/resources/addresources/values-sl-rSI/strings.xml diff --git a/src/main/resources/addresources/values-sq-rAL/strings.xml b/patches/src/main/resources/addresources/values-sq-rAL/strings.xml similarity index 100% rename from src/main/resources/addresources/values-sq-rAL/strings.xml rename to patches/src/main/resources/addresources/values-sq-rAL/strings.xml diff --git a/src/main/resources/addresources/values-sr-rSP/strings.xml b/patches/src/main/resources/addresources/values-sr-rSP/strings.xml similarity index 100% rename from src/main/resources/addresources/values-sr-rSP/strings.xml rename to patches/src/main/resources/addresources/values-sr-rSP/strings.xml diff --git a/src/main/resources/addresources/values-sv-rSE/strings.xml b/patches/src/main/resources/addresources/values-sv-rSE/strings.xml similarity index 100% rename from src/main/resources/addresources/values-sv-rSE/strings.xml rename to patches/src/main/resources/addresources/values-sv-rSE/strings.xml diff --git a/src/main/resources/addresources/values-sw-rKE/strings.xml b/patches/src/main/resources/addresources/values-sw-rKE/strings.xml similarity index 100% rename from src/main/resources/addresources/values-sw-rKE/strings.xml rename to patches/src/main/resources/addresources/values-sw-rKE/strings.xml diff --git a/src/main/resources/addresources/values-ta-rIN/strings.xml b/patches/src/main/resources/addresources/values-ta-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ta-rIN/strings.xml rename to patches/src/main/resources/addresources/values-ta-rIN/strings.xml diff --git a/src/main/resources/addresources/values-te-rIN/strings.xml b/patches/src/main/resources/addresources/values-te-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-te-rIN/strings.xml rename to patches/src/main/resources/addresources/values-te-rIN/strings.xml diff --git a/src/main/resources/addresources/values-th-rTH/strings.xml b/patches/src/main/resources/addresources/values-th-rTH/strings.xml similarity index 100% rename from src/main/resources/addresources/values-th-rTH/strings.xml rename to patches/src/main/resources/addresources/values-th-rTH/strings.xml diff --git a/src/main/resources/addresources/values-tr-rTR/strings.xml b/patches/src/main/resources/addresources/values-tr-rTR/strings.xml similarity index 100% rename from src/main/resources/addresources/values-tr-rTR/strings.xml rename to patches/src/main/resources/addresources/values-tr-rTR/strings.xml diff --git a/src/main/resources/addresources/values-uk-rUA/strings.xml b/patches/src/main/resources/addresources/values-uk-rUA/strings.xml similarity index 100% rename from src/main/resources/addresources/values-uk-rUA/strings.xml rename to patches/src/main/resources/addresources/values-uk-rUA/strings.xml diff --git a/src/main/resources/addresources/values-ur-rIN/strings.xml b/patches/src/main/resources/addresources/values-ur-rIN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-ur-rIN/strings.xml rename to patches/src/main/resources/addresources/values-ur-rIN/strings.xml diff --git a/src/main/resources/addresources/values-uz-rUZ/strings.xml b/patches/src/main/resources/addresources/values-uz-rUZ/strings.xml similarity index 100% rename from src/main/resources/addresources/values-uz-rUZ/strings.xml rename to patches/src/main/resources/addresources/values-uz-rUZ/strings.xml diff --git a/src/main/resources/addresources/values-vi-rVN/strings.xml b/patches/src/main/resources/addresources/values-vi-rVN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-vi-rVN/strings.xml rename to patches/src/main/resources/addresources/values-vi-rVN/strings.xml diff --git a/src/main/resources/addresources/values-zh-rCN/strings.xml b/patches/src/main/resources/addresources/values-zh-rCN/strings.xml similarity index 100% rename from src/main/resources/addresources/values-zh-rCN/strings.xml rename to patches/src/main/resources/addresources/values-zh-rCN/strings.xml diff --git a/src/main/resources/addresources/values-zh-rTW/strings.xml b/patches/src/main/resources/addresources/values-zh-rTW/strings.xml similarity index 100% rename from src/main/resources/addresources/values-zh-rTW/strings.xml rename to patches/src/main/resources/addresources/values-zh-rTW/strings.xml diff --git a/src/main/resources/addresources/values-zu-rZA/strings.xml b/patches/src/main/resources/addresources/values-zu-rZA/strings.xml similarity index 100% rename from src/main/resources/addresources/values-zu-rZA/strings.xml rename to patches/src/main/resources/addresources/values-zu-rZA/strings.xml diff --git a/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml similarity index 91% rename from src/main/resources/addresources/values/arrays.xml rename to patches/src/main/resources/addresources/values/arrays.xml index 829f9533e5..4a4715011e 100644 --- a/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -1,18 +1,18 @@ - + Android VR iOS - + ANDROID_VR IOS - + @string/revanced_spoof_app_version_target_entry_1 @string/revanced_spoof_app_version_target_entry_2 @@ -28,7 +28,7 @@ 17.33.42 - + @string/revanced_miniplayer_type_entry_1 @string/revanced_miniplayer_type_entry_2 @@ -38,7 +38,7 @@ @string/revanced_miniplayer_type_entry_6 - + ORIGINAL PHONE TABLET @@ -57,7 +57,7 @@ TABLET - + @string/revanced_change_start_page_entry_default @string/revanced_change_start_page_entry_search @@ -77,7 +77,7 @@ @string/revanced_change_start_page_entry_browse - + ORIGINAL SEARCH @@ -98,7 +98,7 @@ BROWSE - + @string/revanced_alt_thumbnail_options_entry_1 @string/revanced_alt_thumbnail_options_entry_2 @@ -106,7 +106,7 @@ @string/revanced_alt_thumbnail_options_entry_4 - + ORIGINAL DEARROW DEARROW_STILL_IMAGES @@ -123,7 +123,7 @@ END - + @string/revanced_video_quality_default_entry_1 @string/revanced_video_quality_default_entry_2 @@ -149,7 +149,7 @@ - + @string/revanced_show_deleted_messages_entry_1 @string/revanced_show_deleted_messages_entry_2 @@ -161,10 +161,9 @@ cross-out - - + - + @string/revanced_block_embedded_ads_entry_1 @string/revanced_block_embedded_ads_entry_2 diff --git a/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml similarity index 96% rename from src/main/resources/addresources/values/strings.xml rename to patches/src/main/resources/addresources/values/strings.xml index 295c840772..76310e0cd9 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -31,7 +31,7 @@ This is because Crowdin requires temporarily flattening this file and removing t --> - + Checks failed Open official website Ignore @@ -42,7 +42,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Patched %s days ago APK build date is corrupted - + ReVanced Do you wish to proceed? Reset @@ -62,7 +62,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Official links Donate - + MicroG GmsCore is not installed. Install it. Action needed @@ -73,7 +73,7 @@ This is because Crowdin requires temporarily flattening this file and removing t - + About Ads Alternative thumbnails @@ -86,7 +86,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Misc Video - + Debugging Enable or disable debugging options Debug logging @@ -103,7 +103,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Toast not shown if error occurs Turning off error toasts hides all ReVanced error notifications.\n\nYou will not be notified of any unexpected events. - + Disable like / subscribe button glow Like and subscribe button will not glow when mentioned Like and subscribe button will glow when mentioned @@ -317,7 +317,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Keyword is too short and requires quotes: %s Keyword will hide all videos: %s - + Hide general ads General ads are hidden General ads are shown @@ -355,17 +355,17 @@ This is because Crowdin requires temporarily flattening this file and removing t Hide fullscreen ads only works with older devices - + Hide YouTube Premium promotions YouTube Premium promotions under video player are hidden YouTube Premium promotions under video player are shown - + Hide video ads Video ads are hidden Video ads are shown - + URL copied to clipboard URL with timestamp copied Show copy video URL button @@ -375,13 +375,13 @@ This is because Crowdin requires temporarily flattening this file and removing t Button is shown. Tap to copy video URL with timestamp. Tap and hold to copy video without timestamp Button is not shown - + Remove viewer discretion dialog Dialog will be removed Dialog will be shown This does not bypass the age restriction. It just accepts it automatically. - + External downloads Settings for using an external downloader Show external download button @@ -395,17 +395,17 @@ This is because Crowdin requires temporarily flattening this file and removing t Package name of your installed external downloader app, such as NewPipe or Seal %s is not installed. Please install it. - + Disable precise seeking gesture Gesture is disabled Gesture is enabled - + Enable seekbar tapping Seekbar tapping is enabled Seekbar tapping is disabled - + Enable brightness gesture Brightness swipe is enabled Brightness swipe is disabled @@ -434,12 +434,12 @@ This is because Crowdin requires temporarily flattening this file and removing t Swipe magnitude threshold The amount of threshold for swipe to occur - + Disable auto captions Auto captions are disabled Auto captions are enabled - + Action buttons Hide or show buttons under videos Hide Like and Dislike @@ -475,7 +475,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Save to playlist button is hidden Save to playlist button is shown - + Navigation buttons Hide or change buttons in the navigation bar @@ -502,7 +502,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Labels are hidden Labels are shown - + Flyout menu Hide or show player flyout menu items @@ -547,7 +547,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Watch in VR menu is hidden Watch in VR menu is shown - + Hide previous & next video buttons Buttons are hidden Buttons are shown @@ -562,27 +562,27 @@ This is because Crowdin requires temporarily flattening this file and removing t Autoplay button is hidden Autoplay button is shown - + Hide end screen cards End screen cards are hidden End screen cards are shown - + Disable ambient mode in fullscreen Ambient mode disabled Ambient mode enabled - + Hide info cards Info cards are hidden Info cards are shown - + Disable rolling number animations Rolling numbers are not animated Rolling numbers are animated - + Hide seekbar in video player Video player seekbar is hidden Video player seekbar is shown @@ -590,7 +590,7 @@ This is because Crowdin requires temporarily flattening this file and removing t Thumbnail seekbar is hidden Thumbnail seekbar is shown - + Shorts player Hide or show components in the Shorts player @@ -690,27 +690,27 @@ This is because Crowdin requires temporarily flattening this file and removing t Navigation bar is hidden Navigation bar is shown - + Disable suggested video end screen Suggested videos will be disabled Suggested videos will be shown - + Hide video timestamp Timestamp is hidden Timestamp is shown - + Hide player popup panels Player popup panels are hidden Player popup panels are shown - + Player overlay opacity Opacity value between 0-100, where 0 is transparent Player overlay opacity must be between 0-100 - + Return YouTube Dislike Dislikes temporarily not available (API timed out) @@ -757,17 +757,17 @@ This is because Crowdin requires temporarily flattening this file and removing t Client rate limit encountered %d times %d milliseconds - + Enable wide search bar Wide search bar is enabled Wide search bar is disabled - + Restore old seekbar thumbnails Seekbar thumbnails will appear above the seekbar Seekbar thumbnails will appear in fullscreen - + SponsorBlock Enable SponsorBlock SponsorBlock is a crowd-sourced system for skipping annoying parts of YouTube videos @@ -950,7 +950,7 @@ This is because Crowdin requires temporarily flattening this file and removing t sponsor.ajay.app Data is provided by the SponsorBlock API. Tap here to learn more and see downloads for other platforms - + Spoof app version Version spoofed Version not spoofed @@ -965,7 +965,7 @@ This is because Crowdin requires temporarily flattening this file and removing t 17.41.37 - Restore old playlist shelf 17.33.42 - Restore old UI layout - + Set start page Default Browse channels @@ -983,12 +983,12 @@ This is because Crowdin requires temporarily flattening this file and removing t Trending Watch later - + Disable resuming Shorts player Shorts player will not resume on app startup Shorts player will resume on app startup - + Autoplay Shorts Shorts will autoplay Shorts will repeat @@ -996,13 +996,13 @@ This is because Crowdin requires temporarily flattening this file and removing t Shorts background play will autoplay Shorts background play will repeat - + Enable tablet layout Tablet layout is enabled Tablet layout is disabled Community posts do not show up on tablet layouts x - + Miniplayer Change the style of the in app minimized player Miniplayer type @@ -1040,12 +1040,12 @@ This is because Crowdin requires temporarily flattening this file and removing t Opacity value between 0-100, where 0 is transparent Miniplayer overlay opacity must be between 0-100 - + Enable gradient loading screen Loading screen will have a gradient background Loading screen will have a solid background - + Enable custom seekbar color Custom seekbar color is shown Original seekbar color is shown @@ -1053,12 +1053,12 @@ This is because Crowdin requires temporarily flattening this file and removing t The color of the seekbar Invalid seekbar color value - + Bypass image region restrictions Using image host yt4.ggpht.com Using original image host\n\nEnabling this can fix missing images that are blocked in some regions - + Home tab @@ -1091,7 +1091,7 @@ This is because Crowdin requires temporarily flattening this file and removing t DeArrow temporarily not available (status code: %s) DeArrow temporarily not available - + Show ReVanced announcements Announcements are shown on startup Announcements are not shown on startup @@ -1099,47 +1099,47 @@ This is because Crowdin requires temporarily flattening this file and removing t Failed connecting to announcements provider Dismiss - + Warning Your watch history is not being saved.<br><br>This most likely is caused by a DNS ad blocker or network proxy.<br><br>To fix this, whitelist <b>s.youtube.com</b> or turn off all DNS blockers and proxies. Do not show again - + Enable auto-repeat Auto-repeat is enabled Auto-repeat is disabled - + Spoof device dimensions Device dimensions spoofed\n\nHigher video qualities might be unlocked but you may experience video playback stuttering, worse battery life, and unknown side effects Device dimensions not spoofed\n\nEnabling this can unlock higher video qualities Enabling this can cause video playback stuttering, worse battery life, and unknown side effects. - + GmsCore Settings Settings for GmsCore - + Bypass URL redirects URL redirects are bypassed URL redirects are not bypassed - + Open links in browser Opening links externally Opening links in app - + Remove tracking query parameter Tracking query parameter is removed from links Tracking query parameter is not removed from links - + Disable zoom haptics Haptics are disabled Haptics are enabled - + Automatic quality 2160p 1440p @@ -1158,35 +1158,35 @@ This is because Crowdin requires temporarily flattening this file and removing t wifi Changed default %1$s quality to: %2$s - + Show speed dialog button Button is shown Button is not shown - + Custom playback speeds Add or change the available playback speeds Custom speeds must be less than %s. Using default values. Invalid custom playback speeds. Using default values. - + Remember playback speed changes Playback speed changes apply to all videos Playback speed changes only apply to the current video Default playback speed Changed default speed to: %s - + Restore old video quality menu Old video quality menu is shown Old video quality menu is not shown - + Enable slide to seek Slide to seek is enabled Slide to seek is not enabled - + Spoof video streams Spoof the client video streams to prevent playback issues Spoof video streams @@ -1204,20 +1204,14 @@ This is because Crowdin requires temporarily flattening this file and removing t Android VR spoofing side effects • Audio track menu is missing\n• Stable volume is not available - - - Enable auto HDR brightness - Auto HDR brightness is enabled - Auto HDR brightness is disabled - - + Block audio ads Audio ads are blocked Audio ads are unblocked - + %s is unavailable. Ads may show. Try switching to another ad block service in settings. %s server returned an error. Ads may show. Try switching to another ad block service in settings. Block embedded video ads @@ -1225,30 +1219,30 @@ This is because Crowdin requires temporarily flattening this file and removing t Luminous proxy PurpleAdBlock proxy - + Block video ads Video ads are blocked Video ads are unblocked - + message deleted Show deleted messages Do not show deleted messages Hide deleted messages behind a spoiler Show deleted messages as crossed-out text - + Automatically claim Channel Points Channel Points are claimed automatically Channel Points are not claimed automatically - + Enable Twitch debug mode Twitch debug mode is enabled (not recommended) Twitch debug mode is disabled - + ReVanced Settings Ads Ad blocking settings diff --git a/src/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-hdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-mdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-xhdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-xxhdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced-borderless/drawable-xxxhdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced/drawable-hdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced/drawable-mdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced/drawable-xhdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced/drawable-xxhdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_dark.png b/patches/src/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_dark.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_dark.png rename to patches/src/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_dark.png diff --git a/src/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_light.png b/patches/src/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_light.png similarity index 100% rename from src/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_light.png rename to patches/src/main/resources/change-header/revanced/drawable-xxxhdpi/yt_wordmark_header_light.png diff --git a/src/main/resources/copyvideourl/drawable/revanced_yt_copy.xml b/patches/src/main/resources/copyvideourl/drawable/revanced_yt_copy.xml similarity index 100% rename from src/main/resources/copyvideourl/drawable/revanced_yt_copy.xml rename to patches/src/main/resources/copyvideourl/drawable/revanced_yt_copy.xml diff --git a/src/main/resources/copyvideourl/drawable/revanced_yt_copy_timestamp.xml b/patches/src/main/resources/copyvideourl/drawable/revanced_yt_copy_timestamp.xml similarity index 100% rename from src/main/resources/copyvideourl/drawable/revanced_yt_copy_timestamp.xml rename to patches/src/main/resources/copyvideourl/drawable/revanced_yt_copy_timestamp.xml diff --git a/src/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml similarity index 100% rename from src/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml rename to patches/src/main/resources/copyvideourl/host/layout/youtube_controls_bottom_ui_container.xml diff --git a/src/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_background_color_108.png b/patches/src/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_background_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_background_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_background_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_foreground_color_108.png b/patches/src/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_foreground_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_foreground_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-hdpi/adaptiveproduct_youtube_foreground_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-hdpi/ic_launcher.png b/patches/src/main/resources/custom-branding/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-hdpi/ic_launcher.png rename to patches/src/main/resources/custom-branding/mipmap-hdpi/ic_launcher.png diff --git a/src/main/resources/custom-branding/mipmap-hdpi/ic_launcher_round.png b/patches/src/main/resources/custom-branding/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-hdpi/ic_launcher_round.png rename to patches/src/main/resources/custom-branding/mipmap-hdpi/ic_launcher_round.png diff --git a/src/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_background_color_108.png b/patches/src/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_background_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_background_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_background_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_foreground_color_108.png b/patches/src/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_foreground_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_foreground_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-mdpi/adaptiveproduct_youtube_foreground_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-mdpi/ic_launcher.png b/patches/src/main/resources/custom-branding/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-mdpi/ic_launcher.png rename to patches/src/main/resources/custom-branding/mipmap-mdpi/ic_launcher.png diff --git a/src/main/resources/custom-branding/mipmap-mdpi/ic_launcher_round.png b/patches/src/main/resources/custom-branding/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-mdpi/ic_launcher_round.png rename to patches/src/main/resources/custom-branding/mipmap-mdpi/ic_launcher_round.png diff --git a/src/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_background_color_108.png b/patches/src/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_background_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_background_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_background_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_foreground_color_108.png b/patches/src/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_foreground_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_foreground_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-xhdpi/adaptiveproduct_youtube_foreground_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-xhdpi/ic_launcher.png b/patches/src/main/resources/custom-branding/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xhdpi/ic_launcher.png rename to patches/src/main/resources/custom-branding/mipmap-xhdpi/ic_launcher.png diff --git a/src/main/resources/custom-branding/mipmap-xhdpi/ic_launcher_round.png b/patches/src/main/resources/custom-branding/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xhdpi/ic_launcher_round.png rename to patches/src/main/resources/custom-branding/mipmap-xhdpi/ic_launcher_round.png diff --git a/src/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_background_color_108.png b/patches/src/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_background_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_background_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_background_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_foreground_color_108.png b/patches/src/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_foreground_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_foreground_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-xxhdpi/adaptiveproduct_youtube_foreground_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher.png b/patches/src/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher.png rename to patches/src/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher.png diff --git a/src/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher_round.png b/patches/src/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher_round.png rename to patches/src/main/resources/custom-branding/mipmap-xxhdpi/ic_launcher_round.png diff --git a/src/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_background_color_108.png b/patches/src/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_background_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_background_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_background_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_foreground_color_108.png b/patches/src/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_foreground_color_108.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_foreground_color_108.png rename to patches/src/main/resources/custom-branding/mipmap-xxxhdpi/adaptiveproduct_youtube_foreground_color_108.png diff --git a/src/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher.png b/patches/src/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher.png rename to patches/src/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher.png diff --git a/src/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher_round.png b/patches/src/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from src/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher_round.png rename to patches/src/main/resources/custom-branding/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/src/main/resources/downloads/drawable/revanced_yt_download_button.xml b/patches/src/main/resources/downloads/drawable/revanced_yt_download_button.xml similarity index 100% rename from src/main/resources/downloads/drawable/revanced_yt_download_button.xml rename to patches/src/main/resources/downloads/drawable/revanced_yt_download_button.xml diff --git a/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml similarity index 100% rename from src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml rename to patches/src/main/resources/downloads/host/layout/youtube_controls_bottom_ui_container.xml diff --git a/src/main/resources/settings/host/values/styles.xml b/patches/src/main/resources/settings/host/values/styles.xml similarity index 100% rename from src/main/resources/settings/host/values/styles.xml rename to patches/src/main/resources/settings/host/values/styles.xml diff --git a/src/main/resources/settings/layout/revanced_settings_with_toolbar.xml b/patches/src/main/resources/settings/layout/revanced_settings_with_toolbar.xml similarity index 100% rename from src/main/resources/settings/layout/revanced_settings_with_toolbar.xml rename to patches/src/main/resources/settings/layout/revanced_settings_with_toolbar.xml diff --git a/src/main/resources/settings/xml/revanced_prefs.xml b/patches/src/main/resources/settings/xml/revanced_prefs.xml similarity index 100% rename from src/main/resources/settings/xml/revanced_prefs.xml rename to patches/src/main/resources/settings/xml/revanced_prefs.xml diff --git a/src/main/resources/speedbutton/drawable/revanced_playback_speed_dialog_button.xml b/patches/src/main/resources/speedbutton/drawable/revanced_playback_speed_dialog_button.xml similarity index 100% rename from src/main/resources/speedbutton/drawable/revanced_playback_speed_dialog_button.xml rename to patches/src/main/resources/speedbutton/drawable/revanced_playback_speed_dialog_button.xml diff --git a/src/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml b/patches/src/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml similarity index 100% rename from src/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml rename to patches/src/main/resources/speedbutton/host/layout/youtube_controls_bottom_ui_container.xml diff --git a/src/main/resources/sponsorblock/drawable-xxxhdpi/quantum_ic_skip_next_white_24.png b/patches/src/main/resources/sponsorblock/drawable-xxxhdpi/quantum_ic_skip_next_white_24.png similarity index 100% rename from src/main/resources/sponsorblock/drawable-xxxhdpi/quantum_ic_skip_next_white_24.png rename to patches/src/main/resources/sponsorblock/drawable-xxxhdpi/quantum_ic_skip_next_white_24.png diff --git a/src/main/resources/sponsorblock/drawable/revanced_sb_adjust.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_adjust.xml similarity index 100% rename from src/main/resources/sponsorblock/drawable/revanced_sb_adjust.xml rename to patches/src/main/resources/sponsorblock/drawable/revanced_sb_adjust.xml diff --git a/src/main/resources/sponsorblock/drawable/revanced_sb_backward.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_backward.xml similarity index 100% rename from src/main/resources/sponsorblock/drawable/revanced_sb_backward.xml rename to patches/src/main/resources/sponsorblock/drawable/revanced_sb_backward.xml diff --git a/src/main/resources/sponsorblock/drawable/revanced_sb_compare.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_compare.xml similarity index 100% rename from src/main/resources/sponsorblock/drawable/revanced_sb_compare.xml rename to patches/src/main/resources/sponsorblock/drawable/revanced_sb_compare.xml diff --git a/src/main/resources/sponsorblock/drawable/revanced_sb_edit.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_edit.xml similarity index 100% rename from src/main/resources/sponsorblock/drawable/revanced_sb_edit.xml rename to patches/src/main/resources/sponsorblock/drawable/revanced_sb_edit.xml diff --git a/src/main/resources/sponsorblock/drawable/revanced_sb_forward.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_forward.xml similarity index 100% rename from src/main/resources/sponsorblock/drawable/revanced_sb_forward.xml rename to patches/src/main/resources/sponsorblock/drawable/revanced_sb_forward.xml diff --git a/src/main/resources/sponsorblock/drawable/revanced_sb_logo.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo.xml similarity index 100% rename from src/main/resources/sponsorblock/drawable/revanced_sb_logo.xml rename to patches/src/main/resources/sponsorblock/drawable/revanced_sb_logo.xml diff --git a/src/main/resources/sponsorblock/drawable/revanced_sb_publish.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_publish.xml similarity index 100% rename from src/main/resources/sponsorblock/drawable/revanced_sb_publish.xml rename to patches/src/main/resources/sponsorblock/drawable/revanced_sb_publish.xml diff --git a/src/main/resources/sponsorblock/drawable/revanced_sb_voting.xml b/patches/src/main/resources/sponsorblock/drawable/revanced_sb_voting.xml similarity index 100% rename from src/main/resources/sponsorblock/drawable/revanced_sb_voting.xml rename to patches/src/main/resources/sponsorblock/drawable/revanced_sb_voting.xml diff --git a/src/main/resources/sponsorblock/host/layout/youtube_controls_layout.xml b/patches/src/main/resources/sponsorblock/host/layout/youtube_controls_layout.xml similarity index 100% rename from src/main/resources/sponsorblock/host/layout/youtube_controls_layout.xml rename to patches/src/main/resources/sponsorblock/host/layout/youtube_controls_layout.xml diff --git a/src/main/resources/sponsorblock/layout/revanced_sb_inline_sponsor_overlay.xml b/patches/src/main/resources/sponsorblock/layout/revanced_sb_inline_sponsor_overlay.xml similarity index 86% rename from src/main/resources/sponsorblock/layout/revanced_sb_inline_sponsor_overlay.xml rename to patches/src/main/resources/sponsorblock/layout/revanced_sb_inline_sponsor_overlay.xml index fb57b72524..8d4f7b41a9 100644 --- a/src/main/resources/sponsorblock/layout/revanced_sb_inline_sponsor_overlay.xml +++ b/patches/src/main/resources/sponsorblock/layout/revanced_sb_inline_sponsor_overlay.xml @@ -1,7 +1,7 @@ - - - - JsonPatch.Option( - option.key, - option.default, - option.values, - option.title, - option.description, - option.required, - ) - }, - ) - }.let { - File("patches.json").writeText(GsonBuilder().serializeNulls().create().toJson(it)) - } - - @Suppress("unused") - private class JsonPatch( - val name: String? = null, - val description: String? = null, - val compatiblePackages: Set? = null, - val use: Boolean = true, - val requiresIntegrations: Boolean = false, - val options: List