feat: Update YouTube & YouTube Music patches (#6571)
This commit is a squash of multiple commits, authored by the individuals referenced below. To see the exact commits by each author, see the unsquashed tree at https://github.com/ReVanced/revanced-patches/pull/6571 or with commit 03940665d27a42ed08992757dfe4534bd8243356. Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de> Co-authored-by: hoodles <207470673+hoo-dles@users.noreply.github.com> Co-authored-by: ILoveOpenSourceApplications <117499019+iloveopensourceapplications@users.noreply.github.com> Co-authored-by: LisoUseInAIKyrios <118716522+lisouseinaikyrios@users.noreply.github.com> Co-authored-by: inotia00 <108592928+inotia00@users.noreply.github.com> Co-authored-by: OxrxL <108184954+oxrxl@users.noreply.github.com>
This commit is contained in:
parent
db5e0fe587
commit
88d33b847d
300 changed files with 8226 additions and 3643 deletions
|
|
@ -485,6 +485,10 @@ public final class app/revanced/patches/music/misc/androidauto/UnlockAndroidAuto
|
|||
public static final fun getUnlockAndroidAutoMediaBrowserPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/misc/audio/ForceOriginalAudioPatchKt {
|
||||
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatchKt {
|
||||
public static final fun getRemoveBackgroundPlaybackRestrictionsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -539,10 +543,6 @@ public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPat
|
|||
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/misc/tracks/ForceOriginalAudioPatchKt {
|
||||
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/playservice/VersionCheckPatchKt {
|
||||
public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
public static final fun is_7_16_or_greater ()Z
|
||||
|
|
@ -551,6 +551,8 @@ public final class app/revanced/patches/music/playservice/VersionCheckPatchKt {
|
|||
public static final fun is_8_10_or_greater ()Z
|
||||
public static final fun is_8_11_or_greater ()Z
|
||||
public static final fun is_8_15_or_greater ()Z
|
||||
public static final fun is_8_40_or_greater ()Z
|
||||
public static final fun is_8_41_or_greater ()Z
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
|
||||
|
|
@ -1479,15 +1481,11 @@ public final class app/revanced/patches/youtube/ad/general/HideAdsPatchKt {
|
|||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/ad/getpremium/HideGetPremiumPatchKt {
|
||||
public static final fun getHideGetPremiumPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/ad/video/VideoAdsPatchKt {
|
||||
public static final fun getVideoAdsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlPatchKt {
|
||||
public final class app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoURLPatchKt {
|
||||
public static final fun getCopyVideoURLPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
|
|
@ -1495,6 +1493,10 @@ public final class app/revanced/patches/youtube/interaction/dialog/RemoveViewerD
|
|||
public static final fun getRemoveViewerDiscretionDialogPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/doubletap/AddMoreDoubleTapToSeekLengthOptionsPatchKt {
|
||||
public static final fun getAddMoreDoubleTapToSeekLengthOptionsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/doubletap/DisableChapterSkipDoubleTapPatchKt {
|
||||
public static final fun getDisableDoubleTapActionsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1503,18 +1505,22 @@ public final class app/revanced/patches/youtube/interaction/downloads/DownloadsP
|
|||
public static final fun getDownloadsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/hapticfeedback/DisableHapticFeedbackPatchKt {
|
||||
public static final fun getDisableHapticFeedbackPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/DisablePreciseSeekingGesturePatchKt {
|
||||
public static final fun getDisablePreciseSeekingGesturePatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/EnableSeekbarTappingPatchKt {
|
||||
public static final fun getEnableSeekbarTappingPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideToSeekPatchKt {
|
||||
public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/EnableTapToSeekPatchKt {
|
||||
public static final fun getEnableTapToSeekPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatchKt {
|
||||
public static final fun getHideSeekbarPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1523,10 +1529,6 @@ public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarPatch
|
|||
public static final fun getSeekbarPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatchKt {
|
||||
public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/swipecontrols/SwipeControlsPatchKt {
|
||||
public static final fun getSwipeControlsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1543,12 +1545,12 @@ public final class app/revanced/patches/youtube/layout/branding/header/ChangeHea
|
|||
public static final fun getChangeHeaderPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatchKt {
|
||||
public final class app/revanced/patches/youtube/layout/buttons/action/HideVideoActionsButtonsPatchKt {
|
||||
public static final fun getHideVideoActionButtonsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatchKt {
|
||||
public static final fun getNavigationButtonsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
public final class app/revanced/patches/youtube/layout/buttons/navigation/NavigationBarPatchKt {
|
||||
public static final fun getNavigationBarPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlayerOverlayButtonsPatchKt {
|
||||
|
|
@ -1559,11 +1561,15 @@ public final class app/revanced/patches/youtube/layout/formfactor/ChangeFormFact
|
|||
public static final fun getChangeFormFactorPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/autoplaypreview/HideAutoplayPreviewPatchKt {
|
||||
public static final fun getHideAutoplayPreviewPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndScreenCardsPatchKt {
|
||||
public static final fun getHideEndScreenCardsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/endscreensuggestion/HideEndScreenSuggestedVideoPatchKt {
|
||||
public final class app/revanced/patches/youtube/layout/hide/endscreensuggestedvideo/HideEndScreenSuggestedVideoPatchKt {
|
||||
public static final fun getHideEndScreenSuggestedVideoPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
|
|
@ -1579,10 +1585,14 @@ public final class app/revanced/patches/youtube/layout/hide/infocards/HideInfoCa
|
|||
public static final fun getHideInfoCardsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatchKt {
|
||||
public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenu/HidePlayerFlyoutMenuPatchKt {
|
||||
public static final fun getHidePlayerFlyoutMenuItemsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/player/popup/PlayerPopupPanelsPatchKt {
|
||||
public static final fun getDisablePlayerPopupPanelsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/relatedvideooverlay/HideRelatedVideoOverlayPatchKt {
|
||||
public static final fun getHideRelatedVideoOverlayPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1595,7 +1605,7 @@ public final class app/revanced/patches/youtube/layout/hide/shorts/HideShortsCom
|
|||
public static final fun getHideShortsComponentsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/signintotvpopup/DisableSignInToTvPatchPopupKt {
|
||||
public final class app/revanced/patches/youtube/layout/hide/signintotvpopup/DisableSignInToTVPopupPatchKt {
|
||||
public static final fun getDisableSignInToTVPopupPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
|
|
@ -1607,10 +1617,6 @@ public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatc
|
|||
public static final fun getMiniplayerPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatchKt {
|
||||
public static final fun getDisablePlayerPopupPanelsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/player/fullscreen/ExitFullscreenPatchKt {
|
||||
public static final fun getExitFullscreenPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1637,10 +1643,6 @@ public final class app/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/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatchKt {
|
||||
public static final fun getSeekbarColorPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1681,6 +1683,10 @@ public final class app/revanced/patches/youtube/layout/thumbnails/BypassImageReg
|
|||
public static final fun getBypassImageRegionRestrictionsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/toolbar/ToolbarHookPatchKt {
|
||||
public static final fun getToolbarHookPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/announcements/AnnouncementsPatchKt {
|
||||
public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1693,6 +1699,25 @@ public final class app/revanced/patches/youtube/misc/backgroundplayback/Backgrou
|
|||
public static final fun getRemoveBackgroundPlaybackRestrictionsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/contexthook/ClientContextHookPatchKt {
|
||||
public static final fun addClientVersionHook (Lapp/revanced/patches/youtube/misc/contexthook/Endpoint;Ljava/lang/String;)V
|
||||
public static final fun addOSNameHook (Lapp/revanced/patches/youtube/misc/contexthook/Endpoint;Ljava/lang/String;)V
|
||||
public static final fun getHookClientContextPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/contexthook/Endpoint : java/lang/Enum {
|
||||
public static final field BROWSE Lapp/revanced/patches/youtube/misc/contexthook/Endpoint;
|
||||
public static final field GUIDE Lapp/revanced/patches/youtube/misc/contexthook/Endpoint;
|
||||
public static final field REEL Lapp/revanced/patches/youtube/misc/contexthook/Endpoint;
|
||||
public static final field SEARCH Lapp/revanced/patches/youtube/misc/contexthook/Endpoint;
|
||||
public static fun getEntries ()Lkotlin/enums/EnumEntries;
|
||||
public final fun getGetEndpointMethods ()[Lkotlin/jvm/functions/Function1;
|
||||
public final fun getInstructions ()Ljava/lang/String;
|
||||
public final fun setInstructions (Ljava/lang/String;)V
|
||||
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/youtube/misc/contexthook/Endpoint;
|
||||
public static fun values ()[Lapp/revanced/patches/youtube/misc/contexthook/Endpoint;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt {
|
||||
public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1705,6 +1730,15 @@ public final class app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomain
|
|||
public static final fun getCheckWatchHistoryDomainNameResolutionPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/engagement/EngagementPanelHookPatchKt {
|
||||
public static final fun addEngagementPanelIdHook (Ljava/lang/String;)V
|
||||
public static final fun getEngagementPanelHookPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
public static final fun getPanelControllerMethod ()Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;
|
||||
public static final fun getPanelIdIndex ()I
|
||||
public static final fun getPanelIdRegister ()I
|
||||
public static final fun getPanelIdSmaliInstruction ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/extension/SharedExtensionPatchKt {
|
||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -1717,16 +1751,12 @@ public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatchKt {
|
|||
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/hapticfeedback/DisableHapticFeedbackPatchKt {
|
||||
public static final fun getDisableHapticFeedbackPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
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/Patch;
|
||||
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/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatchKt {
|
||||
|
|
@ -1799,14 +1829,23 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
|
|||
public static final fun is_20_22_or_greater ()Z
|
||||
public static final fun is_20_26_or_greater ()Z
|
||||
public static final fun is_20_28_or_greater ()Z
|
||||
public static final fun is_20_29_or_greater ()Z
|
||||
public static final fun is_20_30_or_greater ()Z
|
||||
public static final fun is_20_31_or_greater ()Z
|
||||
public static final fun is_20_34_or_greater ()Z
|
||||
public static final fun is_20_37_or_greater ()Z
|
||||
public static final fun is_20_39_or_greater ()Z
|
||||
public static final fun is_20_40_or_greater ()Z
|
||||
public static final fun is_20_41_or_greater ()Z
|
||||
public static final fun is_20_45_or_greater ()Z
|
||||
public static final fun is_20_46_or_greater ()Z
|
||||
public static final fun is_20_49_or_greater ()Z
|
||||
public static final fun is_21_02_or_greater ()Z
|
||||
public static final fun is_21_03_or_greater ()Z
|
||||
public static final fun is_21_05_or_greater ()Z
|
||||
public static final fun is_21_06_or_greater ()Z
|
||||
public static final fun is_21_07_or_greater ()Z
|
||||
public static final fun is_21_08_or_greater ()Z
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/privacy/SanitizeSharingLinksPatchKt {
|
||||
|
|
@ -1913,17 +1952,22 @@ public final class app/revanced/patches/youtube/video/videoid/VideoIdPatchKt {
|
|||
}
|
||||
|
||||
public final class app/revanced/util/BytecodeUtilsKt {
|
||||
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;ILjava/lang/String;)V
|
||||
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/extensions/ExternalLabel;)V
|
||||
public static final fun cloneMutable (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;ILjava/util/List;Ljava/lang/String;I)Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;
|
||||
public static synthetic fun cloneMutable$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;ILjava/util/List;Ljava/lang/String;IILjava/lang/Object;)Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;
|
||||
public static final fun cloneMutableAndPreserveParameters (Lapp/revanced/patcher/patch/BytecodePatchContext;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;
|
||||
public static final fun cloneMutableAndPreserveParameters (Lcom/android/tools/smali/dexlib2/iface/Method;Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableClassDef;)Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableMethod;
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
|
||||
public static final fun findFreeRegister (Lcom/android/tools/smali/dexlib2/iface/Method;I[I)I
|
||||
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
|
||||
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
|
||||
public static final fun forEachInstructionAsSequence (Lapp/revanced/patcher/patch/BytecodePatchContext;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function2;)V
|
||||
public static final fun getNumberOfParameterRegisters (Lcom/android/tools/smali/dexlib2/iface/Method;)I
|
||||
public static final fun getNumberOfParameterRegistersLogical (Lcom/android/tools/smali/dexlib2/iface/Method;)I
|
||||
public static final fun getP0Register (Lcom/android/tools/smali/dexlib2/iface/Method;)I
|
||||
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
|
||||
|
|
@ -1983,6 +2027,24 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||
public static final fun traverseClassHierarchy (Lapp/revanced/patcher/patch/BytecodePatchContext;Lapp/revanced/com/android/tools/smali/dexlib2/mutable/MutableClassDef;Lkotlin/jvm/functions/Function1;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/util/FreeRegisterProvider {
|
||||
public final fun availableCount ()I
|
||||
public final fun getAllocatedFreeRegisters ()Ljava/util/List;
|
||||
public final fun getFreeRegister ()I
|
||||
public final fun getMethod ()Lcom/android/tools/smali/dexlib2/iface/Method;
|
||||
public final fun getUsedAndUnAvailableRegisters ()Ljava/util/List;
|
||||
public final fun hasFreeRegisters ()Z
|
||||
}
|
||||
|
||||
public final class app/revanced/util/FreeRegisterProviderKt {
|
||||
public static final fun findFreeRegister (Lcom/android/tools/smali/dexlib2/iface/Method;ILjava/util/List;)I
|
||||
public static final fun findFreeRegister (Lcom/android/tools/smali/dexlib2/iface/Method;I[I)I
|
||||
public static final fun getFreeRegisterProvider (Lcom/android/tools/smali/dexlib2/iface/Method;IILjava/util/List;)Lapp/revanced/util/FreeRegisterProvider;
|
||||
public static final fun getFreeRegisterProvider (Lcom/android/tools/smali/dexlib2/iface/Method;II[I)Lapp/revanced/util/FreeRegisterProvider;
|
||||
public static final fun getRegistersUsed (Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Ljava/util/List;
|
||||
public static final fun getWriteRegister (Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;)Ljava/lang/Integer;
|
||||
}
|
||||
|
||||
public final class app/revanced/util/ResourceGroup {
|
||||
public fun <init> (Ljava/lang/String;[Ljava/lang/String;)V
|
||||
public final fun getResourceDirectoryName ()Ljava/lang/String;
|
||||
|
|
|
|||
|
|
@ -35,23 +35,10 @@ interface IMethodCall {
|
|||
instruction: Instruction35c,
|
||||
instructionIndex: Int,
|
||||
) {
|
||||
val registers = arrayOf(
|
||||
instruction.registerC,
|
||||
instruction.registerD,
|
||||
instruction.registerE,
|
||||
instruction.registerF,
|
||||
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}.",
|
||||
)
|
||||
val args = with(instruction) {
|
||||
arrayOf(registerC, registerD, registerE, registerF, registerG)
|
||||
.take(registerCount).joinToString(", ") { "v$it" }
|
||||
}
|
||||
|
||||
val args = registers.take(argsNum).joinToString(separator = ", ") { reg -> "v$reg" }
|
||||
val replacementMethod =
|
||||
"$methodName(${definedClassName}${methodParams.joinToString(separator = "")})$returnType"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import app.revanced.patches.music.misc.settings.PreferenceScreen
|
|||
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideVideoAdsPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/patches/HideVideoAdsPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val hideMusicVideoAdsPatch = bytecodePatch(
|
||||
|
|
@ -26,6 +27,8 @@ val hideMusicVideoAdsPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ val enableExclusiveAudioPlaybackPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ import app.revanced.patches.music.misc.settings.settingsPatch
|
|||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.util.findFreeRegister
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/PermanentRepeatPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/patches/PermanentRepeatPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val permanentRepeatPatch = bytecodePatch(
|
||||
|
|
@ -29,6 +30,8 @@ val permanentRepeatPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,15 +29,15 @@ private val disableSplashAnimationPatch = bytecodePatch {
|
|||
// causing the original YT Music logo to momentarily flash on screen as the animation starts.
|
||||
//
|
||||
// Could replace the lottie animation file with our own custom animation (app_launch.json),
|
||||
// but the animation is not always the same size as the launch screen and it's still
|
||||
// barely shown. Instead turn off the animation entirely (app will also launch a little faster).
|
||||
// but the animation is not always the same size as the launch screen, and it's still
|
||||
// barely shown. Instead, turn off the animation entirely (app will also launch a little faster).
|
||||
cairoSplashAnimationConfigMethod.apply {
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(
|
||||
ResourceType.LAYOUT["main_activity_launch_animation"],
|
||||
)
|
||||
val checkCastIndex = indexOfFirstInstructionOrThrow(literalIndex) {
|
||||
opcode == Opcode.CHECK_CAST &&
|
||||
getReference<TypeReference>()?.type == "Lcom/airbnb/lottie/LottieAnimationView;"
|
||||
getReference<TypeReference>()?.type == "Lcom/airbnb/lottie/LottieAnimationView;"
|
||||
}
|
||||
val register = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
|
||||
|
||||
|
|
@ -73,6 +73,8 @@ val customBrandingPatch = baseCustomBrandingPatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import app.revanced.patches.shared.misc.mapping.ResourceType
|
|||
|
||||
internal val BytecodePatchContext.cairoSplashAnimationConfigMethod by gettingFirstMethodDeclaratively {
|
||||
name("onCreate")
|
||||
returnType("V")
|
||||
definingClass(YOUTUBE_MUSIC_MAIN_ACTIVITY_CLASS_TYPE)
|
||||
returnType("V")
|
||||
parameterTypes("Landroid/os/Bundle;")
|
||||
instructions(ResourceType.LAYOUT("main_activity_launch_animation"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ internal val BytecodePatchContext.historyMenuItemMethodMatch by composingFirstMe
|
|||
Opcode.RETURN_VOID,
|
||||
)
|
||||
literal { historyMenuItem }
|
||||
custom {
|
||||
immutableClassDef.methods.count() == 5
|
||||
}
|
||||
custom { immutableClassDef.methods.count() == 5 || immutableClassDef.methods.count() == 4 }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.historyMenuItemOfflineTabMethodMatch by composingFirstMethod {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ internal var searchButton = -1L
|
|||
internal var topBarMenuItemImageView = -1L
|
||||
private set
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideButtonsPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/patches/HideButtonsPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val hideButtonsPatch = bytecodePatch(
|
||||
|
|
@ -49,6 +50,8 @@ val hideButtonsPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -91,7 +94,11 @@ val hideButtonsPatch = bytecodePatch(
|
|||
arrayOf(
|
||||
Triple(playerOverlayChipMethod, playerOverlayChip, "hideCastButton"),
|
||||
Triple(searchActionViewMethod, searchButton, "hideSearchButton"),
|
||||
Triple(topBarMenuItemImageViewMethod, topBarMenuItemImageView, "hideNotificationButton"),
|
||||
Triple(
|
||||
topBarMenuItemImageViewMethod,
|
||||
topBarMenuItemImageView,
|
||||
"hideNotificationButton"
|
||||
),
|
||||
).forEach { (method, resourceIdLiteral, methodName) ->
|
||||
method.apply {
|
||||
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(resourceIdLiteral)
|
||||
|
|
@ -104,7 +111,7 @@ val hideButtonsPatch = bytecodePatch(
|
|||
addInstruction(
|
||||
targetIndex + 1,
|
||||
"invoke-static { v$targetRegister }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/view/View;)V",
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||
internal var chipCloud = -1L
|
||||
private set
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideCategoryBarPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/patches/HideCategoryBarPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val hideCategoryBarPatch = bytecodePatch(
|
||||
|
|
@ -32,6 +33,8 @@ val hideCategoryBarPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -46,7 +49,8 @@ val hideCategoryBarPatch = bytecodePatch(
|
|||
|
||||
chipCloudMethodMatch.let {
|
||||
val targetIndex = it[-1]
|
||||
val targetRegister = it.method.getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
val targetRegister =
|
||||
it.method.getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
|
||||
it.method.addInstruction(
|
||||
targetIndex + 1,
|
||||
|
|
|
|||
|
|
@ -8,5 +8,12 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
lithoFilterPatch = lithoFilterPatch,
|
||||
settingsPatch = settingsPatch,
|
||||
filterClasses = setOf("Lapp/revanced/extension/shared/patches/litho/CustomFilter;"),
|
||||
compatibleWithPackages = arrayOf("com.google.android.apps.youtube.music" to setOf("7.29.52", "8.10.52")),
|
||||
compatibleWithPackages = arrayOf(
|
||||
"com.google.android.apps.youtube.music" to setOf(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54"
|
||||
)
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/ChangeMiniplayerColorPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/patches/ChangeMiniplayerColorPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val changeMiniplayerColorPatch = bytecodePatch(
|
||||
|
|
@ -40,6 +41,8 @@ val changeMiniplayerColorPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -71,10 +74,11 @@ val changeMiniplayerColorPatch = bytecodePatch(
|
|||
miniPlayerConstructorMethodMatch.immutableMethod.indexOfFirstInstructionReversedOrThrow {
|
||||
getReference<MethodReference>()?.name == "getColor"
|
||||
}
|
||||
val iPutIndex = miniPlayerConstructorMethodMatch.immutableMethod.indexOfFirstInstructionOrThrow(
|
||||
colorGreyIndex,
|
||||
Opcode.IPUT,
|
||||
)
|
||||
val iPutIndex =
|
||||
miniPlayerConstructorMethodMatch.immutableMethod.indexOfFirstInstructionOrThrow(
|
||||
colorGreyIndex,
|
||||
Opcode.IPUT,
|
||||
)
|
||||
val colorMathPlayerIPutReference = miniPlayerConstructorMethodMatch.immutableMethod
|
||||
.getInstruction<ReferenceInstruction>(iPutIndex).reference
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,11 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
|||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.miniPlayerConstructorMethodMatch by composingFirstMethod {
|
||||
returnType("V")
|
||||
instructions(
|
||||
ResourceType.ID("mpp_player_bottom_sheet"),
|
||||
"sharedToggleMenuItemMutations"(),
|
||||
)
|
||||
internal val BytecodePatchContext.miniPlayerConstructorMethodMatch by composingFirstMethod(
|
||||
"sharedToggleMenuItemMutations"
|
||||
) {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(ResourceType.ID("music_playback_controls"))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,34 +2,45 @@ package app.revanced.patches.music.layout.navigationbar
|
|||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
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 BytecodePatchContext.tabLayoutTextMethodMatch by composingFirstMethod("FEmusic_search") {
|
||||
|
||||
internal val BytecodePatchContext.tabLayoutTextMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
opcodes(
|
||||
Opcode.IGET,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
)
|
||||
custom {
|
||||
containsLiteralInstruction(text1) &&
|
||||
indexOfGetVisibilityInstruction(this) >= 0
|
||||
}
|
||||
}
|
||||
instructions(
|
||||
anyOf(
|
||||
"FEmusic_search"(), // 8.49 and lower.
|
||||
"FEsearch"() // 8.50+
|
||||
),
|
||||
// Hide navigation label.
|
||||
ResourceType.ID("text1"),
|
||||
afterAtMost(
|
||||
5,
|
||||
method { toString() == "Landroid/view/View;->findViewById(I)Landroid/view/View;" }
|
||||
),
|
||||
afterAtMost(
|
||||
5,
|
||||
allOf(Opcode.CHECK_CAST(), type("Landroid/widget/TextView;"))
|
||||
),
|
||||
// Set navigation enum.
|
||||
anyOf(
|
||||
Opcode.SGET_OBJECT(),
|
||||
Opcode.IGET_OBJECT()
|
||||
),
|
||||
afterAtMost(5, allOf(Opcode.IGET(), field { type == "I" })),
|
||||
|
||||
internal fun indexOfGetVisibilityInstruction(method: Method) = method.indexOfFirstInstruction {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
getReference<MethodReference>()?.name == "getVisibility"
|
||||
afterAtMost(
|
||||
5,
|
||||
allOf(
|
||||
Opcode.INVOKE_STATIC(),
|
||||
method { returnType == "L" && parameterTypes.size == 1 && parameterTypes.first() == "I" })
|
||||
),
|
||||
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||
// Hide navigation buttons.
|
||||
method("getVisibility")
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package app.revanced.patches.music.layout.navigationbar
|
|||
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.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
|
||||
|
|
@ -14,9 +13,6 @@ import app.revanced.patches.shared.misc.mapping.ResourceType
|
|||
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.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
||||
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
|
||||
|
|
@ -24,7 +20,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|||
internal var text1 = -1L
|
||||
private set
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/NavigationBarPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/patches/NavigationBarPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val navigationBarPatch = bytecodePatch(
|
||||
|
|
@ -58,6 +55,8 @@ val navigationBarPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -83,39 +82,44 @@ val navigationBarPatch = bytecodePatch(
|
|||
),
|
||||
)
|
||||
|
||||
tabLayoutTextMethodMatch.method.apply {
|
||||
// Hide navigation labels.
|
||||
val constIndex = indexOfFirstLiteralInstructionOrThrow(text1)
|
||||
val targetIndex = indexOfFirstInstructionOrThrow(constIndex, Opcode.CHECK_CAST)
|
||||
val targetParameter = getInstruction<ReferenceInstruction>(targetIndex).reference
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
tabLayoutTextMethodMatch.let {
|
||||
it.method.apply {
|
||||
// Modify in reverse order to preserve match indices.
|
||||
|
||||
if (!targetParameter.toString().endsWith("Landroid/widget/TextView;")) {
|
||||
throw PatchException("Method signature parameter did not match: $targetParameter")
|
||||
// Hide navigation buttons.
|
||||
val pivotTabIndex = it[-1]
|
||||
val pivotTabRegister =
|
||||
getInstruction<FiveRegisterInstruction>(pivotTabIndex).registerC
|
||||
|
||||
addInstruction(
|
||||
pivotTabIndex,
|
||||
"invoke-static { v$pivotTabRegister }, " +
|
||||
"${EXTENSION_CLASS_DESCRIPTOR}->hideNavigationButton(Landroid/view/View;)V"
|
||||
)
|
||||
|
||||
|
||||
// Set navigation enum and hide navigation buttons.
|
||||
val enumIndex = it[7]
|
||||
val enumRegister = getInstruction<OneRegisterInstruction>(enumIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
enumIndex + 1,
|
||||
"invoke-static { v$enumRegister }, " +
|
||||
"${EXTENSION_CLASS_DESCRIPTOR}->setLastAppNavigationEnum(Ljava/lang/Enum;)V"
|
||||
)
|
||||
|
||||
|
||||
// Hide navigation labels.
|
||||
val labelIndex = it[3]
|
||||
val targetParameter = getInstruction<ReferenceInstruction>(labelIndex).reference
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(labelIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
labelIndex + 1,
|
||||
"invoke-static { v$targetRegister }, " +
|
||||
"${EXTENSION_CLASS_DESCRIPTOR}->hideNavigationLabel(Landroid/widget/TextView;)V"
|
||||
)
|
||||
}
|
||||
|
||||
addInstruction(
|
||||
targetIndex + 1,
|
||||
"invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideNavigationLabel(Landroid/widget/TextView;)V",
|
||||
)
|
||||
|
||||
// Set navigation enum and hide navigation buttons.
|
||||
val enumIndex = tabLayoutTextMethodMatch[0] + 3
|
||||
val enumRegister = getInstruction<OneRegisterInstruction>(enumIndex).registerA
|
||||
val insertEnumIndex = indexOfFirstInstructionOrThrow(Opcode.AND_INT_LIT8) - 2
|
||||
|
||||
val pivotTabIndex = indexOfGetVisibilityInstruction(this)
|
||||
val pivotTabRegister = getInstruction<FiveRegisterInstruction>(pivotTabIndex).registerC
|
||||
|
||||
addInstruction(
|
||||
pivotTabIndex,
|
||||
"invoke-static { v$pivotTabRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideNavigationButton(Landroid/view/View;)V",
|
||||
)
|
||||
|
||||
addInstruction(
|
||||
insertEnumIndex,
|
||||
"invoke-static { v$enumRegister }, $EXTENSION_CLASS_DESCRIPTOR->setLastAppNavigationEnum(Ljava/lang/Enum;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ import app.revanced.patches.music.misc.settings.settingsPatch
|
|||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideGetPremiumPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/patches/HideGetPremiumPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val hideGetMusicPremiumPatch = bytecodePatch(
|
||||
|
|
@ -30,6 +31,8 @@ val hideGetMusicPremiumPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -56,7 +59,7 @@ val hideGetMusicPremiumPatch = bytecodePatch(
|
|||
addInstruction(
|
||||
insertIndex + 1,
|
||||
"invoke-virtual {v$getPremiumViewRegister, v$visibilityRegister}, " +
|
||||
"Landroid/view/View;->setVisibility(I)V",
|
||||
"Landroid/view/View;->setVisibility(I)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,19 +17,23 @@ val themePatch = baseThemePatch(
|
|||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
baseThemeResourcePatch(
|
||||
darkColorNames = THEME_DEFAULT_DARK_COLOR_NAMES + setOf(
|
||||
"yt_black_pure",
|
||||
"yt_black_pure_opacity80",
|
||||
"ytm_color_grey_12",
|
||||
"material_grey_800"
|
||||
)
|
||||
getDarkColorNames = {
|
||||
THEME_DEFAULT_DARK_COLOR_NAMES + setOf(
|
||||
"yt_black_pure",
|
||||
"yt_black_pure_opacity80",
|
||||
"ytm_color_grey_12",
|
||||
"material_grey_800"
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52"
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54"
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,19 +18,25 @@ val unlockAndroidAutoMediaBrowserPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
checkCertificateMethod.returnEarly(true)
|
||||
|
||||
searchMediaItemsConstructorMethod.immutableClassDef.getSearchMediaItemsExecuteMethod().apply {
|
||||
val targetIndex = instructions.indexOfFirst {
|
||||
it.opcode == Opcode.IGET_OBJECT && it.fieldReference?.type == "Ljava/lang/String;"
|
||||
}
|
||||
searchMediaItemsConstructorMethod.immutableClassDef.getSearchMediaItemsExecuteMethod()
|
||||
.apply {
|
||||
val targetIndex = instructions.indexOfFirst {
|
||||
it.opcode == Opcode.IGET_OBJECT && it.fieldReference?.type == "Ljava/lang/String;"
|
||||
}
|
||||
|
||||
val register = instructions[targetIndex].registersUsed.first()
|
||||
replaceInstruction(targetIndex, "const-string v$register, \"com.google.android.apps.youtube.music\"")
|
||||
}
|
||||
val register = instructions[targetIndex].registersUsed.first()
|
||||
replaceInstruction(
|
||||
targetIndex,
|
||||
"const-string v$register, \"com.google.android.apps.youtube.music\""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.music.misc.tracks
|
||||
package app.revanced.patches.music.misc.audio
|
||||
|
||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
||||
|
|
@ -20,7 +20,9 @@ val forceOriginalAudioPatch = forceOriginalAudioPatch(
|
|||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52"
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54"
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
@ -19,6 +19,8 @@ val removeBackgroundPlaybackRestrictionsPatch = bytecodePatch(
|
|||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,19 +3,31 @@ package app.revanced.patches.music.misc.debugging
|
|||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||
import app.revanced.patches.music.playservice.is_8_40_or_greater
|
||||
import app.revanced.patches.music.playservice.is_8_41_or_greater
|
||||
import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
|
||||
|
||||
@Suppress("unused")
|
||||
val enableDebuggingPatch = enableDebuggingPatch(
|
||||
sharedExtensionPatch = sharedExtensionPatch,
|
||||
settingsPatch = settingsPatch,
|
||||
compatibleWithPackages = arrayOf(
|
||||
"com.google.android.apps.youtube.music" to setOf(
|
||||
"7.29.52",
|
||||
"8.10.52"
|
||||
block = {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
)
|
||||
),
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
)
|
||||
)
|
||||
},
|
||||
// String feature flag does not appear to be present with YT Music.
|
||||
hookStringFeatureFlag = false,
|
||||
hookStringFeatureFlag = { false },
|
||||
// 8.40 has changes not worth supporting.
|
||||
hookLongFeatureFlag = { !is_8_40_or_greater || is_8_41_or_greater },
|
||||
hookDoubleFeatureFlag = { !is_8_40_or_greater || is_8_41_or_greater },
|
||||
preferenceScreen = PreferenceScreen.MISC,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ val checkWatchHistoryDomainNameResolutionPatch = checkWatchHistoryDomainNameReso
|
|||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52"
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54"
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
|||
MUSIC_PACKAGE_NAME(
|
||||
"7.29.52",
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ val sanitizeSharingLinksPatch = sanitizeSharingLinksPatch(
|
|||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52"
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54"
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,14 +8,18 @@ import app.revanced.patches.all.misc.resources.addResources
|
|||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
|
||||
import app.revanced.patches.music.playservice.is_8_40_or_greater
|
||||
import app.revanced.patches.music.playservice.versionCheckPatch
|
||||
import app.revanced.patches.shared.boldIconsFeatureFlagMethodMatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.*
|
||||
import app.revanced.patches.shared.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.misc.settings.modifyActivityForSettingsInjection
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.inputStreamFromBundledResource
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
|
||||
private const val GOOGLE_API_ACTIVITY_HOOK_CLASS_DESCRIPTOR =
|
||||
private const val MUSIC_ACTIVITY_HOOK_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/settings/MusicActivityHook;"
|
||||
|
||||
private val preferences = mutableSetOf<BasePreference>()
|
||||
|
|
@ -72,6 +76,7 @@ val settingsPatch = bytecodePatch(
|
|||
sharedExtensionPatch,
|
||||
settingsResourcePatch,
|
||||
addResourcesPatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
apply {
|
||||
|
|
@ -90,6 +95,12 @@ val settingsPatch = bytecodePatch(
|
|||
SwitchPreference("revanced_settings_search_history")
|
||||
)
|
||||
|
||||
if (is_8_40_or_greater) {
|
||||
PreferenceScreen.GENERAL.addPreferences(
|
||||
SwitchPreference("revanced_settings_disable_bold_icons")
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceScreen.MISC.addPreferences(
|
||||
TextPreference(
|
||||
key = null,
|
||||
|
|
@ -103,9 +114,18 @@ val settingsPatch = bytecodePatch(
|
|||
modifyActivityForSettingsInjection(
|
||||
googleApiActivityMethod.classDef,
|
||||
googleApiActivityMethod,
|
||||
GOOGLE_API_ACTIVITY_HOOK_CLASS_DESCRIPTOR,
|
||||
MUSIC_ACTIVITY_HOOK_CLASS_DESCRIPTOR,
|
||||
true
|
||||
)
|
||||
|
||||
if (is_8_40_or_greater) {
|
||||
boldIconsFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$MUSIC_ACTIVITY_HOOK_CLASS_DESCRIPTOR->useBoldIcons(Z)Z"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterDependents {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,9 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch(
|
|||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.29.52",
|
||||
"8.10.52"
|
||||
"8.10.52",
|
||||
"8.37.56",
|
||||
"8.40.54"
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@ var is_8_11_or_greater: Boolean by Delegates.notNull()
|
|||
private set
|
||||
var is_8_15_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_8_40_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_8_41_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
|
||||
@Suppress("unused")
|
||||
val versionCheckPatch = resourcePatch(
|
||||
description = "Uses the Play Store service version to find the major/minor version of the YouTube Music target app."
|
||||
) {
|
||||
|
|
@ -38,5 +41,7 @@ val versionCheckPatch = resourcePatch(
|
|||
is_8_10_or_greater = 251099000 <= playStoreServicesVersion
|
||||
is_8_11_or_greater = 251199000 <= playStoreServicesVersion
|
||||
is_8_15_or_greater = 251530000 <= playStoreServicesVersion
|
||||
is_8_40_or_greater = 254080000 <= playStoreServicesVersion
|
||||
is_8_41_or_greater = 254180000 <= playStoreServicesVersion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
package app.revanced.patches.shared
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.composingFirstMethod
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val BytecodePatchContext.castContextFetchMethod by gettingFirstMethodDeclaratively(
|
||||
"Error fetching CastContext."
|
||||
|
|
@ -11,3 +18,14 @@ internal val BytecodePatchContext.primeMethod by gettingFirstMethodDeclaratively
|
|||
"com.android.vending",
|
||||
"com.google.android.GoogleCamera"
|
||||
)
|
||||
|
||||
// Flag is present in YouTube 20.23, but bold icons are missing and forcing them crashes the app.
|
||||
// 20.31 is the first target with all the bold icons present.
|
||||
internal val BytecodePatchContext.boldIconsFeatureFlagMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
45685201L(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package app.revanced.patches.shared.layout.branding
|
||||
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.rawResourcePatch
|
||||
import app.revanced.util.inputStreamFromBundledResource
|
||||
import java.nio.file.Files
|
||||
import java.util.logging.Logger
|
||||
|
||||
/**
|
||||
* Copies a branding license text file to the target apk.
|
||||
|
|
@ -20,6 +22,8 @@ internal val addBrandLicensePatch = rawResourcePatch {
|
|||
|
||||
val targetFile = get(brandingLicenseFileName, false).toPath()
|
||||
|
||||
Files.copy(inputFileStream, targetFile)
|
||||
if (Files.exists(targetFile)) Logger.getLogger(this::class.java.name)
|
||||
.warning("Already patched by ReVanced")
|
||||
else Files.copy(inputFileStream, targetFile)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package app.revanced.patches.shared.layout.branding
|
|||
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.firstImmutableClassDef
|
||||
import app.revanced.patcher.patch.*
|
||||
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
|
|
@ -52,9 +53,11 @@ private val USER_CUSTOM_ADAPTIVE_FILE_NAMES = arrayOf(
|
|||
|
||||
private const val USER_CUSTOM_MONOCHROME_FILE_NAME =
|
||||
"$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml"
|
||||
private const val USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME = "${NOTIFICATION_ICON_NAME}_$CUSTOM_USER_ICON_STYLE_NAME.xml"
|
||||
private const val USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME =
|
||||
"${NOTIFICATION_ICON_NAME}_$CUSTOM_USER_ICON_STYLE_NAME.xml"
|
||||
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/CustomBrandingPatch;"
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/shared/patches/CustomBrandingPatch;"
|
||||
|
||||
/**
|
||||
* Shared custom branding patch for YouTube and YT Music.
|
||||
|
|
@ -75,7 +78,7 @@ internal fun baseCustomBrandingPatch(
|
|||
) = resourcePatch(
|
||||
name = "Custom branding",
|
||||
description = "Adds options to change the app icon and app name. " +
|
||||
"Branding cannot be changed for mounted (root) installations.",
|
||||
"Branding cannot be changed for mounted (root) installations.",
|
||||
) {
|
||||
val customName by stringOption(
|
||||
name = "App name",
|
||||
|
|
@ -116,6 +119,8 @@ internal fun baseCustomBrandingPatch(
|
|||
)
|
||||
|
||||
numberOfPresetAppNamesExtensionMethod.returnEarly(numberOfPresetAppNames)
|
||||
userProvidedCustomNameExtensionMethod.returnEarly(customName != null)
|
||||
userProvidedCustomIconExtensionMethod.returnEarly(customIcon != null)
|
||||
|
||||
notificationMethod.apply {
|
||||
val getBuilderIndex = if (isYouTubeMusic) {
|
||||
|
|
@ -128,7 +133,7 @@ internal fun baseCustomBrandingPatch(
|
|||
val builderCastIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<TypeReference>()
|
||||
opcode == Opcode.CHECK_CAST &&
|
||||
reference?.type == "Landroid/app/Notification\$Builder;"
|
||||
reference?.type == "Landroid/app/Notification\$Builder;"
|
||||
}
|
||||
indexOfFirstInstructionReversedOrThrow(builderCastIndex) {
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/Object;"
|
||||
|
|
@ -173,11 +178,14 @@ internal fun baseCustomBrandingPatch(
|
|||
}
|
||||
|
||||
apply {
|
||||
val useCustomName = customName != null
|
||||
val useCustomIcon = customIcon != null
|
||||
|
||||
addResources("shared", "layout.branding.baseCustomBrandingPatch")
|
||||
addResources(addResourcePatchName, "layout.branding.customBrandingPatch")
|
||||
|
||||
preferenceScreen.addPreferences(
|
||||
if (customName != null) {
|
||||
if (useCustomName) {
|
||||
ListPreference(
|
||||
key = "revanced_custom_branding_name",
|
||||
entriesKey = "revanced_custom_branding_name_custom_entries",
|
||||
|
|
@ -186,7 +194,7 @@ internal fun baseCustomBrandingPatch(
|
|||
} else {
|
||||
ListPreference("revanced_custom_branding_name")
|
||||
},
|
||||
if (customIcon != null) {
|
||||
if (useCustomIcon) {
|
||||
ListPreference(
|
||||
key = "revanced_custom_branding_icon",
|
||||
entriesKey = "revanced_custom_branding_icon_custom_entries",
|
||||
|
|
@ -197,9 +205,6 @@ internal fun baseCustomBrandingPatch(
|
|||
},
|
||||
)
|
||||
|
||||
val useCustomName = customName != null
|
||||
val useCustomIcon = customIcon != null
|
||||
|
||||
iconStyleNames.forEach { style ->
|
||||
copyResources(
|
||||
"custom-branding",
|
||||
|
|
@ -307,7 +312,7 @@ internal fun baseCustomBrandingPatch(
|
|||
activityAliasNameWithIntents,
|
||||
).childNodes
|
||||
|
||||
// The YT application name can appear in some places along side the system
|
||||
// The YT application name can appear in some places alongside the system
|
||||
// YouTube app, such as the settings app list and in the "open with" file picker.
|
||||
// Because the YouTube app cannot be completely uninstalled and only disabled,
|
||||
// use a custom name for this situation to disambiguate which app is which.
|
||||
|
|
@ -316,6 +321,9 @@ internal fun baseCustomBrandingPatch(
|
|||
"@string/revanced_custom_branding_name_entry_2",
|
||||
)
|
||||
|
||||
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 1 // 1 indexing.
|
||||
val enabledIconIndex = if (useCustomIcon) iconStyleNames.size else 0 // 0 indexing.
|
||||
|
||||
for (appNameIndex in 1..numberOfPresetAppNames) {
|
||||
fun aliasName(name: String): String = ".revanced_" + name + '_' + appNameIndex
|
||||
|
||||
|
|
@ -334,14 +342,14 @@ internal fun baseCustomBrandingPatch(
|
|||
)
|
||||
|
||||
// Bundled icons.
|
||||
iconStyleNames.forEach { style ->
|
||||
iconStyleNames.forEachIndexed { iconIndex, style ->
|
||||
application.appendChild(
|
||||
createAlias(
|
||||
aliasName = aliasName(style),
|
||||
iconMipmapName = LAUNCHER_RESOURCE_NAME_PREFIX + style,
|
||||
appNameIndex = appNameIndex,
|
||||
useCustomName = useCustomNameLabel,
|
||||
enabled = false,
|
||||
enabled = (appNameIndex == enabledNameIndex && iconIndex == enabledIconIndex),
|
||||
intentFilters,
|
||||
),
|
||||
)
|
||||
|
|
@ -351,7 +359,7 @@ internal fun baseCustomBrandingPatch(
|
|||
//
|
||||
// Must add all aliases even if the user did not provide a custom icon of their own.
|
||||
// This is because if the user installs with an option, then repatches without the option,
|
||||
// the alias must still exist because if it was previously enabled and then it's removed
|
||||
// the alias must still exist because if it was previously enabled, and then it's removed
|
||||
// the app will become broken and cannot launch. Even if the app data is cleared
|
||||
// it still cannot be launched and the only fix is to uninstall the app.
|
||||
// To prevent this, always include all aliases and use dummy data if needed.
|
||||
|
|
@ -361,7 +369,7 @@ internal fun baseCustomBrandingPatch(
|
|||
iconMipmapName = LAUNCHER_RESOURCE_NAME_PREFIX + CUSTOM_USER_ICON_STYLE_NAME,
|
||||
appNameIndex = appNameIndex,
|
||||
useCustomName = useCustomNameLabel,
|
||||
enabled = false,
|
||||
enabled = appNameIndex == enabledNameIndex && useCustomIcon,
|
||||
intentFilters,
|
||||
),
|
||||
)
|
||||
|
|
@ -413,7 +421,7 @@ internal fun baseCustomBrandingPatch(
|
|||
if (customFiles.isNotEmpty() && customFiles.size != USER_CUSTOM_ADAPTIVE_FILE_NAMES.size) {
|
||||
throw PatchException(
|
||||
"Must include all required icon files " +
|
||||
"but only found " + customFiles.map { it.name },
|
||||
"but only found " + customFiles.map { it.name },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -444,8 +452,8 @@ internal fun baseCustomBrandingPatch(
|
|||
if (!copiedFiles) {
|
||||
throw PatchException(
|
||||
"Expected to find directories and files: " +
|
||||
USER_CUSTOM_ADAPTIVE_FILE_NAMES.contentToString() +
|
||||
"\nBut none were found in the provided option file path: " + iconPathFile.absolutePath,
|
||||
USER_CUSTOM_ADAPTIVE_FILE_NAMES.contentToString() +
|
||||
"\nBut none were found in the provided option file path: " + iconPathFile.absolutePath,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,30 @@ import app.revanced.patcher.patch.BytecodePatchContext
|
|||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val BytecodePatchContext.numberOfPresetAppNamesExtensionMethod by gettingFirstMethodDeclaratively {
|
||||
name("numberOfPresetAppNames")
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
name("numberOfPresetAppNames")
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returnType("I")
|
||||
parameterTypes()
|
||||
}
|
||||
|
||||
|
||||
internal val BytecodePatchContext.userProvidedCustomNameExtensionMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
name("userProvidedCustomName")
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.userProvidedCustomIconExtensionMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
name("userProvidedCustomIcon")
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
}
|
||||
|
||||
// A much simpler method exists that can set the small icon (contains string "414843287017"),
|
||||
// but that has limited usage and this one allows changing any part of the notification.
|
||||
internal val BytecodePatchContext.notificationMethod by gettingFirstMethodDeclaratively(
|
||||
|
|
|
|||
|
|
@ -1,20 +1,26 @@
|
|||
package app.revanced.patches.shared.layout.theme
|
||||
|
||||
import app.revanced.patcher.patch.*
|
||||
import app.revanced.patcher.patch.BytecodePatchBuilder
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
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.util.childElementsSequence
|
||||
import java.util.*
|
||||
|
||||
internal const val THEME_COLOR_OPTION_DESCRIPTION = "Can be a hex color (#RRGGBB) or a color resource reference."
|
||||
internal const val THEME_COLOR_OPTION_DESCRIPTION =
|
||||
"Can be a hex color (#RRGGBB) or a color resource reference."
|
||||
|
||||
internal val THEME_DEFAULT_DARK_COLOR_NAMES = setOf(
|
||||
"yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98",
|
||||
"yt_black2", "yt_black3", "yt_black4", "yt_status_bar_background_dark",
|
||||
"material_grey_850"
|
||||
"yt_black0", "yt_black1", "yt_black2", "yt_black3", "yt_black4",
|
||||
"yt_black1_opacity95", "yt_black1_opacity98",
|
||||
"yt_status_bar_background_dark", "material_grey_850",
|
||||
)
|
||||
|
||||
internal val THEME_DEFAULT_LIGHT_COLOR_NAMES = setOf(
|
||||
"yt_white1", "yt_white1_opacity95", "yt_white1_opacity98",
|
||||
"yt_white2", "yt_white3", "yt_white4"
|
||||
"yt_white1", "yt_white2", "yt_white3", "yt_white4",
|
||||
"yt_white1_opacity95", "yt_white1_opacity98",
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -94,8 +100,8 @@ internal fun baseThemePatch(
|
|||
}
|
||||
|
||||
internal fun baseThemeResourcePatch(
|
||||
darkColorNames: Set<String> = THEME_DEFAULT_DARK_COLOR_NAMES,
|
||||
lightColorNames: Set<String> = THEME_DEFAULT_LIGHT_COLOR_NAMES,
|
||||
getDarkColorNames: () -> Set<String> = { THEME_DEFAULT_DARK_COLOR_NAMES },
|
||||
getLightColorNames: () -> Set<String> = { THEME_DEFAULT_LIGHT_COLOR_NAMES },
|
||||
lightColorReplacement: (() -> String)? = null
|
||||
) = resourcePatch {
|
||||
apply {
|
||||
|
|
@ -114,6 +120,9 @@ internal fun baseThemeResourcePatch(
|
|||
document("res/values/colors.xml").use { document ->
|
||||
val resourcesNode = document.getElementsByTagName("resources").item(0)
|
||||
|
||||
val darkColorNames = getDarkColorNames()
|
||||
val lightColorNames = getLightColorNames()
|
||||
|
||||
resourcesNode.childElementsSequence().forEach { node ->
|
||||
val name = node.getAttribute("name")
|
||||
when {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ internal val BytecodePatchContext.lithoOnBoundsChangeMethodMatch by composingFir
|
|||
returnType("V")
|
||||
parameterTypes("Landroid/graphics/Rect;")
|
||||
|
||||
lateinit var methodDefiningClass: String
|
||||
var methodDefiningClass = ""
|
||||
custom {
|
||||
methodDefiningClass = definingClass
|
||||
true
|
||||
|
|
@ -23,11 +23,7 @@ internal val BytecodePatchContext.lithoOnBoundsChangeMethodMatch by composingFir
|
|||
field { type == "Landroid/graphics/Path;" && definingClass == methodDefiningClass },
|
||||
),
|
||||
afterAtMost(
|
||||
5,
|
||||
method { returnType == "Z" && name == "isStateful" && definingClass == methodDefiningClass },
|
||||
),
|
||||
afterAtMost(
|
||||
5,
|
||||
10,
|
||||
allOf(
|
||||
Opcode.IGET_OBJECT(),
|
||||
field { type == "Landroid/graphics/Paint;" && definingClass == methodDefiningClass },
|
||||
|
|
|
|||
|
|
@ -14,8 +14,11 @@ import app.revanced.patches.all.misc.resources.addResources
|
|||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.cloneMutable
|
||||
import app.revanced.util.findMethodFromToString
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
|
@ -96,58 +99,50 @@ internal fun forceOriginalAudioPatch(
|
|||
).toMutable(),
|
||||
)
|
||||
|
||||
// Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed.
|
||||
val helperMethodClass = type
|
||||
val helperMethodName = "patch_isDefaultAudioTrack"
|
||||
val helperMethod = ImmutableMethod(
|
||||
helperMethodClass,
|
||||
helperMethodName,
|
||||
listOf(ImmutableMethodParameter("Z", null, null)),
|
||||
"Z",
|
||||
AccessFlags.PRIVATE.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(6),
|
||||
).toMutable().apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
iget-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
|
||||
if-eqz v0, :call_extension
|
||||
invoke-virtual { v0 }, Ljava/lang/Boolean;->booleanValue()Z
|
||||
move-result v3
|
||||
return v3
|
||||
|
||||
:call_extension
|
||||
invoke-virtual { p0 }, $audioTrackIdMethod
|
||||
move-result-object v1
|
||||
|
||||
invoke-virtual { p0 }, $audioTrackDisplayNameMethod
|
||||
move-result-object v2
|
||||
|
||||
invoke-static { p1, v1, v2 }, $EXTENSION_CLASS_DESCRIPTOR->isDefaultAudioStream(ZLjava/lang/String;Ljava/lang/String;)Z
|
||||
move-result v3
|
||||
|
||||
invoke-static { v3 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
|
||||
move-result-object v0
|
||||
iput-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
|
||||
return v3
|
||||
""",
|
||||
)
|
||||
// Clone the method to add additional registers because the
|
||||
// isDefaultAudioTrack() has only 1 or 2 registers and 3 are needed.
|
||||
val clonedMethod = isDefaultAudioTrackMethod.cloneMutable(
|
||||
additionalRegisters = 4
|
||||
)
|
||||
|
||||
// Replace existing method with cloned with more registers.
|
||||
methods.apply {
|
||||
remove(isDefaultAudioTrackMethod)
|
||||
add(clonedMethod)
|
||||
}
|
||||
methods.add(helperMethod)
|
||||
|
||||
// Modify isDefaultAudioTrack() to call extension helper method.
|
||||
isDefaultAudioTrackMethod.apply {
|
||||
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
clonedMethod.apply {
|
||||
// Free registers are added
|
||||
val free1 = isDefaultAudioTrackMethod.implementation!!.registerCount + 1
|
||||
val free2 = free1 + 1
|
||||
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.RETURN)
|
||||
val originalResultRegister =
|
||||
getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
index,
|
||||
clonedMethod.addInstructionsAtControlFlowLabel(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-direct { p0, v$register }, $helperMethodClass->$helperMethodName(Z)Z
|
||||
move-result v$register
|
||||
""",
|
||||
iget-object v$free1, p0, $type->$helperFieldName:Ljava/lang/Boolean;
|
||||
if-eqz v$free1, :call_extension
|
||||
invoke-virtual { v$free1 }, Ljava/lang/Boolean;->booleanValue()Z
|
||||
move-result v$free1
|
||||
return v$free1
|
||||
|
||||
:call_extension
|
||||
invoke-virtual { p0 }, $audioTrackIdMethod
|
||||
move-result-object v$free1
|
||||
|
||||
invoke-virtual { p0 }, $audioTrackDisplayNameMethod
|
||||
move-result-object v$free2
|
||||
|
||||
invoke-static { v$originalResultRegister, v$free1, v$free2 }, ${EXTENSION_CLASS_DESCRIPTOR}->isDefaultAudioStream(ZLjava/lang/String;Ljava/lang/String;)Z
|
||||
move-result v$free1
|
||||
|
||||
invoke-static { v$free1 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
|
||||
move-result-object v$free2
|
||||
iput-object v$free2, p0, $type->$helperFieldName:Ljava/lang/Boolean;
|
||||
return v$free1
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
package app.revanced.patches.shared.misc.debugging
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.classDef
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.firstMethodDeclaratively
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.method
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchBuilder
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.*
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
||||
import app.revanced.util.*
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/shared/patches/EnableDebuggingPatch;"
|
||||
|
|
@ -21,21 +30,19 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
|
|||
* Patch shared with YouTube and YT Music.
|
||||
*/
|
||||
internal fun enableDebuggingPatch(
|
||||
sharedExtensionPatch: Patch,
|
||||
settingsPatch: Patch,
|
||||
vararg compatibleWithPackages: Pair<String, Set<String>>,
|
||||
hookStringFeatureFlag: Boolean,
|
||||
block: BytecodePatchBuilder.() -> Unit = {},
|
||||
executeBlock: BytecodePatchContext.() -> Unit = {},
|
||||
hookStringFeatureFlag: BytecodePatchBuilder.() -> Boolean,
|
||||
hookLongFeatureFlag: BytecodePatchBuilder.() -> Boolean,
|
||||
hookDoubleFeatureFlag: BytecodePatchBuilder.() -> Boolean,
|
||||
preferenceScreen: BasePreferenceScreen.Screen,
|
||||
additionalDebugPreferences: List<BasePreference> = emptyList()
|
||||
) = bytecodePatch(
|
||||
name = "Enable debugging",
|
||||
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
|
||||
) {
|
||||
compatibleWith(packages = compatibleWithPackages)
|
||||
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
resourcePatch {
|
||||
apply {
|
||||
copyResources(
|
||||
|
|
@ -50,36 +57,44 @@ internal fun enableDebuggingPatch(
|
|||
"revanced_settings_arrow_left_double.xml",
|
||||
"revanced_settings_arrow_left_one.xml",
|
||||
"revanced_settings_arrow_right_double.xml",
|
||||
"revanced_settings_arrow_right_one.xml",
|
||||
),
|
||||
"revanced_settings_arrow_right_one.xml"
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
block()
|
||||
|
||||
apply {
|
||||
executeBlock()
|
||||
|
||||
addResources("shared", "misc.debugging.enableDebuggingPatch")
|
||||
|
||||
val preferences = setOf(
|
||||
val preferences = mutableSetOf<BasePreference>(
|
||||
SwitchPreference("revanced_debug"),
|
||||
SwitchPreference("revanced_debug_protobuffer"),
|
||||
)
|
||||
|
||||
preferences + additionalDebugPreferences
|
||||
|
||||
preferences += listOf(
|
||||
SwitchPreference("revanced_debug_stacktrace"),
|
||||
SwitchPreference("revanced_debug_toast_on_error"),
|
||||
NonInteractivePreference(
|
||||
"revanced_debug_export_logs_to_clipboard",
|
||||
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
|
||||
selectable = true,
|
||||
selectable = true
|
||||
),
|
||||
NonInteractivePreference(
|
||||
"revanced_debug_logs_clear_buffer",
|
||||
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
|
||||
selectable = true,
|
||||
selectable = true
|
||||
),
|
||||
NonInteractivePreference(
|
||||
"revanced_debug_feature_flags_manager",
|
||||
tag = "app.revanced.extension.shared.settings.preference.FeatureFlagsManagerPreference",
|
||||
selectable = true,
|
||||
),
|
||||
selectable = true
|
||||
)
|
||||
)
|
||||
|
||||
preferenceScreen.addPreferences(
|
||||
|
|
@ -87,67 +102,117 @@ internal fun enableDebuggingPatch(
|
|||
key = "revanced_debug_screen",
|
||||
sorting = Sorting.UNSORTED,
|
||||
preferences = preferences,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
// Hook the methods that look up if a feature flag is active.
|
||||
experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalBooleanFeatureFlagMethod().apply {
|
||||
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
val experimentalBooleanFeatureFlagMethodMatch =
|
||||
experimentalFeatureFlagUtilMethod.immutableClassDef.experimentalBooleanFeatureFlagMethodMatch
|
||||
|
||||
experimentalBooleanFeatureFlagMethodMatch.let {
|
||||
it.method.apply {
|
||||
// Not enough registers in the method. Clone the method and use the
|
||||
// original method as an intermediate to call extension code.
|
||||
|
||||
|
||||
// Copy the method.
|
||||
val helperMethod = cloneMutable(name = "patch_getBooleanFeatureFlag")
|
||||
|
||||
// Add the method.
|
||||
it.classDef.methods.add(helperMethod)
|
||||
|
||||
addInstructions(
|
||||
index,
|
||||
0,
|
||||
"""
|
||||
# Invoke the copied method (helper method).
|
||||
invoke-static { p0, p1, p2, p3 }, $helperMethod
|
||||
move-result p0
|
||||
|
||||
# Redefine boolean in the extension.
|
||||
invoke-static { p0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZJ)Z
|
||||
move-result p0
|
||||
|
||||
# Since the copied method (helper method) has already been invoked, it just returns.
|
||||
return p0
|
||||
"""
|
||||
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalDoubleFeatureFlagMethod().apply {
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
|
||||
if (hookDoubleFeatureFlag())
|
||||
// 21.06+ doesn't have enough registers and needs to also clone.
|
||||
experimentalFeatureFlagUtilMethod.immutableClassDef.getExperimentalDoubleFeatureFlagMethod()
|
||||
.cloneMutableAndPreserveParameters().apply {
|
||||
val helperMethod = cloneMutable(name = "patch_getDoubleFeatureFlag")
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
move-result-wide v0 # Also clobbers v1 (p0) since result is wide.
|
||||
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D
|
||||
move-result-wide v0
|
||||
return-wide v0
|
||||
""",
|
||||
)
|
||||
}
|
||||
classDef.methods.add(helperMethod)
|
||||
|
||||
experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalLongFeatureFlagMethod().apply {
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
# Invoke the copied method (helper method).
|
||||
invoke-static/range { p0 .. p4 }, $helperMethod
|
||||
move-result-wide v0
|
||||
|
||||
# Move parameter registers to lower register range to use invoke-static/range.
|
||||
move-wide v2, p1
|
||||
move-wide v4, p3
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
move-result-wide v0
|
||||
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J
|
||||
move-result-wide v0
|
||||
return-wide v0
|
||||
""",
|
||||
)
|
||||
}
|
||||
invoke-static/range { v0 .. v5 }, ${EXTENSION_CLASS_DESCRIPTOR}->isDoubleFeatureFlagEnabled(DJD)D
|
||||
move-result-wide v0
|
||||
|
||||
if (hookStringFeatureFlag) {
|
||||
experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalStringFeatureFlagMethod().apply {
|
||||
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
# Since the copied method (helper method) has already been invoked, it just returns.
|
||||
return-wide v0
|
||||
"""
|
||||
move-result-object v0
|
||||
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v0
|
||||
return-object v0
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (hookLongFeatureFlag())
|
||||
experimentalFeatureFlagUtilMethod.immutableClassDef.getExperimentalLongFeatureFlagMethod()
|
||||
.cloneMutableAndPreserveParameters().apply {
|
||||
val helperMethod = cloneMutable(name = "patch_getLongFeatureFlag")
|
||||
|
||||
classDef.methods.add(helperMethod)
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
# Invoke the copied method (helper method).
|
||||
invoke-static/range { p0 .. p4 }, $helperMethod
|
||||
move-result-wide v0
|
||||
|
||||
# Move parameter registers to lower register range to use invoke-static/range.
|
||||
move-wide v2, p1
|
||||
move-wide v4, p3
|
||||
|
||||
invoke-static/range { v0 .. v5 }, ${EXTENSION_CLASS_DESCRIPTOR}->isLongFeatureFlagEnabled(JJJ)J
|
||||
move-result-wide v0
|
||||
|
||||
# Since the copied method (helper method) has already been invoked, it just returns.
|
||||
return-wide v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
if (hookStringFeatureFlag())
|
||||
experimentalFeatureFlagUtilMethod.immutableClassDef.getExperimentalStringFeatureFlagMethod()
|
||||
.apply {
|
||||
val helperMethod = cloneMutable(name = "patch_getStringFeatureFlag")
|
||||
|
||||
classDef.methods.add(helperMethod)
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p0, p1, p2, p3 }, $helperMethod
|
||||
move-result-object p0
|
||||
|
||||
invoke-static { p0, p1, p2, p3 }, ${EXTENSION_CLASS_DESCRIPTOR}->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
|
||||
move-result-object p0
|
||||
|
||||
return-object p0
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// There exists other experimental accessor methods for byte[]
|
||||
// and wrappers for obfuscated classes, but currently none of those are hooked.
|
||||
|
|
|
|||
|
|
@ -1,46 +1,55 @@
|
|||
package app.revanced.patches.shared.misc.debugging
|
||||
|
||||
import app.revanced.patcher.ClassDefComposing
|
||||
import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively
|
||||
import app.revanced.patcher.firstMethodDeclaratively
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.custom
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.experimentalFeatureFlagParentMethod by gettingFirstImmutableMethodDeclaratively(
|
||||
internal val BytecodePatchContext.experimentalFeatureFlagUtilMethod by gettingFirstImmutableMethodDeclaratively(
|
||||
"Unable to parse proto typed experiment flag: "
|
||||
) {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returnType("L")
|
||||
parameterTypes("L", "J", "[B")
|
||||
custom {
|
||||
// 'public static' or 'public static final'
|
||||
AccessFlags.STATIC.isSet(accessFlags)
|
||||
&& AccessFlags.PUBLIC.isSet(accessFlags)
|
||||
// "L", "J", "[B" or "L", "J"
|
||||
&& parameters.let { (it.size == 2 || it.size == 3) && it[1].type == "J" }
|
||||
}
|
||||
}
|
||||
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getExperimentalBooleanFeatureFlagMethod() = firstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
internal val ClassDef.experimentalBooleanFeatureFlagMethodMatch by ClassDefComposing.composingFirstMethod {
|
||||
returnType("Z")
|
||||
parameterTypes("L", "J", "Z")
|
||||
custom {
|
||||
// 'public static' or 'public static final'
|
||||
AccessFlags.STATIC.isSet(accessFlags) && AccessFlags.PUBLIC.isSet(accessFlags)
|
||||
}
|
||||
}
|
||||
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getExperimentalDoubleFeatureFlagMethod() = firstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("D")
|
||||
parameterTypes("J", "D")
|
||||
parameterTypes("L", "J", "D")
|
||||
custom { AccessFlags.STATIC.isSet(accessFlags) }
|
||||
}
|
||||
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getExperimentalLongFeatureFlagMethod() = firstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("J")
|
||||
parameterTypes("J", "J")
|
||||
parameterTypes("L", "J", "J")
|
||||
custom { AccessFlags.STATIC.isSet(accessFlags) }
|
||||
}
|
||||
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getExperimentalStringFeatureFlagMethod() = firstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Ljava/lang/String;")
|
||||
parameterTypes("J", "Ljava/lang/String;")
|
||||
parameterTypes("L", "J", "Ljava/lang/String;")
|
||||
custom { AccessFlags.STATIC.isSet(accessFlags) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package app.revanced.patches.shared.misc.gms
|
|||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.googlePlayUtilityMethod by gettingFirstMethodDeclarativelyOrNull(
|
||||
"This should never happen.",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ import app.revanced.patcher.patch.ResourcePatchContext
|
|||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patcher.firstClassDef
|
||||
import app.revanced.patcher.firstImmutableClassDef
|
||||
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
|
||||
|
|
|
|||
|
|
@ -95,18 +95,15 @@ class Replacement(
|
|||
*/
|
||||
private fun indexOfPatternIn(haystack: ByteArray): Int {
|
||||
val needle = bytes
|
||||
|
||||
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
|
||||
for (i in 0 until needle.size) right[needle[i].toInt().and(0xFF)] = i
|
||||
|
||||
var skip: Int
|
||||
for (i in 0..haystackLength - needleLength) {
|
||||
for (i in 0..haystack.size - needle.size) {
|
||||
skip = 0
|
||||
|
||||
for (j in needleLength - 1 downTo 0) {
|
||||
for (j in needle.size - 1 downTo 0) {
|
||||
if (needle[j] != haystack[i + j]) {
|
||||
skip = max(1, j - right[haystack[i + j].toInt().and(0xFF)])
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,32 @@ import app.revanced.patcher.*
|
|||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val BytecodePatchContext.accessibilityIdMethodMatch by composingFirstMethod {
|
||||
instructions(
|
||||
allOf(
|
||||
Opcode.INVOKE_INTERFACE(),
|
||||
method { parameterTypes.isEmpty() && returnType == "Ljava/lang/String;" }
|
||||
),
|
||||
afterAtMost(5, "primary_image"()),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun BytecodePatchContext.getAccessibilityTextMethodMatch(accessibilityIdMethod: MethodReference) = firstMethodComposite {
|
||||
returnType("V")
|
||||
custom {
|
||||
// 'public final synthetic' or 'public final bridge synthetic'.
|
||||
AccessFlags.SYNTHETIC.isSet(accessFlags)
|
||||
}
|
||||
instructions(
|
||||
allOf(
|
||||
Opcode.INVOKE_INTERFACE(),
|
||||
method { parameterTypes.isEmpty() && returnType == "Ljava/lang/String;" }
|
||||
),
|
||||
afterAtMost(5, method { this == accessibilityIdMethod })
|
||||
)
|
||||
}
|
||||
internal val BytecodePatchContext.lithoFilterInitMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass("/LithoFilterPatch;")
|
||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||
|
|
@ -40,10 +65,6 @@ internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by getting
|
|||
opcodes(Opcode.IPUT, Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, Opcode.SUB_INT_2ADDR)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.componentContextParserMethodMatch by composingFirstMethod {
|
||||
instructions("Number of bits must be positive"())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.emptyComponentMethod by gettingFirstImmutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
|
||||
parameterTypes()
|
||||
|
|
|
|||
|
|
@ -2,22 +2,36 @@
|
|||
|
||||
package app.revanced.patches.shared.misc.litho.filter
|
||||
|
||||
import app.revanced.util.getFreeRegisterProvider
|
||||
import app.revanced.com.android.tools.smali.dexlib2.iface.value.MutableEncodedValue.Companion.toMutable
|
||||
import app.revanced.patcher.afterAtMost
|
||||
import app.revanced.patcher.allOf
|
||||
import app.revanced.patcher.classDef
|
||||
import app.revanced.patcher.custom
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
import app.revanced.patcher.extensions.removeInstructions
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.extensions.typeReference
|
||||
import app.revanced.patcher.firstImmutableClassDef
|
||||
import app.revanced.patcher.firstMethodComposite
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.method
|
||||
import app.revanced.patcher.patch.BytecodePatchBuilder
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.findFieldFromToString
|
||||
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.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue
|
||||
|
||||
/**
|
||||
|
|
@ -156,29 +170,67 @@ internal fun lithoFilterPatch(
|
|||
type == builderMethodDescriptor.returnType
|
||||
}.fields.single()
|
||||
|
||||
// Match all component creations methods
|
||||
// Find the method call that gets the value of 'buttonViewModel.accessibilityId'.
|
||||
val accessibilityIdMethod = accessibilityIdMethodMatch.let {
|
||||
it.immutableMethod.getInstruction<ReferenceInstruction>(it[0]).methodReference!!
|
||||
}
|
||||
|
||||
// There's a method in the same class that gets the value of 'buttonViewModel.accessibilityText'.
|
||||
// As this class is abstract, another method that uses a method call is used.
|
||||
val accessibilityTextMethod = getAccessibilityTextMethodMatch(accessibilityIdMethod).let {
|
||||
// Find the method call that gets the value of 'buttonViewModel.accessibilityText'.
|
||||
it.method.getInstruction<ReferenceInstruction>(it[0]).methodReference
|
||||
}
|
||||
|
||||
componentCreateMethod.apply {
|
||||
val insertIndex = componentCreateInsertionIndex()
|
||||
val freeRegister = findFreeRegister(insertIndex)
|
||||
val identifierRegister = findFreeRegister(insertIndex, freeRegister)
|
||||
val pathRegister = findFreeRegister(insertIndex, freeRegister, identifierRegister)
|
||||
|
||||
// Directly access the class related with the buttonViewModel from this method.
|
||||
// This is within 10 lines of insertIndex.
|
||||
val buttonViewModelIndex = indexOfFirstInstructionReversedOrThrow(insertIndex) {
|
||||
opcode == Opcode.CHECK_CAST &&
|
||||
typeReference?.type == accessibilityIdMethod.definingClass
|
||||
}
|
||||
val buttonViewModelRegister =
|
||||
getInstruction<OneRegisterInstruction>(buttonViewModelIndex).registerA
|
||||
val accessibilityIdIndex = buttonViewModelIndex + 2
|
||||
|
||||
// This is an index that checks if there is accessibility-related text.
|
||||
// This is within 10 lines of buttonViewModelIndex.
|
||||
val nullCheckIndex = indexOfFirstInstructionReversedOrThrow(
|
||||
buttonViewModelIndex, Opcode.IF_EQZ
|
||||
)
|
||||
|
||||
val registerProvider = getFreeRegisterProvider(
|
||||
insertIndex, 3, buttonViewModelRegister
|
||||
)
|
||||
val freeRegister = registerProvider.getFreeRegister()
|
||||
val identifierRegister = registerProvider.getFreeRegister()
|
||||
val pathRegister = registerProvider.getFreeRegister()
|
||||
|
||||
// Find a free register to store the accessibilityId and accessibilityText.
|
||||
// This is before the insertion index.
|
||||
val accessibilityRegisterProvider = getFreeRegisterProvider(
|
||||
nullCheckIndex,
|
||||
2,
|
||||
registerProvider.getUsedAndUnAvailableRegisters()
|
||||
)
|
||||
val accessibilityIdRegister = accessibilityRegisterProvider.getFreeRegister()
|
||||
val accessibilityTextRegister = accessibilityRegisterProvider.getFreeRegister()
|
||||
|
||||
addInstructionsAtControlFlowLabel(
|
||||
insertIndex,
|
||||
"""
|
||||
move-object/from16 v$freeRegister, p2 # ConversionContext parameter
|
||||
|
||||
# In YT 20.41 the field is the abstract superclass.
|
||||
# Check it's the actual ConversionContext just in case.
|
||||
# In YouTube 20.41 the field is the abstract superclass.
|
||||
# Verify it's the expected subclass just in case.
|
||||
instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.immutableClassDef.type}
|
||||
if-eqz v$identifierRegister, :unfiltered
|
||||
|
||||
# Get identifier and path from ConversionContext
|
||||
iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField
|
||||
iget-object v$pathRegister, v$freeRegister, $conversionContextPathBuilderField
|
||||
|
||||
# Check if the component should be filtered.
|
||||
invoke-static { v$identifierRegister, v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR->isFiltered(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
|
||||
invoke-static { v$identifierRegister, v$accessibilityIdRegister, v$accessibilityTextRegister, v$pathRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->isFiltered(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/StringBuilder;)Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :unfiltered
|
||||
|
||||
|
|
@ -191,7 +243,31 @@ internal fun lithoFilterPatch(
|
|||
|
||||
:unfiltered
|
||||
nop
|
||||
""",
|
||||
"""
|
||||
)
|
||||
|
||||
// If there is text related to accessibility, get the accessibilityId and accessibilityText.
|
||||
addInstructions(
|
||||
accessibilityIdIndex,
|
||||
"""
|
||||
# Get accessibilityId
|
||||
invoke-interface { v$buttonViewModelRegister }, $accessibilityIdMethod
|
||||
move-result-object v$accessibilityIdRegister
|
||||
|
||||
# Get accessibilityText
|
||||
invoke-interface { v$buttonViewModelRegister }, $accessibilityTextMethod
|
||||
move-result-object v$accessibilityTextRegister
|
||||
"""
|
||||
)
|
||||
|
||||
// If there is no accessibility-related text,
|
||||
// both accessibilityId and accessibilityText use empty values.
|
||||
addInstructions(
|
||||
nullCheckIndex,
|
||||
"""
|
||||
const-string v$accessibilityIdRegister, ""
|
||||
const-string v$accessibilityTextRegister, ""
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,27 +1,25 @@
|
|||
package app.revanced.patches.shared.misc.settings
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.gettingFirstImmutableMethodDeclaratively
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.firstMethodDeclaratively
|
||||
import app.revanced.patcher.name
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.shared.misc.extension.EXTENSION_CLASS_DESCRIPTOR
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.themeLightColorResourceNameMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getThemeLightColorResourceNameMethod() = firstMethodDeclaratively {
|
||||
name("getThemeLightColorResourceName")
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returnType("Ljava/lang/String;")
|
||||
parameterTypes()
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.themeDarkColorResourceNameMethod by gettingFirstMethodDeclaratively {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getThemeDarkColorResourceNameMethod() = firstMethodDeclaratively {
|
||||
name("getThemeDarkColorResourceName")
|
||||
definingClass(EXTENSION_CLASS_DESCRIPTOR)
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returnType("Ljava/lang/String;")
|
||||
parameterTypes()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
package app.revanced.patches.shared.misc.settings
|
||||
|
||||
import app.revanced.patcher.firstImmutableClassDef
|
||||
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.addResource
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.extension.EXTENSION_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.shared.layout.branding.addBrandLicensePatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.BasePreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
|
||||
|
|
@ -22,8 +24,8 @@ private var darkThemeColor: String? = null
|
|||
|
||||
/**
|
||||
* Sets the default theme colors used in various ReVanced specific settings menus.
|
||||
* By default these colors are white and black, but instead can be set to the
|
||||
* same color the target app uses for it's own settings.
|
||||
* By default, these colors are white and black, but instead can be set to the
|
||||
* same color the target app uses for its own settings.
|
||||
*/
|
||||
fun overrideThemeColors(lightThemeColorString: String?, darkThemeColorString: String) {
|
||||
lightThemeColor = lightThemeColorString
|
||||
|
|
@ -32,11 +34,12 @@ fun overrideThemeColors(lightThemeColorString: String?, darkThemeColorString: St
|
|||
|
||||
private val settingsColorPatch = bytecodePatch {
|
||||
afterDependents {
|
||||
val extensionClassDef = firstImmutableClassDef(EXTENSION_CLASS_DESCRIPTOR)
|
||||
if (lightThemeColor != null) {
|
||||
themeLightColorResourceNameMethod.returnEarly(lightThemeColor!!)
|
||||
extensionClassDef.getThemeLightColorResourceNameMethod().returnEarly(lightThemeColor!!)
|
||||
}
|
||||
if (darkThemeColor != null) {
|
||||
themeDarkColorResourceNameMethod.returnEarly(darkThemeColor!!)
|
||||
extensionClassDef.getThemeDarkColorResourceNameMethod().returnEarly(darkThemeColor!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ internal const val EXTENSION_CLASS_DESCRIPTOR =
|
|||
"Lapp/revanced/extension/shared/spoof/SpoofVideoStreamsPatch;"
|
||||
|
||||
private lateinit var buildRequestMethod: MutableMethod
|
||||
private var buildRequestMethodUrlRegister = -1
|
||||
private var buildRequestMethodURLRegister = -1
|
||||
|
||||
internal fun spoofVideoStreamsPatch(
|
||||
extensionClassDescriptor: String,
|
||||
|
|
@ -102,14 +102,14 @@ internal fun spoofVideoStreamsPatch(
|
|||
buildRequestMethod = this
|
||||
|
||||
val newRequestBuilderIndex = buildRequestMethodMatch[0]
|
||||
buildRequestMethodUrlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
|
||||
val freeRegister = findFreeRegister(newRequestBuilderIndex, buildRequestMethodUrlRegister)
|
||||
buildRequestMethodURLRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
|
||||
val freeRegister = findFreeRegister(newRequestBuilderIndex, buildRequestMethodURLRegister)
|
||||
|
||||
addInstructions(
|
||||
newRequestBuilderIndex,
|
||||
"""
|
||||
move-object v$freeRegister, p1
|
||||
invoke-static { v$buildRequestMethodUrlRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V
|
||||
invoke-static { v$buildRequestMethodURLRegister, v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->fetchStreams(Ljava/lang/String;Ljava/util/Map;)V
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
|
@ -164,7 +164,7 @@ internal fun spoofVideoStreamsPatch(
|
|||
move-result v0
|
||||
if-eqz v0, :disabled
|
||||
|
||||
# Get video id.
|
||||
# Get video ID.
|
||||
iget-object v2, p1, $videoDetailsClass->c:Ljava/lang/String;
|
||||
if-eqz v2, :disabled
|
||||
|
||||
|
|
@ -202,15 +202,15 @@ internal fun spoofVideoStreamsPatch(
|
|||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$buildRequestMethodUrlRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetAttRequest(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$buildRequestMethodUrlRegister
|
||||
invoke-static { v$buildRequestMethodURLRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetAttRequest(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$buildRequestMethodURLRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Remove /videoplayback request body to fix playback.
|
||||
// region Remove video playback 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.
|
||||
|
|
|
|||
|
|
@ -2,23 +2,72 @@ package app.revanced.patches.youtube.ad.general
|
|||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionReversed
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
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.Opcode
|
||||
|
||||
internal val BytecodePatchContext.fullScreenEngagementAdContainerMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.fullScreenEngagementAdContainerMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes()
|
||||
custom {
|
||||
containsLiteralInstruction(fullScreenEngagementAdContainer) &&
|
||||
indexOfAddListInstruction(this) >= 0
|
||||
}
|
||||
instructions(
|
||||
ResourceType.ID("fullscreen_engagement_ad_container"),
|
||||
Opcode.IGET_BOOLEAN(),
|
||||
allOf(
|
||||
Opcode.INVOKE_VIRTUAL(),
|
||||
method {
|
||||
name == "add" && returnType == "Z"
|
||||
&& parameterTypes.size == 1 && parameterTypes[0] == "Ljava/lang/Object;"
|
||||
}
|
||||
),
|
||||
allOf(
|
||||
Opcode.INVOKE_VIRTUAL(),
|
||||
method {
|
||||
name == "add" && returnType == "Z"
|
||||
&& parameterTypes.size == 1 && parameterTypes[0] == "Ljava/lang/Object;"
|
||||
}
|
||||
),
|
||||
allOf(
|
||||
Opcode.INVOKE_VIRTUAL(),
|
||||
method { name == "size" && returnType == "I" && parameterTypes.isEmpty() }
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun indexOfAddListInstruction(method: Method) = method.indexOfFirstInstructionReversed {
|
||||
getReference<MethodReference>()?.name == "add"
|
||||
internal val BytecodePatchContext.getPremiumViewMethodMatch by composingFirstMethod {
|
||||
name("onMeasure")
|
||||
definingClass("Lcom/google/android/apps/youtube/app/red/presenter/CompactYpcOfferModuleView;")
|
||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("I", "I")
|
||||
opcodes(
|
||||
Opcode.ADD_INT_2ADDR,
|
||||
Opcode.ADD_INT_2ADDR,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.RETURN_VOID
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.lithoDialogBuilderMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("[B", "L")
|
||||
instructions(
|
||||
allOf(Opcode.INVOKE_VIRTUAL(), method("show")),
|
||||
ResourceType.STYLE("SlidingDialogAnimation")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
internal val BytecodePatchContext.playerOverlayTimelyShelfMethod by gettingFirstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("Ljava/lang/Object;")
|
||||
instructions(
|
||||
"player_overlay_timely_shelf"(),
|
||||
"innertube_cue_range"(),
|
||||
"Null id"(),
|
||||
"Null onExitActions"()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package app.revanced.patches.youtube.ad.general
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.fieldReference
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.instructions
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.extensions.wideLiteral
|
||||
import app.revanced.patcher.firstMethod
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
|
|
@ -13,23 +15,32 @@ import app.revanced.patches.shared.misc.fix.verticalscroll.verticalScrollPatch
|
|||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.contexthook.Endpoint
|
||||
import app.revanced.patches.youtube.misc.contexthook.addOSNameHook
|
||||
import app.revanced.patches.shared.misc.litho.filter.addLithoFilter
|
||||
import app.revanced.patches.youtube.ad.getpremium.hideGetPremiumPatch
|
||||
import app.revanced.patches.youtube.misc.contexthook.hookClientContextPatch
|
||||
import app.revanced.patches.youtube.misc.engagement.addEngagementPanelIdHook
|
||||
import app.revanced.patches.youtube.misc.engagement.engagementPanelHookPatch
|
||||
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch
|
||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_14_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.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.forEachInstructionAsSequence
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import app.revanced.util.injectHideViewCall
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/litho/AdsFilter;"
|
||||
|
||||
internal var adAttributionId = -1L
|
||||
private set
|
||||
internal var fullScreenEngagementAdContainer = -1L
|
||||
private set
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/litho/AdsFilter;"
|
||||
|
||||
private val hideAdsResourcePatch = resourcePatch {
|
||||
dependsOn(
|
||||
|
|
@ -37,28 +48,30 @@ private val hideAdsResourcePatch = resourcePatch {
|
|||
settingsPatch,
|
||||
resourceMappingPatch,
|
||||
addResourcesPatch,
|
||||
hookClientContextPatch,
|
||||
engagementPanelHookPatch,
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "ad.general.hideAdsResourcePatch")
|
||||
|
||||
PreferenceScreen.ADS.addPreferences(
|
||||
SwitchPreference("revanced_hide_creator_store_shelf"),
|
||||
SwitchPreference("revanced_hide_end_screen_store_banner"),
|
||||
SwitchPreference("revanced_hide_fullscreen_ads"),
|
||||
SwitchPreference("revanced_hide_general_ads"),
|
||||
SwitchPreference("revanced_hide_merchandise_banners"),
|
||||
SwitchPreference("revanced_hide_paid_promotion_label"),
|
||||
SwitchPreference("revanced_hide_player_popup_ads"),
|
||||
SwitchPreference("revanced_hide_self_sponsor_ads"),
|
||||
SwitchPreference("revanced_hide_shopping_links"),
|
||||
SwitchPreference("revanced_hide_view_products_banner"),
|
||||
SwitchPreference("revanced_hide_web_search_results"),
|
||||
SwitchPreference("revanced_hide_youtube_premium_promotions")
|
||||
)
|
||||
|
||||
addLithoFilter("Lapp/revanced/extension/youtube/patches/litho/AdsFilter;")
|
||||
addEngagementPanelIdHook("$EXTENSION_CLASS_DESCRIPTOR->hidePlayerPopupAds(Ljava/lang/String;)Z")
|
||||
|
||||
adAttributionId = ResourceType.ID["ad_attribution"]
|
||||
fullScreenEngagementAdContainer = ResourceType.ID["fullscreen_engagement_ad_container"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,35 +81,109 @@ val hideAdsPatch = bytecodePatch(
|
|||
description = "Adds options to remove general ads.",
|
||||
) {
|
||||
dependsOn(
|
||||
hideGetPremiumPatch,
|
||||
hideAdsResourcePatch,
|
||||
verticalScrollPatch,
|
||||
fixBackToExitGesturePatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
// Hide fullscreen ad
|
||||
|
||||
lithoDialogBuilderMethodMatch.let {
|
||||
it.method.apply {
|
||||
// Find the class name of the custom dialog
|
||||
val dialogClass = getInstruction(it[0]).methodReference!!.definingClass
|
||||
|
||||
// The dialog can be closed after dialog.show(),
|
||||
// and it is better to close the dialog after the layout of the dialog has changed
|
||||
val insertIndex = indexOfFirstInstructionReversedOrThrow {
|
||||
opcode == Opcode.IPUT_OBJECT && fieldReference?.type == dialogClass
|
||||
}
|
||||
val insertRegister =
|
||||
getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||
val freeRegister = findFreeRegister(insertIndex, insertRegister)
|
||||
|
||||
addInstructionsAtControlFlowLabel(
|
||||
insertIndex,
|
||||
"""
|
||||
move-object/from16 v$freeRegister, p1
|
||||
invoke-static { v$insertRegister, v$freeRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->closeFullscreenAd(Ljava/lang/Object;[B)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Hide get premium
|
||||
|
||||
getPremiumViewMethodMatch.method.apply {
|
||||
val startIndex = getPremiumViewMethodMatch[0]
|
||||
val measuredWidthRegister = getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val measuredHeightInstruction = getInstruction<TwoRegisterInstruction>(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.
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
// Hide player overlay view. This can be hidden with a regular litho filter
|
||||
// but an empty space remains.
|
||||
if (is_20_14_or_greater) {
|
||||
playerOverlayTimelyShelfMethod.addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static {}, ${EXTENSION_CLASS_DESCRIPTOR}->hideAds()Z
|
||||
move-result v0
|
||||
if-eqz v0, :show
|
||||
return-void
|
||||
:show
|
||||
nop
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Hide end screen store banner.
|
||||
|
||||
fullScreenEngagementAdContainerMethod.apply {
|
||||
val addListIndex = indexOfAddListInstruction(this)
|
||||
val addListInstruction = getInstruction<FiveRegisterInstruction>(addListIndex)
|
||||
val listRegister = addListInstruction.registerC
|
||||
val objectRegister = addListInstruction.registerD
|
||||
fullScreenEngagementAdContainerMethodMatch.let {
|
||||
it.method.apply {
|
||||
val insertIndex = it[3]
|
||||
val insertInstruction = getInstruction<FiveRegisterInstruction>(insertIndex)
|
||||
val listRegister = insertInstruction.registerC
|
||||
val objectRegister = insertInstruction.registerD
|
||||
|
||||
replaceInstruction(
|
||||
addListIndex,
|
||||
"invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR" +
|
||||
"->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V",
|
||||
)
|
||||
replaceInstruction(
|
||||
insertIndex,
|
||||
"invoke-static { v$listRegister, v$objectRegister }, " +
|
||||
"${EXTENSION_CLASS_DESCRIPTOR}->" +
|
||||
"hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Hide ad views.
|
||||
|
|
@ -114,7 +201,22 @@ val hideAdsPatch = bytecodePatch(
|
|||
return@forEachInstructionAsSequence insertIndex to viewRegister
|
||||
|
||||
}) { method, (insertIndex, viewRegister) ->
|
||||
method.injectHideViewCall(insertIndex, viewRegister, EXTENSION_CLASS_DESCRIPTOR, "hideAdAttributionView")
|
||||
method.injectHideViewCall(
|
||||
insertIndex,
|
||||
viewRegister,
|
||||
EXTENSION_CLASS_DESCRIPTOR,
|
||||
"hideAdAttributionView"
|
||||
)
|
||||
}
|
||||
|
||||
setOf(
|
||||
Endpoint.BROWSE,
|
||||
Endpoint.SEARCH,
|
||||
).forEach { endpoint ->
|
||||
addOSNameHook(
|
||||
endpoint,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->hideAds(Ljava/lang/String;)Ljava/lang/String;",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
package app.revanced.patches.youtube.ad.getpremium
|
||||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.getPremiumViewMethodMatch by composingFirstMethod {
|
||||
name("onMeasure")
|
||||
definingClass("Lcom/google/android/apps/youtube/app/red/presenter/CompactYpcOfferModuleView;")
|
||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("I", "I")
|
||||
opcodes(
|
||||
Opcode.ADD_INT_2ADDR,
|
||||
Opcode.ADD_INT_2ADDR,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.RETURN_VOID,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package app.revanced.patches.youtube.ad.getpremium
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.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
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/HideGetPremiumPatch;"
|
||||
|
||||
val hideGetPremiumPatch = bytecodePatch(
|
||||
description = "Hides YouTube Premium signup promotions under the video player.",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "ad.getpremium.hideGetPremiumPatch")
|
||||
|
||||
PreferenceScreen.ADS.addPreferences(
|
||||
SwitchPreference("revanced_hide_get_premium"),
|
||||
)
|
||||
|
||||
getPremiumViewMethodMatch.let {
|
||||
val startIndex = it[0]
|
||||
val measuredWidthRegister = it.method.getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val measuredHeightInstruction = it.method.getInstruction<TwoRegisterInstruction>(startIndex + 1)
|
||||
|
||||
val measuredHeightRegister = measuredHeightInstruction.registerA
|
||||
val tempRegister = measuredHeightInstruction.registerB
|
||||
|
||||
it.method.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.
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,10 +24,12 @@ val videoAdsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import app.revanced.patches.youtube.video.information.videoInformationPatch
|
|||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
|
||||
private val copyVideoUrlResourcePatch = resourcePatch {
|
||||
private val copyVideoURLResourcePatch = resourcePatch {
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
playerControlsPatch,
|
||||
|
|
@ -20,7 +20,7 @@ private val copyVideoUrlResourcePatch = resourcePatch {
|
|||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "interaction.copyvideourl.copyVideoUrlResourcePatch")
|
||||
addResources("youtube", "interaction.copyvideourl.copyVideoURLResourcePatch")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_copy_video_url"),
|
||||
|
|
@ -46,25 +46,27 @@ val copyVideoURLPatch = bytecodePatch(
|
|||
description = "Adds options to display buttons in the video player to copy video URLs.",
|
||||
) {
|
||||
dependsOn(
|
||||
copyVideoUrlResourcePatch,
|
||||
copyVideoURLResourcePatch,
|
||||
playerControlsPatch,
|
||||
videoInformationPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
val extensionPlayerPackage = "Lapp/revanced/extension/youtube/videoplayer"
|
||||
val buttonsDescriptors = listOf(
|
||||
"$extensionPlayerPackage/CopyVideoUrlButton;",
|
||||
"$extensionPlayerPackage/CopyVideoUrlTimestampButton;",
|
||||
"$extensionPlayerPackage/CopyVideoURLButton;",
|
||||
"$extensionPlayerPackage/CopyVideoURLTimestampButton;",
|
||||
)
|
||||
|
||||
buttonsDescriptors.forEach { descriptor ->
|
||||
|
|
@ -2,6 +2,8 @@ package app.revanced.patches.youtube.interaction.dialog
|
|||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.createDialogMethodMatch by composingFirstMethod {
|
||||
returnType("V")
|
||||
|
|
@ -13,3 +15,28 @@ internal val BytecodePatchContext.createDialogMethodMatch by composingFirstMetho
|
|||
method { toString() == "Landroid/app/AlertDialog;->show()V" },
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
internal val BytecodePatchContext.createModernDialogMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
Opcode.MOVE_RESULT(),
|
||||
method { toString() == $$"Landroid/app/AlertDialog$Builder;->setIcon(I)Landroid/app/AlertDialog$Builder;" },
|
||||
method { toString() == $$"Landroid/app/AlertDialog$Builder;->create()Landroid/app/AlertDialog;" }
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.playabilityStatusEnumMethod by gettingFirstImmutableMethod(
|
||||
"OK",
|
||||
"ERROR",
|
||||
"UNPLAYABLE",
|
||||
"LOGIN_REQUIRED",
|
||||
"CONTENT_CHECK_REQUIRED",
|
||||
"AGE_CHECK_REQUIRED",
|
||||
"LIVE_STREAM_OFFLINE",
|
||||
"FULLSCREEN_ONLY",
|
||||
"GL_PLAYBACK_REQUIRED",
|
||||
"AGE_VERIFICATION_REQUIRED",
|
||||
)
|
||||
|
|
@ -1,15 +1,28 @@
|
|||
package app.revanced.patches.youtube.interaction.dialog
|
||||
|
||||
import app.revanced.patcher.accessFlags
|
||||
import app.revanced.patcher.custom
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.instructions
|
||||
import app.revanced.patcher.extensions.replaceInstructions
|
||||
import app.revanced.patcher.firstMethodDeclaratively
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.returnType
|
||||
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.backgroundPlaybackManagerShortsMethod
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/RemoveViewerDiscretionDialogPatch;"
|
||||
|
|
@ -18,7 +31,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
|
|||
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.",
|
||||
"by accepting it automatically. This does not bypass the age restriction.",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
|
|
@ -28,10 +41,12 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -45,13 +60,56 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
|
|||
createDialogMethodMatch.let {
|
||||
it.method.apply {
|
||||
val showDialogIndex = it[-1]
|
||||
val dialogRegister = getInstruction<FiveRegisterInstruction>(showDialogIndex).registerC
|
||||
val dialogRegister =
|
||||
getInstruction<FiveRegisterInstruction>(showDialogIndex).registerC
|
||||
|
||||
replaceInstructions(
|
||||
showDialogIndex,
|
||||
"invoke-static { v$dialogRegister }, $EXTENSION_CLASS_DESCRIPTOR->confirmDialog(Landroid/app/AlertDialog;)V",
|
||||
"invoke-static { v$dialogRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->" +
|
||||
"confirmDialog(Landroid/app/AlertDialog;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
createModernDialogMethodMatch.let {
|
||||
it.method.apply {
|
||||
val showDialogIndex = it[-1]
|
||||
val dialogRegister =
|
||||
getInstruction<FiveRegisterInstruction>(showDialogIndex).registerC
|
||||
|
||||
replaceInstructions(
|
||||
showDialogIndex,
|
||||
"invoke-static { v$dialogRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->" +
|
||||
"confirmDialog(Landroid/app/AlertDialog\$Builder;)Landroid/app/AlertDialog;",
|
||||
)
|
||||
|
||||
val dialogStyleIndex = it[0]
|
||||
val dialogStyleRegister =
|
||||
getInstruction<OneRegisterInstruction>(dialogStyleIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
dialogStyleIndex + 1,
|
||||
"""
|
||||
invoke-static { v$dialogStyleRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->disableModernDialog(Z)Z
|
||||
move-result v$dialogStyleRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
backgroundPlaybackManagerShortsMethod.immutableClassDef.firstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returnType("Z")
|
||||
parameterTypes(playabilityStatusEnumMethod.immutableClassDef.type)
|
||||
custom {
|
||||
// There's another similar method that's difficult to match uniquely,
|
||||
// Instruction counter is used to identify the target method.
|
||||
instructions.count() < 10
|
||||
}
|
||||
}.addInstruction(
|
||||
0,
|
||||
"invoke-static { p0 }, ${EXTENSION_CLASS_DESCRIPTOR}->" +
|
||||
"setPlayabilityStatus(Ljava/lang/Enum;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
package app.revanced.patches.youtube.interaction.doubletap
|
||||
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||
import app.revanced.util.removeFromParent
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Suppress("unused")
|
||||
val addMoreDoubleTapToSeekLengthOptionsPatch = resourcePatch(
|
||||
name = "Add more double tap to seek length options",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
// Values are hard coded to keep patching simple.
|
||||
val doubleTapLengthOptionsString = "3, 5, 10, 15, 20, 30, 60, 120, 180, 240"
|
||||
|
||||
val doubleTapLengths = doubleTapLengthOptionsString
|
||||
.replace(" ", "")
|
||||
.split(",")
|
||||
if (doubleTapLengths.isEmpty()) throw PatchException("Invalid double-tap length elements")
|
||||
|
||||
document("res/values/arrays.xml").use { document ->
|
||||
fun Element.removeAllChildren() {
|
||||
val children = childNodes // Calling childNodes creates a new list.
|
||||
for (i in children.length - 1 downTo 0) {
|
||||
children.item(i).removeFromParent()
|
||||
}
|
||||
}
|
||||
|
||||
val values = document.childNodes.findElementByAttributeValueOrThrow(
|
||||
attributeName = "name",
|
||||
value = "double_tap_length_values"
|
||||
)
|
||||
values.removeAllChildren()
|
||||
|
||||
val entries = document.childNodes.findElementByAttributeValueOrThrow(
|
||||
attributeName = "name",
|
||||
value = "double_tap_length_entries"
|
||||
)
|
||||
entries.removeAllChildren()
|
||||
|
||||
doubleTapLengths.forEach { length ->
|
||||
val item = document.createElement("item")
|
||||
item.textContent = length
|
||||
entries.appendChild(item)
|
||||
values.appendChild(item.cloneNode(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,10 @@ val disableDoubleTapActionsPatch = bytecodePatch(
|
|||
"com.google.android.youtube"(
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -77,12 +80,13 @@ val disableDoubleTapActionsPatch = bytecodePatch(
|
|||
""",
|
||||
)
|
||||
|
||||
doubleTapInfoGetSeekSourceMethod.immutableClassDef.getDoubleTapInfoCtorMethod().addInstructions(
|
||||
0,
|
||||
"""
|
||||
doubleTapInfoGetSeekSourceMethod.immutableClassDef.getDoubleTapInfoCtorMethod()
|
||||
.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p3 }, $EXTENSION_CLASS_DESCRIPTOR->disableDoubleTapChapters(Z)Z
|
||||
move-result p3
|
||||
""",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,15 +55,17 @@ private val downloadsResourcePatch = resourcePatch {
|
|||
}
|
||||
}
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DownloadsPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/DownloadsPatch;"
|
||||
|
||||
internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;"
|
||||
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.",
|
||||
"using the in-app download button or a video player action button.",
|
||||
) {
|
||||
dependsOn(
|
||||
downloadsResourcePatch,
|
||||
|
|
@ -73,10 +75,12 @@ val downloadsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ internal val BytecodePatchContext.offlineVideoEndpointMethod by gettingFirstMeth
|
|||
parameterTypes(
|
||||
"Ljava/util/Map;",
|
||||
"L",
|
||||
"Ljava/lang/String", // VideoId
|
||||
"Ljava/lang/String", // Video ID
|
||||
"L",
|
||||
)
|
||||
instructions(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
package app.revanced.patches.youtube.interaction.hapticfeedback
|
||||
|
||||
import app.revanced.patcher.extensions.ExternalLabel
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.fieldReference
|
||||
import app.revanced.patcher.extensions.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.all.misc.transformation.IMethodCall
|
||||
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
|
||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||
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 com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||
"Lapp/revanced/extension/youtube/patches/DisableHapticFeedbackPatch"
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
||||
|
||||
@Suppress("unused")
|
||||
val disableHapticFeedbackPatch = bytecodePatch(
|
||||
name = "Disable haptic feedback",
|
||||
description = "Adds an option to disable haptic feedback in the player for various actions.",
|
||||
) {
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
transformInstructionsPatch(
|
||||
filterMap = { classDef, _, instruction, instructionIndex ->
|
||||
filterMapInstruction35c<MethodCall>(
|
||||
EXTENSION_CLASS_DESCRIPTOR_PREFIX,
|
||||
classDef,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
},
|
||||
transform = { method, entry ->
|
||||
val (methodType, instruction, instructionIndex) = entry
|
||||
methodType.replaceInvokeVirtualWithExtension(
|
||||
EXTENSION_CLASS_DESCRIPTOR,
|
||||
method,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "misc.hapticfeedback.disableHapticFeedbackPatch")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
PreferenceScreenPreference(
|
||||
"revanced_disable_haptic_feedback",
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_disable_haptic_feedback_chapters"),
|
||||
SwitchPreference("revanced_disable_haptic_feedback_precise_seeking"),
|
||||
SwitchPreference("revanced_disable_haptic_feedback_seek_undo"),
|
||||
SwitchPreference("revanced_disable_haptic_feedback_tap_and_hold"),
|
||||
SwitchPreference("revanced_disable_haptic_feedback_zoom"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
arrayOf(
|
||||
markerHapticsMethod to "disableChapterVibrate",
|
||||
scrubbingHapticsMethod to "disablePreciseSeekingVibrate",
|
||||
seekUndoHapticsMethod to "disableSeekUndoVibrate",
|
||||
zoomHapticsMethod to "disableZoomVibrate",
|
||||
).forEach { (method, methodName) ->
|
||||
method.addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->$methodName()Z
|
||||
move-result v0
|
||||
if-eqz v0, :vibrate
|
||||
return-void
|
||||
""",
|
||||
ExternalLabel("vibrate", method.getInstruction(0)),
|
||||
)
|
||||
}
|
||||
|
||||
val vibratorField = tapAndHoldHapticsHandlerMethodMatch.let {
|
||||
it.immutableMethod.getInstruction(it[-1]).fieldReference!!
|
||||
}
|
||||
// Function, because it can be the same method as getTapAndHoldSpeedMethodMatch.
|
||||
getTapAndHoldHapticsMethodMatch(vibratorField).let {
|
||||
it.method.apply {
|
||||
val index = it[0]
|
||||
val register = getInstruction<TwoRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
invoke-static { v$register }, ${EXTENSION_CLASS_DESCRIPTOR}->disableTapAndHoldVibrate(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private enum class MethodCall(
|
||||
override val definedClassName: String,
|
||||
override val methodName: String,
|
||||
override val methodParams: Array<String>,
|
||||
override val returnType: String,
|
||||
) : IMethodCall {
|
||||
VibrationEffect(
|
||||
"Landroid/os/Vibrator;",
|
||||
"vibrate",
|
||||
arrayOf("Landroid/os/VibrationEffect;"),
|
||||
"V",
|
||||
),
|
||||
VibrationMilliseconds(
|
||||
"Landroid/os/Vibrator;",
|
||||
"vibrate",
|
||||
arrayOf("J"),
|
||||
"V",
|
||||
),
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package app.revanced.patches.youtube.interaction.hapticfeedback
|
||||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
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 BytecodePatchContext.markerHapticsMethod by gettingFirstMethodDeclaratively(
|
||||
"Failed to execute markers haptics vibrate.",
|
||||
) {
|
||||
returnType("V")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.scrubbingHapticsMethod by gettingFirstMethodDeclaratively(
|
||||
"Failed to haptics vibrate for fine scrubbing.",
|
||||
) {
|
||||
returnType("V")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.seekUndoHapticsMethod by gettingFirstMethodDeclaratively(
|
||||
"Failed to execute seek undo haptics vibrate.",
|
||||
) {
|
||||
returnType("V")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.tapAndHoldHapticsHandlerMethodMatch by composingFirstMethod {
|
||||
name("<init>")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
returnType("V")
|
||||
parameterTypes("Landroid/content/Context;", "Landroid/os/Handler;")
|
||||
instructions(
|
||||
"vibrator"(),
|
||||
allOf(Opcode.CHECK_CAST(), type("Landroid/os/Vibrator;")),
|
||||
after(allOf(Opcode.IPUT_OBJECT(), field { type == "Ljava/lang/Object;" }))
|
||||
)
|
||||
}
|
||||
|
||||
internal fun BytecodePatchContext.getTapAndHoldHapticsMethodMatch(vibratorFieldReference: FieldReference) =
|
||||
firstMethodComposite {
|
||||
name("run")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
allOf(Opcode.IGET_OBJECT(), field { this == vibratorFieldReference }),
|
||||
allOf(Opcode.CHECK_CAST(), type("Landroid/os/Vibrator;")),
|
||||
"Failed to easy seek haptics vibrate."()
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.zoomHapticsMethod by gettingFirstMethodDeclaratively(
|
||||
"Failed to haptics vibrate for video zoom",
|
||||
) {
|
||||
returnType("V")
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ val enableSlideToSeekPatch = bytecodePatch(
|
|||
|
||||
var modifiedMethods = false
|
||||
|
||||
// Restore the behaviour to slide to seek.
|
||||
// Restore the behavior to slide to seek.
|
||||
|
||||
val checkIndex = slideToSeekMethodMatch[0]
|
||||
val checkReference = slideToSeekMethodMatch.method.getInstruction(checkIndex)
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
|||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/TapToSeekPatch;"
|
||||
|
||||
val enableSeekbarTappingPatch = bytecodePatch(
|
||||
val enableTapToSeekPatch = bytecodePatch(
|
||||
description = "Adds an option to enable tap to seek on the seekbar of the video player.",
|
||||
) {
|
||||
dependsOn(
|
||||
|
|
@ -27,14 +27,14 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
|||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "interaction.seekbar.enableSeekbarTappingPatch")
|
||||
addResources("youtube", "interaction.seekbar.enableTapToSeekPatch")
|
||||
|
||||
PreferenceScreen.SEEKBAR.addPreferences(
|
||||
SwitchPreference("revanced_seekbar_tapping"),
|
||||
SwitchPreference("revanced_tap_to_seek"),
|
||||
)
|
||||
|
||||
// Find the required methods to tap the seekbar.
|
||||
val seekbarTappingMethods = onTouchEventHandlerMethodMatch.let {
|
||||
val tapToSeekMethods = onTouchEventHandlerMethodMatch.let {
|
||||
fun getReference(index: Int) = it.method.getInstruction<ReferenceInstruction>(index)
|
||||
.reference as MethodReference
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
|||
)
|
||||
}
|
||||
|
||||
seekbarTappingMethodMatch.let {
|
||||
tapToSeekMethodMatch.let {
|
||||
val insertIndex = it[-1] + 1
|
||||
|
||||
it.method.apply {
|
||||
|
|
@ -62,13 +62,13 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
|||
xAxisRegister,
|
||||
)
|
||||
|
||||
val oMethod = seekbarTappingMethods[0]
|
||||
val nMethod = seekbarTappingMethods[1]
|
||||
val oMethod = tapToSeekMethods[0]
|
||||
val nMethod = tapToSeekMethods[1]
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->seekbarTappingEnabled()Z
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->tapToSeekEnabled()Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :disabled
|
||||
invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $oMethod
|
||||
|
|
@ -2,10 +2,7 @@ package app.revanced.patches.youtube.interaction.seekbar
|
|||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.extensions.instructions
|
||||
import app.revanced.patcher.extensions.stringReference
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patches.youtube.misc.playservice.*
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
|
@ -61,25 +58,6 @@ internal val BytecodePatchContext.disableFastForwardGestureMethodMatch by compos
|
|||
custom { instructions.count() > 30 }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.customTapAndHoldMethodMatch by composingFirstMethod {
|
||||
name("run")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes()
|
||||
instructions(2.0f.toRawBits().toLong()())
|
||||
custom {
|
||||
// Code is found in different methods with different strings.
|
||||
val findSearchLandingKey = (is_19_34_or_greater && !is_19_47_or_greater) ||
|
||||
(is_20_19_or_greater && !is_20_20_or_greater) || is_20_31_or_greater
|
||||
|
||||
indexOfFirstInstruction {
|
||||
val string = stringReference?.string
|
||||
string == "Failed to easy seek haptics vibrate." ||
|
||||
(findSearchLandingKey && string == "search_landing_cache_key")
|
||||
} >= 0
|
||||
}
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.onTouchEventHandlerMethodMatch by composingFirstMethod {
|
||||
name("onTouchEvent")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.PUBLIC)
|
||||
|
|
@ -103,7 +81,7 @@ internal val BytecodePatchContext.onTouchEventHandlerMethodMatch by composingFir
|
|||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.seekbarTappingMethodMatch by composingFirstMethod {
|
||||
internal val BytecodePatchContext.tapToSeekMethodMatch by composingFirstMethod {
|
||||
name("onTouchEvent")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
|
|
@ -132,15 +110,6 @@ internal val BytecodePatchContext.slideToSeekMethodMatch by composingFirstMethod
|
|||
literal { 67108864 }
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.fullscreenSeekbarThumbnailsQualityMethod by gettingFirstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
45399684L(), // Video stream seekbar thumbnails feature flag.
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.fullscreenLargeSeekbarFeatureFlagMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
|
|
|
|||
|
|
@ -6,24 +6,24 @@ import app.revanced.patcher.patch.bytecodePatch
|
|||
val seekbarPatch = bytecodePatch(
|
||||
name = "Seekbar",
|
||||
description = "Adds options to disable precise seeking when swiping up on the seekbar, " +
|
||||
"slide to seek instead of playing at 2x speed when pressing and holding, " +
|
||||
"tapping the player seekbar to seek, " +
|
||||
"and hiding the video player seekbar.",
|
||||
"slide to seek instead of playing at 2x speed when pressing and holding, " +
|
||||
"tapping the player seekbar to seek, and hiding the video player seekbar.",
|
||||
) {
|
||||
dependsOn(
|
||||
disablePreciseSeekingGesturePatch,
|
||||
enableSlideToSeekPatch,
|
||||
enableSeekbarTappingPatch,
|
||||
enableTapToSeekPatch,
|
||||
hideSeekbarPatch,
|
||||
seekbarThumbnailsPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
package app.revanced.patches.youtube.interaction.seekbar
|
||||
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.instructions
|
||||
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.fullscreenSeekbarThumbnailsMethod
|
||||
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.is_20_09_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/SeekbarThumbnailsPatch;"
|
||||
|
||||
val seekbarThumbnailsPatch = bytecodePatch(
|
||||
description = "Adds an option to use high quality fullscreen seekbar thumbnails.",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
addResourcesPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
apply {
|
||||
if (is_20_09_or_greater) {
|
||||
// High quality seekbar thumbnails is partially broken in 20.09
|
||||
// and the code is completely removed in 20.10+
|
||||
return@apply
|
||||
}
|
||||
|
||||
addResources("youtube", "layout.seekbar.seekbarThumbnailsPatch")
|
||||
|
||||
if (is_19_17_or_greater) {
|
||||
PreferenceScreen.SEEKBAR.addPreferences(
|
||||
SwitchPreference("revanced_seekbar_thumbnails_high_quality"),
|
||||
)
|
||||
} else {
|
||||
PreferenceScreen.SEEKBAR.addPreferences(
|
||||
SwitchPreference("revanced_restore_old_seekbar_thumbnails"),
|
||||
SwitchPreference(
|
||||
key = "revanced_seekbar_thumbnails_high_quality",
|
||||
summaryOnKey = "revanced_seekbar_thumbnails_high_quality_legacy_summary_on",
|
||||
summaryOffKey = "revanced_seekbar_thumbnails_high_quality_legacy_summary_on",
|
||||
),
|
||||
)
|
||||
|
||||
fullscreenSeekbarThumbnailsMethod.apply {
|
||||
val moveResultIndex = instructions.lastIndex - 1
|
||||
|
||||
addInstruction(
|
||||
moveResultIndex,
|
||||
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->useFullscreenSeekbarThumbnails()Z",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fullscreenSeekbarThumbnailsQualityMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->useHighQualityFullscreenThumbnails()Z
|
||||
move-result v0
|
||||
return v0
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,10 @@ private val swipeControlsResourcePatch = resourcePatch {
|
|||
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
|
||||
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
|
||||
ListPreference("revanced_swipe_overlay_style"),
|
||||
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
||||
TextPreference(
|
||||
"revanced_swipe_overlay_background_opacity",
|
||||
inputType = InputType.NUMBER
|
||||
),
|
||||
TextPreference(
|
||||
"revanced_swipe_overlay_progress_brightness_color",
|
||||
tag = "app.revanced.extension.shared.settings.preference.ColorPickerWithOpacitySliderPreference",
|
||||
|
|
@ -101,10 +104,12 @@ val swipeControlsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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.subtitleButtonControllerMethod
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;"
|
||||
|
|
@ -25,10 +26,12 @@ val disableAutoCaptionsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -54,7 +57,7 @@ val disableAutoCaptionsPatch = bytecodePatch(
|
|||
|
||||
arrayOf(
|
||||
startVideoInformerMethod to 0,
|
||||
storyboardRendererDecoderRecommendedLevelMethod to 1,
|
||||
subtitleButtonControllerMethod to 1,
|
||||
).forEach { (method, enabled) ->
|
||||
method.addInstructions(
|
||||
0,
|
||||
|
|
|
|||
|
|
@ -14,23 +14,8 @@ internal val BytecodePatchContext.startVideoInformerMethod by gettingFirstMethod
|
|||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.storyboardRendererDecoderRecommendedLevelMethod by gettingFirstMethodDeclaratively("#-1#") {
|
||||
returnType("V")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameterTypes("L")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.subtitleTrackMethod by gettingFirstMethodDeclaratively("DISABLE_CAPTIONS_OPTION") {
|
||||
definingClass("/SubtitleTrack;")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
opcodes(
|
||||
Opcode.CONST_STRING,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.RETURN,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,12 @@ val customBrandingPatch = baseCustomBrandingPatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ private val customHeaderResourceFileNames = variants.map { variant ->
|
|||
"${CUSTOM_HEADER_RESOURCE_NAME}_$variant.png"
|
||||
}.toTypedArray()
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeHeaderPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/ChangeHeaderPatch;"
|
||||
|
||||
private val changeHeaderBytecodePatch = bytecodePatch {
|
||||
dependsOn(
|
||||
|
|
@ -106,10 +107,12 @@ val changeHeaderPatch = resourcePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import java.util.logging.Logger
|
|||
@Suppress("unused")
|
||||
val hideVideoActionButtonsPatch = resourcePatch(
|
||||
name = "Hide video action buttons",
|
||||
description = "Adds options to hide action buttons (such as the Download button) under videos.",
|
||||
description = "Adds options to hide action buttons (such as the Download button) under videos. " +
|
||||
"Patching version 20.21.37 or lower can hide more player button types."
|
||||
) {
|
||||
dependsOn(
|
||||
resourceMappingPatch,
|
||||
|
|
@ -27,10 +28,12 @@ val hideVideoActionButtonsPatch = resourcePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
// 20.22+ does not yet support hiding all player buttons.
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -43,27 +46,20 @@ val hideVideoActionButtonsPatch = resourcePatch(
|
|||
SwitchPreference("revanced_hide_like_dislike_button"),
|
||||
SwitchPreference("revanced_hide_comments_button"),
|
||||
SwitchPreference("revanced_hide_save_button"),
|
||||
)
|
||||
SwitchPreference("revanced_hide_remix_button"),
|
||||
SwitchPreference("revanced_hide_share_button"),
|
||||
|
||||
if (is_20_22_or_greater) {
|
||||
// FIXME: 20.22+ filtering of the action buttons doesn't work because
|
||||
// the buffer is the same for all buttons.
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"\n!!!" +
|
||||
"\n!!! Not all player action buttons can be set hidden when patching 20.22+" +
|
||||
"\n!!! Patch 20.21.37 or lower if you want to hide player action buttons" +
|
||||
"\n!!!",
|
||||
)
|
||||
} else {
|
||||
|
||||
// 20.22+ cannot hide all action buttons because of buffer changes.
|
||||
if (!is_20_22_or_greater) {
|
||||
preferences.addAll(
|
||||
listOf(
|
||||
SwitchPreference("revanced_hide_hype_button"),
|
||||
SwitchPreference("revanced_hide_ask_button"),
|
||||
SwitchPreference("revanced_hide_clip_button"),
|
||||
SwitchPreference("revanced_hide_promote_button"),
|
||||
SwitchPreference("revanced_hide_remix_button"),
|
||||
SwitchPreference("revanced_hide_report_button"),
|
||||
SwitchPreference("revanced_hide_share_button"),
|
||||
SwitchPreference("revanced_hide_shop_button"),
|
||||
SwitchPreference("revanced_hide_stop_ads_button"),
|
||||
SwitchPreference("revanced_hide_thanks_button"),
|
||||
|
|
@ -78,6 +74,6 @@ val hideVideoActionButtonsPatch = resourcePatch(
|
|||
),
|
||||
)
|
||||
|
||||
addLithoFilter("Lapp/revanced/extension/youtube/patches/litho/ButtonsFilter;")
|
||||
addLithoFilter("Lapp/revanced/extension/youtube/patches/litho/VideoActionButtonsFilter;")
|
||||
}
|
||||
}
|
||||
|
|
@ -2,17 +2,10 @@ package app.revanced.patches.youtube.layout.buttons.navigation
|
|||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.addCreateButtonViewMethodMatch by composingFirstMethod {
|
||||
instructions(
|
||||
"Android Wear"(),
|
||||
Opcode.IF_EQZ(),
|
||||
after("Android Automotive"()),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.createPivotBarMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameterTypes(
|
||||
|
|
@ -34,6 +27,28 @@ internal val BytecodePatchContext.animatedNavigationTabsFeatureFlagMethodMatch b
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
internal val BytecodePatchContext.pivotBarStyleMethodMatch by composingFirstMethod {
|
||||
definingClass("/PivotBar;")
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
opcodes(
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.XOR_INT_2ADDR
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.pivotBarChangedMethodMatch by composingFirstMethod {
|
||||
name("onConfigurationChanged")
|
||||
definingClass("/PivotBar;")
|
||||
returnType("V")
|
||||
opcodes(
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.translucentNavigationStatusBarFeatureFlagMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
|
|
@ -63,3 +78,20 @@ internal val BytecodePatchContext.translucentNavigationButtonsSystemFeatureFlagM
|
|||
45632194L(), // Translucent system buttons feature flag.
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.setWordmarkHeaderMethod by gettingFirstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("Landroid/widget/ImageView;")
|
||||
instructions(
|
||||
ResourceType.ATTR("ytPremiumWordmarkHeader"),
|
||||
ResourceType.ATTR("ytWordmarkHeader")
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.wideSearchbarLayoutMethod by gettingFirstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/view/View;")
|
||||
parameterTypes("L", "L")
|
||||
instructions(ResourceType.LAYOUT("action_bar_ringo"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,253 @@
|
|||
package app.revanced.patches.youtube.layout.buttons.navigation
|
||||
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
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.layout.toolbar.hookToolbar
|
||||
import app.revanced.patches.youtube.layout.toolbar.toolbarHookPatch
|
||||
import app.revanced.patches.youtube.misc.contexthook.Endpoint
|
||||
import app.revanced.patches.youtube.misc.contexthook.addOSNameHook
|
||||
import app.revanced.patches.youtube.misc.contexthook.hookClientContextPatch
|
||||
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.playservice.is_19_25_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_15_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_31_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.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
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
|
||||
import kotlin.collections.plusAssign
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/NavigationBarPatch;"
|
||||
|
||||
val navigationBarPatch = bytecodePatch(
|
||||
name = "Navigation bar",
|
||||
description = "Adds options to hide and change the bottom navigation bar (such as the Shorts button) "
|
||||
+ " and the upper navigation toolbar. Patching version 20.21.37 and lower also adds a setting to use a wide searchbar."
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
navigationBarHookPatch,
|
||||
versionCheckPatch,
|
||||
hookClientContextPatch,
|
||||
toolbarHookPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "layout.buttons.navigation.navigationBarPatch")
|
||||
|
||||
val preferences = mutableSetOf(
|
||||
SwitchPreference("revanced_hide_home_button"),
|
||||
SwitchPreference("revanced_hide_shorts_button"),
|
||||
SwitchPreference("revanced_hide_create_button"),
|
||||
SwitchPreference("revanced_hide_subscriptions_button"),
|
||||
SwitchPreference("revanced_hide_notifications_button"),
|
||||
SwitchPreference("revanced_switch_create_with_notifications_button"),
|
||||
SwitchPreference("revanced_hide_navigation_button_labels"),
|
||||
SwitchPreference("revanced_narrow_navigation_buttons"),
|
||||
)
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_light")
|
||||
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark")
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
SwitchPreference("revanced_disable_translucent_status_bar")
|
||||
)
|
||||
|
||||
if (is_20_15_or_greater) {
|
||||
preferences += SwitchPreference("revanced_navigation_bar_animations")
|
||||
}
|
||||
}
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
PreferenceScreenPreference(
|
||||
key = "revanced_navigation_buttons_screen",
|
||||
sorting = Sorting.UNSORTED,
|
||||
preferences = preferences
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
// Switch create with notifications button.
|
||||
addOSNameHook(
|
||||
Endpoint.GUIDE,
|
||||
"${EXTENSION_CLASS_DESCRIPTOR}->switchCreateWithNotificationButton(Ljava/lang/String;)Ljava/lang/String;",
|
||||
)
|
||||
|
||||
// Hide navigation button labels.
|
||||
createPivotBarMethodMatch.let {
|
||||
it.method.apply {
|
||||
val setTextIndex = it[0]
|
||||
val targetRegister = getInstruction<FiveRegisterInstruction>(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)
|
||||
|
||||
// Force on/off translucent effect on status bar and navigation buttons.
|
||||
if (is_19_25_or_greater) {
|
||||
translucentNavigationStatusBarFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
|
||||
)
|
||||
}
|
||||
|
||||
translucentNavigationButtonsFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||
)
|
||||
}
|
||||
|
||||
translucentNavigationButtonsSystemFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_20_15_or_greater) {
|
||||
animatedNavigationTabsFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useAnimatedNavigationButtons(Z)Z",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
arrayOf(
|
||||
pivotBarChangedMethodMatch,
|
||||
pivotBarStyleMethodMatch
|
||||
).forEach { match ->
|
||||
match.method.apply {
|
||||
val targetIndex = match[1] + 1
|
||||
val register = getInstruction<OneRegisterInstruction>(targetIndex - 1).registerA
|
||||
|
||||
addInstructions(
|
||||
targetIndex,
|
||||
"""
|
||||
invoke-static { v$register }, ${EXTENSION_CLASS_DESCRIPTOR}->enableNarrowNavigationButton(Z)Z
|
||||
move-result v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Toolbar.
|
||||
//
|
||||
|
||||
val toolbarPreferences = mutableSetOf(
|
||||
SwitchPreference("revanced_hide_toolbar_create_button"),
|
||||
SwitchPreference("revanced_hide_toolbar_notification_button"),
|
||||
SwitchPreference("revanced_hide_toolbar_search_button")
|
||||
)
|
||||
if (!is_20_31_or_greater) {
|
||||
toolbarPreferences += SwitchPreference("revanced_wide_searchbar")
|
||||
}
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
PreferenceScreenPreference(
|
||||
key = "revanced_toolbar_screen",
|
||||
sorting = Sorting.UNSORTED,
|
||||
preferences = toolbarPreferences
|
||||
)
|
||||
)
|
||||
|
||||
hookToolbar("${EXTENSION_CLASS_DESCRIPTOR}->hideCreateButton")
|
||||
hookToolbar("${EXTENSION_CLASS_DESCRIPTOR}->hideNotificationButton")
|
||||
hookToolbar("${EXTENSION_CLASS_DESCRIPTOR}->hideSearchButton")
|
||||
|
||||
|
||||
//
|
||||
// Wide searchbar.
|
||||
//
|
||||
|
||||
// YT removed the legacy text search text field all code required to use it.
|
||||
// This functionality could be restored by adding a search text field to the toolbar
|
||||
// with a listener that artificially clicks the toolbar search button.
|
||||
if (!is_20_31_or_greater) {
|
||||
// Navigate to the method that checks if the YT logo is shown beside the search bar.
|
||||
val shouldShowLogoMethod = with(setWordmarkHeaderMethod) {
|
||||
val invokeStaticIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_STATIC && methodReference?.returnType == "Z"
|
||||
}
|
||||
navigate(this).to(invokeStaticIndex).stop()
|
||||
}
|
||||
|
||||
shouldShowLogoMethod.apply {
|
||||
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructionsAtControlFlowLabel(
|
||||
index,
|
||||
"""
|
||||
invoke-static { v$register }, ${EXTENSION_CLASS_DESCRIPTOR}->enableWideSearchbar(Z)Z
|
||||
move-result v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Fix missing left padding when using wide searchbar.
|
||||
wideSearchbarLayoutMethod.apply {
|
||||
findInstructionIndicesReversedOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Landroid/view/LayoutInflater;" && reference.name == "inflate"
|
||||
}.forEach { inflateIndex ->
|
||||
val register =
|
||||
getInstruction<OneRegisterInstruction>(inflateIndex + 1).registerA
|
||||
|
||||
addInstruction(
|
||||
inflateIndex + 2,
|
||||
"invoke-static { v$register }, " +
|
||||
"${EXTENSION_CLASS_DESCRIPTOR}->setActionBar(Landroid/view/View;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
package app.revanced.patches.youtube.layout.buttons.navigation
|
||||
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.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.playservice.is_19_25_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_15_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.insertLiteralOverride
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;"
|
||||
|
||||
@Suppress("ObjectPropertyName")
|
||||
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,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "layout.buttons.navigation.navigationButtonsPatch")
|
||||
|
||||
val preferences = mutableSetOf(
|
||||
SwitchPreference("revanced_hide_home_button"),
|
||||
SwitchPreference("revanced_hide_shorts_button"),
|
||||
SwitchPreference("revanced_hide_create_button"),
|
||||
SwitchPreference("revanced_hide_subscriptions_button"),
|
||||
SwitchPreference("revanced_hide_notifications_button"),
|
||||
SwitchPreference("revanced_switch_create_with_notifications_button"),
|
||||
SwitchPreference("revanced_hide_navigation_button_labels"),
|
||||
)
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_light")
|
||||
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark")
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
SwitchPreference("revanced_disable_translucent_status_bar"),
|
||||
)
|
||||
}
|
||||
|
||||
if (is_20_15_or_greater) {
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
SwitchPreference("revanced_navigation_bar_animations"),
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
PreferenceScreenPreference(
|
||||
key = "revanced_navigation_buttons_screen",
|
||||
sorting = Sorting.UNSORTED,
|
||||
preferences = preferences,
|
||||
),
|
||||
)
|
||||
|
||||
// Switch create with notifications button.
|
||||
addCreateButtonViewMethodMatch.method.apply {
|
||||
val conditionalCheckIndex = addCreateButtonViewMethodMatch[1]
|
||||
val conditionRegister =
|
||||
getInstruction<OneRegisterInstruction>(conditionalCheckIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
conditionalCheckIndex,
|
||||
"""
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->switchCreateWithNotificationButton()Z
|
||||
move-result v$conditionRegister
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
// Hide navigation button labels.
|
||||
createPivotBarMethodMatch.let {
|
||||
it.method.apply {
|
||||
val setTextIndex = it[0]
|
||||
val targetRegister = getInstruction<FiveRegisterInstruction>(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)
|
||||
|
||||
// Force on/off translucent effect on status bar and navigation buttons.
|
||||
if (is_19_25_or_greater) {
|
||||
translucentNavigationStatusBarFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
|
||||
)
|
||||
}
|
||||
|
||||
translucentNavigationButtonsFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||
)
|
||||
}
|
||||
|
||||
translucentNavigationButtonsSystemFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_20_15_or_greater) {
|
||||
animatedNavigationTabsFeatureFlagMethodMatch.let {
|
||||
it.method.insertLiteralOverride(
|
||||
it[0],
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useAnimatedNavigationButtons(Z)Z",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import app.revanced.patcher.*
|
|||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.mediaRouteButtonMethod by gettingFirstMethodDeclaratively {
|
||||
name("setVisibility")
|
||||
|
|
@ -30,3 +31,23 @@ internal val BytecodePatchContext.inflateControlsGroupLayoutStubMethodMatch by c
|
|||
method("inflate"),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.fullscreenButtonMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameterTypes("Landroid/view/View;")
|
||||
returnType("V")
|
||||
instructions(
|
||||
ResourceType.ID("fullscreen_button"),
|
||||
Opcode.CHECK_CAST()
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.titleAnchorMethodMatch by composingFirstMethod {
|
||||
returnType("V")
|
||||
instructions(
|
||||
ResourceType.ID("player_collapse_button"),
|
||||
Opcode.CHECK_CAST(),
|
||||
ResourceType.ID("title_anchor"),
|
||||
Opcode.MOVE_RESULT_OBJECT()
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
|
|||
val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
||||
name = "Hide player overlay buttons",
|
||||
description = "Adds options to hide the player Cast, Autoplay, Captions, Previous & Next buttons, and the player " +
|
||||
"control buttons background.",
|
||||
"control buttons background.",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
|
|
@ -38,10 +38,12 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -49,23 +51,26 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
|||
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"),
|
||||
SwitchPreference("revanced_hide_captions_button"),
|
||||
SwitchPreference("revanced_hide_cast_button"),
|
||||
SwitchPreference("revanced_hide_collapse_button"),
|
||||
SwitchPreference("revanced_hide_fullscreen_button"),
|
||||
SwitchPreference("revanced_hide_player_control_buttons_background"),
|
||||
SwitchPreference("revanced_hide_player_previous_next_buttons"),
|
||||
)
|
||||
|
||||
// region Hide player next/previous button.
|
||||
|
||||
getLayoutConstructorMethodMatch().let {
|
||||
val insertIndex = it[-1]
|
||||
val viewRegister = it.method.getInstruction<FiveRegisterInstruction>(insertIndex).registerC
|
||||
val viewRegister =
|
||||
it.method.getInstruction<FiveRegisterInstruction>(insertIndex).registerC
|
||||
|
||||
it.method.addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR" +
|
||||
"->hidePreviousNextButtons(Landroid/view/View;)V",
|
||||
"->hidePreviousNextButtons(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +89,7 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
|||
if (is_20_28_or_greater) {
|
||||
arrayOf(
|
||||
castButtonPlayerFeatureFlagMethodMatch,
|
||||
castButtonActionFeatureFlagMethodMatch,
|
||||
castButtonActionFeatureFlagMethodMatch, // Cast button in the feed.
|
||||
).forEach { match ->
|
||||
match.method.insertLiteralOverride(
|
||||
match[0],
|
||||
|
|
@ -118,14 +123,14 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
|||
val gotoIndex = indexOfFirstInstructionOrThrow(constIndex) {
|
||||
val parameterTypes = getReference<MethodReference>()?.parameterTypes
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
parameterTypes?.size == 2 &&
|
||||
parameterTypes.first() == "Landroid/view/ViewStub;"
|
||||
parameterTypes?.size == 2 &&
|
||||
parameterTypes.first() == "Landroid/view/ViewStub;"
|
||||
} + 1
|
||||
|
||||
addInstructionsWithLabels(
|
||||
constIndex,
|
||||
"""
|
||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideAutoPlayButton()Z
|
||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideAutoplayButton()Z
|
||||
move-result v$constRegister
|
||||
if-nez v$constRegister, :hidden
|
||||
""",
|
||||
|
|
@ -135,6 +140,54 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
|||
|
||||
// endregion
|
||||
|
||||
|
||||
// region Hide collapse button.
|
||||
|
||||
titleAnchorMethodMatch.let {
|
||||
it.method.apply {
|
||||
val titleAnchorIndex = it[-1]
|
||||
val titleAnchorRegister = getInstruction<OneRegisterInstruction>(titleAnchorIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
titleAnchorIndex + 1,
|
||||
"invoke-static { v$titleAnchorRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->setTitleAnchorStartMargin(Landroid/view/View;)V"
|
||||
)
|
||||
|
||||
val playerCollapseButtonIndex = it[1]
|
||||
val playerCollapseButtonRegister = getInstruction<OneRegisterInstruction>(playerCollapseButtonIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
playerCollapseButtonIndex + 1,
|
||||
"invoke-static { v$playerCollapseButtonRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->hideCollapseButton(Landroid/widget/ImageView;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hide fullscreen button.
|
||||
|
||||
fullscreenButtonMethodMatch.let {
|
||||
it.method.apply {
|
||||
val castIndex = it[1]
|
||||
val insertIndex = castIndex + 1
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(castIndex).registerA
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$insertRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->hideFullscreenButton(Landroid/widget/ImageView;)Landroid/widget/ImageView;
|
||||
move-result-object v$insertRegister
|
||||
if-nez v$insertRegister, :show
|
||||
return-void
|
||||
""",
|
||||
ExternalLabel("show", getInstruction(insertIndex))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hide player control buttons background.
|
||||
|
||||
inflateControlsGroupLayoutStubMethodMatch.let {
|
||||
|
|
|
|||
|
|
@ -7,15 +7,16 @@ 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.layout.buttons.navigation.navigationButtonsPatch
|
||||
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 com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val changeFormFactorPatch = bytecodePatch(
|
||||
|
|
@ -26,15 +27,17 @@ val changeFormFactorPatch = bytecodePatch(
|
|||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
navigationButtonsPatch,
|
||||
navigationBarHookPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
package app.revanced.patches.youtube.layout.hide.autoplaypreview
|
||||
|
||||
import app.revanced.patcher.extensions.ExternalLabel
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.methodReference
|
||||
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.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.getLayoutConstructorMethodMatch
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstResourceIdOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import kotlin.collections.first
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/HideAutoplayPreviewPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val hideAutoplayPreviewPatch = bytecodePatch(
|
||||
name = "Hide autoplay preview",
|
||||
description = "Adds an option to hide the autoplay preview at the end of videos.",
|
||||
) {
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
sharedExtensionPatch,
|
||||
resourceMappingPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
)
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "layout.hide.autoplaypreview.hideAutoplayPreviewPatch")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_hide_autoplay_preview")
|
||||
)
|
||||
|
||||
getLayoutConstructorMethodMatch().method.apply {
|
||||
val constIndex = indexOfFirstResourceIdOrThrow("autonav_preview_stub")
|
||||
val constRegister = getInstruction<OneRegisterInstruction>(constIndex).registerA
|
||||
val gotoIndex = indexOfFirstInstructionOrThrow(constIndex) {
|
||||
val parameterTypes = methodReference?.parameterTypes
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
parameterTypes?.size == 2 &&
|
||||
parameterTypes.first() == "Landroid/view/ViewStub;"
|
||||
} + 1
|
||||
|
||||
addInstructionsWithLabels(
|
||||
constIndex,
|
||||
"""
|
||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideAutoplayPreview()Z
|
||||
move-result v$constRegister
|
||||
if-nez v$constRegister, :hidden
|
||||
""",
|
||||
ExternalLabel("hidden", getInstruction(gotoIndex)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,9 @@ package app.revanced.patches.youtube.layout.hide.endscreencards
|
|||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
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.reference.FieldReference
|
||||
|
||||
internal val BytecodePatchContext.layoutCircleMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
|
|
@ -33,7 +29,7 @@ internal val BytecodePatchContext.layoutIconMethodMatch by composingFirstMethod
|
|||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
|
||||
)
|
||||
)
|
||||
literal { layoutIcon }
|
||||
}
|
||||
|
||||
|
|
@ -55,14 +51,18 @@ internal val BytecodePatchContext.showEndscreenCardsMethod by gettingFirstMethod
|
|||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
instructions(
|
||||
allOf(Opcode.IPUT_OBJECT(), field { type == "Ljava/lang/String;" }),
|
||||
allOf(Opcode.IGET_OBJECT(), field { type == "Ljava/lang/String;" }),
|
||||
afterAtMost(7, allOf(Opcode.INVOKE_VIRTUAL(), method("ordinal"))),
|
||||
5L(),
|
||||
8L(),
|
||||
9L()
|
||||
)
|
||||
custom {
|
||||
immutableClassDef.methods.count() == 5 &&
|
||||
containsLiteralInstruction(0) &&
|
||||
containsLiteralInstruction(5) &&
|
||||
containsLiteralInstruction(8) &&
|
||||
indexOfFirstInstruction {
|
||||
val reference = getReference<FieldReference>()
|
||||
reference?.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"
|
||||
} >= 0
|
||||
immutableClassDef.methods.count() == 5
|
||||
// 'public final' or 'final'
|
||||
&& AccessFlags.FINAL.isSet(accessFlags)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package app.revanced.patches.youtube.layout.hide.endscreencards
|
|||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
|
|
@ -35,7 +36,7 @@ private val hideEndScreenCardsResourcePatch = resourcePatch {
|
|||
addResources("youtube", "layout.hide.endscreencards.hideEndScreenCardsResourcePatch")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_hide_endscreen_cards"),
|
||||
SwitchPreference("revanced_hide_end_screen_cards"),
|
||||
)
|
||||
|
||||
fun idOf(name: String) = ResourceType.LAYOUT["endscreen_element_layout_$name"]
|
||||
|
|
@ -62,10 +63,12 @@ val hideEndScreenCardsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -82,7 +85,7 @@ val hideEndScreenCardsPatch = bytecodePatch(
|
|||
addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static { v$viewRegister }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->hideEndScreenCardView(Landroid/view/View;)V",
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->hideEndScreenCardView(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.youtube.layout.hide.endscreensuggestion
|
||||
package app.revanced.patches.youtube.layout.hide.endscreensuggestedvideo
|
||||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.extensions.instructions
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.youtube.layout.hide.endscreensuggestion
|
||||
package app.revanced.patches.youtube.layout.hide.endscreensuggestedvideo
|
||||
|
||||
import app.revanced.patcher.extensions.ExternalLabel
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
|
|
@ -31,15 +31,17 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch")
|
||||
addResources("youtube", "layout.hide.endscreensuggestedvideo.hideEndScreenSuggestedVideoPatch")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_end_screen_suggested_video"),
|
||||
|
|
@ -49,18 +51,21 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch(
|
|||
val endScreenMethod = navigate(it.immutableMethod).to(it[-1]).stop()
|
||||
|
||||
endScreenMethod.apply {
|
||||
val autoNavStatusMethodName = autoNavConstructorMethod.immutableClassDef.getAutoNavStatusMethod().name
|
||||
val autoNavStatusMethodName =
|
||||
autoNavConstructorMethod.immutableClassDef.getAutoNavStatusMethod().name
|
||||
|
||||
val invokeIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = methodReference
|
||||
reference?.name == autoNavStatusMethodName &&
|
||||
reference.returnType == "Z" &&
|
||||
reference.parameterTypes.isEmpty()
|
||||
reference.returnType == "Z" &&
|
||||
reference.parameterTypes.isEmpty()
|
||||
}
|
||||
|
||||
val iGetObjectIndex = indexOfFirstInstructionReversedOrThrow(invokeIndex, Opcode.IGET_OBJECT)
|
||||
val iGetObjectIndex =
|
||||
indexOfFirstInstructionReversedOrThrow(invokeIndex, Opcode.IGET_OBJECT)
|
||||
val invokeReference = getInstruction<ReferenceInstruction>(invokeIndex).reference
|
||||
val iGetObjectReference = getInstruction<ReferenceInstruction>(iGetObjectIndex).reference
|
||||
val iGetObjectReference =
|
||||
getInstruction<ReferenceInstruction>(iGetObjectIndex).reference
|
||||
val opcodeName = getInstruction(invokeIndex).opcode.name
|
||||
|
||||
addInstructionsWithLabels(
|
||||
|
|
@ -30,15 +30,20 @@ val disableFullscreenAmbientModePatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "layout.hide.fullscreenambientmode.disableFullscreenAmbientModePatch")
|
||||
addResources(
|
||||
"youtube",
|
||||
"layout.hide.fullscreenambientmode.disableFullscreenAmbientModePatch"
|
||||
)
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_disable_fullscreen_ambient_mode"),
|
||||
|
|
|
|||
|
|
@ -1,34 +1,96 @@
|
|||
package app.revanced.patches.youtube.layout.hide.general
|
||||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.after
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutMethod
|
||||
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.ClassDef
|
||||
|
||||
/**
|
||||
* 20.26+
|
||||
*/
|
||||
internal val BytecodePatchContext.hideShowMoreButtonMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL, AccessFlags.SYNTHETIC)
|
||||
|
||||
internal val BytecodePatchContext.hideShowMoreButtonSetViewMethodMatch by composingFirstMethod {
|
||||
returnType("V")
|
||||
parameterTypes("L", "Ljava/lang/Object;")
|
||||
|
||||
var methodDefiningClass = ""
|
||||
custom {
|
||||
methodDefiningClass = definingClass
|
||||
true
|
||||
}
|
||||
instructions(
|
||||
ResourceType.LAYOUT("expand_button_down"),
|
||||
method { toString() == "Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View;" },
|
||||
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||
ResourceType.ID("link_text_start"),
|
||||
allOf(
|
||||
Opcode.IPUT_OBJECT(),
|
||||
field { type == "Landroid/widget/TextView;" && definingClass == methodDefiningClass }),
|
||||
ResourceType.ID("expand_button_container"),
|
||||
allOf(
|
||||
Opcode.IPUT_OBJECT(),
|
||||
field { type == "Landroid/view/View;" && definingClass == methodDefiningClass })
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.hideShowMoreLegacyButtonMethodMatch by composingFirstMethod {
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getHideShowMoreButtonGetParentViewMethod() =
|
||||
firstImmutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/view/View;")
|
||||
parameterTypes()
|
||||
}
|
||||
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getHideShowMoreButtonMethod() = firstMethodDeclaratively {
|
||||
returnType("V")
|
||||
parameterTypes("L", "Ljava/lang/Object;")
|
||||
instructions(
|
||||
allOf(
|
||||
Opcode.INVOKE_VIRTUAL(),
|
||||
method {
|
||||
toString() == "Landroid/view/View;->setContentDescription(Ljava/lang/CharSequence;)V"
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 20.21+
|
||||
*/
|
||||
internal val BytecodePatchContext.hideSubscribedChannelsBarConstructorMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.LAYOUT("expand_button_down"),
|
||||
method { toString() == "Landroid/view/View;->inflate(Landroid/content/Context;ILandroid/view/ViewGroup;)Landroid/view/View;" },
|
||||
Opcode.MOVE_RESULT_OBJECT(),
|
||||
ResourceType.ID("parent_container"),
|
||||
afterAtMost(3, Opcode.MOVE_RESULT_OBJECT()),
|
||||
afterAtMost(
|
||||
5,
|
||||
allOf(Opcode.NEW_INSTANCE(), type($$"Landroid/widget/LinearLayout$LayoutParams;"))
|
||||
)
|
||||
)
|
||||
custom { immutableClassDef.anyField { type == "Landroid/support/v7/widget/RecyclerView;" } }
|
||||
}
|
||||
|
||||
/**
|
||||
* ~ 20.21
|
||||
*/
|
||||
internal val BytecodePatchContext.hideSubscribedChannelsBarConstructorLegacyMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.ID("parent_container"),
|
||||
afterAtMost(3, Opcode.MOVE_RESULT_OBJECT()),
|
||||
afterAtMost(
|
||||
5,
|
||||
allOf(Opcode.NEW_INSTANCE(), type($$"Landroid/widget/LinearLayout$LayoutParams;"))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
internal val ClassDef.hideSubscribedChannelsBarLandscapeMethodMatch by ClassDefComposing.composingFirstMethod {
|
||||
returnType("V")
|
||||
parameterTypes()
|
||||
instructions(
|
||||
ResourceType.DIMEN("parent_view_width_in_wide_mode"),
|
||||
allOf(Opcode.INVOKE_VIRTUAL(), method("getDimensionPixelSize")),
|
||||
after(Opcode.MOVE_RESULT())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -146,3 +208,124 @@ internal val BytecodePatchContext.hideViewCountMethodMatch by composingFirstMeth
|
|||
Opcode.RETURN_OBJECT,
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.searchBoxTypingStringMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L")
|
||||
instructions(
|
||||
allOf(Opcode.IGET_OBJECT(), field { type == "Ljava/util/Collection;" }),
|
||||
afterAtMost(5, method { toString() == "Ljava/util/ArrayList;-><init>(Ljava/util/Collection;)V" }),
|
||||
allOf(Opcode.IGET_OBJECT(), field { type == "Ljava/lang/String;" }),
|
||||
afterAtMost(5, method { toString() == "Ljava/lang/String;->isEmpty()Z" }),
|
||||
ResourceType.DIMEN("suggestion_category_divider_height")
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.searchSuggestionEndpointConstructorMethod by gettingFirstImmutableMethodDeclaratively(
|
||||
"\u2026 "
|
||||
) {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
returnType("V")
|
||||
}
|
||||
|
||||
internal val ClassDef.searchSuggestionEndpointMethodMatch by ClassDefComposing.composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Z")
|
||||
parameterTypes()
|
||||
|
||||
var methodDefiningClass = ""
|
||||
custom {
|
||||
methodDefiningClass = definingClass
|
||||
true
|
||||
}
|
||||
|
||||
instructions(
|
||||
allOf(
|
||||
Opcode.IGET_OBJECT(),
|
||||
field { definingClass == methodDefiningClass && type == "Ljava/lang/String;" }),
|
||||
allOf(
|
||||
Opcode.INVOKE_STATIC(),
|
||||
method { toString() == "Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z" }),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.latestVideosContentPillMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L", "Z")
|
||||
instructions(
|
||||
ResourceType.LAYOUT("content_pill"),
|
||||
method {
|
||||
toString() == "Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View;"
|
||||
},
|
||||
after(Opcode.MOVE_RESULT_OBJECT())
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.latestVideosBarMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes("L", "Z")
|
||||
instructions(
|
||||
ResourceType.LAYOUT("bar"),
|
||||
method {
|
||||
toString() == "Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View;"
|
||||
},
|
||||
after(Opcode.MOVE_RESULT_OBJECT())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
internal val BytecodePatchContext.bottomSheetMenuItemBuilderMethodMatch by composingFirstMethod {
|
||||
returnType("L")
|
||||
parameterTypes("L")
|
||||
instructions(
|
||||
allOf(
|
||||
Opcode.INVOKE_STATIC(),
|
||||
method {
|
||||
returnType == "Ljava/lang/CharSequence;" &&
|
||||
parameterTypes.size == 1 && parameterTypes[0].startsWith("L")
|
||||
}
|
||||
),
|
||||
after(Opcode.MOVE_RESULT_OBJECT()),
|
||||
"Text missing for BottomSheetMenuItem."()
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.contextualMenuItemBuilderMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL, AccessFlags.SYNTHETIC)
|
||||
returnType("V")
|
||||
parameterTypes("L", "L")
|
||||
instructions(
|
||||
allOf(Opcode.CHECK_CAST(), type("Landroid/widget/TextView;")),
|
||||
afterAtMost(
|
||||
5,
|
||||
method { toString() == "Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V" }
|
||||
),
|
||||
ResourceType.DIMEN("poster_art_width_default"),
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.channelTabBuilderMethod by gettingFirstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("Landroid/view/View;")
|
||||
parameterTypes(
|
||||
"Ljava/lang/CharSequence;",
|
||||
"Ljava/lang/CharSequence;",
|
||||
"Z",
|
||||
"L"
|
||||
)
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.channelTabRendererMethod by gettingFirstMethodDeclaratively(
|
||||
"TabRenderer.content contains SectionListRenderer but the tab does not have a section list controller."
|
||||
) {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
parameterTypes(
|
||||
"L",
|
||||
"Ljava/util/List;",
|
||||
"I"
|
||||
)
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package app.revanced.patches.youtube.layout.hide.general
|
||||
|
||||
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patcher.CompositeMatch
|
||||
import app.revanced.patcher.classDef
|
||||
import app.revanced.patcher.extensions.*
|
||||
import app.revanced.patcher.immutableClassDef
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
|
|
@ -9,9 +11,11 @@ import app.revanced.patches.shared.layout.hide.general.hideLayoutComponentsPatch
|
|||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.*
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
|
||||
import app.revanced.patches.youtube.misc.engagement.engagementPanelHookPatch
|
||||
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_20_26_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_20_21_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
|
||||
|
|
@ -19,12 +23,18 @@ import app.revanced.util.findFreeRegister
|
|||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
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.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.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
|
||||
|
||||
internal var albumCardId = -1L
|
||||
private set
|
||||
|
|
@ -71,6 +81,7 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
hideLayoutComponentsResourcePatch,
|
||||
navigationBarHookPatch,
|
||||
versionCheckPatch,
|
||||
engagementPanelHookPatch,
|
||||
resourceMappingPatch,
|
||||
),
|
||||
filterClasses = setOf(
|
||||
|
|
@ -82,15 +93,23 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
),
|
||||
compatibleWithPackages = arrayOf(
|
||||
"com.google.android.youtube" to setOf(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
),
|
||||
) {
|
||||
addResources("youtube", "layout.hide.general.hideLayoutComponentsPatch")
|
||||
|
||||
PreferenceScreen.ADS.addPreferences(
|
||||
// Uses horizontal shelf and a buffer, which requires managing in a single place in the code
|
||||
// to ensure the generic "hide horizontal shelves" doesn't hide when it should show.
|
||||
SwitchPreference("revanced_hide_creator_store_shelf")
|
||||
)
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
PreferenceScreenPreference(
|
||||
key = "revanced_hide_description_components_screen",
|
||||
|
|
@ -99,15 +118,22 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
SwitchPreference("revanced_hide_ask_section"),
|
||||
SwitchPreference("revanced_hide_attributes_section"),
|
||||
SwitchPreference("revanced_hide_chapters_section"),
|
||||
SwitchPreference("revanced_hide_course_progress_section"),
|
||||
SwitchPreference("revanced_hide_explore_section"),
|
||||
SwitchPreference("revanced_hide_explore_course_section"),
|
||||
SwitchPreference("revanced_hide_explore_podcast_section"),
|
||||
SwitchPreference("revanced_hide_featured_links_section"),
|
||||
SwitchPreference("revanced_hide_featured_places_section"),
|
||||
SwitchPreference("revanced_hide_featured_videos_section"),
|
||||
SwitchPreference("revanced_hide_info_cards_section"),
|
||||
SwitchPreference("revanced_hide_gaming_section"),
|
||||
SwitchPreference("revanced_hide_how_this_was_made_section"),
|
||||
SwitchPreference("revanced_hide_hype_points"),
|
||||
SwitchPreference("revanced_hide_info_cards_section"),
|
||||
SwitchPreference("revanced_hide_key_concepts_section"),
|
||||
SwitchPreference("revanced_hide_podcast_section"),
|
||||
SwitchPreference("revanced_hide_music_section"),
|
||||
SwitchPreference("revanced_hide_subscribe_button"),
|
||||
SwitchPreference("revanced_hide_transcript_section"),
|
||||
SwitchPreference("revanced_hide_quizzes_section")
|
||||
),
|
||||
),
|
||||
PreferenceScreenPreference(
|
||||
|
|
@ -118,13 +144,14 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
SwitchPreference("revanced_hide_comments_channel_guidelines"),
|
||||
SwitchPreference("revanced_hide_comments_by_members_header"),
|
||||
SwitchPreference("revanced_hide_comments_section"),
|
||||
SwitchPreference("revanced_hide_comments_section_in_home_feed"),
|
||||
SwitchPreference("revanced_hide_comments_community_guidelines"),
|
||||
SwitchPreference("revanced_hide_comments_create_a_short_button"),
|
||||
SwitchPreference("revanced_hide_comments_emoji_and_timestamp_buttons"),
|
||||
SwitchPreference("revanced_hide_comments_preview_comment"),
|
||||
SwitchPreference("revanced_hide_comments_thanks_button"),
|
||||
),
|
||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||
sorting = Sorting.UNSORTED,
|
||||
),
|
||||
SwitchPreference("revanced_hide_channel_bar"),
|
||||
SwitchPreference("revanced_hide_channel_watermark"),
|
||||
|
|
@ -132,29 +159,34 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
SwitchPreference("revanced_hide_emergency_box"),
|
||||
SwitchPreference("revanced_hide_info_panels"),
|
||||
SwitchPreference("revanced_hide_join_membership_button"),
|
||||
SwitchPreference("revanced_hide_live_chat_replay_button"),
|
||||
SwitchPreference("revanced_hide_medical_panels"),
|
||||
SwitchPreference("revanced_hide_quick_actions"),
|
||||
SwitchPreference("revanced_hide_related_videos"),
|
||||
SwitchPreference("revanced_hide_subscribers_community_guidelines"),
|
||||
SwitchPreference("revanced_hide_timed_reactions"),
|
||||
SwitchPreference("revanced_hide_video_title")
|
||||
)
|
||||
|
||||
PreferenceScreen.FEED.addPreferences(
|
||||
PreferenceScreenPreference(
|
||||
key = "revanced_hide_keyword_content_screen",
|
||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||
sorting = Sorting.UNSORTED,
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_hide_keyword_content_home"),
|
||||
SwitchPreference("revanced_hide_keyword_content_subscriptions"),
|
||||
SwitchPreference("revanced_hide_keyword_content_search"),
|
||||
TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE),
|
||||
TextPreference(
|
||||
"revanced_hide_keyword_content_phrases",
|
||||
inputType = InputType.TEXT_MULTI_LINE
|
||||
),
|
||||
NonInteractivePreference(
|
||||
key = "revanced_hide_keyword_content_about",
|
||||
tag = "app.revanced.extension.shared.settings.preference.BulletPointPreference",
|
||||
),
|
||||
NonInteractivePreference(
|
||||
key = "revanced_hide_keyword_content_about_whole_words",
|
||||
tag = "app.revanced.extension.youtube.settings.preference.HtmlPreference",
|
||||
tag = "app.revanced.extension.youtube.settings.preference.HTMLPreference",
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -170,6 +202,18 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
PreferenceScreenPreference(
|
||||
key = "revanced_channel_screen",
|
||||
preferences = setOf(
|
||||
PreferenceCategory(
|
||||
titleKey = null,
|
||||
sorting = Sorting.UNSORTED,
|
||||
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_hide_channel_tab"),
|
||||
TextPreference(
|
||||
"revanced_hide_channel_tab_filter_strings",
|
||||
inputType = InputType.TEXT_MULTI_LINE
|
||||
),
|
||||
)
|
||||
),
|
||||
SwitchPreference("revanced_hide_community_button"),
|
||||
SwitchPreference("revanced_hide_for_you_shelf"),
|
||||
SwitchPreference("revanced_hide_join_button"),
|
||||
|
|
@ -185,6 +229,18 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
SwitchPreference("revanced_hide_community_posts"),
|
||||
SwitchPreference("revanced_hide_compact_banner"),
|
||||
SwitchPreference("revanced_hide_expandable_card"),
|
||||
PreferenceCategory(
|
||||
titleKey = null,
|
||||
sorting = Sorting.UNSORTED,
|
||||
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_hide_feed_flyout_menu"),
|
||||
TextPreference(
|
||||
"revanced_hide_feed_flyout_menu_filter_strings",
|
||||
inputType = InputType.TEXT_MULTI_LINE
|
||||
),
|
||||
)
|
||||
),
|
||||
SwitchPreference("revanced_hide_floating_microphone_button"),
|
||||
SwitchPreference(
|
||||
key = "revanced_hide_horizontal_shelves",
|
||||
|
|
@ -192,6 +248,7 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
),
|
||||
SwitchPreference("revanced_hide_image_shelf"),
|
||||
SwitchPreference("revanced_hide_latest_posts"),
|
||||
SwitchPreference("revanced_hide_latest_videos_button"),
|
||||
SwitchPreference("revanced_hide_mix_playlists"),
|
||||
SwitchPreference("revanced_hide_movies_section"),
|
||||
SwitchPreference("revanced_hide_notify_me_button"),
|
||||
|
|
@ -206,7 +263,13 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
SwitchPreference("revanced_hide_doodles"),
|
||||
)
|
||||
|
||||
// region Mix playlists
|
||||
if (is_20_21_or_greater) {
|
||||
PreferenceScreen.FEED.addPreferences(
|
||||
SwitchPreference("revanced_hide_you_may_like_section")
|
||||
)
|
||||
}
|
||||
|
||||
// region Hide mix playlists
|
||||
|
||||
parseElementFromBufferMethodMatch.let {
|
||||
it.method.apply {
|
||||
|
|
@ -214,12 +277,18 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
val insertIndex = startIndex + 1
|
||||
|
||||
val byteArrayParameter = "p3"
|
||||
val conversionContextRegister = getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val returnEmptyComponentInstruction = instructions.last { it.opcode == Opcode.INVOKE_STATIC }
|
||||
val conversionContextRegister =
|
||||
getInstruction<TwoRegisterInstruction>(startIndex).registerA
|
||||
val returnEmptyComponentInstruction =
|
||||
instructions.last { it.opcode == Opcode.INVOKE_STATIC }
|
||||
val returnEmptyComponentRegister =
|
||||
(returnEmptyComponentInstruction as FiveRegisterInstruction).registerC
|
||||
val freeRegister =
|
||||
findFreeRegister(insertIndex, conversionContextRegister, returnEmptyComponentRegister)
|
||||
findFreeRegister(
|
||||
insertIndex,
|
||||
conversionContextRegister,
|
||||
returnEmptyComponentRegister
|
||||
)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex,
|
||||
|
|
@ -239,7 +308,7 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
|
||||
// endregion
|
||||
|
||||
// region Watermark (legacy code for old versions of YouTube)
|
||||
// region Hide watermark (legacy code for old versions of YouTube)
|
||||
|
||||
playerOverlayMethod.immutableClassDef.getShowWatermarkMethod().apply {
|
||||
val index = implementation!!.instructions.size - 5
|
||||
|
|
@ -256,25 +325,94 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
|
||||
// endregion
|
||||
|
||||
// region Show more button
|
||||
// region Hide Show more button
|
||||
|
||||
(if (is_20_26_or_greater) hideShowMoreButtonMethodMatch else hideShowMoreLegacyButtonMethodMatch).let {
|
||||
val (textViewField, buttonContainerField) = hideShowMoreButtonSetViewMethodMatch.let {
|
||||
val textViewIndex = it[1]
|
||||
val buttonContainerIndex = it[3]
|
||||
|
||||
Pair(
|
||||
it.method.getInstruction<ReferenceInstruction>(textViewIndex).reference,
|
||||
it.method.getInstruction<ReferenceInstruction>(buttonContainerIndex).reference
|
||||
)
|
||||
}
|
||||
|
||||
val parentViewMethod = hideShowMoreButtonSetViewMethodMatch.immutableClassDef
|
||||
.getHideShowMoreButtonGetParentViewMethod()
|
||||
|
||||
hideShowMoreButtonSetViewMethodMatch.immutableClassDef.getHideShowMoreButtonMethod().apply {
|
||||
val helperMethod = ImmutableMethod(
|
||||
definingClass,
|
||||
"patch_hideShowMoreButton",
|
||||
listOf(),
|
||||
"V",
|
||||
AccessFlags.PRIVATE.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(7),
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
move-object/from16 v0, p0
|
||||
invoke-virtual { v0 }, $parentViewMethod
|
||||
move-result-object v1
|
||||
iget-object v2, v0, $buttonContainerField
|
||||
iget-object v3, v0, $textViewField
|
||||
invoke-static { v1, v2, v3 }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideShowMoreButton(Landroid/view/View;Landroid/view/View;Landroid/widget/TextView;)V
|
||||
return-void
|
||||
"""
|
||||
)
|
||||
}.also(classDef.methods::add)
|
||||
|
||||
findInstructionIndicesReversedOrThrow(Opcode.RETURN_VOID).forEach { index ->
|
||||
addInstruction(index, "invoke-direct/range { p0 .. p0 }, $helperMethod")
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
|
||||
// region Hide Subscribed channels bar
|
||||
|
||||
// Tablet
|
||||
val methodMatch = if (is_20_21_or_greater)
|
||||
hideSubscribedChannelsBarConstructorMethodMatch
|
||||
else hideSubscribedChannelsBarConstructorLegacyMethodMatch
|
||||
|
||||
methodMatch.let {
|
||||
it.method.apply {
|
||||
val moveRegisterIndex = it[-1]
|
||||
val viewRegister = getInstruction<OneRegisterInstruction>(moveRegisterIndex).registerA
|
||||
val index = it[1]
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
val insertIndex = moveRegisterIndex + 1
|
||||
addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
||||
"->hideShowMoreButton(Landroid/view/View;)V",
|
||||
index + 1,
|
||||
"invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
||||
"->hideSubscribedChannelsBar(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Phone (landscape mode)
|
||||
methodMatch.immutableClassDef.hideSubscribedChannelsBarLandscapeMethodMatch.let {
|
||||
it.method.apply {
|
||||
val index = it[-1]
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideSubscribedChannelsBar(I)I
|
||||
move-result v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region crowdfunding box
|
||||
// region Hide Crowdfunding box
|
||||
|
||||
crowdfundingBoxMethodMatch.let {
|
||||
it.method.apply {
|
||||
val insertIndex = it[-1]
|
||||
|
|
@ -283,14 +421,14 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static {v$objectRegister}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
||||
"->hideCrowdfundingBox(Landroid/view/View;)V",
|
||||
"->hideCrowdfundingBox(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region hide album cards
|
||||
// region Hide Album cards
|
||||
|
||||
albumCardsMethodMatch.let {
|
||||
it.method.apply {
|
||||
|
|
@ -301,14 +439,14 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
||||
"->hideAlbumCard(Landroid/view/View;)V",
|
||||
"->hideAlbumCard(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region hide floating microphone
|
||||
// region Hide Floating microphone
|
||||
|
||||
showFloatingMicrophoneButtonMethodMatch.let {
|
||||
it.method.apply {
|
||||
|
|
@ -327,7 +465,27 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
|
||||
// endregion
|
||||
|
||||
// region 'Yoodles'
|
||||
// region Hide latest videos button
|
||||
|
||||
listOf(
|
||||
latestVideosContentPillMethodMatch,
|
||||
latestVideosBarMethodMatch,
|
||||
).forEach { match ->
|
||||
match.method.apply {
|
||||
val moveIndex = match[-1]
|
||||
val viewRegister = getInstruction<OneRegisterInstruction>(moveIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
moveIndex + 1,
|
||||
"invoke-static { v$viewRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR" +
|
||||
"->hideLatestVideosButton(Landroid/view/View;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hide 'Yoodles'
|
||||
|
||||
yoodlesImageViewMethod.apply {
|
||||
findInstructionIndicesReversedOrThrow {
|
||||
|
|
@ -339,14 +497,14 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
replaceInstruction(
|
||||
insertIndex,
|
||||
"invoke-static { v$imageViewRegister, v$drawableRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->" +
|
||||
"setDoodleDrawable(Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;)V",
|
||||
"setDoodleDrawable(Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region hide view count
|
||||
// region Hide view count
|
||||
|
||||
hideViewCountMethodMatch.method.apply {
|
||||
val startIndex = hideViewCountMethodMatch[0]
|
||||
|
|
@ -356,10 +514,10 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
val applyDimensionIndex = indexOfFirstInstructionReversedOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
opcode == Opcode.INVOKE_STATIC &&
|
||||
reference?.definingClass == "Landroid/util/TypedValue;" &&
|
||||
reference.returnType == "F" &&
|
||||
reference.name == "applyDimension" &&
|
||||
reference.parameterTypes == listOf("I", "F", "Landroid/util/DisplayMetrics;")
|
||||
reference?.definingClass == "Landroid/util/TypedValue;" &&
|
||||
reference.returnType == "F" &&
|
||||
reference.name == "applyDimension" &&
|
||||
reference.parameterTypes == listOf("I", "F", "Landroid/util/DisplayMetrics;")
|
||||
}
|
||||
|
||||
// A float value is passed which is used to determine subtitle text size.
|
||||
|
|
@ -378,7 +536,7 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
|
||||
// endregion
|
||||
|
||||
// region hide filter bar
|
||||
// region Hide filter bar
|
||||
|
||||
/**
|
||||
* Patch a [Method] with a given [instructions].
|
||||
|
|
@ -416,6 +574,179 @@ val hideLayoutComponentsPatch = hideLayoutComponentsPatch(
|
|||
|
||||
relatedChipCloudMethodMatch.patch<OneRegisterInstruction>(1) { register ->
|
||||
"invoke-static { v$register }, " +
|
||||
"$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V"
|
||||
"$LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideInRelatedVideos(Landroid/view/View;)V"
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hide You may like section
|
||||
|
||||
if (is_20_21_or_greater) {
|
||||
val searchSuggestionEndpointField =
|
||||
searchSuggestionEndpointConstructorMethod.immutableClassDef
|
||||
.searchSuggestionEndpointMethodMatch.let {
|
||||
it.method.getInstruction(it[0]).fieldReference!!
|
||||
}
|
||||
val searchSuggestionEndpointClass = searchSuggestionEndpointField.definingClass
|
||||
|
||||
searchBoxTypingStringMethodMatch.let {
|
||||
it.method.apply {
|
||||
// A collection of search suggestions.
|
||||
// This includes trending search (also known as 'You may like' section)
|
||||
// and your search history.
|
||||
|
||||
val searchSuggestionCollectionField = getInstruction(it[0]).fieldReference!!
|
||||
val typedStringField = getInstruction(it[2]).fieldReference!!
|
||||
|
||||
val helperMethod = ImmutableMethod(
|
||||
definingClass,
|
||||
"patch_setSearchSuggestions",
|
||||
listOf(
|
||||
ImmutableMethodParameter(
|
||||
parameterTypes.first().toString(),
|
||||
null,
|
||||
null
|
||||
)
|
||||
),
|
||||
"V",
|
||||
AccessFlags.PRIVATE.value or AccessFlags.FINAL.value,
|
||||
annotations,
|
||||
null,
|
||||
MutableMethodImplementation(7),
|
||||
).toMutable().apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
move-object/from16 v0, p1
|
||||
iget-object v1, v0, $typedStringField
|
||||
|
||||
# Check if the setting is enabled and if the typed string is empty.
|
||||
invoke-static { v1 }, ${LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR}->hideYouMayLikeSection(Ljava/lang/String;)Z
|
||||
move-result v1
|
||||
|
||||
# If the setting is disabled or the typed string is not empty, do nothing.
|
||||
if-eqz v1, :ignore
|
||||
|
||||
## Get a collection of search suggestions.
|
||||
iget-object v1, v0, $searchSuggestionCollectionField
|
||||
|
||||
# Iterate through the collection and check if the search suggestion is the search history.
|
||||
invoke-interface { v1 }, Ljava/util/Collection;->iterator()Ljava/util/Iterator;
|
||||
move-result-object v2
|
||||
|
||||
:loop
|
||||
invoke-interface { v2 }, Ljava/util/Iterator;->hasNext()Z
|
||||
move-result v3
|
||||
if-eqz v3, :exit
|
||||
invoke-interface { v2 }, Ljava/util/Iterator;->next()Ljava/lang/Object;
|
||||
move-result-object v3
|
||||
instance-of v4, v3, $searchSuggestionEndpointClass
|
||||
if-eqz v4, :loop
|
||||
check-cast v3, $searchSuggestionEndpointClass
|
||||
|
||||
# Each search suggestion has a command endpoint.
|
||||
# If the search suggestion is the search history, the command includes the keyword '/delete'.
|
||||
iget-object v4, v3, $searchSuggestionEndpointField
|
||||
invoke-static { v3, v4 }, ${LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR}->isSearchHistory(Ljava/lang/Object;Ljava/lang/String;)Z
|
||||
move-result v3
|
||||
|
||||
# If this search suggestion is the search history, do nothing.
|
||||
if-nez v3, :loop
|
||||
|
||||
# If this search suggestion is not the search history, remove it from the search suggestions collection.
|
||||
invoke-interface { v2 }, Ljava/util/Iterator;->remove()V
|
||||
goto :loop
|
||||
|
||||
# Save the updated collection to a field.
|
||||
:exit
|
||||
iput-object v1, v0, $searchSuggestionCollectionField
|
||||
|
||||
:ignore
|
||||
return-void
|
||||
"""
|
||||
)
|
||||
}.also(it.classDef.methods::add)
|
||||
|
||||
addInstruction(
|
||||
0,
|
||||
"invoke-direct/range { p0 .. p1 }, $helperMethod"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hide flyout menu items
|
||||
|
||||
bottomSheetMenuItemBuilderMethodMatch.let {
|
||||
it.method.apply {
|
||||
val index = it[1]
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstructions(
|
||||
index + 1,
|
||||
"""
|
||||
invoke-static { v$register }, ${LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR}->hideFlyoutMenu(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
contextualMenuItemBuilderMethodMatch.let {
|
||||
it.method.apply {
|
||||
val index = it[1]
|
||||
val targetInstruction = getInstruction<FiveRegisterInstruction>(index)
|
||||
|
||||
addInstruction(
|
||||
index + 1,
|
||||
"invoke-static { v${targetInstruction.registerC}, v${targetInstruction.registerD} }, " +
|
||||
"${LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR}->hideFlyoutMenu(Landroid/widget/TextView;Ljava/lang/CharSequence;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hide channel tab
|
||||
|
||||
channelTabRendererMethod.apply {
|
||||
val iteratorIndex = indexOfFirstInstructionReversedOrThrow {
|
||||
methodReference?.name == "hasNext"
|
||||
}
|
||||
|
||||
val iteratorRegister = getInstruction<FiveRegisterInstruction>(iteratorIndex).registerC
|
||||
val targetIndex = indexOfFirstInstructionReversedOrThrow {
|
||||
val reference = methodReference
|
||||
|
||||
opcode == Opcode.INVOKE_INTERFACE &&
|
||||
reference?.returnType == channelTabBuilderMethod.returnType &&
|
||||
reference.parameterTypes == channelTabBuilderMethod.parameterTypes
|
||||
}
|
||||
|
||||
val objectIndex = indexOfFirstInstructionReversedOrThrow(
|
||||
targetIndex,
|
||||
Opcode.IGET_OBJECT
|
||||
)
|
||||
val objectInstruction = getInstruction<TwoRegisterInstruction>(objectIndex)
|
||||
val objectReference = getInstruction<ReferenceInstruction>(objectIndex).reference
|
||||
|
||||
addInstructionsWithLabels(
|
||||
objectIndex + 1,
|
||||
"""
|
||||
invoke-static { v${objectInstruction.registerA} }, ${LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR}->hideChannelTab(Ljava/lang/String;)Z
|
||||
move-result v${objectInstruction.registerA}
|
||||
if-eqz v${objectInstruction.registerA}, :ignore
|
||||
invoke-interface { v$iteratorRegister }, Ljava/util/Iterator;->remove()V
|
||||
goto :next_iterator
|
||||
:ignore
|
||||
iget-object v${objectInstruction.registerA}, v${objectInstruction.registerB}, $objectReference
|
||||
""",
|
||||
ExternalLabel("next_iterator", getInstruction(iteratorIndex))
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,10 +47,12 @@ val hideInfoCardsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -61,21 +63,21 @@ val hideInfoCardsPatch = bytecodePatch(
|
|||
SwitchPreference("revanced_hide_info_cards"),
|
||||
)
|
||||
|
||||
// Edit: This old non litho code may be obsolete and no longer used by any supported versions.
|
||||
// Edit: This old non-litho code may be obsolete and no longer used by any supported versions.
|
||||
infocardsIncognitoParentMethod.immutableClassDef.getInfocardsIncognitoMethod().apply {
|
||||
val invokeInstructionIndex = implementation!!.instructions.indexOfFirst {
|
||||
it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal &&
|
||||
((it as ReferenceInstruction).reference.toString() == "Landroid/view/View;->setVisibility(I)V")
|
||||
((it as ReferenceInstruction).reference.toString() == "Landroid/view/View;->setVisibility(I)V")
|
||||
}
|
||||
|
||||
addInstruction(
|
||||
invokeInstructionIndex,
|
||||
"invoke-static {v${getInstruction<FiveRegisterInstruction>(invokeInstructionIndex).registerC}}," +
|
||||
" Lapp/revanced/extension/youtube/patches/HideInfoCardsPatch;->hideInfoCardsIncognito(Landroid/view/View;)V",
|
||||
" Lapp/revanced/extension/youtube/patches/HideInfoCardsPatch;->hideInfoCardsIncognito(Landroid/view/View;)V",
|
||||
)
|
||||
}
|
||||
|
||||
// Edit: This old non litho code may be obsolete and no longer used by any supported versions.
|
||||
// Edit: This old non-litho code may be obsolete and no longer used by any supported versions.
|
||||
infocardsMethodCallMethodMatch.let {
|
||||
val invokeInterfaceIndex = it[-1]
|
||||
it.method.apply {
|
||||
|
|
@ -97,7 +99,8 @@ val hideInfoCardsPatch = bytecodePatch(
|
|||
}
|
||||
|
||||
// Info cards can also appear as Litho components.
|
||||
val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/litho/HideInfoCardsFilter;"
|
||||
val filterClassDescriptor =
|
||||
"Lapp/revanced/extension/youtube/patches/litho/HideInfoCardsFilter;"
|
||||
addLithoFilter(filterClassDescriptor)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.youtube.layout.hide.player.flyoutmenupanel
|
||||
package app.revanced.patches.youtube.layout.hide.player.flyoutmenu
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
|
|
@ -25,15 +25,18 @@ val hidePlayerFlyoutMenuItemsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/litho/PlayerFlyoutMenuItemsFilter;"
|
||||
val filterClassDescriptor =
|
||||
"Lapp/revanced/extension/youtube/patches/litho/PlayerFlyoutMenuItemsFilter;"
|
||||
|
||||
addResources("youtube", "layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch")
|
||||
|
||||
|
|
@ -42,24 +45,24 @@ val hidePlayerFlyoutMenuItemsPatch = bytecodePatch(
|
|||
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_stable_volume"),
|
||||
SwitchPreference("revanced_hide_player_flyout_listen_with_youtube_music"),
|
||||
SwitchPreference("revanced_hide_player_flyout_help"),
|
||||
SwitchPreference("revanced_hide_player_flyout_speed"),
|
||||
SwitchPreference("revanced_hide_player_flyout_lock_screen"),
|
||||
SwitchPreference(
|
||||
key = "revanced_hide_player_flyout_audio_track",
|
||||
tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference",
|
||||
tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference"
|
||||
),
|
||||
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
|
||||
SwitchPreference("revanced_hide_player_flyout_sleep_timer"),
|
||||
SwitchPreference("revanced_hide_player_flyout_video_quality"),
|
||||
SwitchPreference("revanced_hide_player_flyout_video_quality_footer"),
|
||||
SwitchPreference("revanced_hide_player_flyout_additional_settings"),
|
||||
SwitchPreference("revanced_hide_player_flyout_ambient_mode"),
|
||||
SwitchPreference("revanced_hide_player_flyout_stable_volume"),
|
||||
SwitchPreference("revanced_hide_player_flyout_loop_video"),
|
||||
SwitchPreference("revanced_hide_player_flyout_sleep_timer"),
|
||||
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
addLithoFilter(filterClassDescriptor)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.youtube.layout.panels.popup
|
||||
package app.revanced.patches.youtube.layout.hide.player.popup
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
|
|
@ -8,8 +8,10 @@ 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.getEngagementPanelControllerMethodMatch
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DisablePlayerPopupPanelsPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/DisablePlayerPopupPanelsPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val disablePlayerPopupPanelsPatch = bytecodePatch(
|
||||
|
|
@ -24,10 +26,12 @@ val disablePlayerPopupPanelsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -38,7 +42,7 @@ val disablePlayerPopupPanelsPatch = bytecodePatch(
|
|||
SwitchPreference("revanced_hide_player_popup_panels"),
|
||||
)
|
||||
|
||||
engagementPanelControllerMethod.addInstructionsWithLabels(
|
||||
getEngagementPanelControllerMethodMatch().method.addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disablePlayerPopupPanels()Z
|
||||
|
|
@ -30,10 +30,12 @@ val hideRelatedVideoOverlayPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,10 +30,12 @@ val disableRollingNumberAnimationsPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,21 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
|||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
|
||||
internal val BytecodePatchContext.componentContextParserMethod by gettingFirstImmutableMethodDeclaratively {
|
||||
returnType("L")
|
||||
instructions(
|
||||
"Failed to parse Element proto."(),
|
||||
"Cannot read theme key from model."()
|
||||
)
|
||||
}
|
||||
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getTreeNodeResultListMethod() = firstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
|
||||
returnType("Ljava/util/List;")
|
||||
instructions(allOf(Opcode.INVOKE_STATIC(), method("nCopies")))
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.shortsBottomBarContainerMethodMatch by composingFirstMethod {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("V")
|
||||
|
|
@ -19,9 +34,6 @@ internal val BytecodePatchContext.shortsBottomBarContainerMethodMatch by composi
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 19.41 to 20.44.
|
||||
*/
|
||||
|
||||
context(_: BytecodePatchContext)
|
||||
internal fun ClassDef.getRenderBottomNavigationBarMethodMatch() = firstMethodDeclaratively {
|
||||
|
|
@ -34,10 +46,10 @@ internal fun ClassDef.getRenderBottomNavigationBarMethodMatch() = firstMethodDec
|
|||
after(Opcode.IF_EQZ()),
|
||||
after(Opcode.INVOKE_INTERFACE()),
|
||||
Opcode.MONITOR_EXIT(),
|
||||
after(Opcode.RETURN_VOID()),
|
||||
after(Opcode.MOVE_EXCEPTION()),
|
||||
after(Opcode.MONITOR_EXIT()),
|
||||
after(Opcode.THROW()),
|
||||
Opcode.RETURN_VOID(),
|
||||
Opcode.MOVE_EXCEPTION(),
|
||||
Opcode.MONITOR_EXIT(),
|
||||
Opcode.THROW()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -59,8 +71,7 @@ internal val BytecodePatchContext.legacyRenderBottomNavigationBarLegacyParentMet
|
|||
}
|
||||
|
||||
/**
|
||||
* Identical to [legacyRenderBottomNavigationBarLegacyParentMethod]
|
||||
* except this has an extra parameter.
|
||||
* 19.41 - 20.44
|
||||
*/
|
||||
internal val BytecodePatchContext.renderBottomNavigationBarLegacy1941ParentMethod by gettingFirstImmutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
|
|
@ -78,6 +89,9 @@ internal val BytecodePatchContext.renderBottomNavigationBarLegacy1941ParentMetho
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 20.45+
|
||||
*/
|
||||
internal val BytecodePatchContext.renderBottomNavigationBarParentMethod by gettingFirstImmutableMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returnType("[Ljava/lang/Class;")
|
||||
|
|
|
|||
|
|
@ -12,11 +12,13 @@ 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.music.shared.conversionContextToStringMethod
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
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.shared.misc.litho.filter.addLithoFilter
|
||||
import app.revanced.patches.youtube.misc.engagement.engagementPanelHookPatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||
|
|
@ -25,8 +27,6 @@ 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.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import java.util.logging.Logger
|
||||
|
||||
internal val hideShortsAppShortcutOption = booleanOption(
|
||||
name = "Hide Shorts app shortcut",
|
||||
|
|
@ -54,82 +54,65 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
|
|||
|
||||
addResources("youtube", "layout.hide.shorts.hideShortsComponentsResourcePatch")
|
||||
|
||||
val preferences = mutableSetOf(
|
||||
// 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"),
|
||||
)
|
||||
|
||||
if (is_20_22_or_greater) {
|
||||
// FIXME: The buffer is very different for 20.22+ and these current cannot be hidden.
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"\n!!!" +
|
||||
"\n!!! Shorts action buttons currently cannot be set hidden when patching 20.22+" +
|
||||
"\n!!! Patch 20.21.37 or lower if you want to hide Shorts action buttons" +
|
||||
"\n!!!",
|
||||
)
|
||||
} else {
|
||||
preferences.addAll(
|
||||
listOf(
|
||||
SwitchPreference("revanced_hide_shorts_comments_button"),
|
||||
SwitchPreference("revanced_hide_shorts_share_button"),
|
||||
SwitchPreference("revanced_hide_shorts_remix_button"),
|
||||
SwitchPreference("revanced_hide_shorts_sound_button"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
preferences.addAll(
|
||||
listOf(
|
||||
// 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_preview_comment"),
|
||||
SwitchPreference("revanced_hide_shorts_save_sound_button"),
|
||||
SwitchPreference("revanced_hide_shorts_use_sound_button"),
|
||||
SwitchPreference("revanced_hide_shorts_use_template_button"),
|
||||
SwitchPreference("revanced_hide_shorts_upcoming_button"),
|
||||
SwitchPreference("revanced_hide_shorts_effect_button"),
|
||||
SwitchPreference("revanced_hide_shorts_green_screen_button"),
|
||||
SwitchPreference("revanced_hide_shorts_hashtag_button"),
|
||||
SwitchPreference("revanced_hide_shorts_live_preview"),
|
||||
SwitchPreference("revanced_hide_shorts_new_posts_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_auto_dubbed_label"),
|
||||
SwitchPreference("revanced_hide_shorts_location_label"),
|
||||
SwitchPreference("revanced_hide_shorts_channel_bar"),
|
||||
SwitchPreference("revanced_hide_shorts_info_panel"),
|
||||
SwitchPreference("revanced_hide_shorts_full_video_link_label"),
|
||||
SwitchPreference("revanced_hide_shorts_video_title"),
|
||||
SwitchPreference("revanced_hide_shorts_sound_metadata_label"),
|
||||
SwitchPreference("revanced_hide_shorts_navigation_bar"),
|
||||
),
|
||||
)
|
||||
|
||||
PreferenceScreen.SHORTS.addPreferences(
|
||||
SwitchPreference("revanced_hide_shorts_channel"),
|
||||
SwitchPreference("revanced_hide_shorts_home"),
|
||||
SwitchPreference("revanced_hide_shorts_search"),
|
||||
SwitchPreference("revanced_hide_shorts_subscriptions"),
|
||||
SwitchPreference("revanced_hide_shorts_video_description"),
|
||||
SwitchPreference("revanced_hide_shorts_history"),
|
||||
|
||||
PreferenceScreenPreference(
|
||||
key = "revanced_shorts_player_screen",
|
||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||
preferences = preferences,
|
||||
),
|
||||
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.
|
||||
// Like fountain may no longer be used by YT anymore.
|
||||
//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_preview_comment"),
|
||||
SwitchPreference("revanced_hide_shorts_save_sound_button"),
|
||||
SwitchPreference("revanced_hide_shorts_use_sound_button"),
|
||||
SwitchPreference("revanced_hide_shorts_use_template_button"),
|
||||
SwitchPreference("revanced_hide_shorts_upcoming_button"),
|
||||
SwitchPreference("revanced_hide_shorts_effect_button"),
|
||||
SwitchPreference("revanced_hide_shorts_green_screen_button"),
|
||||
SwitchPreference("revanced_hide_shorts_hashtag_button"),
|
||||
SwitchPreference("revanced_hide_shorts_live_preview"),
|
||||
SwitchPreference("revanced_hide_shorts_new_posts_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_ai_button"),
|
||||
SwitchPreference("revanced_hide_shorts_auto_dubbed_label"),
|
||||
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.
|
||||
|
|
@ -157,28 +140,33 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
|
|||
}
|
||||
}
|
||||
|
||||
private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/litho/ShortsFilter;"
|
||||
private const val FILTER_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/litho/ShortsFilter;"
|
||||
|
||||
@Suppress("unused")
|
||||
val hideShortsComponentsPatch = bytecodePatch(
|
||||
name = "Hide Shorts components",
|
||||
description = "Adds options to hide components related to Shorts.",
|
||||
description = "Adds options to hide components related to Shorts. " +
|
||||
"Patching version 20.21.37 or lower can hide more Shorts player button types."
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
lithoFilterPatch,
|
||||
hideShortsComponentsResourcePatch,
|
||||
resourceMappingPatch,
|
||||
engagementPanelHookPatch,
|
||||
navigationBarHookPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
// 20.22+ does not yet support hiding Shorts action buttons.
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -188,26 +176,66 @@ val hideShortsComponentsPatch = bytecodePatch(
|
|||
apply {
|
||||
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
|
||||
|
||||
// region Hide sound button.
|
||||
|
||||
val id = ResourceType.DIMEN["reel_player_right_pivot_v2_size"]
|
||||
|
||||
forEachInstructionAsSequence({ _, method, instruction, index ->
|
||||
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence null
|
||||
if (!is_21_05_or_greater) {
|
||||
forEachInstructionAsSequence({ _, method, instruction, index ->
|
||||
if (instruction.wideLiteral != id) return@forEachInstructionAsSequence null
|
||||
|
||||
val targetIndex = method.indexOfFirstInstructionOrThrow(index) {
|
||||
methodReference?.name == "getDimensionPixelSize"
|
||||
} + 1
|
||||
val targetIndex = method.indexOfFirstInstructionOrThrow(index) {
|
||||
methodReference?.name == "getDimensionPixelSize"
|
||||
} + 1
|
||||
|
||||
val sizeRegister = method.getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
val sizeRegister = method.getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
|
||||
return@forEachInstructionAsSequence targetIndex to sizeRegister
|
||||
}) { method, (targetIndex, sizeRegister) ->
|
||||
firstMethod(method).addInstructions(
|
||||
targetIndex + 1,
|
||||
"""
|
||||
return@forEachInstructionAsSequence targetIndex to sizeRegister
|
||||
}) { method, (targetIndex, sizeRegister) ->
|
||||
firstMethod(method).addInstructions(
|
||||
targetIndex + 1,
|
||||
"""
|
||||
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
|
||||
move-result v$sizeRegister
|
||||
""",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Hide action buttons.
|
||||
|
||||
if (is_20_22_or_greater) {
|
||||
componentContextParserMethod.immutableClassDef.getTreeNodeResultListMethod().apply {
|
||||
val conversionContextPathBuilderField =
|
||||
conversionContextToStringMethod.immutableClassDef
|
||||
.fields.single { field -> field.type == "Ljava/lang/StringBuilder;" }
|
||||
|
||||
val insertIndex = implementation!!.instructions.lastIndex
|
||||
val listRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
|
||||
|
||||
val registerProvider = getFreeRegisterProvider(insertIndex, 2)
|
||||
val freeRegister = registerProvider.getFreeRegister()
|
||||
val pathRegister = registerProvider.getFreeRegister()
|
||||
|
||||
addInstructionsAtControlFlowLabel(
|
||||
insertIndex,
|
||||
"""
|
||||
move-object/from16 v$freeRegister, p2
|
||||
|
||||
# In YouTube 20.41 field is the abstract superclass.
|
||||
# Verify it's the expected subclass just in case.
|
||||
instance-of v$pathRegister, v$freeRegister, ${conversionContextToStringMethod.immutableClassDef}
|
||||
if-eqz v$pathRegister, :ignore
|
||||
|
||||
iget-object v$pathRegister, v$freeRegister, $conversionContextPathBuilderField
|
||||
invoke-static { v$pathRegister, v$listRegister }, ${FILTER_CLASS_DESCRIPTOR}->hideActionButtons(Ljava/lang/StringBuilder;Ljava/util/List;)V
|
||||
:ignore
|
||||
nop
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
|
@ -262,7 +290,7 @@ val hideShortsComponentsPatch = bytecodePatch(
|
|||
// region Disable experimental Shorts flags.
|
||||
|
||||
// Flags might be present in earlier targets, but they are not found in 19.47.53.
|
||||
// If these flags are forced on, the experimental layout is still not used and
|
||||
// If these flags are forced on, the experimental layout is still not used, and
|
||||
// it appears the features requires additional server side data to fully use.
|
||||
if (is_20_07_or_greater) {
|
||||
// Experimental Shorts player uses Android native buttons and not Litho,
|
||||
|
|
@ -274,7 +302,7 @@ val hideShortsComponentsPatch = bytecodePatch(
|
|||
shortsExperimentalPlayerFeatureFlagMethod.returnLate(false)
|
||||
|
||||
// Experimental UI renderer must also be disabled since it requires the
|
||||
// experimental Shorts player. If this is enabled but Shorts player
|
||||
// experimental Shorts player. If this is enabled but Shorts player
|
||||
// is disabled then the app crashes when the Shorts player is opened.
|
||||
renderNextUIFeatureFlagMethod.returnLate(false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ 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/DisableSignInToTvPopupPatch;"
|
||||
"Lapp/revanced/extension/youtube/patches/DisableSignInToTVPopupPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val disableSignInToTVPopupPatch = bytecodePatch(
|
||||
|
|
@ -27,24 +27,26 @@ val disableSignInToTVPopupPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
apply {
|
||||
addResources("youtube", "layout.hide.signintotv.disableSignInToTvPopupPatch")
|
||||
addResources("youtube", "layout.hide.signintotv.disableSignInToTVPopupPatch")
|
||||
|
||||
PreferenceScreen.MISC.addPreferences(
|
||||
SwitchPreference("revanced_disable_signin_to_tv_popup"),
|
||||
SwitchPreference("revanced_disable_sign_in_to_tv_popup"),
|
||||
)
|
||||
|
||||
signInToTvPopupMethod.addInstructionsWithLabels(
|
||||
signInToTVPopupMethod.addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableSignInToTvPopup()Z
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->disableSignInToTVPopup()Z
|
||||
move-result v0
|
||||
if-eqz v0, :allow_sign_in_popup
|
||||
const/4 v0, 0x0
|
||||
|
|
@ -7,7 +7,7 @@ import app.revanced.patcher.patch.BytecodePatchContext
|
|||
import app.revanced.patcher.returnType
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceType
|
||||
|
||||
internal val BytecodePatchContext.signInToTvPopupMethod by gettingFirstMethodDeclaratively {
|
||||
internal val BytecodePatchContext.signInToTVPopupMethod by gettingFirstMethodDeclaratively {
|
||||
returnType("Z")
|
||||
parameterTypes("Ljava/lang/String;", "Z", "L")
|
||||
instructions(ResourceType.STRING("mdx_seamless_tv_sign_in_drawer_fragment_title"))
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ 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/HideTimestampPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/HideTimestampPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val hideTimestampPatch = bytecodePatch(
|
||||
|
|
@ -24,10 +25,12 @@ val hideTimestampPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ import com.android.tools.smali.dexlib2.iface.ClassDef
|
|||
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
|
||||
|
||||
// In later targets this feature flag does nothing and is dead code.
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L
|
||||
internal const val MINIPLAYER_MODERN_TYPE_1_FEATURE_KEY = 45623000L
|
||||
internal const val MINIPLAYER_MODERN_TYPE_2_FEATURE_KEY = 45623273L
|
||||
internal const val MINIPLAYER_MODERN_TYPE_3_FEATURE_KEY = 45623076L
|
||||
internal const val MINIPLAYER_MODERN_TYPE_4_FEATURE_KEY = 45674402L
|
||||
internal const val MINIPLAYER_DOUBLE_TAP_FEATURE_KEY = 45628823L
|
||||
internal const val MINIPLAYER_DRAG_DROP_FEATURE_KEY = 45628752L
|
||||
internal const val MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY = 45658112L
|
||||
|
|
@ -21,11 +23,13 @@ internal const val MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY = 45652224L
|
|||
internal const val MINIPLAYER_INITIAL_SIZE_FEATURE_KEY = 45640023L
|
||||
internal const val MINIPLAYER_DISABLED_FEATURE_KEY = 45657015L
|
||||
internal const val MINIPLAYER_ANIMATED_EXPAND_FEATURE_KEY = 45644360L
|
||||
// In later targets this feature flag does nothing and is dead code.
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L
|
||||
|
||||
internal val BytecodePatchContext.miniplayerModernConstructorMethod by gettingFirstMethodDeclaratively {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
45623000L(), // Magic number found in the constructor.
|
||||
MINIPLAYER_MODERN_TYPE_1_FEATURE_KEY(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -148,8 +152,14 @@ internal val BytecodePatchContext.miniplayerMinimumSizeMethodMatch by composingF
|
|||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
instructions(
|
||||
ResourceType.DIMEN("miniplayer_max_size"),
|
||||
192L(), // Default miniplayer width constant.
|
||||
128L(), // Default miniplayer height constant.
|
||||
anyOf( // Default miniplayer width constant.
|
||||
192L(),
|
||||
192.0f.toRawBits().toLong()(), // 21.03+
|
||||
),
|
||||
anyOf( // Default miniplayer height constant.
|
||||
128L(),
|
||||
128.0f.toRawBits().toLong()(), // 21.03+
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,13 +53,15 @@ private val miniplayerResourcePatch = resourcePatch {
|
|||
|
||||
// Only required for exactly 19.16
|
||||
if (!is_19_17_or_greater) {
|
||||
ytOutlinePictureInPictureWhite24 = ResourceType.DRAWABLE["yt_outline_picture_in_picture_white_24"]
|
||||
ytOutlinePictureInPictureWhite24 =
|
||||
ResourceType.DRAWABLE["yt_outline_picture_in_picture_white_24"]
|
||||
ytOutlineXWhite24 = ResourceType.DRAWABLE["yt_outline_x_white_24"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/MiniplayerPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/MiniplayerPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val miniplayerPatch = bytecodePatch(
|
||||
|
|
@ -75,10 +77,12 @@ val miniplayerPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -143,7 +147,10 @@ val miniplayerPatch = bytecodePatch(
|
|||
}
|
||||
|
||||
if (is_19_26_or_greater) {
|
||||
preferences += TextPreference("revanced_miniplayer_width_dip", inputType = InputType.NUMBER)
|
||||
preferences += TextPreference(
|
||||
"revanced_miniplayer_width_dip",
|
||||
inputType = InputType.NUMBER
|
||||
)
|
||||
}
|
||||
|
||||
preferences += TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
|
||||
|
|
@ -167,7 +174,8 @@ val miniplayerPatch = bytecodePatch(
|
|||
)
|
||||
}
|
||||
|
||||
fun Method.findReturnIndicesReversed() = findInstructionIndicesReversedOrThrow(Opcode.RETURN)
|
||||
fun Method.findReturnIndicesReversed() =
|
||||
findInstructionIndicesReversedOrThrow(Opcode.RETURN)
|
||||
|
||||
/**
|
||||
* Adds an override to force legacy tablet miniplayer to be used or not used.
|
||||
|
|
@ -227,11 +235,12 @@ val miniplayerPatch = bytecodePatch(
|
|||
// Parts of the YT code is removed in 20.37+ and the legacy player no longer works.
|
||||
|
||||
if (!is_20_37_or_greater) {
|
||||
miniplayerDimensionsCalculatorParentMethod.immutableClassDef.getMiniplayerOverrideNoContextMethod().apply {
|
||||
findReturnIndicesReversed().forEach { index ->
|
||||
insertLegacyTabletMiniplayerOverride(index)
|
||||
miniplayerDimensionsCalculatorParentMethod.immutableClassDef.getMiniplayerOverrideNoContextMethod()
|
||||
.apply {
|
||||
findReturnIndicesReversed().forEach { index ->
|
||||
insertLegacyTabletMiniplayerOverride(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
|
|
@ -265,7 +274,11 @@ val miniplayerPatch = bytecodePatch(
|
|||
|
||||
insertModernMiniplayerTypeOverride(iPutIndex)
|
||||
} else {
|
||||
findReturnIndicesReversed().forEach { index -> insertModernMiniplayerOverride(index) }
|
||||
findReturnIndicesReversed().forEach { index ->
|
||||
insertModernMiniplayerOverride(
|
||||
index
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -355,17 +368,23 @@ val miniplayerPatch = bytecodePatch(
|
|||
// YT fixed this mistake in 19.17.
|
||||
// Fix this, by swapping the drawable resource values with each other.
|
||||
if (!is_19_17_or_greater) {
|
||||
miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernExpandCloseDrawablesMethod().apply {
|
||||
listOf(
|
||||
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
|
||||
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
|
||||
).forEach { (originalResource, replacementResource) ->
|
||||
val imageResourceIndex = indexOfFirstLiteralInstructionOrThrow(originalResource)
|
||||
val register = getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA
|
||||
miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernExpandCloseDrawablesMethod()
|
||||
.apply {
|
||||
listOf(
|
||||
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
|
||||
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
|
||||
).forEach { (originalResource, replacementResource) ->
|
||||
val imageResourceIndex =
|
||||
indexOfFirstLiteralInstructionOrThrow(originalResource)
|
||||
val register =
|
||||
getInstruction<OneRegisterInstruction>(imageResourceIndex).registerA
|
||||
|
||||
replaceInstruction(imageResourceIndex, "const v$register, $replacementResource")
|
||||
replaceInstruction(
|
||||
imageResourceIndex,
|
||||
"const v$register, $replacementResource"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
|
@ -377,7 +396,7 @@ val miniplayerPatch = bytecodePatch(
|
|||
findInstructionIndicesReversedOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
opcode == Opcode.INVOKE_INTERFACE &&
|
||||
reference?.returnType == "Z" && reference.parameterTypes.isEmpty()
|
||||
reference?.returnType == "Z" && reference.parameterTypes.isEmpty()
|
||||
}.forEach { index ->
|
||||
val register = getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
|
||||
|
|
@ -417,11 +436,12 @@ val miniplayerPatch = bytecodePatch(
|
|||
}
|
||||
}
|
||||
|
||||
miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernAddViewListenerMethod().addInstruction(
|
||||
0,
|
||||
"invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"hideMiniplayerSubTexts(Landroid/view/View;)V",
|
||||
)
|
||||
miniplayerModernViewParentMethod.immutableClassDef.getMiniplayerModernAddViewListenerMethod()
|
||||
.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,
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
package app.revanced.patches.youtube.layout.panels.popup
|
||||
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
|
||||
internal val BytecodePatchContext.engagementPanelControllerMethod by gettingFirstMethodDeclaratively(
|
||||
"EngagementPanelController: cannot show EngagementPanel before EngagementPanelController.init() has been called.",
|
||||
"[EngagementPanel] Cannot show EngagementPanel before EngagementPanelController.init() has been called.",
|
||||
) {
|
||||
returnType("L")
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||
|
||||
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
|
||||
|
|
@ -9,10 +10,9 @@ import app.revanced.patches.youtube.misc.playercontrols.playerControlsPatch
|
|||
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.video.information.videoEndMethod
|
||||
import app.revanced.patches.youtube.video.information.playerStatusMethod
|
||||
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
@Suppress("unused")
|
||||
|
|
@ -22,10 +22,12 @@ val exitFullscreenPatch = bytecodePatch(
|
|||
) {
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -51,13 +53,15 @@ val exitFullscreenPatch = bytecodePatch(
|
|||
ListPreference("revanced_exit_fullscreen"),
|
||||
)
|
||||
|
||||
videoEndMethod.apply {
|
||||
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.RETURN_VOID)
|
||||
playerStatusMethod.apply {
|
||||
// +1 to ensure inserted after the loop logic added by the "Loop video" patch.
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.SGET_OBJECT) + 1
|
||||
|
||||
addInstructionsAtControlFlowLabel(
|
||||
addInstruction(
|
||||
insertIndex,
|
||||
"invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->endOfVideoReached()V",
|
||||
)
|
||||
"invoke-static/range { p1 .. p1 }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->endOfVideoReached(Ljava/lang/Enum;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ internal val openVideosFullscreenHookPatch = bytecodePatch {
|
|||
// Remove A/B feature call that forces what this patch already does.
|
||||
// Cannot use the A/B flag to accomplish the same goal because 19.50+
|
||||
// Shorts fullscreen regular player does not use fullscreen
|
||||
// if the player is minimized and it must be forced using other conditional check.
|
||||
// if the player is minimized, and it must be forced using other conditional check.
|
||||
it.method.insertLiteralOverride(
|
||||
it[-1],
|
||||
false,
|
||||
|
|
|
|||
|
|
@ -21,12 +21,13 @@ val openVideosFullscreenPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"19.43.41",
|
||||
"19.47.53",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,12 @@ val customPlayerOverlayOpacityPatch = bytecodePatch(
|
|||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.43.41",
|
||||
"20.14.43",
|
||||
"20.21.37",
|
||||
"20.31.40",
|
||||
"20.26.46",
|
||||
"20.31.42",
|
||||
"20.37.48",
|
||||
"20.40.45"
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -45,12 +47,13 @@ val customPlayerOverlayOpacityPatch = bytecodePatch(
|
|||
createPlayerOverviewMethodMatch.let {
|
||||
it.method.apply {
|
||||
val viewRegisterIndex = it[-1]
|
||||
val viewRegister = getInstruction<OneRegisterInstruction>(viewRegisterIndex).registerA
|
||||
val viewRegister =
|
||||
getInstruction<OneRegisterInstruction>(viewRegisterIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
viewRegisterIndex + 1,
|
||||
"invoke-static { v$viewRegister }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->changeOpacity(Landroid/widget/ImageView;)V",
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->changeOpacity(Landroid/widget/ImageView;)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue