From eee16922779f994f5752190a20a9016ea98ec4cb Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 10 Oct 2024 19:43:01 +0200 Subject: [PATCH] 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 | 147 +- .../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 | 77 +- .../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 | 43 +- .../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 | 34 +- .../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 | 88 +- .../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 | 63 +- .../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 | 390 +-- .../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 | 148 +- .../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, 22895 insertions(+), 29623 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 250871bcc..193a26af0 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 2afa0596c..33c8a7211 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 b210aad5c..498cca413 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 501541766..62f6eb424 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 e086a70c4..f1854e4b5 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 ff81c99ad..b3d61b10b 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 0b4976b98..000000000 --- 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 9ed141678..000000000 --- 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 4ac3cb98b..148f321cd 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 5a96ec636..ce9413cb5 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 3a4060085..abf731bd3 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 000000000..b7b1b9b29 --- /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 000000000..dab82da23 --- /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 ccb324694..35c7b24f7 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 000000000..434b97e27 --- /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 994ddd455..7cfd385c6 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." + description = "The user.", ) -} \ No newline at end of file + + 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 000000000..b17eea94d --- /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 000000000..b50ccfc88 --- /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 000000000..3e086f95c --- /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 4e3dedd32..97320c664 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 000000000..7256d1edb --- /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 7aaee12b4..a8bb80517 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 000000000..abf7f002c --- /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 dc300df4a..26a3be2b0 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 000000000..9565e8381 --- /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 000000000..9c13b7034 --- /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 000000000..3633541e7 --- /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 000000000..af689f5ed --- /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 6e7fbb936..c2e39dc24 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 5736ff247..0c39436d1 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 e651c4d63..48d9ffa58 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 5170332db..b8bfb4432 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 000000000..5a726b791 --- /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 000000000..1339149f4 --- /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 000000000..bbe71e8c7 --- /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 000000000..144f165de --- /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 000000000..96bbab5cc --- /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 000000000..88302ef4c --- /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 000000000..a0a79f6db --- /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 000000000..387b0a0be --- /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 000000000..fa9eaf009 --- /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 000000000..59b0644d9 --- /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 000000000..609b03cf1 --- /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 000000000..543e40b43 --- /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 000000000..7c732378f --- /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 000000000..0c201f9fc --- /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 000000000..293d7ee82 --- /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 000000000..7699b7be1 --- /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 000000000..4bdeaf4a5 --- /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 f76ab78aa..4c8ee1c54 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 50c7f76a8..fec7f1249 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 000000000..9144bf749 --- /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 000000000..81cbb8c58 --- /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 000000000..8880c010e --- /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 000000000..281872662 --- /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 b71636793..558b24eba 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 000000000..6ddeb3e07 --- /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 000000000..d03ce31e7 --- /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 000000000..406d28fe4 --- /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 1fb7bf440..84e6d5876 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 000000000..95f2a3dba --- /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 000000000..fa7ff3c14 --- /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 040012d45..f0aaad890 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 constStringIndex = it.location.index - val constStringRegister = (it as OneRegisterInstruction).registerA - - replaceInstruction( - constStringIndex, - "const-string v$constStringRegister, \"$spoofedFeature\"", - ) + 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 + + 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 000000000..f47c1a3d9 --- /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 000000000..3ed14a29d --- /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 000000000..54c20a7f8 --- /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 000000000..91693d047 --- /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 000000000..62e1e5f16 --- /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 000000000..2c5a0aec1 --- /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 000000000..ae269f817 --- /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 000000000..2fa2c5b85 --- /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 000000000..84db55457 --- /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 000000000..daea6581c --- /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 000000000..38f814f6d --- /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 000000000..9440e20d3 --- /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 000000000..61cd9605f --- /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 ae427b744..12295f90c 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 000000000..573bd72e3 --- /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 000000000..3fff6a270 --- /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 000000000..65d052729 --- /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 000000000..1291c4212 --- /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 000000000..30242b8d9 --- /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 000000000..a0a6e0170 --- /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 000000000..79d41368e --- /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 000000000..6345541e1 --- /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 000000000..5a00dc68c --- /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 000000000..730c523d4 --- /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 000000000..0dfbf5cda --- /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 000000000..6d49ef6a9 --- /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 b77fd051f..75912318b 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 000000000..6637b57e7 --- /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 000000000..1f16bb10e --- /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 000000000..fa475fbbc --- /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 000000000..a2fa6329c --- /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 000000000..ca13b71c8 --- /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 000000000..24d3f8192 --- /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 000000000..3d4e223f6 --- /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 000000000..651491ca6 --- /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 000000000..0a1d181d2 --- /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 000000000..14105f762 --- /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 000000000..bb038f26b --- /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 000000000..e3eee2499 --- /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 000000000..27c18eae0 --- /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 000000000..6ce0519ad --- /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 000000000..b39b2822f --- /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 000000000..6d09557c8 --- /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 c86f79427..02a978f0f 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 000000000..13820d29d --- /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 000000000..31c3be9d6 --- /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 000000000..f2169744f --- /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 000000000..f4d754216 --- /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 000000000..d7f0f03de --- /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 000000000..0dc7e02e4 --- /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 000000000..29558ab4b --- /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 000000000..08711e1fb --- /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 000000000..f3c96dc44 --- /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 000000000..01167b6e4 --- /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 000000000..b499cdbc7 --- /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 000000000..957f055b6 --- /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 000000000..7e791af12 --- /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 000000000..e1cf24e1a --- /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 000000000..e6c1c69fe --- /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 000000000..1e1a43f9a --- /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 000000000..7131e143d --- /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 000000000..e38ca6015 --- /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 000000000..6bc4c21e5 --- /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 000000000..6f3fd4a1b --- /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 000000000..160e2db27 --- /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 000000000..47be5c2fd --- /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 70346ec2c..c747f426e 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 000000000..f4f5d48c0 --- /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 000000000..c44f91116 --- /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 000000000..e2bffa451 --- /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 000000000..a8a1f3a18 --- /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 000000000..69463c510 --- /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 000000000..a24ba416f --- /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 fe01d7b59..90c0bbb91 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 000000000..152ee8edd --- /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 cd784ab20..5d7a20783 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 000000000..5a2a7c5da --- /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 000000000..301f2f9a5 --- /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 000000000..00d0caa18 --- /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 000000000..adf0d7bd8 --- /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 000000000..6722f4223 --- /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 000000000..f6c282cbd --- /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 000000000..f62533e0c --- /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 000000000..8c2d579ef --- /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 000000000..6d9e4dc5c --- /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 000000000..cf4ba8b08 --- /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 000000000..794f21bcb --- /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 000000000..3e2addaa0 --- /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 000000000..584d2f48a --- /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 000000000..a4d2c9e22 --- /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 000000000..32957d5bd --- /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 311803195..f3f6d6669 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 000000000..c99df5707 --- /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 000000000..c556e76a2 --- /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 000000000..e7dd78912 --- /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 000000000..fae110042 --- /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 000000000..ccc0346a6 --- /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 000000000..b21b36a0d --- /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 000000000..bb87c2114 --- /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 000000000..649dffc16 --- /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 000000000..fefe25160 --- /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 000000000..618e2f145 --- /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 000000000..cc06fd396 --- /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 000000000..fb4df92f2 --- /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 000000000..a2b1530b8 --- /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 000000000..31975f44b --- /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 000000000..665dba5a4 --- /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 000000000..b35a85320 --- /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 000000000..3d92d142b --- /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 000000000..1e7e4e2e8 --- /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 000000000..4bce1362c --- /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 0565bfefa..3ec7d8c69 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 000000000..36fe06279 --- /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 000000000..1c427c967 --- /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 000000000..c6498ee00 --- /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 000000000..465faf120 --- /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 000000000..e6c591748 --- /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 000000000..ad3fc0cb1 --- /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 000000000..32a676f9d --- /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 000000000..76343a530 --- /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 000000000..14d0f4766 --- /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 000000000..263602f73 --- /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 000000000..3313c7ff7 --- /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 000000000..4ff8be461 --- /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 000000000..bfd687096 --- /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 000000000..d9b5b9fd4 --- /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 000000000..e055493bd --- /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 000000000..e87e2e7b4 --- /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 f7cbc8771..7dc955912 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 000000000..ca74997aa --- /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 000000000..e50158cdd --- /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 000000000..85d51573c --- /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 000000000..21c788a89 --- /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 000000000..c7902b1f4 --- /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 000000000..1d9aaa5a8 --- /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 000000000..67f02676f --- /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 000000000..00244b4df --- /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 000000000..f7287fcc3 --- /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 000000000..80da3f6bc --- /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 000000000..4bac74de7 --- /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 000000000..a53007045 --- /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 1a06cd54d..3123563ee 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 000000000..a43e13fe6 --- /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 000000000..116205a34 --- /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 000000000..09fe16247 --- /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 000000000..2eac1cbe2 --- /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 000000000..8a3a0f810 --- /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 000000000..db449a93e --- /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 000000000..3381fd2bb --- /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 000000000..fb14ca80c --- /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 000000000..f7efe3103 --- /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 000000000..40723ca27 --- /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 000000000..df927dd4a --- /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 000000000..8c3066c91 --- /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 000000000..0eabd2f54 --- /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 000000000..58cc5082f --- /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 000000000..3d7a1e444 --- /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 000000000..80d041ada --- /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 000000000..2ebb6a234 --- /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 000000000..b5f613d54 --- /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 000000000..c338385bf --- /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 000000000..e372d926c --- /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 2c165e1f1..ef198b491 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") - - data class ResourceElement(val type: String, val name: String, val id: Long) } + +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 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 1570cc3dc..ea14b4a7b 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 381a2cf3d..648f5eefd 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 787115dba..d4ecaae7e 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 049966a2c..2b21a8413 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 d329a9177..81de61569 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 000000000..0e81d7e59 --- /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 000000000..39a8ae64f --- /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 c32a7b097..d93b31af4 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") +) { + compatibleWith("com.sony.songpal.mdr") - override fun execute(context: BytecodeContext) { - CreateTabsFingerprint.result?.mutableMethod?.apply { + 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 000000000..5d7498c5f --- /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 000000000..4f59c5f6a --- /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 000000000..28780ea57 --- /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 ea94b3f2a..19d692863 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 000000000..e41333879 --- /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 000000000..2954b4d99 --- /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 08446f986..89a2ff5fc 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 000000000..688fe3604 --- /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 000000000..3a50ae407 --- /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 9418c9f76..30d8a0bb4 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 000000000..365235bee --- /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 000000000..d8a8940d2 --- /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 000000000..761e32206 --- /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 000000000..857842fc6 --- /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 6b493c4b1..d7e3a3088 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 eecdeb38d..80894391e 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 000000000..0458f45d3 --- /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 000000000..0d96767a2 --- /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 000000000..c1af24825 --- /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 000000000..1204a36f2 --- /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 000000000..0efa845fb --- /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 000000000..82714c651 --- /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 000000000..4bd688de4 --- /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 000000000..5e2aa37d8 --- /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 000000000..7301c58ba --- /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 000000000..4f899661e --- /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 000000000..eb2868374 --- /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 b1522e880..73374b513 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 000000000..94afc211f --- /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 000000000..f58141fac --- /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 000000000..ce372ea42 --- /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 000000000..5cb6db64c --- /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 000000000..221036bb9 --- /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 000000000..81153a1dc --- /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 000000000..b714b9d1c --- /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 000000000..24a49dd35 --- /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 000000000..1b2f0a0b0 --- /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 000000000..929ef8672 --- /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 000000000..a40f5251f --- /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 000000000..214868d04 --- /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 000000000..d1c4d6de6 --- /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 000000000..79ee38711 --- /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 1c40b5d9c..408958179 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, ) - override fun execute(context: BytecodeContext) { + compatibleWith( + "com.ss.android.ugc.trill", + "com.zhiliaoapp.musically", + ) + + 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 000000000..3e98d213e --- /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 000000000..4a02c6221 --- /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 000000000..4b3138d67 --- /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 000000000..12ffa15c1 --- /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 368879cfa..c8121f705 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) - override fun execute(context: BytecodeContext) { - BrightnessFingerprint.result?.mutableMethod?.apply { + compatibleWith("de.tudortmund.app") + + 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 000000000..1c056b0c6 --- /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 77d31b9f3..4a9bbe297 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 000000000..c166cb83b --- /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 000000000..3a46c19b2 --- /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 000000000..fbd7cf712 --- /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 55b67f2fa..67e051a7b 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 000000000..7f1bc208f --- /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 000000000..7d00f2f1b --- /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 0088c3aa6..e24ca2884 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 94367009c..e3d420e93 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 addOverride: (name: String, value: String) -> Unit private set +/** + * 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 - override fun execute(context: BytecodeContext) = GetFeatureValueFingerprint.result?.let { - val configurationClass = it.method.definingClass - val featureClass = it.method.parameterTypes[0].toString() +@Suppress("unused") +val overrideFeatureFlagsPatch = bytecodePatch( + description = "Forcibly set the value of A/B testing features of your choice.", +) { + val getFeatureValueMatch by getFeatureValueFingerprint() + + 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 000000000..11616fcc9 --- /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 000000000..d51aead2b --- /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 000000000..64fa2ed79 --- /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 000000000..3a69b74a5 --- /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 000000000..ff44e1bdb --- /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 000000000..b8ed3bfc4 --- /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 000000000..6827a99e3 --- /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 000000000..21e9cb6d2 --- /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 000000000..4d36bc22d --- /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 000000000..cbf817469 --- /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 000000000..48ecefba8 --- /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 000000000..d03449733 --- /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 000000000..5c0eed6eb --- /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 000000000..21da99a5b --- /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 000000000..93996e77a --- /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 740f8cedf..15a03f7c3 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, + ) - SettingsPatch.PreferenceScreen.CHAT.GENERAL.addPreferences( - SwitchPreference("revanced_auto_claim_channel_points") + compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1")) + + val communityPointsButtonViewDelegateMatch by communityPointsButtonViewDelegateFingerprint() + + execute { + addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch") + + 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 000000000..80abc9ac4 --- /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 000000000..25cd5acc1 --- /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 000000000..665180c19 --- /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 000000000..9a46867ab --- /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 000000000..2299d3bb5 --- /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 000000000..43d5bb39b --- /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 000000000..423687792 --- /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 000000000..dc100acb1 --- /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 000000000..0f9157d6b --- /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 000000000..625b6f0bb --- /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 000000000..83b65581e --- /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 51d62a998..b7c3d3e7a 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 000000000..f1e6a879b --- /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 000000000..4f9b38ff6 --- /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 000000000..253379615 --- /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 000000000..ca757dcdd --- /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 000000000..cf4578b0c --- /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 000000000..6cde25b50 --- /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 000000000..cefe3226f --- /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 000000000..0d5d0e6f8 --- /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 000000000..c9cea00c7 --- /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 000000000..62e7a4f00 --- /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 000000000..0809324c7 --- /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 000000000..dae609164 --- /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 000000000..41627aaaa --- /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 000000000..818c04cbd --- /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 000000000..d33880de7 --- /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 000000000..67b930ae8 --- /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 000000000..e326c2682 --- /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 000000000..6c0d99cca --- /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 000000000..f199f127c --- /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 000000000..3f4cac757 --- /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 000000000..7abaeda88 --- /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 000000000..7629d1760 --- /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 000000000..ced812393 --- /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 240886f01..91cc0e8df 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 000000000..5c098d551 --- /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 000000000..0c9fe544c --- /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 232e65e6a..b25287590 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 000000000..caccae382 --- /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 000000000..34b64a8ad --- /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 000000000..f10fc8e83 --- /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 000000000..df2f161e4 --- /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 000000000..f07aeeca0 --- /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 000000000..2e5eebba9 --- /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 000000000..3087769f8 --- /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 000000000..0a31f1c30 --- /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 000000000..3cee8b453 --- /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 000000000..8a46798d4 --- /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 000000000..3e1c15629 --- /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 ec720ec97..d1670aeda 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" +) { + compatibleWith("com.google.android.youtube") - 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" } - - 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 d0614297a..8b184c57f 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" +) { + compatibleWith("com.google.android.youtube") - 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( + 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 000000000..e0ee58273 --- /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 000000000..eebd0befb --- /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 000000000..43b221ade --- /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 000000000..386f18bcd --- /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 000000000..380b9309f --- /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 000000000..f165098f0 --- /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 000000000..a7955c59e --- /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 000000000..59d859e80 --- /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 000000000..e63f8950d --- /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 000000000..a286d70fd --- /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 000000000..7f20c1433 --- /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 000000000..463e4cdc6 --- /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 229f08d30..0b4e008f9 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 -@Patch( +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;" + +@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;" + dependsOn( + lithoFilterPatch, + settingsPatch, + addResourcesPatch, + hideLayoutComponentsResourcePatch, + navigationBarHookPatch, + ) - override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) - SettingsPatch.PreferenceScreen.PLAYER.addPreferences( - PreferenceScreen( + 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,41 +389,20 @@ object HideLayoutComponentsPatch : BytecodePatch( // 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 + /** + * 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 = @@ -379,5 +410,24 @@ object HideLayoutComponentsPatch : BytecodePatch( addInstructions(insertIndex, instructions(register)) } + + filterBarHeightMatch.patch { register -> + """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInFeed(I)I + move-result v$register + """ + } + + searchResultsChipBarMatch.patch(-1, -2) { register -> + """ + invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInSearch(I)I + move-result v$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 000000000..5088472a1 --- /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 000000000..5e5b4c9a6 --- /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 000000000..9062a5331 --- /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 000000000..2c8ac9d05 --- /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 000000000..0759935cd --- /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 000000000..e38ea167b --- /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 000000000..6d84b1526 --- /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 000000000..436f3db2f --- /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 000000000..78efc0680 --- /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 000000000..d109c6455 --- /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 000000000..ae9604807 --- /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 000000000..28abd6cba --- /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 000000000..111f93426 --- /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 000000000..0c31cc83b --- /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 000000000..3914d53e2 --- /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 000000000..433cce068 --- /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 000000000..998bd6129 --- /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 000000000..abe87d8cf --- /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 000000000..a56b1d9dd --- /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 000000000..20f47e958 --- /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 000000000..69fc26dae --- /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 000000000..7f39f9bb5 --- /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 000000000..35fafcfe1 --- /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 000000000..a21927289 --- /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 000000000..8b114bf3c --- /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 000000000..cd48868f5 --- /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 000000000..d9953de47 --- /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 000000000..612ceb6c8 --- /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 000000000..644866da8 --- /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 000000000..969d90e02 --- /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 000000000..3ec97ea95 --- /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 000000000..5c62ff3b2 --- /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 000000000..022084020 --- /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 000000000..9f88c08b0 --- /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 000000000..326ebbe14 --- /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 000000000..7b07f5ecf --- /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 000000000..30667a85b --- /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 000000000..9ed098a1b --- /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 000000000..5fa87d900 --- /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 000000000..a99daf720 --- /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 000000000..614ae9052 --- /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 000000000..cdb7dc4ed --- /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 000000000..00684b2f7 --- /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 000000000..9558b965c --- /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 000000000..b954b27e1 --- /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 000000000..aa4f6f922 --- /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 000000000..f0cc13531 --- /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 000000000..b354c75e1 --- /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 000000000..4f99a4cf5 --- /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 000000000..c82d363e1 --- /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 000000000..f4433fe29 --- /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 000000000..369e3ea74 --- /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 000000000..6a0e7d1f4 --- /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 000000000..fed0199aa --- /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 000000000..ae5aee5e7 --- /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 b936d7bf9..902d65bf1 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 000000000..35ac5f7ae --- /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 000000000..71bd1c62c --- /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 000000000..c560d7e72 --- /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 2437dfda6..8bbd0ec77 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 000000000..1fea1f9bd --- /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 000000000..8e21147f6 --- /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 000000000..fcd5298ac --- /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 000000000..002c6e863 --- /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 000000000..73f6fae75 --- /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 000000000..15e7b3c6e --- /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 000000000..5c0b0c816 --- /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 000000000..a3936d54d --- /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 000000000..0f5ade30a --- /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 000000000..fe9002a32 --- /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 000000000..5bf4561f1 --- /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 000000000..f682f6439 --- /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 000000000..2faae3acc --- /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 000000000..5ae0f6122 --- /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 4a704ca54..882e4537f 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 000000000..72734bba7 --- /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 000000000..04da4a967 --- /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 000000000..09aa7bf4c --- /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 000000000..16863b200 --- /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 000000000..42298d82f --- /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 000000000..5a731c905 --- /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 000000000..65cb70769 --- /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 000000000..31cdccd8b --- /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 000000000..a7c72504e --- /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 000000000..d0c961c9d --- /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 000000000..a52a8e0d4 --- /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 000000000..603329de7 --- /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 000000000..3febd4a73 --- /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 000000000..e33674a38 --- /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 3e3c7000e..79ca86a4d 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, + ) - override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) + compatibleWith( + "com.google.android.youtube"( + "18.38.44", + "18.49.37", + "19.16.39", + "19.25.37", + "19.34.42", + ), + ) - SettingsPatch.PreferenceScreen.VIDEO.addPreferences( + val videoQualitySetterMatch by videoQualitySetterFingerprint() + val videoQualityItemOnClickParentMatch by videoQualityItemOnClickParentFingerprint() + val newVideoQualityChangedMatch by newVideoQualityChangedFingerprint() + + execute { context -> + addResources("youtube", "video.quality.rememberVideoQualityPatch") + + 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 + newVideoQualityChangedMatch.mutableMethod.apply { + val index = newVideoQualityChangedMatch.patternMatch!!.startIndex + val qualityRegister = getInstruction(index).registerA - addInstruction( - index + 1, - "invoke-static {v$qualityRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->userChangedQualityInNewFlyout(I)V" - ) - } - } ?: throw NewVideoQualityChangedFingerprint.exception + 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 000000000..592335bfc --- /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 000000000..0537d0413 --- /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 000000000..f6d2ac9de --- /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 000000000..d3aa805d8 --- /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 000000000..3924588b4 --- /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 000000000..accc2498e --- /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 000000000..c2b73fbee --- /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 000000000..35d95d8da --- /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 000000000..7f5469da3 --- /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 000000000..182e88996 --- /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 000000000..af38f28f4 --- /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 000000000..6ca1d5e57 --- /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 c9b146645..97ee4f2aa 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 MethodFingerprint.alsoResolve(context: BytecodeContext, parentFingerprint: MethodFingerprint) = - also { resolve(context, parentFingerprint.resultOrThrow().classDef) }.resultOrThrow() +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. + */ +// 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 2f0d93f84..02671bbd4 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 829f9533e..4a4715011 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 295c84077..76310e0cd 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 fb57b7252..8d4f7b41a 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